diff --git a/index.js b/index.js index def119d..280d528 100644 --- a/index.js +++ b/index.js @@ -473,6 +473,34 @@ //. Constructor for homogeneous Array types. var Array_ = UnaryTypeWithUrl('Array', typeEq('Array'), id); + //# Array0 :: Type + //. + //. Type whose sole member is `[]`. + var Array0 = NullaryTypeWithUrl( + 'sanctuary-def/Array0', + function(x) { return typeEq('Array')(x) && x.length === 0; } + ); + + //# Array1 :: Type -> Type + //. + //. Constructor for singleton Array types. + var Array1 = UnaryTypeWithUrl( + 'sanctuary-def/Array1', + function(x) { return typeEq('Array')(x) && x.length === 1; }, + id + ); + + //# Array2 :: Type -> Type -> Type + //. + //. Constructor for heterogeneous Array types of length 2. `['foo', true]` is + //. a member of `Array2 String Boolean`. + var Array2 = BinaryTypeWithUrl( + 'sanctuary-def/Array2', + function(x) { return typeEq('Array')(x) && x.length === 2; }, + function(array2) { return [array2[0]]; }, + function(array2) { return [array2[1]]; } + ); + //# Boolean :: Type //. //. Type comprising `true` and `false`. @@ -676,17 +704,6 @@ //. constructor function. var Object_ = NullaryTypeWithUrl('Object', typeEq('Object')); - //# Pair :: Type -> Type -> Type - //. - //. Constructor for tuple types of length 2. Arrays are said to represent - //. tuples. `['foo', 42]` is a member of `Pair String Number`. - var Pair = BinaryTypeWithUrl( - 'sanctuary-def/Pair', - function(x) { return typeEq('Array')(x) && x.length === 2; }, - function(pair) { return [pair[0]]; }, - function(pair) { return [pair[1]]; } - ); - //# PositiveFiniteNumber :: Type //. //. Type comprising every [`FiniteNumber`][] value greater than zero. @@ -1489,7 +1506,8 @@ //# BinaryType :: String -> String -> (Any -> Boolean) -> (t a b -> Array a) -> (t a b -> Array b) -> (Type -> Type -> Type) //. - //. Type constructor for types with two type variables (such as [`Pair`][]). + //. Type constructor for types with two type variables (such as + //. [`Array2`][]). //. //. To define a binary type `t a b` one must provide: //. @@ -2574,6 +2592,9 @@ AnyFunction: AnyFunction, Arguments: Arguments, Array: fromUncheckedUnaryType(Array_), + Array0: Array0, + Array1: fromUncheckedUnaryType(Array1), + Array2: fromUncheckedBinaryType(Array2), Boolean: Boolean_, Date: Date_, Error: Error_, @@ -2594,7 +2615,6 @@ Nullable: fromUncheckedUnaryType(Nullable), Number: Number_, Object: Object_, - Pair: fromUncheckedBinaryType(Pair), PositiveFiniteNumber: PositiveFiniteNumber, PositiveInteger: PositiveInteger, PositiveNumber: PositiveNumber, @@ -2630,6 +2650,7 @@ //. [Monoid]: https://github.com/fantasyland/fantasy-land#monoid //. [Setoid]: https://github.com/fantasyland/fantasy-land#setoid //. [`Array`]: #Array +//. [`Array2`]: #Array2 //. [`BinaryType`]: #BinaryType //. [`Date`]: #Date //. [`FiniteNumber`]: #FiniteNumber @@ -2638,7 +2659,6 @@ //. [`NonGlobalRegExp`]: #NonGlobalRegExp //. [`Number`]: #Number //. [`Object.create`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create -//. [`Pair`]: #Pair //. [`RegExp`]: #RegExp //. [`RegexFlags`]: #RegexFlags //. [`String`]: #String diff --git a/test/index.js b/test/index.js index 86a8287..768a456 100644 --- a/test/index.js +++ b/test/index.js @@ -1652,6 +1652,67 @@ Since there is no type of which all the above values are members, the type-varia eq($.Array(a).url, `https://github.com/sanctuary-js/sanctuary-def/tree/v${version}#Array`); }); + test('provides the "Array0" type', () => { + eq($.Array0.name, 'sanctuary-def/Array0'); + eq($.Array0.url, `https://github.com/sanctuary-js/sanctuary-def/tree/v${version}#Array0`); + + const isEmptyArray = $.test($.env, $.Array0); + eq(isEmptyArray(null), false); + eq(isEmptyArray([]), true); + eq(isEmptyArray([0]), false); + }); + + test('provides the "Array1" type constructor', () => { + eq(typeof $.Array1, 'function'); + eq($.Array1.length, 1); + eq($.Array1.toString(), 'Array1 :: Type -> Type'); + eq($.Array1(a).name, 'sanctuary-def/Array1'); + eq($.Array1(a).url, `https://github.com/sanctuary-js/sanctuary-def/tree/v${version}#Array1`); + eq($.Array1(a).toString(), '(Array1 a)'); + + const isSingletonStringArray = $.test($.env, $.Array1($.String)); + eq(isSingletonStringArray(null), false); + eq(isSingletonStringArray([]), false); + eq(isSingletonStringArray([0]), false); + eq(isSingletonStringArray(['x']), true); + eq(isSingletonStringArray(['x', 'y']), false); + }); + + test('provides the "Array2" type constructor', () => { + eq(typeof $.Array2, 'function'); + eq($.Array2.length, 2); + eq($.Array2.toString(), 'Array2 :: Type -> Type -> Type'); + eq($.Array2(a, b).name, 'sanctuary-def/Array2'); + eq($.Array2(a, b).url, `https://github.com/sanctuary-js/sanctuary-def/tree/v${version}#Array2`); + eq($.Array2(a, b).toString(), '(Array2 a b)'); + eq($.Array2(a)(b).toString(), '(Array2 a b)'); + + // fst :: Array2 a b -> a + const fst = def('fst', {}, [$.Array2(a, b), a], array2 => array2[0]); + + // snd :: Array2 a b -> b + const snd = def('snd', {}, [$.Array2(a, b), b], array2 => array2[1]); + + eq(fst(['foo', 42]), 'foo'); + eq(snd(['foo', 42]), 42); + + throws( + () => fst(['foo']), + TypeError, + `Invalid value + +fst :: Array2 a b -> a + ^^^^^^^^^^ + 1 + +1) ["foo"] :: Array String + +The value at position 1 is not a member of ‘Array2 a b’. + +See https://github.com/sanctuary-js/sanctuary-def/tree/v${version}#Array2 for information about the sanctuary-def/Array2 type. +`); + }); + test('provides the "Boolean" type', () => { eq($.Boolean.name, 'Boolean'); eq($.Boolean.url, `https://github.com/sanctuary-js/sanctuary-def/tree/v${version}#Boolean`); @@ -2113,49 +2174,6 @@ Since there is no type of which all the above values are members, the type-varia `); }); - test('provides the "Pair" type constructor', () => { - eq(typeof $.Pair, 'function'); - eq($.Pair.length, 2); - eq($.Pair.toString(), 'Pair :: Type -> Type -> Type'); - eq($.Pair(a, b).name, 'sanctuary-def/Pair'); - eq($.Pair(a, b).url, `https://github.com/sanctuary-js/sanctuary-def/tree/v${version}#Pair`); - eq($.Pair(a, b).toString(), '(Pair a b)'); - eq($.Pair(a)(b).toString(), '(Pair a b)'); - - // fst :: Pair a b -> a - const fst = - def('fst', - {}, - [$.Pair(a, b), a], - pair => pair[0]); - - // snd :: Pair a b -> b - const snd = - def('snd', - {}, - [$.Pair(a, b), b], - pair => pair[1]); - - eq(fst(['foo', 42]), 'foo'); - eq(snd(['foo', 42]), 42); - - throws( - () => fst(['foo']), - TypeError, - `Invalid value - -fst :: Pair a b -> a - ^^^^^^^^ - 1 - -1) ["foo"] :: Array String - -The value at position 1 is not a member of ‘Pair a b’. - -See https://github.com/sanctuary-js/sanctuary-def/tree/v${version}#Pair for information about the sanctuary-def/Pair type. -`); - }); - test('uses Z.toString-like string representations', () => { // f :: Null -> Null const f = @@ -2874,11 +2892,11 @@ reduce_ :: ((a, b) -> a) -> a -> Array b -> a The value at position 1 is not a member of ‘(a, b) -> a’. `); - // unfoldr :: (b -> Maybe (Pair a b)) -> b -> Array a + // unfoldr :: (b -> Maybe (Array2 a b)) -> b -> Array a const unfoldr = def('unfoldr', {}, - [$.Function([b, Maybe($.Pair(a, b))]), b, $.Array(a)], + [$.Function([b, Maybe($.Array2(a, b))]), b, $.Array(a)], (f, x) => { const result = []; for (let m = f(x); m.isJust; m = f(m.value[1])) { @@ -2887,7 +2905,7 @@ The value at position 1 is not a member of ‘(a, b) -> a’. return result; }); - // h :: Integer -> Maybe (Pair Integer Integer) + // h :: Integer -> Maybe (Array2 Integer Integer) const h = n => n >= 5 ? Nothing : Just([n, n + 1]); eq(unfoldr(h, 5), []); @@ -2899,13 +2917,13 @@ The value at position 1 is not a member of ‘(a, b) -> a’. TypeError, `Invalid value -unfoldr :: (b -> Maybe (Pair a b)) -> b -> Array a - ^^^^^^^^^^^^^^^^^^^^^^^ - 1 +unfoldr :: (b -> Maybe (Array2 a b)) -> b -> Array a + ^^^^^^^^^^^^^^^^^^^^^^^^^ + 1 1) null :: Null -The value at position 1 is not a member of ‘b -> Maybe (Pair a b)’. +The value at position 1 is not a member of ‘b -> Maybe (Array2 a b)’. `); // T :: a -> (a -> b) -> b