Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expand documentation of type signatures and fix inconsistencies #259

Merged
merged 1 commit into from
Sep 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 72 additions & 31 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,59 @@
//. ## Types
//.
//. Sanctuary uses Haskell-like type signatures to describe the types of
//. values, including functions. `'foo'`, for example, has type `String`;
//. `[1, 2, 3]` has type `Array Number`. The arrow (`->`) is used to express
//. a function's type. `Math.abs`, for example, has type `Number -> Number`.
//. That is, it takes an argument of type `Number` and returns a value of
//. type `Number`.
//. values, including functions. `'foo'`, for example, is a member of `String`;
//. `[1, 2, 3]` is a member of `Array Number`. The double colon (`::`) is used
//. to mean "is a member of", so one could write:
//.
//. [`R.map`][R.map] has type `(a -> b) -> Array a -> Array b`. That is,
//. it takes an argument of type `a -> b` and returns a value of type
//. `Array a -> Array b`. `a` and `b` are type variables: applying `R.map`
//. to a value of type `String -> Number` will result in a value of type
//. `Array String -> Array Number`.
//. 'foo' :: String
//. [1, 2, 3] :: Array Number
//.
//. An identifier may appear to the left of the double colon:
//.
//. Math.PI :: Number
//.
//. The arrow (`->`) is used to express a function's type:
//.
//. Math.abs :: Number -> Number
//.
//. That states that `Math.abs` is a unary function which takes an argument
//. of type `Number` and returns a value of type `Number`.
//.
//. Some functions are parametrically polymorphic: their types are not fixed.
//. Type variables are used in the representations of such functions:
//.
//. S.I :: a -> a
//.
//. `a` is a type variable. Type variables are not capitalized, so they
//. are differentiable from type identifiers (which are always capitalized).
//. By convention type variables have single-character names. The signature
//. above states that `S.I` takes a value of any type and returns a value of
//. the same type. Some signatures feature multiple type variables:
//.
//. S.K :: a -> b -> a
//.
//. It must be possible to replace all occurrences of `a` with a concrete type.
//. The same applies for each other type variable. For the function above, the
//. types with which `a` and `b` are replaced may be different, but needn't be.
//.
//. Since all Sanctuary functions are curried (they accept their arguments
//. one at a time), a binary function is represented as a unary function which
//. returns a unary function: `* -> * -> *`. This aligns neatly with Haskell,
//. which uses curried functions exclusively. In JavaScript, though, we may
//. wish to represent the types of functions with arities less than or greater
//. than one. The general form is `(<input-types>) -> <output-type>`, where
//. `<input-types>` comprises zero or more comma–space (<code>, </code>)
//. -separated type representations:
//.
//. - `() -> String`
//. - `(a, b) -> a`
//. - `(a, b, c) -> d`
//.
//. `Number -> Number` can thus be seen as shorthand for `(Number) -> Number`.
//.
//. The question mark (`?`) is used to represent types which include `null`
//. and `undefined` as members. `String?`, for example, represents the type
//. comprising `null`, `undefined`, and all strings.
//.
//. Sanctuary embraces types. JavaScript doesn't support algebraic data types,
//. but these can be simulated by providing a group of data constructors which
Expand All @@ -61,6 +103,10 @@
//. (for any type `a`) with an argument of type `a -> b` (for any type `b`),
//. it returns a value of type `Maybe b`._
//.
//. The squiggly arrow is also used when representing non-function properties.
//. `Maybe a ~> Boolean`, for example, represents a Boolean property of a value
//. of type `Maybe a`.
//.
//. Sanctuary supports type classes: constraints on type variables. Whereas
//. `a -> a` implicitly supports every type, `Functor f => (a -> b) -> f a ->
//. f b` requires that `f` be a type which satisfies the requirements of the
Expand Down Expand Up @@ -182,25 +228,20 @@
return f(g(x));
};

// filter :: (Monad m, Monoid m) => ((a -> Boolean), m a) -> m a
// filter :: (Monad m, Monoid (m a)) => (a -> Boolean, m a) -> m a
var filter = function(pred, m) {
return m.chain(function(x) {
return pred(x) ? m.of(x) : m.empty();
});
};

// hasMethod :: String -> Any -> Boolean
// hasMethod :: String -> a -> Boolean
var hasMethod = function(name) {
return function(x) {
return x != null && typeof x[name] === 'function';
};
};

// inspect :: -> String
var inspect = /* istanbul ignore next */ function() {
return this.toString();
};

// negativeZero :: a -> Boolean
var negativeZero = R.either(R.equals(-0), R.equals(new Number(-0)));

Expand Down Expand Up @@ -835,12 +876,12 @@
[a, $Maybe(a)],
function(x) { return Just(x); });

//# Maybe#@@type :: String
//# Maybe#@@type :: Maybe a ~> String
//.
//. Maybe type identifier, `'sanctuary/Maybe'`.
Maybe.prototype['@@type'] = 'sanctuary/Maybe';

//# Maybe#isNothing :: Boolean
//# Maybe#isNothing :: Maybe a ~> Boolean
//.
//. `true` if `this` is Nothing; `false` if `this` is a Just.
//.
Expand All @@ -852,7 +893,7 @@
//. false
//. ```

//# Maybe#isJust :: Boolean
//# Maybe#isJust :: Maybe a ~> Boolean
//.
//. `true` if `this` is a Just; `false` if `this` is Nothing.
//.
Expand Down Expand Up @@ -1116,7 +1157,7 @@
return maybe.isJust ? R.map(Just, maybe.value) : of(maybe);
});

//# Maybe#toBoolean :: Maybe a ~> Boolean
//# Maybe#toBoolean :: Maybe a ~> () -> Boolean
//.
//. Returns `false` if `this` is Nothing; `true` if `this` is a Just.
//.
Expand All @@ -1133,7 +1174,7 @@
[$Maybe(a), $.Boolean],
prop('isJust'));

//# Maybe#toString :: Maybe a ~> String
//# Maybe#toString :: Maybe a ~> () -> String
//.
//. Returns the string representation of the Maybe.
//.
Expand All @@ -1153,7 +1194,7 @@
: 'Nothing';
});

//# Maybe#inspect :: Maybe a ~> String
//# Maybe#inspect :: Maybe a ~> () -> String
//.
//. Returns the string representation of the Maybe. This method is used by
//. `util.inspect` and the REPL to format a Maybe for display.
Expand All @@ -1167,7 +1208,7 @@
//. > S.Just([1, 2, 3]).inspect()
//. 'Just([1, 2, 3])'
//. ```
Maybe.prototype.inspect = inspect;
Maybe.prototype.inspect = function() { return this.toString(); };

//# Nothing :: Maybe a
//.
Expand Down Expand Up @@ -1491,12 +1532,12 @@
[b, $Either(a, b)],
function(x) { return Right(x); });

//# Either#@@type :: String
//# Either#@@type :: Either a b ~> String
//.
//. Either type identifier, `'sanctuary/Either'`.
Either.prototype['@@type'] = 'sanctuary/Either';

//# Either#isLeft :: Boolean
//# Either#isLeft :: Either a b ~> Boolean
//.
//. `true` if `this` is a Left; `false` if `this` is a Right.
//.
Expand All @@ -1508,7 +1549,7 @@
//. false
//. ```

//# Either#isRight :: Boolean
//# Either#isRight :: Either a b ~> Boolean
//.
//. `true` if `this` is a Right; `false` if `this` is a Left.
//.
Expand Down Expand Up @@ -1746,7 +1787,7 @@
return either.isRight ? R.map(Right, either.value) : of(either);
});

//# Either#toBoolean :: Either a b ~> Boolean
//# Either#toBoolean :: Either a b ~> () -> Boolean
//.
//. Returns `false` if `this` is a Left; `true` if `this` is a Right.
//.
Expand All @@ -1763,7 +1804,7 @@
[$Either(a, b), $.Boolean],
prop('isRight'));

//# Either#toString :: Either a b ~> String
//# Either#toString :: Either a b ~> () -> String
//.
//. Returns the string representation of the Either.
//.
Expand All @@ -1783,7 +1824,7 @@
'(' + R.toString(either.value) + ')';
});

//# Either#inspect :: Either a b ~> String
//# Either#inspect :: Either a b ~> () -> String
//.
//. Returns the string representation of the Either. This method is used by
//. `util.inspect` and the REPL to format a Either for display.
Expand All @@ -1797,7 +1838,7 @@
//. > S.Right([1, 2, 3]).inspect()
//. 'Right([1, 2, 3])'
//. ```
Either.prototype.inspect = inspect;
Either.prototype.inspect = function() { return this.toString(); };

//# Left :: a -> Either a b
//.
Expand Down
5 changes: 5 additions & 0 deletions test/Either/Left.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ describe('Left', function() {
eq(S.Left('abc').toString(), 'Left("abc")');
});

it('provides an "inspect" method', function() {
eq(S.Left('abc').inspect.length, 0);
eq(S.Left('abc').inspect(), 'Left("abc")');
});

it('implements Semigroup', function() {
var a = S.Left('foo');
var b = S.Left('bar');
Expand Down
5 changes: 5 additions & 0 deletions test/Either/Right.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ describe('Right', function() {
eq(S.Right([1, 2, 3]).toString(), 'Right([1, 2, 3])');
});

it('provides an "inspect" method', function() {
eq(S.Right([1, 2, 3]).inspect.length, 0);
eq(S.Right([1, 2, 3]).inspect(), 'Right([1, 2, 3])');
});

it('implements Semigroup', function() {
var a = S.Right('foo');
var b = S.Right('bar');
Expand Down
5 changes: 5 additions & 0 deletions test/Maybe/Just.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,11 @@ describe('Just', function() {
eq(S.Just([1, 2, 3]).toString(), 'Just([1, 2, 3])');
});

it('provides an "inspect" method', function() {
eq(S.Just([1, 2, 3]).inspect.length, 0);
eq(S.Just([1, 2, 3]).inspect(), 'Just([1, 2, 3])');
});

it('implements Semigroup', function() {
var a = S.Just('foo');
var b = S.Just('bar');
Expand Down
5 changes: 5 additions & 0 deletions test/Maybe/Nothing.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ describe('Nothing', function() {
eq(S.Nothing.toString(), 'Nothing');
});

it('provides an "inspect" method', function() {
eq(S.Nothing.inspect.length, 0);
eq(S.Nothing.inspect(), 'Nothing');
});

it('implements Semigroup', function() {
var a = S.Nothing;
var b = S.Nothing;
Expand Down