Skip to content

Commit 07a950a

Browse files
committed
Merge pull request #1 from purescript/docs
Documentation
2 parents 8441644 + 3612827 commit 07a950a

File tree

5 files changed

+137
-18
lines changed

5 files changed

+137
-18
lines changed

Gruntfile.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ module.exports = function(grunt) {
3434
}
3535
],
3636

37+
pscDocs: {
38+
readme: {
39+
src: "src/**/*.purs",
40+
dest: "README.md"
41+
}
42+
},
43+
3744
execute: {
3845
tests: {
3946
src: "tmp/index.js"
@@ -47,6 +54,6 @@ module.exports = function(grunt) {
4754
grunt.loadNpmTasks("grunt-purescript");
4855

4956
grunt.registerTask("test", ["pscMake:tests", "copy", "execute:tests"]);
50-
grunt.registerTask("make", ["pscMake:lib", "dotPsci"]);
57+
grunt.registerTask("make", ["pscMake:lib", "dotPsci", "pscDocs"]);
5158
grunt.registerTask("default", ["clean", "make", "test"]);
5259
};

README.md

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,68 @@
1-
# purescript-exists
1+
# Module Documentation
22

3-
Existential types as a library
3+
## Module Data.Exists
44

5-
## Building
5+
#### `Exists`
66

7+
``` purescript
8+
data Exists :: (* -> *) -> *
79
```
8-
npm install
9-
bower update
10-
grunt
10+
11+
This type constructor can be used to existentially quantify over a type of kind `*`.
12+
13+
Specifically, the type `Exists f` is isomorphic to the existential type `exists a. f a`.
14+
15+
Existential types can be encoded using universal types (`forall`) for endofunctors in more general
16+
categories. The benefit of this library is that, by using the FFI, we can create an efficient
17+
representation of the existential by simply hiding type information.
18+
19+
For example, consider the type `exists s. Tuple s (s -> Tuple s a)` which represents infinite streams
20+
of elements of type `a`.
21+
22+
This type can be constructed by creating a type constructor `StreamF` as follows:
23+
24+
```purescript
25+
data StreamF a s = StreamF s (s -> Tuple s a)
26+
```
27+
28+
We can then define the type of streams using `Exists`:
29+
30+
```purescript
31+
type Stream a = Exists (StreamF a)
32+
```
33+
34+
#### `mkExists`
35+
36+
``` purescript
37+
mkExists :: forall f a. f a -> Exists f
38+
```
39+
40+
The `mkExists` function is used to introduce a value of type `Exists f`, by providing a value of
41+
type `f a`, for some type `a` which will be hidden in the existentially-quantified type.
42+
43+
For example, to create a value of type `Stream Number`, we might use `mkExists` as follows:
44+
45+
```purescript
46+
nats :: Stream Number
47+
nats = mkExists $ StreamF 0 (\n -> Tuple (n + 1) n)
1148
```
49+
50+
#### `runExists`
51+
52+
``` purescript
53+
runExists :: forall f r. (forall a. f a -> r) -> Exists f -> r
54+
```
55+
56+
The `runExists` function is used to eliminate a value of type `Exists f`. The rank 2 type ensures
57+
that the existentially-quantified type does not escape its scope. Since the function is required
58+
to work for _any_ type `a`, it will work for the existentially-quantified type.
59+
60+
For example, we can write a function to obtain the head of a stream by using `runExists` as follows:
61+
62+
```purescript
63+
head :: forall a. Stream a -> a
64+
head = runExists head'
65+
where
66+
head' :: forall s. StreamF a s -> a
67+
head' (StreamF s f) = snd (f s)
68+
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
"grunt-contrib-copy": "~0.5.0",
1212
"grunt-contrib-clean": "~0.5.0",
1313
"grunt-execute": "~0.1.5",
14-
"grunt-purescript": "~0.5.0"
14+
"grunt-purescript": "~0.6.0"
1515
}
1616
}

src/Data/Exists.purs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,56 @@
11
module Data.Exists where
22

3+
-- | This type constructor can be used to existentially quantify over a type of kind `*`.
4+
-- |
5+
-- | Specifically, the type `Exists f` is isomorphic to the existential type `exists a. f a`.
6+
-- |
7+
-- | Existential types can be encoded using universal types (`forall`) for endofunctors in more general
8+
-- | categories. The benefit of this library is that, by using the FFI, we can create an efficient
9+
-- | representation of the existential by simply hiding type information.
10+
-- |
11+
-- | For example, consider the type `exists s. Tuple s (s -> Tuple s a)` which represents infinite streams
12+
-- | of elements of type `a`.
13+
-- |
14+
-- | This type can be constructed by creating a type constructor `StreamF` as follows:
15+
-- |
16+
-- | ```purescript
17+
-- | data StreamF a s = StreamF s (s -> Tuple s a)
18+
-- | ```
19+
-- |
20+
-- | We can then define the type of streams using `Exists`:
21+
-- |
22+
-- | ```purescript
23+
-- | type Stream a = Exists (StreamF a)
24+
-- | ```
325
foreign import data Exists :: (* -> *) -> *
4-
26+
27+
-- | The `mkExists` function is used to introduce a value of type `Exists f`, by providing a value of
28+
-- | type `f a`, for some type `a` which will be hidden in the existentially-quantified type.
29+
-- |
30+
-- | For example, to create a value of type `Stream Number`, we might use `mkExists` as follows:
31+
-- |
32+
-- | ```purescript
33+
-- | nats :: Stream Number
34+
-- | nats = mkExists $ StreamF 0 (\n -> Tuple (n + 1) n)
35+
-- | ```
536
foreign import mkExists
637
"function mkExists(fa) {\
738
\ return fa;\
839
\}" :: forall f a. f a -> Exists f
9-
40+
41+
-- | The `runExists` function is used to eliminate a value of type `Exists f`. The rank 2 type ensures
42+
-- | that the existentially-quantified type does not escape its scope. Since the function is required
43+
-- | to work for _any_ type `a`, it will work for the existentially-quantified type.
44+
-- |
45+
-- | For example, we can write a function to obtain the head of a stream by using `runExists` as follows:
46+
-- |
47+
-- | ```purescript
48+
-- | head :: forall a. Stream a -> a
49+
-- | head = runExists head'
50+
-- | where
51+
-- | head' :: forall s. StreamF a s -> a
52+
-- | head' (StreamF s f) = snd (f s)
53+
-- | ```
1054
foreign import runExists
1155
"function runExists(f) {\
1256
\ return function(fa) {\

tests/Tests.purs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,22 @@ module Main where
33
import Debug.Trace
44
import Data.Exists
55

6-
data Example a = Example a (a -> String)
7-
8-
runExample :: Exists Example -> String
9-
runExample = runExists (\(Example a f) -> f a)
10-
11-
test = runExample (mkExists (Example "Done" show))
12-
13-
main = trace test
6+
data Tuple a b = Tuple a b
7+
8+
snd :: forall a b. Tuple a b -> b
9+
snd (Tuple _ b) = b
10+
11+
data StreamF a s = StreamF s (s -> Tuple s a)
12+
13+
type Stream a = Exists (StreamF a)
14+
15+
nats :: Stream Number
16+
nats = mkExists $ StreamF 0 (\n -> Tuple (n + 1) n)
17+
18+
head :: forall a. Stream a -> a
19+
head = runExists head'
20+
where
21+
head' :: forall s. StreamF a s -> a
22+
head' (StreamF s f) = snd (f s)
23+
24+
main = print $ head nats

0 commit comments

Comments
 (0)