From 2da9d55b53b448562d10a5460b6acf0f09dac1b1 Mon Sep 17 00:00:00 2001 From: David Chambers Date: Sun, 26 Feb 2017 19:39:59 +0100 Subject: [PATCH] =?UTF-8?q?require=20environments=20to=20actually=20be=20o?= =?UTF-8?q?f=20type=20=E2=80=98Array=20Type=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- index.js | 100 +++++++++++++++++++++++++++++--------------------- test/index.js | 18 ++++----- 3 files changed, 68 insertions(+), 52 deletions(-) diff --git a/Makefile b/Makefile index 243b7c9..ff9d155 100644 --- a/Makefile +++ b/Makefile @@ -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: "^ *//(# |[.] // |[.] - )", ignoreUrls: true}]' \ -- index.js $(ESLINT) \ --env node \ diff --git a/index.js b/index.js index 8d627fc..ef6e353 100644 --- a/index.js +++ b/index.js @@ -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 @@ -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 @@ -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 @@ -726,23 +752,23 @@ //. //. An array of [types][]: //. - //. - [`AnyFunction`][] - //. - [`Arguments`][] - //. - [`Array`][] - //. - [`Boolean`][] - //. - [`Date`][] - //. - [`Error`][] - //. - [`Null`][] - //. - [`Number`][] - //. - [`Object`][] - //. - [`RegExp`][] - //. - [`StrMap`][] - //. - [`String`][] - //. - [`Undefined`][] - var env = applyParameterizedTypes([ + //. - AnyFunction + //. - Arguments + //. - Array(Unknown) + //. - Boolean + //. - Date + //. - Error + //. - Null + //. - Number + //. - Object + //. - RegExp + //. - StrMap(Unknown) + //. - String + //. - Undefined + var env = [ AnyFunction, Arguments, - Array_, + Array_(Unknown), Boolean_, Date_, Error_, @@ -750,10 +776,10 @@ Number_, Object_, RegExp_, - StrMap, + StrMap(Unknown), String_, Undefined - ]); + ]; // Type :: Type var Type = NullaryType( @@ -1201,8 +1227,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; } @@ -2345,8 +2370,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, @@ -2442,33 +2466,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 diff --git a/test/index.js b/test/index.js index 584d0e5..2332d63 100644 --- a/test/index.js +++ b/test/index.js @@ -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 @@ -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) @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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