
Type Safe Redis Queries: A Case Study of Type-Level Programming in Haskell Ting-Yan Lai∗ Tyng-Ruey Chuang Shin-Cheng Mu Institute of Information Science Institute of Information Science Institute of Information Science Academia Sinica Academia Sinica Academia Sinica Nangang, Taipei City, Taiwan Nangang, Taipei City, Taiwan Nangang, Taipei City, Taiwan [email protected] [email protected] [email protected] Abstract primitive datatype. Numbers, for example, are serialized to Redis is an in-memory data structure store, often used as a strings before being saved in the data store, and parsed back database, with a Haskell interface Hedis. Redis is dynami- to numbers to be manipulated with. While the concept is Redis cally typed — a key can be discarded and re-associated to simple, is used as an essential component in a num- a value of a different type, and a command, when fetching ber of popular, matured services, including Twitter, GitHub, a value of a type it does not expect, signals a runtime er- Weibo, StackOverflow, and Flickr. ror. We develop a domain-specific language that, by exploit- For an example, consider the following sequence of com- Redis ing Haskell type-level programming techniques including mands, entered through the interactive interface of . indexed monad, type-level literals and closed type families, The keys some-set and another-set are both assigned a keeps track of types of values in the database and statically set of strings. The two calls to command SADD respectively guarantees that type errors cannot happen for a class of Re- add three and two strings to the two sets, before SINTER dis programs. takes their intersection: redis> SADD some-set a b c CCS Concepts • Software and its engineering → Func- (integer) 3 tional languages; Domain specific languages; • Informa- redis> SADD another-set a b tion systems → Query languages; (integer) 2 Keywords Haskell, Redis, Type-Level Programming, Type redis> SINTER some-set another-set Safety, Key-Value Store 1) "a" Note from the Authors: 2) "b" This work is to be presented at the 2nd Workshop on Type-Driven Notice that the keys some-set and another-set, if not ex- Development (TyDe 2017), September 3, 2017, Oxford, UK. This pa- isting before the call to SADD, are created on site. The calls per is not included in the workshop proceedings published by the to SADD return the sizes of the resulting sets. ACM. The authors choose to place it in the public domain. Many third party libraries provide interfaces for general purpose programming languages to access Redis through 1 Introduction its TCP protocol. For Haskell, the most popular library is 1 Hedis 2 Redis Redis is an open source, in-memory data structure store . A (normal) computation returning a value of that can be used as database, cache, and message broker. A type a is represented in Haskell by Redis (Either Reply a), arXiv:1708.09158v1 [cs.PL] 30 Aug 2017 Redis data store can be thought of as a set of key-value pairs. where the type Redis is a monad, while Either Reply a in- The value can be a string, a list of strings, a set of strings, dicates that the computation either returns a value of type or a hash table of strings, etc. However, string is the only a, or fails with an error message represented by type Reply. The following program implements the previous example: ∗Ting-Yan Lai was a summer intern from the Inst. of Computer Science and Engineering, National Chiao Tung Univ., Taiwan, when part of this work program :: Redis (Either Reply [ByteString]) was developed. program = do sadd "some-set" ["a", "b"] 1hps://redis.io sadd "another-set" ["a", "b", "c"] To the extent possible under law, the authors have waived all copy- sinter ["some-set", "another-set"] . right and related or neighboring rights to this paper. For avoidance of doubt, this work is released under the CC0 Public Domain Dedication The function (hp://creativecommons.org/publicdomain/zero/1.0/). Anyone can copy, modify, distribute and perform this work, even for commercial purposes, sadd :: ByteString →[ByteString] → all without asking permission. Redis (Either Reply Integer) TyDe’17, September 3, 2017, Oxford, UK ©/ No Copyright. This work is in the public domain. 2hps://hackage.haskell.org/package/hedis TyDe’17,September3,2017,Oxford,UK Ting-YanLai,Tyng-Ruey Chuang, and Shin-Cheng Mu takes a key and a list of values as arguments, and returns a embedded language (DSEL) that exploits the rich type sys- Redis computation yielding Integer. Keys and values, being tem of Haskell to not only ensure the absence of Redis type nothing but binary strings in Redis, are represented using errors (at least in the simplified situation where there is one Haskell ByteString. client accessing the store), but also provides better documen- tations. We wish to be sure that a program calling INCR, for The Problems Most commands work only with data of example, can be type checked only if we can statically guar- certain types. In the following example, the key some-string antee that the value to be accessed is indeed an integer. We is assigned a string foo — the command SET always assigns wish to see from the type of operators such as LLEN when it a string to a key. The subsequent call to SADD, which adds a canbecalled,andallowittobeusedonlyincontextsthatare value to a set, thus raises a runtime error. safe. We may even want to explicitly declare a fresh key and redis> SET some-string foo its type, to avoid reusing an existing key by accident, and to OK prevent it from unexpectedly being used as some other type. redis> SADD some-string bar This paper discusses the techniques we used and the expe- (error) WRONGTYPE Operation against a key riences we learned from building such a language, Edis. We holding the wrong kind of value constructed an indexed monad, on top of the monad Redis, For another source of type error, the command INCR key in- which is indexed by a dictionary that maintains the set of crements the value associated with key by one. With strings currently defined keys and their types. To represent the dic- being the only primitive type, Redis parses the stored string tionary, we need to encode variable bindings with type-level to an integer and, after incrementation, stores a string back. lists and strings. And to manipulate the dictionary, we ap- If the string cannot be parsed as an integer, a runtime error plied various type-level programming techniques. To sum- is raised. marize our contributions: We point out a peculiar pattern of value creation and up- dating in Redis: the same command is used both to create a Edis key-value pair and to update them. Similar to SADD,the com- • We present , a statically typed domain-specific mand LPUSH appends a value (a string) to a list, or creates language embedded in Haskell (with extension pro- one if the key does not exist: vided by the Glasgow Haskell Compiler) and built on Hedis redis> LPUSH some-list bar . Serializable Haskell datatypes are automatically Redis (integer) 1 converted before being written to data store. Available keys and their types are kept track of in Another command LLEN returns the length of the list, and type-level dictionaries. The types of embedded com- signals an error if the key is not associated with a list: mands state clearly their preconditions and postcon- redis> LLEN some-list ditions on the available keys and types, and a program (integer) 1 is allowed to be constructed only if it is guaranteed redis> SET some-string foo not to fail with a type error, assuming that it is the OK only client accessing the store. redis> LLEN some-string • We demonstrate the use of various type-level program- (error) WRONGTYPE Operation against a key ming techniques, including data kinds, singleton types holding the wrong kind of value and proxies, closed type families, etc., to define type- Curiously, however, when applied to a key not yet created, level lists and operations that observe and manipulate Redis designers chose to let LLEN return 0: the lists. redis> LLEN nonexistent • This is (yet another) example of encoding effects and (integer) 0 constraints of programs in types, using indexed monad [1], Being a simple wrapper on top of the TCP protocol of Re- closed type-families [5] and constraint kinds [16]. dis, Hedis inherits all the behaviors. Executing the follow- ing program yields the same error, but wrapped in a Haskell In Section 2 we introduce indexed monads, to reason about constructor: Le (Error "WRONGTYPE Operation against pre and postconditions of stateful programs, and in Section 3 a key holding the wrong kind of value"). we review the basics of type-level programming in Haskell program :: Redis (Either Reply Integer) that allows us to build type-level dictionaries to keep track program = do set "some-string" "foo" of keys and types. Embeddings of Redis commands are pre- sadd "some-string" ["a"] . sented in Section 4. In Section 5 we discuss various issues re- garding design choices, limitations of this approach, as well Such a programming model is certainly very error-prone. as possible future works, before we review related work in Working within Haskell, a host language with a strong typ- Section 6. ing system, one naturally wishes to build a domain-specific Type Safe Redis eries: A Case Study of Type-Level Programming in Haskell TyDe’17, September 3, 2017, Oxford, UK 2 Indexed Monads The command PING in Redis does nothing but replying a Hedis Stateful computations are often reasoned using Hoare logic.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages10 Page
-
File Size-