Skip to content

Commit 41b0b77

Browse files
committed
define $.Function and $.UnaryTypeVariable
1 parent c3fa873 commit 41b0b77

File tree

3 files changed

+1376
-834
lines changed

3 files changed

+1376
-834
lines changed

README.md

+135-67
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const Integer = ...;
2323
// NonZeroInteger :: Type
2424
const NonZeroInteger = ...;
2525

26-
// env :: [Type]
26+
// env :: Array Type
2727
const env = $.env.concat([Integer, NonZeroInteger]);
2828
```
2929

@@ -165,6 +165,14 @@ $.Any :: Type
165165

166166
Type comprising every JavaScript value.
167167

168+
#### `AnyFunction`
169+
170+
```haskell
171+
$.AnyFunction :: Type
172+
```
173+
174+
Type comprising every Function value.
175+
168176
#### `Arguments`
169177

170178
```haskell
@@ -218,10 +226,15 @@ Type comprising every [`ValidNumber`](#validnumber) value except `Infinity` and
218226
#### `Function`
219227

220228
```haskell
221-
$.Function :: Type
229+
$.Function :: [Type] -> Type
222230
```
223231

224-
Type comprising every Function value.
232+
Constructor for Function types.
233+
234+
Examples:
235+
236+
- `$.Function([$.Date, $.String])` represents the `Date -> String` type; and
237+
- `$.Function([a, b, a])` represents the `(a, b) -> a` type.
225238

226239
#### `Integer`
227240

@@ -474,63 +487,6 @@ bodies of incoming POST requests against these types.
474487

475488
sanctuary-def provides several functions for defining types.
476489

477-
#### `TypeVariable`
478-
479-
Polymorphism is powerful. Not being able to define a function for all types
480-
would be very limiting indeed: one couldn't even define the identity function!
481-
482-
```haskell
483-
TypeVariable :: String -> Type
484-
```
485-
486-
Before defining a polymorphic function one must define one or more type
487-
variables:
488-
489-
```javascript
490-
const a = $.TypeVariable('a');
491-
const b = $.TypeVariable('b');
492-
493-
// id :: a -> a
494-
const id = def('id', {}, [a, a], x => x);
495-
496-
id(42);
497-
// => 42
498-
499-
id(null);
500-
// => null
501-
```
502-
503-
The same type variable may be used in multiple positions, creating a
504-
constraint:
505-
506-
```javascript
507-
// cmp :: a -> a -> Number
508-
const cmp =
509-
def('cmp', {}, [a, a, $.Number], (x, y) => x < y ? -1 : x > y ? 1 : 0);
510-
511-
cmp(42, 42);
512-
// => 0
513-
514-
cmp('a', 'z');
515-
// => -1
516-
517-
cmp('z', 'a');
518-
// => 1
519-
520-
cmp(0, '1');
521-
// ! TypeError: Type-variable constraint violation
522-
//
523-
// cmp :: a -> a -> Number
524-
// ^ ^
525-
// 1 2
526-
//
527-
// 1) 0 :: Number
528-
//
529-
// 2) "1" :: String
530-
//
531-
// Since there is no type of which all the above values are members, the type-variable constraint has been violated.
532-
```
533-
534490
#### `NullaryType`
535491

536492
`NullaryType` is used to construct types with no type variables. `$.Number` is
@@ -602,7 +558,7 @@ rem(42, 0);
602558
defined via `UnaryType`.
603559

604560
```javascript
605-
// sum :: [Number] -> Number
561+
// sum :: Array Number -> Number
606562
const sum =
607563
def('sum', {}, [$.Array($.Number), $.Number], xs => xs.reduce((x, y) => x + y, 0));
608564

@@ -629,9 +585,10 @@ To define a unary type `t a` one must provide:
629585
if (and only if) the value is a member of `t x` for some type `x`;
630586

631587
- a function which takes any value of type `t a` and returns an array
632-
of the values of type `a` contained in the `t` (exposed as `t._1`); and
588+
of the values of type `a` contained in the `t` (exposed as
589+
`t.types.$1.extractor`); and
633590

634-
- the type of `a` (exposed as `t.$1`).
591+
- the type of `a` (exposed as `t.types.$1.type`).
635592

636593
```haskell
637594
UnaryType :: String -> (Any -> Boolean) -> (t a -> [a]) -> Type -> Type
@@ -701,14 +658,16 @@ To define a binary type `t a b` one must provide:
701658
`x` and `y`;
702659

703660
- a function which takes any value of type `t a b` and returns an array
704-
of the values of type `a` contained in the `t` (exposed as `t._1`);
661+
of the values of type `a` contained in the `t` (exposed as
662+
`t.types.$1.extractor`);
705663

706664
- a function which takes any value of type `t a b` and returns an array
707-
of the values of type `b` contained in the `t` (exposed as `t._2`);
665+
of the values of type `b` contained in the `t` (exposed as
666+
`t.types.$2.extractor`);
708667

709-
- the type of `a` (exposed as `t.$1`); and
668+
- the type of `a` (exposed as `t.types.$1.type`); and
710669

711-
- the type of `b` (exposed as `t.$2`).
670+
- the type of `b` (exposed as `t.types.$2.type`).
712671

713672
```haskell
714673
BinaryType :: String -> (Any -> Boolean) -> (t a b -> [a]) -> (t a b -> [b]) -> Type -> Type -> Type
@@ -870,6 +829,115 @@ dist(0);
870829
// The value at position 1 is not a member of ‘{ x :: FiniteNumber, y :: FiniteNumber }’.
871830
```
872831

832+
#### `TypeVariable`
833+
834+
Polymorphism is powerful. Not being able to define a function for all types
835+
would be very limiting indeed: one couldn't even define the identity function!
836+
837+
```haskell
838+
TypeVariable :: String -> Type
839+
```
840+
841+
Before defining a polymorphic function one must define one or more type
842+
variables:
843+
844+
```javascript
845+
const a = $.TypeVariable('a');
846+
const b = $.TypeVariable('b');
847+
848+
// id :: a -> a
849+
const id = def('id', {}, [a, a], x => x);
850+
851+
id(42);
852+
// => 42
853+
854+
id(null);
855+
// => null
856+
```
857+
858+
The same type variable may be used in multiple positions, creating a
859+
constraint:
860+
861+
```javascript
862+
// cmp :: a -> a -> Number
863+
const cmp =
864+
def('cmp', {}, [a, a, $.Number], (x, y) => x < y ? -1 : x > y ? 1 : 0);
865+
866+
cmp(42, 42);
867+
// => 0
868+
869+
cmp('a', 'z');
870+
// => -1
871+
872+
cmp('z', 'a');
873+
// => 1
874+
875+
cmp(0, '1');
876+
// ! TypeError: Type-variable constraint violation
877+
//
878+
// cmp :: a -> a -> Number
879+
// ^ ^
880+
// 1 2
881+
//
882+
// 1) 0 :: Number
883+
//
884+
// 2) "1" :: String
885+
//
886+
// Since there is no type of which all the above values are members, the type-variable constraint has been violated.
887+
```
888+
889+
#### `UnaryTypeVariable`
890+
891+
As its name suggests, `UnaryTypeVariable` combines [`UnaryType`](#unarytype)
892+
and [`TypeVariable`](#typevariable).
893+
894+
To define a unary type variable `t a` one must provide:
895+
896+
- a name (conventionally matching `^[a-z]$`); and
897+
898+
- the type of `a` (exposed as `t.types.$1.type`).
899+
900+
```haskell
901+
UnaryTypeVariable :: String -> Type -> Type
902+
```
903+
904+
Consider the type of a generalized `map`:
905+
906+
```haskell
907+
map :: Functor f => (a -> b) -> f a -> f b
908+
```
909+
910+
`f` is a unary type variable. With two (regular) type variables, one unary
911+
type variable, and one [type class](#type-classes) it's possible to define
912+
a fully polymorphic `map` function:
913+
914+
```javascript
915+
const a = $.TypeVariable('a');
916+
const b = $.TypeVariable('b');
917+
const f = $.UnaryTypeVariable('f');
918+
919+
// Functor :: TypeClass
920+
const Functor = ...;
921+
922+
// map :: Functor f => (a -> b) -> f a -> f b
923+
const map =
924+
def('map',
925+
{f: [Functor]},
926+
[$.Function([a, b]), f(a), f(b)],
927+
(fn, functor) => functor.map(fn));
928+
```
929+
930+
Whereas a regular type variable is fully resolved (`a` might become
931+
`Array (Array String)`, for example), a unary type variable defers to
932+
its type argument, which may itself be a type variable. The type argument
933+
corresponds to the type argument of a unary type or the *second* type
934+
argument of a binary type. The second type argument of `Map k v`, for
935+
example, is `v`. One could replace `Functor => f` with `Map k` or with
936+
`Map Integer`, but not with `Map`.
937+
938+
This shallow inspection makes it possible to constrain a value's "outer"
939+
and "inner" types independently.
940+
873941
### Type classes
874942

875943
`concatS`, defined earlier, is a function which concatenates two strings.

0 commit comments

Comments
 (0)