Skip to content

Commit

Permalink
require environments to actually be of type ‘Array Type’
Browse files Browse the repository at this point in the history
  • Loading branch information
davidchambers committed Feb 26, 2017
1 parent 40a8cd5 commit 050777e
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 52 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ lint:
--global module \
--global require \
--global self \
--rule 'max-len: [error, {code: 79, ignorePattern: "^ *//(#|[.] //) ", ignoreUrls: true}]' \
--rule 'max-len: [error, {code: 79, ignorePattern: "^ *//(# |[.] // |[.] - <code>)", ignoreUrls: true}]' \
-- index.js
$(ESLINT) \
--env node \
Expand Down
100 changes: 58 additions & 42 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@
//. const env = $.env.concat([Integer, NonZeroInteger]);
//. ```
//.
//. Type constructors such as `List :: Type -> Type` cannot be included in
//. an environment as they're not of the correct type. One could, though,
//. use a type constructor to define a fixed number of concrete types:
//.
//. ```javascript
//. // env :: Array Type
//. const env = $.env.concat([
//. List($.Number), // :: Type
//. List($.String), // :: Type
//. List(List($.Number)), // :: Type
//. List(List($.String)), // :: Type
//. List(List(List($.Number))), // :: Type
//. List(List(List($.String))), // :: Type
//. ]);
//. ```
//.
//. Not only would this be tedious, but one could never enumerate all possible
//. types as there are infinitely many. Instead, one should use [`Unknown`][]:
//.
//. ```javascript
//. // env :: Array Type
//. const env = $.env.concat([List($.Unknown)]);
//. ```
//.
//. The next step is to define a `def` function for the environment:
//.
//. ```javascript
Expand Down Expand Up @@ -408,15 +432,6 @@
return BinaryType(name, functionUrl(name), test, _1, _2);
}

// applyParameterizedTypes :: Array Type -> Array Type
function applyParameterizedTypes(types) {
return Z.map(function(x) {
return typeof x === 'function' ?
x.apply(null, Z.map(K(Unknown), range(0, x.length))) :
x;
}, types);
}

//. ### Types
//.
//. Conceptually, a type is a set of values. One can think of a value of
Expand Down Expand Up @@ -701,9 +716,20 @@

//# Unknown :: Type
//.
//. Type used internally to represent missing type information. The type of
//. `[]`, for example, is `Array ???`. This type is exported solely for use
//. by other Sanctuary packages.
//. Type used to represent missing type information. The type of `[]`,
//. for example, is `Array ???`.
//.
//. May be used with type constructors when defining environments. Given a
//. type constructor `List :: Type -> Type`, one could use `List($.Unknown)`
//. to include an infinite number of types in an environment:
//.
//. - `List Number`
//. - `List String`
//. - `List (List Number)`
//. - `List (List String)`
//. - `List (List (List Number))`
//. - `List (List (List String))`
//. - `...`
var Unknown = new _Type(UNKNOWN, '', '', always2('???'), K(true), [], {});

//# ValidDate :: Type
Expand All @@ -726,34 +752,34 @@
//.
//. An array of [types][]:
//.
//. - [`AnyFunction`][]
//. - [`Arguments`][]
//. - [`Array`][]
//. - [`Boolean`][]
//. - [`Date`][]
//. - [`Error`][]
//. - [`Null`][]
//. - [`Number`][]
//. - [`Object`][]
//. - [`RegExp`][]
//. - [`StrMap`][]
//. - [`String`][]
//. - [`Undefined`][]
var env = applyParameterizedTypes([
//. - <code><a href="#AnyFunction">AnyFunction</a></code>
//. - <code><a href="#Arguments">Arguments</a></code>
//. - <code><a href="#Array">Array</a>(<a href="#Unknown">Unknown</a>)</code>
//. - <code><a href="#Boolean">Boolean</a></code>
//. - <code><a href="#Date">Date</a></code>
//. - <code><a href="#Error">Error</a></code>
//. - <code><a href="#Null">Null</a></code>
//. - <code><a href="#Number">Number</a></code>
//. - <code><a href="#Object">Object</a></code>
//. - <code><a href="#RegExp">RegExp</a></code>
//. - <code><a href="#StrMap">StrMap</a>(<a href="#Unknown">Unknown</a>)</code>
//. - <code><a href="#String">String</a></code>
//. - <code><a href="#Undefined">Undefined</a></code>
var env = [
AnyFunction,
Arguments,
Array_,
Array_(Unknown),
Boolean_,
Date_,
Error_,
Null,
Number_,
Object_,
RegExp_,
StrMap,
StrMap(Unknown),
String_,
Undefined
]);
];

// Type :: Type
var Type = NullaryType(
Expand Down Expand Up @@ -1204,8 +1230,7 @@
//. Using types as predicates is useful in other contexts too. One could,
//. for example, define a [record type][] for each endpoint of a REST API
//. and validate the bodies of incoming POST requests against these types.
function test(_env, t, x) {
var env = applyParameterizedTypes(_env);
function test(env, t, x) {
var typeInfo = {name: 'name', constraints: {}, types: [t]};
return satisfactoryTypes(env, typeInfo, {}, t, 0, [], [x]).isRight;
}
Expand Down Expand Up @@ -2350,8 +2375,7 @@
throw new RangeError(q(def.name) + ' cannot define a function ' +
'with arity greater than nine');
}
return curry({checkTypes: opts.checkTypes,
env: applyParameterizedTypes(opts.env)},
return curry(opts,
{name: name, constraints: constraints, types: expTypes},
{},
values,
Expand Down Expand Up @@ -2447,33 +2471,25 @@
}));

//. [FL:Semigroup]: https://github.com/fantasyland/fantasy-land#semigroup
//. [`AnyFunction`]: #AnyFunction
//. [`Arguments`]: #Arguments
//. [`Array`]: #Array
//. [`BinaryType`]: #BinaryType
//. [`Boolean`]: #Boolean
//. [`Date`]: #Date
//. [`Error`]: #Error
//. [`FiniteNumber`]: #FiniteNumber
//. [`GlobalRegExp`]: #GlobalRegExp
//. [`Integer`]: #Integer
//. [`NonGlobalRegExp`]: #NonGlobalRegExp
//. [`Null`]: #Null
//. [`Number`]: #Number
//. [`Object`]: #Object
//. [`Object.create`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
//. [`Pair`]: #Pair
//. [`RegExp`]: #RegExp
//. [`RegexFlags`]: #RegexFlags
//. [`StrMap`]: #StrMap
//. [`String`]: #String
//. [`SyntaxError`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError
//. [`TypeClass`]: https://github.com/sanctuary-js/sanctuary-type-classes#TypeClass
//. [`TypeError`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError
//. [`TypeVariable`]: #TypeVariable
//. [`UnaryType`]: #UnaryType
//. [`UnaryTypeVariable`]: #UnaryTypeVariable
//. [`Undefined`]: #Undefined
//. [`Unknown`]: #Unknown
//. [`ValidNumber`]: #ValidNumber
//. [`env`]: #env
//. [arguments]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
Expand Down
18 changes: 9 additions & 9 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ describe('def', function() {
});

it('reports type error correctly for parameterized types', function() {
var env = $.env.concat([Either, Maybe]);
var env = $.env.concat([Either($.Unknown, $.Unknown), Maybe($.Unknown)]);
var def = $.create({checkTypes: true, env: env});

// a00 :: a -> a -> a
Expand Down Expand Up @@ -1889,7 +1889,7 @@ describe('def', function() {
});

it('supports polymorphism via type variables', function() {
var env = $.env.concat([Either, Maybe, $Pair]);
var env = $.env.concat([Either($.Unknown, $.Unknown), Maybe($.Unknown), $Pair($.Unknown, $.Unknown)]);
var def = $.create({checkTypes: true, env: env});

// aa :: a -> a -> (a, a)
Expand Down Expand Up @@ -2110,7 +2110,7 @@ describe('def', function() {
});

it('supports arbitrary nesting of types', function() {
var env = $.env.concat([Either, $.Integer]);
var env = $.env.concat([Either($.Unknown, $.Unknown), $.Integer]);
var def = $.create({checkTypes: true, env: env});

// unnest :: Array (Array a) -> Array a
Expand Down Expand Up @@ -2218,7 +2218,7 @@ describe('def', function() {
});

it('does not allow heterogeneous arrays', function() {
var env = $.env.concat([Either]);
var env = $.env.concat([Either($.Unknown, $.Unknown)]);
var def = $.create({checkTypes: true, env: env});

// concat :: Array a -> Array a -> Array a
Expand Down Expand Up @@ -2326,7 +2326,7 @@ describe('def', function() {
function(pair) { return [pair[1]]; }
);

var env = $.env.concat([Either, Pair]);
var env = $.env.concat([Either($.Unknown, $.Unknown), Pair($.Unknown, $.Unknown)]);
var def = $.create({checkTypes: true, env: env});

// id :: a -> a
Expand Down Expand Up @@ -2530,7 +2530,7 @@ describe('def', function() {
});

it('supports type-class constraints', function() {
var env = $.env.concat([Integer, Maybe, Either]);
var env = $.env.concat([Integer, Maybe($.Unknown), Either($.Unknown, $.Unknown)]);
var def = $.create({checkTypes: true, env: env});

// Alternative :: TypeClass
Expand Down Expand Up @@ -2735,7 +2735,7 @@ describe('def', function() {
});

it('supports unary type variables', function() {
var env = $.env.concat([Either, Maybe]);
var env = $.env.concat([Either($.Unknown, $.Unknown), Maybe($.Unknown)]);
var def = $.create({checkTypes: true, env: env});

// f :: Type -> Type
Expand Down Expand Up @@ -2811,7 +2811,7 @@ describe('def', function() {
});

it('supports binary type variables', function() {
var env = $.env.concat([$Pair]);
var env = $.env.concat([$Pair($.Unknown, $.Unknown)]);
var def = $.create({checkTypes: true, env: env});

// f :: (Type, Type) -> Type
Expand Down Expand Up @@ -2839,7 +2839,7 @@ describe('def', function() {
function(x) { count += 1; return false; }
);

var env = [$.Array, Maybe, $.Number, Void];
var env = [$.Array($.Unknown), Maybe($.Unknown), $.Number, Void];
var def = $.create({checkTypes: true, env: env});

// head :: Array a -> Maybe a
Expand Down

0 comments on commit 050777e

Please sign in to comment.