sanctuary-dynamic v0.14.1
Sanctuary (Dynamic)
A fork of Sanctuary that does not require any static typing. All the power of Sanctuary, with dynamism of Ramda.
Install
yarn add sanctuary-dynamic
Overview
Sanctuary is a JavaScript functional programming library inspired by Haskell and PureScript. It's stricter than Ramda, and provides a similar suite of functions.
Sanctuary promotes programs composed of simple, pure functions. Such programs are easier to comprehend, test, and maintain – they are also a pleasure to write.
Sanctuary provides two data types, Maybe and Either, both of
which are compatible with Fantasy Land. Thanks to these data types
even Sanctuary functions which may fail, such as head
, are
composable.
Sanctuary makes it possible to write safe code without null checks. In JavaScript it's trivial to introduce a possible run-time type error:
words[0].toUpperCase()
If words
is []
we'll get a familiar error at run-time:
TypeError: Cannot read property 'toUpperCase' of undefined
Sanctuary gives us a fighting chance of avoiding such errors. We might write:
S.map(S.toUpper, S.head(words))
Sanctuary is designed to work in Node.js and in ES5-compatible browsers.
Types
Sanctuary uses Haskell-like type signatures to describe the types of
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:
'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 (, )
-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 return values with the same set of methods. A value of the Either type, for example, is created via the Left constructor or the Right constructor.
It's necessary to extend Haskell's notation to describe implicit arguments
to the methods provided by Sanctuary's types. In x.map(y)
, for example,
the map
method takes an implicit argument x
in addition to the explicit
argument y
. The type of the value upon which a method is invoked appears
at the beginning of the signature, separated from the arguments and return
value by a squiggly arrow (~>
). The type of the fantasy-land/map
method
of the Maybe type is written Maybe a ~> (a -> b) -> Maybe b
. One could
read this as:
When the fantasy-land/map
method is invoked on a value of type Maybe a
(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
Functor type class. Type-class constraints appear at the beginning of a
type signature, separated from the rest of the signature by a fat arrow
(=>
).
Type representatives
What is the type of Number
? One answer is a -> Number
, since it's a
function which takes an argument of any type and returns a Number value.
When provided as the first argument to is
, though, Number
is
really the value-level representative of the Number type.
Sanctuary uses the TypeRep pseudotype to describe type representatives. For example:
Number :: TypeRep Number
Number
is the sole inhabitant of the TypeRep Number type.
Type checking
Sanctuary functions are defined via sanctuary-def to provide run-time type checking. This is tremendously useful during development: type errors are reported immediately, avoiding circuitous stack traces (at best) and silent failures due to type coercion (at worst). For example:
S.add(2, true)
// ! TypeError: Invalid value
//
// add :: FiniteNumber -> FiniteNumber -> FiniteNumber
// ^^^^^^^^^^^^
// 1
//
// 1) true :: Boolean
//
// The value at position 1 is not a member of ‘FiniteNumber’.
//
// See https://github.com/sanctuary-js/sanctuary-def/tree/v0.14.0#FiniteNumber for information about the sanctuary-def/FiniteNumber type.
Compare this to the behaviour of Ramda's unchecked equivalent:
R.add(2, true)
// => 3
There is a performance cost to run-time type checking. One may wish to
disable type checking in certain contexts to avoid paying this cost.
create
facilitates the creation of a Sanctuary module which
does not perform type checking.
In Node, one could use an environment variable to determine whether to perform type checking:
const { create, env } = require('sanctuary')
const checkTypes = process.env.NODE_ENV !== 'production'
const S = create({ checkTypes, env })
API
Takes an options record and returns a Sanctuary module. checkTypes
specifies whether to enable type checking. The module's polymorphic
functions (such as I
) require each value associated with a
type variable to be a member of at least one type in the environment.
A well-typed application of a Sanctuary function will produce the same result regardless of whether type checking is enabled. If type checking is enabled, a badly typed application will produce an exception with a descriptive error message.
The following snippet demonstrates defining a custom type and using
create
to produce a Sanctuary module which is aware of that type:
const { create, env } = require('sanctuary')
const $ = require('sanctuary-def')
const type = require('sanctuary-type-identifiers')
// Identity :: a -> Identity a
const Identity = function Identity(x) {
if (!(this instanceof Identity)) return new Identity(x)
this.value = x
}
Identity['@@type'] = 'my-package/Identity@1'
Identity.prototype['fantasy-land/map'] = function(f) {
return Identity(f(this.value))
}
// IdentityType :: Type -> Type
const IdentityType = $.UnaryType(
Identity['@@type'],
'http://example.com/my-package#Identity',
x => type(x) === Identity['@@type'],
identity => [identity.value],
)
const S = create({
checkTypes: process.env.NODE_ENV !== 'production',
env: env.concat([IdentityType($.Unknown)]),
})
S.map(S.sub(1), Identity(43))
// => Identity(42)
See also env
.
The default environment, which may be used as is or as the basis of a
custom environment in conjunction with create
.
Placeholder
Sanctuary functions are designed with partial application in mind.
In many cases one can define a more specific function in terms of
a more general one simply by applying the more general function to
some (but not all) of its arguments. For example, one could define
sum :: Foldable f => f Number -> Number
as S.reduce(S.add, 0)
.
In some cases, though, there are multiple orders in which one may
wish to provide a function's arguments. S.concat('prefix')
is a
function which prefixes its argument, but how would one define a
function which suffixes its argument? It's possible with the help
of __
, the special placeholder value.
The placeholder indicates a hole to be filled at some future time.
The following are all equivalent (_
represents the placeholder):
f(x, y, z)
f(_, y, z)(x)
f(_, _, z)(x, y)
f(_, _, z)(_, y)(x)
The special placeholder value.
> S.map(S.concat('@'), ['foo', 'bar', 'baz'])
['@foo', '@bar', '@baz']
> S.map(S.concat(S.__, '?'), ['foo', 'bar', 'baz'])
['foo?', 'bar?', 'baz?']
Classify
Returns the result of parsing the type identifier of the given value.
> S.type(S.Just(42))
{namespace: Just('sanctuary'), name: 'Maybe', version: 0}
> S.type([1, 2, 3])
{namespace: Nothing, name: 'Array', version: 0}
Takes a type representative and a value of any
type and returns true
iff the given value is of the specified type.
Subtyping is not respected.
> S.is(Number, 42)
true
> S.is(Object, 42)
false
> S.is(String, 42)
false
Showable
Alias of Z.toString
.
> S.toString(-0)
'-0'
> S.toString(['foo', 'bar', 'baz'])
'["foo", "bar", "baz"]'
> S.toString({x: 1, y: 2, z: 3})
'{"x": 1, "y": 2, "z": 3}'
> S.toString(S.Left(S.Right(S.Just(S.Nothing))))
'Left(Right(Just(Nothing)))'
Fantasy Land
Sanctuary is compatible with the Fantasy Land specification.
Curried version of Z.equals
which requires two arguments of the
same type.
To compare values of different types first use create
to
create a Sanctuary module with type checking disabled, then use that
module's equals
function.
> S.equals(0, -0)
true
> S.equals(NaN, NaN)
true
> S.equals(S.Just([1, 2, 3]), S.Just([1, 2, 3]))
true
> S.equals(S.Just([1, 2, 3]), S.Just([1, 2, 4]))
false
Returns true
iff the second argument is less than the first
according to Z.lt
. The arguments must be provided one at a time.
See also lt_
.
> S.filter(S.lt(3), [1, 2, 3, 4, 5])
[1, 2]
Returns true
iff the first argument is less than the second
according to Z.lt
.
See also lt
.
> S.lt_([1, 2, 3], [1, 2, 3])
false
> S.lt_([1, 2, 3], [1, 2, 4])
true
> S.lt_([1, 2, 3], [1, 2])
false
Returns true
iff the second argument is less than or equal to
the first according to Z.lte
. The arguments must be provided one
at a time.
See also lte_
.
> S.filter(S.lte(3), [1, 2, 3, 4, 5])
[1, 2, 3]
Returns true
iff the first argument is less than or equal to the
second according to Z.lte
.
See also lte
.
> S.lte_([1, 2, 3], [1, 2, 3])
true
> S.lte_([1, 2, 3], [1, 2, 4])
true
> S.lte_([1, 2, 3], [1, 2])
false
Returns true
iff the second argument is greater than the first
according to Z.gt
. The arguments must be provided one at a time.
See also gt_
.
> S.filter(S.gt(3), [1, 2, 3, 4, 5])
[4, 5]
Returns true
iff the first argument is greater than the second
according to Z.gt
.
See also gt
.
> S.gt_([1, 2, 3], [1, 2, 3])
false
> S.gt_([1, 2, 3], [1, 2, 4])
false
> S.gt_([1, 2, 3], [1, 2])
true
Returns true
iff the second argument is greater than or equal
to the first according to Z.gte
. The arguments must be provided
one at a time.
See also gte_
.
> S.filter(S.gte(3), [1, 2, 3, 4, 5])
[3, 4, 5]
Returns true
iff the first argument is greater than or equal to
the second according to Z.gte
.
See also gte
.
> S.gte_([1, 2, 3], [1, 2, 3])
true
> S.gte_([1, 2, 3], [1, 2, 4])
false
> S.gte_([1, 2, 3], [1, 2])
true
Returns the smaller of its two arguments (according to Z.lte
).
See also max
.
> S.min(10, 2)
2
> S.min(new Date('1999-12-31'), new Date('2000-01-01'))
new Date('1999-12-31')
> S.min('10', '2')
'10'
Returns the larger of its two arguments (according to Z.lte
).
See also min
.
> S.max(10, 2)
10
> S.max(new Date('1999-12-31'), new Date('2000-01-01'))
new Date('2000-01-01')
> S.max('10', '2')
'2'
> S.id(Function)(42)
42
Curried version of Z.concat
.
> S.concat('abc', 'def')
'abcdef'
> S.concat([1, 2, 3], [4, 5, 6])
[1, 2, 3, 4, 5, 6]
> S.concat({x: 1, y: 2}, {y: 3, z: 4})
{x: 1, y: 3, z: 4}
> S.concat(S.Just([1, 2, 3]), S.Just([4, 5, 6]))
Just([1, 2, 3, 4, 5, 6])
> S.concat(Sum(18), Sum(24))
Sum(42)
> S.empty(String)
''
> S.empty(Array)
[]
> S.empty(Object)
{}
> S.empty(Sum)
Sum(0)
Type-safe version of Z.invert
.
> S.invert(Sum(5))
Sum(-5)
Curried version of Z.map
.
> S.map(Math.sqrt, [1, 4, 9])
[1, 2, 3]
> S.map(Math.sqrt, {x: 1, y: 4, z: 9})
{x: 1, y: 2, z: 3}
> S.map(Math.sqrt, S.Just(9))
Just(3)
> S.map(Math.sqrt, S.Right(9))
Right(3)
Replacing Functor f => f
with Function x
produces the B combinator
from combinatory logic (i.e. compose
):
Functor f => (a -> b) -> f a -> f b
(a -> b) -> Function x a -> Function x b
(a -> c) -> Function x a -> Function x c
(b -> c) -> Function x b -> Function x c
(b -> c) -> Function a b -> Function a c
(b -> c) -> (a -> b) -> (a -> c)
> S.map(Math.sqrt, S.add(1))(99)
10
Curried version of Z.bimap
.
> S.bimap(S.toUpper, Math.sqrt, S.Left('foo'))
Left('FOO')
> S.bimap(S.toUpper, Math.sqrt, S.Right(64))
Right(8)
Curried version of Z.promap
.
> S.promap(Math.abs, S.add(1), Math.sqrt)(-100)
11
Curried version of Z.alt
.
> S.alt(S.Nothing, S.Just(1))
Just(1)
> S.alt(S.Just(2), S.Just(3))
Just(2)
> S.alt(S.Left('X'), S.Right(1))
Right(1)
> S.alt(S.Right(2), S.Right(3))
Right(2)
> S.zero(Array)
[]
> S.zero(Object)
{}
> S.zero(S.Maybe)
Nothing
Takes a curried binary function, an initial value, and a Foldable, and applies the function to the initial value and the Foldable's first value, then applies the function to the result of the previous application and the Foldable's second value. Repeats this process until each of the Foldable's values has been used. Returns the initial value if the Foldable is empty; the result of the final application otherwise.
> S.reduce(S.add, 0, [1, 2, 3, 4, 5])
15
> S.reduce(xs => x => [x].concat(xs), [], [1, 2, 3, 4, 5])
[5, 4, 3, 2, 1]
Curried version of Z.traverse
.
> S.traverse(Array, S.words, S.Just('foo bar baz'))
[Just('foo'), Just('bar'), Just('baz')]
> S.traverse(Array, S.words, S.Nothing)
[Nothing]
> S.traverse(S.Maybe, S.parseInt(16), ['A', 'B', 'C'])
Just([10, 11, 12])
> S.traverse(S.Maybe, S.parseInt(16), ['A', 'B', 'C', 'X'])
Nothing
> S.traverse(S.Maybe, S.parseInt(16), {a: 'A', b: 'B', c: 'C'})
Just({a: 10, b: 11, c: 12})
> S.traverse(S.Maybe, S.parseInt(16), {a: 'A', b: 'B', c: 'C', x: 'X'})
Nothing
Curried version of Z.sequence
. Inverts the given t (f a)
to produce an f (t a)
.
> S.sequence(Array, S.Just([1, 2, 3]))
[Just(1), Just(2), Just(3)]
> S.sequence(S.Maybe, [S.Just(1), S.Just(2), S.Just(3)])
Just([1, 2, 3])
> S.sequence(S.Maybe, [S.Just(1), S.Just(2), S.Nothing])
Nothing
> S.sequence(S.Maybe, {a: S.Just(1), b: S.Just(2), c: S.Just(3)})
Just({a: 1, b: 2, c: 3})
> S.sequence(S.Maybe, {a: S.Just(1), b: S.Just(2), c: S.Nothing})
Nothing
Curried version of Z.ap
.
> S.ap([Math.sqrt, x => x * x], [1, 4, 9, 16, 25])
[1, 2, 3, 4, 5, 1, 16, 81, 256, 625]
> S.ap({x: Math.sqrt, y: S.add(1), z: S.sub(1)}, {w: 4, x: 4, y: 4})
{x: 2, y: 5}
> S.ap(S.Just(Math.sqrt), S.Just(64))
Just(8)
Replacing Apply f => f
with Function x
produces the S combinator
from combinatory logic:
Apply f => f (a -> b) -> f a -> f b
Function x (a -> b) -> Function x a -> Function x b
Function x (a -> c) -> Function x a -> Function x c
Function x (b -> c) -> Function x b -> Function x c
Function a (b -> c) -> Function a b -> Function a c
(a -> b -> c) -> (a -> b) -> (a -> c)
> S.ap(s => n => s.slice(0, n), s => Math.ceil(s.length / 2))('Haskell')
'Hask'
Promotes a curried binary function to a function which operates on two Applys.
> S.lift2(S.add, S.Just(2), S.Just(3))
Just(5)
> S.lift2(S.add, S.Just(2), S.Nothing)
Nothing
> S.lift2(S.and, S.Just(true), S.Just(true))
Just(true)
> S.lift2(S.and, S.Just(true), S.Just(false))
Just(false)
Promotes a curried ternary function to a function which operates on three Applys.
> S.lift3(S.reduce, S.Just(S.add), S.Just(0), S.Just([1, 2, 3]))
Just(6)
> S.lift3(S.reduce, S.Just(S.add), S.Just(0), S.Nothing)
Nothing
Curried version of Z.apFirst
. Combines two effectful actions,
keeping only the result of the first. Equivalent to Haskell's (<*)
function.
See also apSecond
.
> S.apFirst([1, 2], [3, 4])
[1, 1, 2, 2]
> S.apFirst(S.Just(1), S.Just(2))
Just(1)
Curried version of Z.apSecond
. Combines two effectful actions,
keeping only the result of the second. Equivalent to Haskell's (*>)
function.
See also apFirst
.
> S.apSecond([1, 2], [3, 4])
[3, 4, 3, 4]
> S.apSecond(S.Just(1), S.Just(2))
Just(2)
Curried version of Z.of
.
> S.of(Array, 42)
[42]
> S.of(Function, 42)(null)
42
> S.of(S.Maybe, 42)
Just(42)
> S.of(S.Either, 42)
Right(42)
Curried version of Z.chain
.
> S.chain(x => [x, x], [1, 2, 3])
[1, 1, 2, 2, 3, 3]
> S.chain(n => s => s.slice(0, n), s => Math.ceil(s.length / 2))('slice')
'sli'
> S.chain(S.parseInt(10), S.Just('123'))
Just(123)
> S.chain(S.parseInt(10), S.Just('XXX'))
Nothing
Type-safe version of Z.join
.
Removes one level of nesting from a nested monadic structure.
> S.join([[1], [2], [3]])
[1, 2, 3]
> S.join([[[1, 2, 3]]])
[[1, 2, 3]]
> S.join(S.Just(S.Just(1)))
S.Just(1)
Replacing Chain m => m
with Function x
produces the W combinator
from combinatory logic:
Chain m => m (m a) -> m a
Function x (Function x a) -> Function x a
(x -> x -> a) -> (x -> a)
> S.join(S.concat)('abc')
'abcabc'
Performs a chain
-like computation with constant stack usage.
Similar to Z.chainRec
, but curried and more convenient due to the
use of the Either type to indicate completion (via a Right).
> S.chainRec(Array,
. s => s.length === 2 ? S.map(S.Right, [s + '!', s + '?'])
. : S.map(S.Left, [s + 'o', s + 'n']),
. '')
['oo!', 'oo?', 'on!', 'on?', 'no!', 'no?', 'nn!', 'nn?']
Curried version of Z.extend
.
> S.extend(S.joinWith(''), ['x', 'y', 'z'])
['xyz', 'yz', 'z']
Type-safe version of Z.extract
.
Type-safe version of Z.contramap
.
> S.contramap(s => s.length, Math.sqrt)('Sanctuary')
3
Curried version of Z.filter
. Filters its second argument in
accordance with the given predicate.
See also filterM
.
> S.filter(S.odd, [1, 2, 3, 4, 5])
[1, 3, 5]
Curried version of Z.filterM
. Filters its second argument in
accordance with the given predicate.
See also filter
.
> S.filterM(S.odd, [1, 2, 3, 4, 5])
[1, 3, 5]
> S.filterM(S.odd, S.Just(9))
Just(9)
> S.filterM(S.odd, S.Just(4))
Nothing
Discards the first inner value which does not satisfy the predicate, and all subsequent inner values.
> S.takeWhile(S.odd, [3, 3, 3, 7, 6, 3, 5, 4])
[3, 3, 3, 7]
> S.takeWhile(S.even, [3, 3, 3, 7, 6, 3, 5, 4])
[]
Retains the first inner value which does not satisfy the predicate, and all subsequent inner values.
> S.dropWhile(S.odd, [3, 3, 3, 7, 6, 3, 5, 4])
[6, 3, 5, 4]
> S.dropWhile(S.even, [3, 3, 3, 7, 6, 3, 5, 4])
[3, 3, 3, 7, 6, 3, 5, 4]
Combinator
The I combinator. Returns its argument. Equivalent to Haskell's id
function.
> S.I('foo')
'foo'
The K combinator. Takes two values and returns the first. Equivalent to
Haskell's const
function.
> S.K('foo', 'bar')
'foo'
> S.map(S.K(42), S.range(0, 5))
[42, 42, 42, 42, 42]
The A combinator. Takes a function and a value, and returns the result
of applying the function to the value. Equivalent to Haskell's ($)
function.
> S.A(S.add(1), 42)
43
> S.map(S.A(S.__, 100), [S.add(1), Math.sqrt])
[101, 10]
The T (thrush) combinator. Takes a value and a function, and returns
the result of applying the function to the value. Equivalent to Haskell's
(&)
function.
> S.T(42, S.add(1))
43
> S.map(S.T(100), [S.add(1), Math.sqrt])
[101, 10]
Function
Curries the given binary function.
> S.map(S.curry2(Math.pow)(10), [1, 2, 3])
[10, 100, 1000]
> S.map(S.curry2(Math.pow, 10), [1, 2, 3])
[10, 100, 1000]
Curries the given ternary function.
> global.replaceString = S.curry3((what, replacement, string) =>
. string.replace(what, replacement)
. )
replaceString
> replaceString('banana')('orange')('banana icecream')
'orange icecream'
> replaceString('banana', 'orange', 'banana icecream')
'orange icecream'
Curries the given quaternary function.
> global.createRect = S.curry4((x, y, width, height) =>
. ({x, y, width, height})
. )
createRect
> createRect(0)(0)(10)(10)
{x: 0, y: 0, width: 10, height: 10}
> createRect(0, 0, 10, 10)
{x: 0, y: 0, width: 10, height: 10}
Curries the given quinary function.
> global.toUrl = S.curry5((protocol, creds, hostname, port, pathname) =>
. protocol + '//' +
. S.maybe('', _ => _.username + ':' + _.password + '@', creds) +
. hostname +
. S.maybe('', S.concat(':'), port) +
. pathname
. )
toUrl
> toUrl('https:')(S.Nothing)('example.com')(S.Just('443'))('/foo/bar')
'https://example.com:443/foo/bar'
> toUrl('https:', S.Nothing, 'example.com', S.Just('443'), '/foo/bar')
'https://example.com:443/foo/bar'
Takes a curried binary function and two values, and returns the result of applying the function to the values in reverse order.
This is the C combinator from combinatory logic.
> S.flip(S.concat, 'foo', 'bar')
'barfoo'
Composition
Curried version of Z.compose
.
When specialized to Function, compose
composes two unary functions,
from right to left (this is the B combinator from combinatory logic).
The generalized type signature indicates that compose
is compatible
with any Semigroupoid.
See also pipe
.
> S.compose(Math.sqrt, S.add(1))(99)
10
Takes an array of functions assumed to be unary and a value of any type, and returns the result of applying the sequence of transformations to the initial value.
In general terms, pipe
performs left-to-right composition of an array
of functions. pipe([f, g, h], x)
is equivalent to h(g(f(x)))
.
> S.pipe([S.add(1), Math.sqrt, S.sub(1)], 99)
9
Takes a binary function f
, a unary function g
, and two
values x
and y
. Returns f(g(x))(g(y))
.
This is the P combinator from combinatory logic.
> S.on(S.concat, S.reverse, [1, 2, 3], [4, 5, 6])
[3, 2, 1, 6, 5, 4]
Maybe type
The Maybe type represents optional values: a value of type Maybe a
is
either a Just whose value is of type a
or Nothing (with no value).
The Maybe type satisfies the Ord, Monoid, Monad, Alternative, Traversable, and Extend specifications.
A UnaryType
for use with sanctuary-def.
The type representative for the Maybe type.
Nothing.
> S.Nothing
Nothing
Takes a value of any type and returns a Just with the given value.
> S.Just(42)
Just(42)
Maybe type identifier, 'sanctuary/Maybe'
.
Returns Nothing.
It is idiomatic to use empty
rather than use this function
directly.
> S.empty(S.Maybe)
Nothing
Takes a value of any type and returns a Just with the given value.
It is idiomatic to use of
rather than use this function
directly.
> S.of(S.Maybe, 42)
Just(42)
Returns Nothing.
It is idiomatic to use zero
rather than use this function
directly.
> S.zero(S.Maybe)
Nothing
true
if this
is Nothing; false
if this
is a Just.
> S.Nothing.isNothing
true
> S.Just(42).isNothing
false
true
if this
is a Just; false
if this
is Nothing.
> S.Just(42).isJust
true
> S.Nothing.isJust
false
Returns the string representation of the Maybe.
> S.toString(S.Nothing)
'Nothing'
> S.toString(S.Just([1, 2, 3]))
'Just([1, 2, 3])'
Returns the string representation of the Maybe. This method is used by
util.inspect
and the REPL to format a Maybe for display.
See also Maybe#toString
.
> S.Nothing.inspect()
'Nothing'
> S.Just([1, 2, 3]).inspect()
'Just([1, 2, 3])'
Takes a value m
of the same type and returns true
if:
this
andm
are both Nothing; orthis
andm
are both Justs, and their values are equal according toZ.equals
.
It is idiomatic to use equals
rather than use this method
directly.
> S.equals(S.Nothing, S.Nothing)
true
> S.equals(S.Just([1, 2, 3]), S.Just([1, 2, 3]))
true
> S.equals(S.Just([1, 2, 3]), S.Just([3, 2, 1]))
false
> S.equals(S.Just([1, 2, 3]), S.Nothing)
false
Takes a value m
of the same type and returns true
if:
this
is Nothing; orthis
andm
are both Justs and the value ofthis
is less than or equal to the value ofm
according toZ.lte
.
It is idiomatic to use lte
or lte_
rather than use
this method directly.
> S.lte_(S.Nothing, S.Nothing)
true
> S.lte_(S.Nothing, S.Just(0))
true
> S.lte_(S.Just(0), S.Nothing)
false
> S.lte_(S.Just(0), S.Just(1))
true
> S.lte_(S.Just(1), S.Just(0))
false
Returns the result of concatenating two Maybe values of the same type.
a
must have a Semigroup.
If this
is Nothing and the argument is Nothing, this method returns
Nothing.
If this
is a Just and the argument is a Just, this method returns a
Just whose value is the result of concatenating this Just's value and
the given Just's value.
Otherwise, this method returns the Just.
It is idiomatic to use concat
rather than use this method
directly.
> S.concat(S.Nothing, S.Nothing)
Nothing
> S.concat(S.Just([1, 2, 3]), S.Just([4, 5, 6]))
Just([1, 2, 3, 4, 5, 6])
> S.concat(S.Nothing, S.Just([1, 2, 3]))
Just([1, 2, 3])
> S.concat(S.Just([1, 2, 3]), S.Nothing)
Just([1, 2, 3])
Takes a function and returns this
if this
is Nothing; otherwise
it returns a Just whose value is the result of applying the function
to this Just's value.
It is idiomatic to use map
rather than use this method
directly.
> S.map(Math.sqrt, S.Nothing)
Nothing
> S.map(Math.sqrt, S.Just(9))
Just(3)
Takes a Maybe and returns Nothing unless this
is a Just and the
argument is a Just, in which case it returns a Just whose value is
the result of applying the given Just's value to this Just's value.
It is idiomatic to use ap
rather than use this method directly.
> S.ap(S.Nothing, S.Nothing)
Nothing
> S.ap(S.Nothing, S.Just(9))
Nothing
> S.ap(S.Just(Math.sqrt), S.Nothing)
Nothing
> S.ap(S.Just(Math.sqrt), S.Just(9))
Just(3)
Takes a function and returns this
if this
is Nothing; otherwise
it returns the result of applying the function to this Just's value.
It is idiomatic to use chain
rather than use this method
directly.
> S.chain(S.parseFloat, S.Nothing)
Nothing
> S.chain(S.parseFloat, S.Just('xxx'))
Nothing
> S.chain(S.parseFloat, S.Just('12.34'))
Just(12.34)
Chooses between this
and the other Maybe provided as an argument.
Returns this
if this
is a Just; the other Maybe otherwise.
It is idiomatic to use alt
rather than use this method
directly.
> S.alt(S.Nothing, S.Nothing)
Nothing
> S.alt(S.Nothing, S.Just(1))
Just(1)
> S.alt(S.Just(2), S.Nothing)
Just(2)
> S.alt(S.Just(3), S.Just(4))
Just(3)
Takes a function and an initial value of any type, and returns:
the initial value if
this
is Nothing; otherwisethe result of applying the function to the initial value and this Just's value.
It is idiomatic to use reduce
rather than use this method
directly.
> S.reduce(S.curry2(Math.pow), 10, S.Nothing)
10
> S.reduce(S.curry2(Math.pow), 10, S.Just(3))
1000
Takes the type representative of some Applicative and a function which returns a value of that Applicative, and returns:
the result of applying the type representative's
of
function tothis
ifthis
is Nothing; otherwisethe result of mapping
Just
over the result of applying the first function to this Just's value.
It is idiomatic to use traverse
rather than use this
method directly.
> S.traverse(Array, S.words, S.Nothing)
[Nothing]
> S.traverse(Array, S.words, S.Just('foo bar baz'))
[Just('foo'), Just('bar'), Just('baz')]
Takes a function and returns this
if this
is Nothing; otherwise
it returns a Just whose value is the result of applying the function
to this
.
It is idiomatic to use extend
rather than use this method
directly.
> S.extend(x => x.value + 1, S.Nothing)
Nothing
> S.extend(x => x.value + 1, S.Just(42))
Just(43)
Returns true
if the given Maybe is Nothing; false
if it is a Just.
> S.isNothing(S.Nothing)
true
> S.isNothing(S.Just(42))
false
Returns true
if the given Maybe is a Just; false
if it is Nothing.
> S.isJust(S.Just(42))
true
> S.isJust(S.Nothing)
false
Takes a default value and a Maybe, and returns the Maybe's value if the Maybe is a Just; the default value otherwise.
See also fromMaybe_
and
maybeToNullable
.
> S.fromMaybe(0, S.Just(42))
42
> S.fromMaybe(0, S.Nothing)
0
Variant of fromMaybe
which takes a thunk so the default
value is only computed if required.
> function fib(n) { return n <= 1 ? n : fib(n - 2) + fib(n - 1); }
> S.fromMaybe_(() => fib(30), S.Just(1000000))
1000000
> S.fromMaybe_(() => fib(30), S.Nothing)
832040
Returns the given Maybe's value if the Maybe is a Just; null
otherwise.
Nullable is defined in sanctuary-def.
See also fromMaybe
.
> S.maybeToNullable(S.Just(42))
42
> S.maybeToNullable(S.Nothing)
null
Takes a value and returns Nothing if the value is null
or undefined
;
Just the value otherwise.
> S.toMaybe(null)
Nothing
> S.toMaybe(42)
Just(42)
Takes a value of any type, a function, and a Maybe. If the Maybe is a Just, the return value is the result of applying the function to the Just's value. Otherwise, the first argument is returned.
See also maybe_
.
> S.maybe(0, S.prop('length'), S.Just('refuge'))
6
> S.maybe(0, S.prop('length'), S.Nothing)
0
Variant of maybe
which takes a thunk so the default value
is only computed if required.
> function fib(n) { return n <= 1 ? n : fib(n - 2) + fib(n - 1); }
> S.maybe_(() => fib(30), Math.sqrt, S.Just(1000000))
1000
> S.maybe_(() => fib(30), Math.sqrt, S.Nothing)
832040
Takes an array of Maybes and returns an array containing each Just's
value. Equivalent to Haskell's catMaybes
function.
> S.justs([S.Just('foo'), S.Nothing, S.Just('baz')])
['foo', 'baz']
Takes a function and an array, applies the function to each element of the array, and returns an array of "successful" results. If the result of applying the function to an element of the array is Nothing, the result is discarded; if the result is a Just, the Just's value is included in the output array.
In general terms, mapMaybe
filters an array while mapping over it.
> S.mapMaybe(S.head, [[], [1, 2, 3], [], [4, 5, 6], []])
[1, 4]
Takes a unary function f
which may throw and a value x
of any type,
and applies f
to x
inside a try
block. If an exception is caught,
the return value is Nothing; otherwise the return value is Just the
result of applying f
to x
.
See also encaseEither
.
> S.encase(eval, '1 + 1')
Just(2)
> S.encase(eval, '1 +')
Nothing
Binary version of encase
.
Ternary version of encase
.
Converts a Maybe to an Either. Nothing becomes a Left (containing the first argument); a Just becomes a Right.
See also eitherToMaybe
.
> S.maybeToEither('Expecting an integer', S.parseInt(10, 'xyz'))
Left('Expecting an integer')
> S.maybeToEither('Expecting an integer', S.parseInt(10, '42'))
Right(42)
Either type
The Either type represents values with two possibilities: a value of type
Either a b
is either a Left whose value is of type a
or a Right whose
value is of type b
.
The Either type satisfies the Ord, Semigroup, Monad, Alt, Traversable, Extend, and Bifunctor specifications.
A BinaryType
for use with sanctuary-def.
The type representative for the Either type.
Takes a value of any type and returns a Left with the given value.
> S.Left('Cannot divide by zero')
Left('Cannot divide by zero')
Takes a value of any type and returns a Right with the given value.
> S.Right(42)
Right(42)
Either type identifier, 'sanctuary/Either'
.
Takes a value of any type and returns a Right with the given value.
It is idiomatic to use of
rather than use this function
directly.
> S.of(S.Either, 42)
Right(42)
true
if this
is a Left; false
if this
is a Right.
> S.Left('Cannot divide by zero').isLeft
true
> S.Right(42).isLeft
false
true
if this
is a Right; false
if this
is a Left.
> S.Right(42).isRight
true
> S.Left('Cannot divide by zero').isRight
false
Returns the string representation of the Either.
> S.toString(S.Left('Cannot divide by zero'))
'Left("Cannot divide by zero")'
> S.toString(S.Right([1, 2, 3]))
'Right([1, 2, 3])'
Returns the string representation of the Either. This method is used by
util.inspect
and the REPL to format a Either for display.
See also Either#toString
.
> S.Left('Cannot divide by zero').inspect()
'Left("Cannot divide by zero")'
> S.Right([1, 2, 3]).inspect()
'Right([1, 2, 3])'
Takes a value e
of the same type and returns true
if:
this
ande
are both Lefts or both Rights, and their values are equal according toZ.equals
.
It is idiomatic to use equals
rather than use this method
directly.
> S.equals(S.Right([1, 2, 3]), S.Right([1, 2, 3]))
true
> S.equals(S.Right([1, 2, 3]), S.Left([1, 2, 3]))
false
Takes a value e
of the same type and returns true
if:
this
is a Left ande
is a Right; orthis
ande
are both Lefts or both Rights, and the value ofthis
is less than or equal to the value ofe
according toZ.lte
.
It is idiomatic to use lte
or lte_
rather than use
this method directly.
> S.lte_(S.Left(10), S.Right(0))
true
> S.lte_(S.Right(0), S.Left(10))
false
> S.lte_(S.Right(0), S.Right(1))
true
> S.lte_(S.Right(1), S.Right(0))
false
Returns the result of concatenating two Either values of the same type.
a
must have a Semigroup, as must b
.
If this
is a Left and the argument is a Left, this method returns a
Left whose value is the result of concatenating this Left's value and
the given Left's value.
If this
is a Right and the argument is a Right, this method returns a
Right whose value is the result of concatenating this Right's value and
the given Right's value.
Otherwise, this method returns the Right.
It is idiomatic to use concat
rather than use this method
directly.
> S.concat(S.Left('abc'), S.Left('def'))
Left('abcdef')
> S.concat(S.Right([1, 2, 3]), S.Right([4, 5, 6]))
Right([1, 2, 3, 4, 5, 6])
> S.concat(S.Left('abc'), S.Right([1, 2, 3]))
Right([1, 2, 3])
> S.concat(S.Right([1, 2, 3]), S.Left('abc'))
Right([1, 2, 3])
Takes a function and returns this
if this
is a Left; otherwise it
returns a Right whose value is the result of applying the function to
this Right's value.
It is idiomatic to use map
rather than use this method
directly.
See also Either#fantasy-land/bimap
.
> S.map(Math.sqrt, S.Left('Cannot divide by zero'))
Left('Cannot divide by zero')
> S.map(Math.sqrt, S.Right(9))
Right(3)
Takes two functions and returns:
a Left whose value is the result of applying the first function to this Left's value if
this
is a Left; otherwisea Right whose value is the result of applying the second function to this Right's value.
Similar to Either#fantasy-land/map
, but supports mapping over the
left side as well as the right side.
It is idiomatic to use bimap
rather than use this method
directly.
> S.bimap(S.toUpper, S.add(1), S.Left('abc'))
Left('ABC')
> S.bimap(S.toUpper, S.add(1), S.Right(42))
Right(43)
Takes an Either and returns a Left unless this
is a Right and the
argument is a Right, in which case it returns a Right whose value is
the result of applying the given Right's value to this Right's value.
It is idiomatic to use ap
rather than use this method directly.
> S.ap(S.Left('No such function'), S.Left('Cannot divide by zero'))
Left('No such function')
> S.ap(S.Left('No such function'), S.Right(9))
Left('No such function')
> S.ap(S.Right(Math.sqrt), S.Left('Cannot divide by zero'))
Left('Cannot divide by zero')
> S.ap(S.Right(Math.sqrt), S.Right(9))
Right(3)
Takes a function and returns this
if this
is a Left; otherwise
it returns the result of applying the function to this Right's value.
It is idiomatic to use chain
rather than use this method
directly.
> global.sqrt = n =>
. n < 0 ? S.Left('Cannot represent square root of negative number')
. : S.Right(Math.sqrt(n))
sqrt
> S.chain(sqrt, S.Left('Cannot divide by zero'))
Left('Cannot divide by zero')
> S.chain(sqrt, S.Right(-1))
Left('Cannot represent square root of negative number')
> S.chain(sqrt, S.Right(25))
Right(5)
Chooses between this
and the other Either provided as an argument.
Returns this
if this
is a Right; the other Either otherwise.
It is idiomatic to use alt
rather than use this method
directly.
> S.alt(S.Left('A'), S.Left('B'))
Left('B')
> S.alt(S.Left('C'), S.Right(1))
Right(1)
> S.alt(S.Right(2), S.Left('D'))
Right(2)
> S.alt(S.Right(3), S.Right(4))
Right(3)
Takes a function and an initial value of any type, and returns:
the initial value if
this
is a Left; otherwisethe result of applying the function to the initial value and this Right's value.
It is idiomatic to use reduce
rather than use this method
directly.
> S.reduce(S.curry2(Math.pow), 10, S.Left('Cannot divide by zero'))
10
> S.reduce(S.curry2(Math.pow), 10, S.Right(3))
1000
Takes the type representative of some Applicative and a function which returns a value of that Applicative, and returns:
the result of applying the type representative's
of
function tothis
ifthis
is a Left; otherwisethe result of mapping
Right
over the result of applying the first function to this Right's value.
It is idiomatic to use traverse
rather than use this
method directly.
> S.traverse(Array, S.words, S.Left('Request failed'))
[Left('Request failed')]
> S.traverse(Array, S.words, S.Right('foo bar baz'))
[Right('foo'), Right('bar'), Right('baz')]
Takes a function and returns this
if this
is a Left; otherwise it
returns a Right whose value is the result of applying the function to
this
.
It is idiomatic to use extend
rather than use this method
directly.
> S.extend(x => x.value + 1, S.Left('Cannot divide by zero'))
Left('Cannot divide by zero')
> S.extend(x => x.value + 1, S.Right(42))
Right(43)
Returns true
if the given Either is a Left; false
if it is a Right.
> S.isLeft(S.Left('Cannot divide by zero'))
true
> S.isLeft(S.Right(42))
false
Returns true
if the given Either is a Right; false
if it is a Left.
> S.isRight(S.Right(42))
true
> S.isRight(S.Left('Cannot divide by zero'))
false
Takes a default value and an Either, and returns the Right value if the Either is a Right; the default value otherwise.
> S.fromEither(0, S.Right(42))
42
> S.fromEither(0, S.Left(42))
0
Converts an arbitrary value to an Either: a Left if the value is null
or undefined
; a Right otherwise. The first argument specifies the
value of the Left in the "failure" case.
> S.toEither('XYZ', null)
Left('XYZ')
> S.toEither('XYZ', 'ABC')
Right('ABC')
> S.map(S.prop('0'), S.toEither('Invalid protocol', 'ftp://example.com/'.match(/^https?:/)))
Left('Invalid protocol')
> S.map(S.prop('0'), S.toEither('Invalid protocol', 'https://example.com/'.match(/^https?:/)))
Right('https:')
Takes two functions and an Either, and returns the result of applying the first function to the Left's value, if the Either is a Left, or the result of applying the second function to the Right's value, if the Either is a Right.
> S.either(S.toUpper, S.toString, S.Left('Cannot divide by zero'))
'CANNOT DIVIDE BY ZERO'
> S.either(S.toUpper, S.toString, S.Right(42))
'42'
Takes an array of Eithers and returns an array containing each Left's value.
See also rights
.
> S.lefts([S.Right(20), S.Left('foo'), S.Right(10), S.Left('bar')])
['foo', 'bar']
6 years ago