Skip to content

Commit 52765d6

Browse files
MarekSuchanekroper79
authored andcommitted
Tutorial 12: GHC Extensions and Dependent Types (#19)
* Outline for tutorial * some intros * GHC extensions done * LiquidHaskell and links * Grammar fix * corrections
1 parent cac38d5 commit 52765d6

File tree

1 file changed

+254
-0
lines changed

1 file changed

+254
-0
lines changed

tutorials/12_exts-deptypes.md

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
# GHC Extensions and Dependent Types
2+
3+
This lecture is a sort of "gourmet" -- the concepts presented here are not used every day nor are common in production, nor are they topics to speak over coffee with mediocre programmers. Rather they represent cutting-edge topics that may potentially write the future of programming. Enjoy ;-)
4+
5+
## GHC Language Extensions
6+
7+
Language extensions are used to enable language features in Haskell that may seem useful in certain cases. They can be used to loosen restrictions in the type system or add completely new language constructs to Haskell. As you already know, they can be enabled using the `{-# LANGUAGE <ext> #-}` pragma or using flags `-X<ext>`. You should always consider using those extensions over normal Haskell, because it may potentially bring some risks. It is advisable to read some discussions on forums.
8+
9+
### TypeFamilies
10+
11+
This extension allows use and definition of indexed type and data families to facilitate type-level programming. Indexed type families, or type families for short, are type constructors that represent sets of types. Set members are denoted by supplying the type family constructor with type parameters, which are called type indices. The difference between vanilla parametrized type constructors and family constructors is much like between parametrically polymorphic functions and (ad-hoc polymorphic) methods of type classes. Parametric polymorphic functions behave the same in all type instances, whereas class methods can change their behaviour in dependence on the class type parameters. Similarly, vanilla type constructors imply the same data representation for all type instances, but family constructors can have varying representation types for varying type indices. (see [GHC docs](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#type-families))
12+
13+
```haskell
14+
{-# LANGUAGE TypeFamilies #-}
15+
16+
-- Declare a list-like data family
17+
data family XList a
18+
19+
-- Declare a list-like instance for Char
20+
data instance XList Char = XCons !Char !(XList Char) | XNil
21+
22+
-- Declare a number-like instance for ()
23+
data instance XList () = XListUnit !Int
24+
25+
class XLength a where
26+
xlength :: XList a -> Int
27+
28+
instance XLength Char where
29+
xlength XNil = 0
30+
xlength (XCons _ r) = 1 + xlength r
31+
32+
instance XLength () where
33+
xlength (XListUnit _) = 1
34+
```
35+
36+
### GADTs
37+
38+
[Generalized algebraic data type](https://en.wikipedia.org/wiki/Generalized_algebraic_data_type) are a generalization of the algebraic data types that you are familiar with. Basically, they allow you to explicitly write down the types of the constructors. In this chapter, you'll learn why this is useful and how to declare your own. GADTs are mainly used to implement domain-specific languages, and so this section will introduce them with a corresponding example.
39+
40+
```haskell
41+
{-# LANGUAGE GADTs #-}
42+
43+
data Expr a where
44+
I :: Int -> Expr Int
45+
B :: Bool -> Expr Bool
46+
Add :: Expr Int -> Expr Int -> Expr Int
47+
Mul :: Expr Int -> Expr Int -> Expr Int
48+
Eq :: Expr Int -> Expr Int -> Expr Bool
49+
And :: Expr Bool -> Expr Bool -> Expr Bool
50+
51+
eval :: Expr a -> a
52+
eval (I n) = n -- return Int
53+
eval (B b) = b -- returns bool
54+
eval (Add e1 e2) = eval e1 + eval e2 -- return Int
55+
eval (Mul e1 e2) = eval e1 * eval e2 -- return Int
56+
eval (Eq e1 e2) = eval e1 == eval e2 -- returns bool
57+
eval (And e1 e2) = eval e1 && eval e2 -- returns bool
58+
```
59+
60+
Complete example: [Haskell - GADT](https://en.wikibooks.org/wiki/Haskell/GADT)
61+
62+
### QuasiQuotes
63+
64+
[Quasiquoting](https://wiki.haskell.org/Quasiquotation) allows programmers to use custom, domain-specific syntax to construct fragments of their program. Along with Haskell's existing support for domain specific languages, you are now free to use new syntactic forms for your EDSLs. We've already seen it used in Yesod or Debug. Another simple use is with [Text.RawString.QQ](http://hackage.haskell.org/package/raw-strings-qq/docs/Text-RawString-QQ.html) to allow multiline strings:
65+
66+
```haskell
67+
{-# LANGUAGE QuasiQuotes #-}
68+
import Text.RawString.QQ
69+
70+
multiline :: String
71+
multiline = [r|<HTML>
72+
<HEAD>
73+
<TITLE>Auto-generated html formated source</TITLE>
74+
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
75+
</HEAD>
76+
<BODY LINK="800080" BGCOLOR="#ffffff">
77+
<P> </P>
78+
<PRE>|]
79+
```
80+
81+
You can, of course, write your own DSL or simplify the syntax for yourself. All you have to do is implement your [QuasiQuoter](http://hackage.haskell.org/package/template-haskell/docs/Language-Haskell-TH-Quote.html#t:QuasiQuoter) (part of Template Haskell). For example, you can create a simple string-string map with semicolon and newlines:
82+
83+
```haskell
84+
{-# LANGUAGE TemplateHaskell #-}
85+
module MapBuilder (mapBuilder) where
86+
87+
import Language.Haskell.TH
88+
import Language.Haskell.TH.Quote
89+
90+
-- | Map Builder quasiquoter
91+
mapBuilder = QuasiQuoter
92+
{ quoteExp = mapBuilderParser -- expressions
93+
, quotePat = undefined -- patterns
94+
, quoteType = undefined -- types
95+
, quoteDec = undefined -- declarations
96+
}
97+
98+
-- | Split string to two parts by given char
99+
splitByFirst :: String -> Char -> (String, String)
100+
splitByFirst str sep = splitByFirst' "" str
101+
where
102+
splitByFirst' a [] = (a, "")
103+
splitByFirst' a (x:xs)
104+
| x == sep = (a, xs)
105+
| otherwise = splitByFirst' (a++[x]) xs
106+
107+
-- | Trim spaces and tabs from left of the string
108+
trimLeft :: String -> String
109+
trimLeft "" = ""
110+
trimLeft (x:xs)
111+
| x `elem` [' ', '\t'] = trimLeft xs
112+
| otherwise = x : trimLeft xs
113+
114+
-- | Parse [(String, String)] map from String
115+
mapBuilderParser :: String -> Q Exp
116+
mapBuilderParser = return . ListE . map parseTuples . filter (/="") . map trimLeft . lines
117+
where
118+
parseTuples :: String -> Exp
119+
parseTuples xs = TupE [LitE . StringL $ key, LitE . StringL $ val]
120+
where
121+
parts = splitByFirst xs ':'
122+
key = fst parts
123+
val = snd parts
124+
```
125+
126+
Then you can simply import defined quasiquoter and use it:
127+
128+
```haskell
129+
{-# LANGUAGE QuasiQuotes #-}
130+
131+
import MapBuilder
132+
133+
mymap1 :: [(String, String)]
134+
mymap1 = [mapBuilder|
135+
a:10
136+
b:22
137+
c:hello
138+
it:has:no:problem
139+
|]
140+
141+
mymap2 :: [(String, Int)]
142+
mymap2 = map strstr2strint [mapBuilder|
143+
suchama4:1210
144+
perglr:1535
145+
|]
146+
147+
strstr2strint :: (String, String) -> (String, Int)
148+
strstr2strint (x, y) = (x, read y)
149+
```
150+
151+
Beautiful, right?!
152+
153+
### Template Haskell
154+
155+
[Template Haskell](http://hackage.haskell.org/package/template-haskell) is a GHC extension to Haskell that adds compile-time metaprogramming facilities. The original design can be found here: http://research.microsoft.com/en-us/um/people/simonpj/papers/meta-haskell/. You could have seen part of it in action in the previous section about quasiquoting but it can do much more although quasiquotes are an important part of it. Great explanation is [here](https://ocharles.org.uk/blog/guest-posts/2014-12-22-template-haskell.html) and [here](https://markkarpov.com/tutorial/th.html).
156+
157+
## Dependent and Refinement Types
158+
159+
A dependent type is a type whose definition depends on a value. Such types can be for example:
160+
161+
* pair of integers where the second is greater than the first,
162+
* people with age between 18 and 65,
163+
* string in email format (matches given regex).
164+
165+
Dependent types add complexity to a type system. Deciding the equality of dependent types in a program may require computations. If arbitrary values are allowed in dependent types, then deciding type equality may involve deciding whether two arbitrary programs produce the same result; hence type checking may become undecidable.
166+
167+
### Agda
168+
169+
[Agda](http://wiki.portal.chalmers.se/agda/pmwiki.php) is a dependently typed functional programming language originally developed by Ulf Norell at Chalmers University of Technology with the implementation described in his PhD thesis. But current version, Agda 2, is a completely rewritten previous Agda from 1999.
170+
171+
Visit https://github.com/agda/agda where you find some examples as well!
172+
173+
### Idris
174+
175+
[Idris](https://www.idris-lang.org) is a general-purpose purely functional programming language with dependent types, strict or optional lazy evaluation and features such as a totality checker. Idris is highly affected by Haskell and Agda which is visible in its syntax.
176+
177+
Its features are influenced by Haskell and ML, and include:
178+
179+
* Full dependent types with dependent pattern matching
180+
* Simple foreign function interface (to C)
181+
* Compiler-supported interactive editing: the compiler helps you write code using the types
182+
where clauses, with a rule, simple case expressions, pattern matching let and lambda bindings
183+
* Dependent records with projection and update
184+
* Interfaces (similar to type classes in Haskell)
185+
* Type-driven overloading resolution
186+
* `do` notation and idiom brackets
187+
* Indentation significant syntax
188+
* Extensible syntax
189+
* Cumulative universes
190+
* Totality checking
191+
* Hugs-style interactive environment
192+
193+
On their website, you can find a documentation with [examples](https://www.idris-lang.org/example/) such as Vectors:
194+
195+
```idris
196+
infixr 5 ::
197+
198+
data Vect : Nat -> Type -> Type where
199+
Nil : Vect Z a
200+
(::) : a -> Vect k a -> Vect (S k) a
201+
202+
app : Vect n a -> Vect m a -> Vect (n + m) a
203+
app Nil ys = ys
204+
app (x :: xs) ys = x :: app xs ys
205+
```
206+
207+
### LiquidHaskell
208+
209+
LiquidHaskell is a static verifier for Haskell, based on Liquid Types. It allows annotating code with invariants that complement the invariants imposed by the types. These invariants are checked with a SMT solver. It is not about dependent types but [refinement types](https://en.wikipedia.org/wiki/Refinement_(computing)#Refinement_types) (you refine some defined type with rules, not build it dependent from the scratch).
210+
211+
Visit: https://ucsd-progsys.github.io/liquidhaskell-blog/
212+
213+
```haskell
214+
{--! run liquid with no-termination -}
215+
216+
module SimpleRefinements where
217+
import Prelude hiding ((!!), length)
218+
import Language.Haskell.Liquid.Prelude
219+
220+
221+
-- |Simple Refinement Types
222+
223+
{-@ zero :: {v:Int | v = 0} @-}
224+
zero :: Int
225+
zero = 0
226+
227+
{-@ type Even = {v:Int | v mod 2 = 0} @-}
228+
229+
{-@ zero'' :: Even @-}
230+
zero'' :: Int
231+
zero'' = 0
232+
233+
-- |Lists
234+
235+
infixr `C`
236+
data L a = N | C a (L a)
237+
238+
{-@ natList :: L Nat @-}
239+
natList :: L Int
240+
natList = 0 `C` 1 `C` 3 `C` N
241+
242+
{-@ evenList :: L Even @-}
243+
evenList :: L Int
244+
evenList = 0 `C` 2 `C` 8 `C` N
245+
```
246+
247+
## Further reading
248+
249+
* [Haskell Wiki - Language extensions](https://wiki.haskell.org/Language_extensions)
250+
* [24 Days of GHC Extensions](https://ocharles.org.uk/blog/pages/2014-12-01-24-days-of-ghc-extensions.html)
251+
* [Agda](https://github.com/agda/agda)
252+
* [Idris](https://www.idris-lang.org)
253+
* [Idris - tutorial](http://docs.idris-lang.org/en/latest/tutorial/)
254+
* [LiquidHaskell](https://ucsd-progsys.github.io/liquidhaskell-blog/)

0 commit comments

Comments
 (0)