❑ Sanctuary
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 that 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.❑ Folktale
Folktale, like Sanctuary, is a standard library for functional programming in JavaScript. It is well designed and well documented. Whereas Sanctuary treats JavaScript as a member of the ML language family, Folktale embraces JavaScript's object-oriented programming model. Programming with Folktale resembles programming with Scala.❑ Ramda
Ramda provides several functions that return problematic values such asundefined
, Infinity
, or NaN
when applied to unsuitable
inputs. These are known as partial functions. Partial functions
necessitate the use of guards or null checks. In order to safely use
R.head
, for example, one must ensure that the array is non-empty:if (R.isEmpty (xs)) {
// ...
} else {
return f (R.head (xs));
}
Using the Maybe type renders such guards (and null checks) unnecessary.
Changing functions such as R.head
to return Maybe values was proposed
in ramda/ramda#683, but was considered too much of a stretch for
JavaScript programmers. Sanctuary was released the following month,
in January 2015, as a companion library to Ramda.In addition to broadening in scope in the years since its release, Sanctuary's philosophy has diverged from Ramda's in several respects.
❑ Totality
Every Sanctuary function is defined for every value that is a member of the function's input type. Such functions are known as total functions. Ramda, on the other hand, contains a number of partial functions.❑ Information preservation
Certain Sanctuary functions preserve more information than their Ramda counterparts. Examples:|> R.tail ([]) |> S.tail ([])
[] Nothing
|> R.tail (['foo']) |> S.tail (['foo'])
[] Just ([])
|> R.replace (/^x/) ('') ('abc') |> S.stripPrefix ('x') ('abc')
'abc' Nothing
|> R.replace (/^x/) ('') ('xabc') |> S.stripPrefix ('x') ('xabc')
'abc' Just ('abc')
❑ Invariants
Sanctuary performs rigorous type checking of inputs and outputs, and throws a descriptive error if a type error is encountered. This allows bugs to be caught and fixed early in the development cycle.Ramda operates on the garbage in, garbage outGIGO principle. Functions are documented to take arguments of particular types, but these invariants are not enforced. The problem with this approach in a language as permissive as JavaScript is that there's no guarantee that garbage input will produce garbage output (ramda/ramda#1413). Ramda performs ad hoc type checking in some such cases (ramda/ramda#1419).
Sanctuary can be configured to operate in garbage in, garbage out mode. Ramda cannot be configured to enforce its invariants.
❑ Currying
Sanctuary functions are curried. There is, for example, exactly one way to applyS.reduce
to S.add
, 0
, and xs
:-
S.reduce (S.add) (0) (xs)
Ramda functions are also curried, but in a complex manner. There are four ways to apply
R.reduce
to R.add
, 0
, and xs
:-
R.reduce (R.add) (0) (xs)
- R.reduce (R.add) (0, xs)
- R.reduce (R.add, 0) (xs)
- R.reduce (R.add, 0, xs)
Ramda supports all these forms because curried functions enable partial application, one of the library's tenets, but
f(x)(y)(z)
is considered
too unfamiliar and too unattractive to appeal to JavaScript programmers.Sanctuary's developers prefer a simple, unfamiliar construct to a complex, familiar one. Familiarity can be acquired; complexity is intrinsic.
The lack of breathing room in
f(x)(y)(z)
impairs readability. The simple
solution to this problem, proposed in #438, is to include a space when
applying a function: f (x) (y) (z)
.Ramda also provides a special placeholder value,
R.__
, that removes
the restriction that a function must be applied to its arguments in order.
The following expressions are equivalent:-
R.reduce (R.__, 0, xs) (R.add)
- R.reduce (R.add, R.__, xs) (0)
- R.reduce (R.__, 0) (R.add) (xs)
- R.reduce (R.__, 0) (R.add, xs)
- R.reduce (R.__, R.__, xs) (R.add) (0)
- R.reduce (R.__, R.__, xs) (R.add, 0)
❑ Variadic functions
Ramda provides several functions that take any number of arguments. These are known as variadic functions. Additionally, Ramda provides several functions that take variadic functions as arguments. Although natural in a dynamically typed language, variadic functions are at odds with the type notation Ramda and Sanctuary both use, leading to some indecipherable type signatures such as this one:R.lift :: (*... -> *...) -> ([*]... -> [*])
Sanctuary has no variadic functions, nor any functions that take variadic
functions as arguments. Sanctuary provides two "lift" functions, each with
a helpful type signature:S.lift2 :: Apply f => (a -> b -> c) -> f a -> f b -> f c
S.lift3 :: Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
❑ Implicit context
Ramda providesR.bind
and R.invoker
for working with methods.
Additionally, many Ramda functions use Function#call
or Function#apply
to preserve context. Sanctuary makes no allowances for this
.❑ Transducers
Several Ramda functions act as transducers. Sanctuary provides no support for transducers.❑ Modularity
Whereas Ramda has no dependencies, Sanctuary has a modular design: sanctuary-def provides type checking, sanctuary-type-classes provides Fantasy Land functions and type classes, sanctuary-show provides string representations, and algebraic data types are provided by sanctuary-either, sanctuary-maybe, and sanctuary-pair. Not only does this approach reduce the complexity of Sanctuary itself, but it allows these components to be reused in other contexts.❑ 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 that 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 that 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
.Sanctuary embraces types. JavaScript doesn't support algebraic data types, but these can be simulated by providing a group of data constructors that 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 that 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 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.22.0#FiniteNumber for information about the 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. Type checking is disabled by default if
process.env.NODE_ENV
is 'production'
. If this
rule is unsuitable for a given program, one may use create
to create a Sanctuary module based on a different rule. For example:const S = sanctuary.create ({
checkTypes: localStorage.getItem ('SANCTUARY_CHECK_TYPES') === 'true',
env: sanctuary.env,
});
Occasionally one may wish to perform an operation that is not type safe, such as mapping over an object with heterogeneous values. This is possible via selective use of
unchecked
functions.❑ Installation
npm install sanctuary
will install Sanctuary for use in Node.js.To add Sanctuary to a website, add the following
<script>
element,
replacing X.Y.Z
with a version number greater than or equal to 2.0.2
:<script src="https://cdn.jsdelivr.net/gh/sanctuary-js/sanctuary@X.Y.Z/dist/bundle.js"></script>
Optionally, define aliases for various modules:
const S = window.sanctuary;
const $ = window.sanctuaryDef;
// ...
❑ API
❑ Configure
create :: { checkTypes :: Boolean, env :: Array Type } -> Module
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 that 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 = x => {
const identity = Object.create (Identity$prototype);
identity.value = x;
return identity;
};
// identityTypeIdent :: String
const identityTypeIdent = 'my-package/Identity@1';
const Identity$prototype = {
'@@type': identityTypeIdent,
'@@show': function() { return `Identity (${S.show (this.value)})`; },
'fantasy-land/map': function(f) { return Identity (f (this.value)); },
};
// IdentityType :: Type -> Type
const IdentityType = $.UnaryType
('Identity')
('http://example.com/my-package#Identity')
([])
(x => type (x) === identityTypeIdent)
(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
.env :: Array Type
The Sanctuary module's environment ((S.create ({checkTypes, env})).env
is a reference to env
). Useful in conjunction with create
.> S.env
[ $.AnyFunction,
. $.Arguments,
. $.Array ($.Unknown),
. $.Array2 ($.Unknown) ($.Unknown),
. $.Boolean,
. $.Buffer,
. $.Date,
. $.Descending ($.Unknown),
. $.Either ($.Unknown) ($.Unknown),
. $.Error,
. $.Fn ($.Unknown) ($.Unknown),
. $.HtmlElement,
. $.Identity ($.Unknown),
. $.JsMap ($.Unknown) ($.Unknown),
. $.JsSet ($.Unknown),
. $.Maybe ($.Unknown),
. $.Module,
. $.Null,
. $.Number,
. $.Object,
. $.Pair ($.Unknown) ($.Unknown),
. $.RegExp,
. $.StrMap ($.Unknown),
. $.String,
. $.Symbol,
. $.Type,
. $.TypeClass,
. $.Undefined ]
unchecked :: Module
A complete Sanctuary module that performs no type checking. This is
useful as it permits operations that Sanctuary's type checking would
disallow, such as mapping over an object with heterogeneous values.See also
create
.> S.unchecked.map (S.show) ({x: 'foo', y: true, z: 42})
{x: '"foo"', y: 'true', z: '42'}
Opting out of type checking may cause type errors to go unnoticed.
> S.unchecked.add (2) ('2')
'22'
❑ Classify
type :: Any -> { namespace :: Maybe String, name :: String, version :: NonNegativeInteger }
Returns the result of parsing the type identifier of the given value.> S.type (S.Just (42))
{namespace: Just ('sanctuary-maybe'), name: 'Maybe', version: 1}
> S.type ([1, 2, 3])
{namespace: Nothing, name: 'Array', version: 0}
is :: Type -> Any -> Boolean
Returns true
iff the given value is a member of the specified type.
See $.test
for details.> S.is ($.Array ($.Integer)) ([1, 2, 3])
true
> S.is ($.Array ($.Integer)) ([1, 2, 3.14])
false
❑ Showable
show :: Any -> String
Alias of show
.> S.show (-0)
'-0'
> S.show (['foo', 'bar', 'baz'])
'["foo", "bar", "baz"]'
> S.show ({x: 1, y: 2, z: 3})
'{"x": 1, "y": 2, "z": 3}'
> S.show (S.Left (S.Right (S.Just (S.Nothing))))
'Left (Right (Just (Nothing)))'
❑ Fantasy Land
Sanctuary is compatible with the Fantasy Land specification.equals :: Setoid a => a -> a -> Boolean
Curried version of Z.equals
that 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
lt :: Ord a => a -> a -> Boolean
Returns true
iff the second argument is less than the first
according to Z.lt
.> S.filter (S.lt (3)) ([1, 2, 3, 4, 5])
[1, 2]
lte :: Ord a => a -> a -> Boolean
Returns true
iff the second argument is less than or equal to
the first according to Z.lte
.> S.filter (S.lte (3)) ([1, 2, 3, 4, 5])
[1, 2, 3]
gt :: Ord a => a -> a -> Boolean
Returns true
iff the second argument is greater than the first
according to Z.gt
.> S.filter (S.gt (3)) ([1, 2, 3, 4, 5])
[4, 5]
gte :: Ord a => a -> a -> Boolean
Returns true
iff the second argument is greater than or equal
to the first according to Z.gte
.> S.filter (S.gte (3)) ([1, 2, 3, 4, 5])
[3, 4, 5]
min :: Ord a => a -> a -> a
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'
max :: Ord a => a -> a -> a
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'
clamp :: Ord a => a -> a -> a -> a
Takes a lower bound, an upper bound, and a value of the same type.
Returns the value if it is within the bounds; the nearer bound otherwise.See also
min
and max
.> S.clamp (0) (100) (42)
42
> S.clamp (0) (100) (-1)
0
> S.clamp ('A') ('Z') ('~')
'Z'
id :: Category c => TypeRep c -> c
Type-safesanctuary-def version of Z.id
.> S.id (Function) (42)
42
concat :: Semigroup a => a -> a -> a
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)
empty :: Monoid a => TypeRep a -> a
Type-safesanctuary-def version of Z.empty
.> S.empty (String)
''
> S.empty (Array)
[]
> S.empty (Object)
{}
> S.empty (Sum)
Sum (0)
invert :: Group g => g -> g
Type-safesanctuary-def version of Z.invert
.> S.invert (Sum (5))
Sum (-5)
filter :: Filterable f => (a -> Boolean) -> f a -> f a
Curried version of Z.filter
. Discards every element that does not
satisfy the predicate.See also
reject
.> S.filter (S.odd) ([1, 2, 3])
[1, 3]
> S.filter (S.odd) ({x: 1, y: 2, z: 3})
{x: 1, z: 3}
> S.filter (S.odd) (S.Nothing)
Nothing
> S.filter (S.odd) (S.Just (0))
Nothing
> S.filter (S.odd) (S.Just (1))
Just (1)
reject :: Filterable f => (a -> Boolean) -> f a -> f a
Curried version of Z.reject
. Discards every element that satisfies
the predicate.See also
filter
.> S.reject (S.odd) ([1, 2, 3])
[2]
> S.reject (S.odd) ({x: 1, y: 2, z: 3})
{y: 2}
> S.reject (S.odd) (S.Nothing)
Nothing
> S.reject (S.odd) (S.Just (0))
Just (0)
> S.reject (S.odd) (S.Just (1))
Nothing
map :: Functor f => (a -> b) -> f a -> f b
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)
> S.map (Math.sqrt) (S.Pair (99980001) (99980001))
Pair (99980001) (9999)
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
flip :: Functor f => f (a -> b) -> a -> f b
Curried version of Z.flip
. Maps over the given functions, applying
each to the given value.Replacing
Functor f => f
with Function x
produces the C combinator
from combinatory logic:Functor f => f (a -> b) -> a -> f b
Function x (a -> b) -> a -> Function x b
Function x (a -> c) -> a -> Function x c
Function x (b -> c) -> b -> Function x c
Function a (b -> c) -> b -> Function a c
(a -> b -> c) -> b -> a -> c
> S.flip (S.concat) ('!') ('foo')
'foo!'
> S.flip ([Math.floor, Math.ceil]) (1.5)
[1, 2]
> S.flip ({floor: Math.floor, ceil: Math.ceil}) (1.5)
{floor: 1, ceil: 2}
> S.flip (Cons (Math.floor) (Cons (Math.ceil) (Nil))) (1.5)
Cons (1) (Cons (2) (Nil))
bimap :: Bifunctor f => (a -> b) -> (c -> d) -> f a c -> f b d
Curried version of Z.bimap
.> S.bimap (S.toUpper) (Math.sqrt) (S.Pair ('foo') (64))
Pair ('FOO') (8)
> S.bimap (S.toUpper) (Math.sqrt) (S.Left ('foo'))
Left ('FOO')
> S.bimap (S.toUpper) (Math.sqrt) (S.Right (64))
Right (8)
mapLeft :: Bifunctor f => (a -> b) -> f a c -> f b c
Curried version of Z.mapLeft
. Maps the given function over the left
side of a Bifunctor.> S.mapLeft (S.toUpper) (S.Pair ('foo') (64))
Pair ('FOO') (64)
> S.mapLeft (S.toUpper) (S.Left ('foo'))
Left ('FOO')
> S.mapLeft (S.toUpper) (S.Right (64))
Right (64)
promap :: Profunctor p => (a -> b) -> (c -> d) -> p b c -> p a d
Curried version of Z.promap
.> S.promap (Math.abs) (S.add (1)) (Math.sqrt) (-100)
11
alt :: Alt f => f a -> f a -> f a
Curried version of Z.alt
with arguments flipped to facilitate
partial application.> S.alt (S.Just ('default')) (S.Nothing)
Just ('default')
> S.alt (S.Just ('default')) (S.Just ('hello'))
Just ('hello')
> S.alt (S.Right (0)) (S.Left ('X'))
Right (0)
> S.alt (S.Right (0)) (S.Right (1))
Right (1)
zero :: Plus f => TypeRep f -> f a
Type-safesanctuary-def version of Z.zero
.> S.zero (Array)
[]
> S.zero (Object)
{}
> S.zero (S.Maybe)
Nothing
reduce :: Foldable f => (b -> a -> b) -> b -> f a -> b
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.See also
reduce_
.> S.reduce (S.add) (0) ([1, 2, 3, 4, 5])
15
> S.reduce (xs => x => S.prepend (x) (xs)) ([]) ([1, 2, 3, 4, 5])
[5, 4, 3, 2, 1]
reduce_ :: Foldable f => (a -> b -> b) -> b -> f a -> b
Variant of reduce
that takes a reducing function with
arguments flipped.> S.reduce_ (S.append) ([]) (Cons (1) (Cons (2) (Cons (3) (Nil))))
[1, 2, 3]
> S.reduce_ (S.prepend) ([]) (Cons (1) (Cons (2) (Cons (3) (Nil))))
[3, 2, 1]
traverse :: (Applicative f, Traversable t) => TypeRep f -> (a -> f b) -> t a -> f (t b)
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
sequence :: (Applicative f, Traversable t) => TypeRep f -> t (f a) -> f (t a)
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
ap :: Apply f => f (a -> b) -> f a -> f b
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'
lift2 :: Apply f => (a -> b -> c) -> f a -> f b -> f c
Promotes a curried binary function to a function that 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)
lift3 :: Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
Promotes a curried ternary function to a function that 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
apFirst :: Apply f => f a -> f b -> f a
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)
apSecond :: Apply f => f a -> f b -> f b
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)
of :: Applicative f => TypeRep f -> a -> f a
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)
chain :: Chain m => (a -> m b) -> m a -> m b
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
join :: Chain m => m (m a) -> m a
Type-safesanctuary-def 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)))
Just (1)
> S.join (S.Pair ('foo') (S.Pair ('bar') ('baz')))
Pair ('foobar') ('baz')
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'
chainRec :: ChainRec m => TypeRep m -> (a -> m (Either a b)) -> a -> m b
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?']
extend :: Extend w => (w a -> b) -> w a -> w b
Curried version of Z.extend
.> S.extend (S.joinWith ('')) (['x', 'y', 'z'])
['xyz', 'yz', 'z']
> S.extend (f => f ([3, 4])) (S.reverse) ([1, 2])
[4, 3, 2, 1]
duplicate :: Extend w => w a -> w (w a)
Type-safesanctuary-def version of Z.duplicate
.
Adds one level of nesting to a comonadic structure.> S.duplicate (S.Just (1))
Just (Just (1))
> S.duplicate ([1])
[[1]]
> S.duplicate ([1, 2, 3])
[[1, 2, 3], [2, 3], [3]]
> S.duplicate (S.reverse) ([1, 2]) ([3, 4])
[4, 3, 2, 1]
extract :: Comonad w => w a -> a
Type-safesanctuary-def version of Z.extract
.> S.extract (S.Pair ('foo') ('bar'))
'bar'
contramap :: Contravariant f => (b -> a) -> f a -> f b
Type-safesanctuary-def version of Z.contramap
.> S.contramap (s => s.length) (Math.sqrt) ('Sanctuary')
3
❑ Combinator
I :: a -> a
The I combinator. Returns its argument. Equivalent to Haskell's id
function.> S.I ('foo')
'foo'
K :: a -> b -> a
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]
T :: a -> (a -> b) -> b
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
curry2 :: ((a, b) -> c) -> a -> b -> c
Curries the given binary function.> S.map (S.curry2 (Math.pow) (10)) ([1, 2, 3])
[10, 100, 1000]
curry3 :: ((a, b, c) -> d) -> a -> b -> c -> d
Curries the given ternary function.> const replaceString = S.curry3 ((what, replacement, string) =>
. string.replace (what, replacement)
. )
> replaceString ('banana') ('orange') ('banana icecream')
'orange icecream'
curry4 :: ((a, b, c, d) -> e) -> a -> b -> c -> d -> e
Curries the given quaternary function.> const createRect = S.curry4 ((x, y, width, height) =>
. ({x, y, width, height})
. )
> createRect (0) (0) (10) (10)
{x: 0, y: 0, width: 10, height: 10}
curry5 :: ((a, b, c, d, e) -> f) -> a -> b -> c -> d -> e -> f
Curries the given quinary function.> const toUrl = S.curry5 ((protocol, creds, hostname, port, pathname) =>
. protocol + '//' +
. S.maybe ('') (S.flip (S.concat) ('@')) (creds) +
. hostname +
. S.maybe ('') (S.concat (':')) (port) +
. pathname
. )
> toUrl ('https:') (S.Nothing) ('example.com') (S.Just ('443')) ('/foo/bar')
'https://example.com:443/foo/bar'
❑ Composition
compose :: Semigroupoid s => s b c -> s a b -> s a c
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
pipe :: Foldable f => f (Any -> Any) -> a -> b
Takes a sequence 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 a sequence
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
pipeK :: (Foldable f, Chain m) => f (Any -> m Any) -> m a -> m b
Takes a sequence of functions assumed to be unary that return values
with a Chain, and a value of that Chain, and returns the result
of applying the sequence of transformations to the initial value.In general terms,
pipeK
performs left-to-right Kleisli composition
of an sequence of functions. pipeK ([f, g, h]) (x)
is equivalent to
chain (h) (chain (g) (chain (f) (x)))
.> S.pipeK ([S.tail, S.tail, S.head]) (S.Just ([1, 2, 3, 4]))
Just (3)
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
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]
❑ Pair
Pair is the canonical product type: a value of typePair a b
always
contains exactly two values: one of type a
; one of type b
.The implementation is provided by sanctuary-pair.
Pair :: a -> b -> Pair a b
Pair's sole data constructor. Additionally, it serves as the
Pair type representative.> S.Pair ('foo') (42)
Pair ('foo') (42)
pair :: (a -> b -> c) -> Pair a b -> c
Case analysis for the Pair a b
type.> S.pair (S.concat) (S.Pair ('foo') ('bar'))
'foobar'
fst :: Pair a b -> a
fst (Pair (x) (y))
is equivalent to x
.> S.fst (S.Pair ('foo') (42))
'foo'
snd :: Pair a b -> b
snd (Pair (x) (y))
is equivalent to y
.> S.snd (S.Pair ('foo') (42))
42
swap :: Pair a b -> Pair b a
swap (Pair (x) (y))
is equivalent to Pair (y) (x)
.> S.swap (S.Pair ('foo') (42))
Pair (42) ('foo')
❑ Maybe
The Maybe type represents optional values: a value of typeMaybe a
is
either Nothing (the empty value) or a Just whose value is of type a
.The implementation is provided by sanctuary-maybe.
Maybe :: TypeRep Maybe
Maybe type representative.Nothing :: Maybe a
The empty value of type Maybe a
.> S.Nothing
Nothing
Just :: a -> Maybe a
Constructs a value of type Maybe a
from a value of type a
.> S.Just (42)
Just (42)
isNothing :: Maybe a -> Boolean
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
isJust :: Maybe a -> Boolean
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
maybe :: b -> (a -> b) -> Maybe a -> b
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_
and fromMaybe
.> S.maybe (0) (S.prop ('length')) (S.Just ('refuge'))
6
> S.maybe (0) (S.prop ('length')) (S.Nothing)
0
maybe_ :: (() -> b) -> (a -> b) -> Maybe a -> b
Variant of maybe
that 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
fromMaybe :: a -> Maybe a -> a
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
maybe
, fromMaybe_
, and
maybeToNullable
.> S.fromMaybe (0) (S.Just (42))
42
> S.fromMaybe (0) (S.Nothing)
0
fromMaybe_ :: (() -> a) -> Maybe a -> a
Variant of fromMaybe
that 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
justs :: (Filterable f, Functor f) => f (Maybe a) -> f a
Discards each element that is Nothing, and unwraps each element that is
a Just. Related to Haskell's catMaybes
function.See also
lefts
and rights
.> S.justs ([S.Just ('foo'), S.Nothing, S.Just ('baz')])
['foo', 'baz']
mapMaybe :: (Filterable f, Functor f) => (a -> Maybe b) -> f a -> f b
Takes a function and a structure, applies the function to each element
of the structure, and returns the "successful" results. If the result of
applying the function to an element is Nothing, the result is discarded;
if the result is a Just, the Just's value is included.> S.mapMaybe (S.head) ([[], [1, 2, 3], [], [4, 5, 6], []])
[1, 4]
> S.mapMaybe (S.head) ({x: [1, 2, 3], y: [], z: [4, 5, 6]})
{x: 1, z: 4}
maybeToNullable :: Maybe a -> Nullable a
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
maybeToEither :: a -> Maybe b -> Either a b
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
The Either type represents values with two possibilities: a value of typeEither a b
is either a Left whose value is of type a
or a Right whose
value is of type b
.The implementation is provided by sanctuary-either.
Either :: TypeRep Either
Either type representative.Left :: a -> Either a b
Constructs a value of type Either a b
from a value of type a
.> S.Left ('Cannot divide by zero')
Left ('Cannot divide by zero')
Right :: b -> Either a b
Constructs a value of type Either a b
from a value of type b
.> S.Right (42)
Right (42)
isLeft :: Either a b -> Boolean
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
isRight :: Either a b -> Boolean
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
either :: (a -> c) -> (b -> c) -> Either a b -> c
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.See also
fromLeft
and fromRight
.> S.either (S.toUpper) (S.show) (S.Left ('Cannot divide by zero'))
'CANNOT DIVIDE BY ZERO'
> S.either (S.toUpper) (S.show) (S.Right (42))
'42'
fromLeft :: a -> Either a b -> a
Takes a default value and an Either, and returns the Left value
if the Either is a Left; the default value otherwise.See also
either
and fromRight
.> S.fromLeft ('abc') (S.Left ('xyz'))
'xyz'
> S.fromLeft ('abc') (S.Right (123))
'abc'
fromRight :: b -> Either a b -> b
Takes a default value and an Either, and returns the Right value
if the Either is a Right; the default value otherwise.See also
either
and fromLeft
.> S.fromRight (123) (S.Right (789))
789
> S.fromRight (123) (S.Left ('abc'))
123
fromEither :: b -> Either a b -> b
Takes a default value and an Either, and returns the Right value
if the Either is a Right; the default value otherwise.The behaviour of
fromEither
is likely to change in a future release.
Please use fromRight
instead.> S.fromEither (0) (S.Right (42))
42
> S.fromEither (0) (S.Left (42))
0
lefts :: (Filterable f, Functor f) => f (Either a b) -> f a
Discards each element that is a Right, and unwraps each element that is
a Left.See also
rights
.> S.lefts ([S.Right (20), S.Left ('foo'), S.Right (10), S.Left ('bar')])
['foo', 'bar']
rights :: (Filterable f, Functor f) => f (Either a b) -> f b
Discards each element that is a Left, and unwraps each element that is
a Right.See also
lefts
.> S.rights ([S.Right (20), S.Left ('foo'), S.Right (10), S.Left ('bar')])
[20, 10]
tagBy :: (a -> Boolean) -> a -> Either a a
Takes a predicate and a value, and returns a Right of the value if it
satisfies the predicate; a Left of the value otherwise.> S.tagBy (S.odd) (0)
Left (0)
> S.tagBy (S.odd) (1)
Right (1)
encase :: Throwing e a b -> a -> Either e b
Takes a function that may throw and returns a pure function.> S.encase (JSON.parse) ('["foo","bar","baz"]')
Right (['foo', 'bar', 'baz'])
> S.encase (JSON.parse) ('[')
Left (new SyntaxError ('Unexpected end of JSON input'))
eitherToMaybe :: Either a b -> Maybe b
Converts an Either to a Maybe. A Left becomes Nothing; a Right becomes
a Just.See also
maybeToEither
.> S.eitherToMaybe (S.Left ('Cannot divide by zero'))
Nothing
> S.eitherToMaybe (S.Right (42))
Just (42)
❑ Logic
and :: Boolean -> Boolean -> Boolean
Boolean "and".> S.and (false) (false)
false
> S.and (false) (true)
false
> S.and (true) (false)
false
> S.and (true) (true)
true
or :: Boolean -> Boolean -> Boolean
Boolean "or".> S.or (false) (false)
false
> S.or (false) (true)
true
> S.or (true) (false)
true
> S.or (true) (true)
true
not :: Boolean -> Boolean
Boolean "not".See also
complement
.> S.not (false)
true
> S.not (true)
false
complement :: (a -> Boolean) -> a -> Boolean
Takes a unary predicate and a value of any type, and returns the logical
negation of applying the predicate to the value.See also
not
.> Number.isInteger (42)
true
> S.complement (Number.isInteger) (42)
false
boolean :: a -> a -> Boolean -> a
Case analysis for the Boolean
type. boolean (x) (y) (b)
evaluates
to x
if b
is false
; to y
if b
is true
.> S.boolean ('no') ('yes') (false)
'no'
> S.boolean ('no') ('yes') (true)
'yes'
ifElse :: (a -> Boolean) -> (a -> b) -> (a -> b) -> a -> b
Takes a unary predicate, a unary "if" function, a unary "else"
function, and a value of any type, and returns the result of
applying the "if" function to the value if the value satisfies
the predicate; the result of applying the "else" function to the
value otherwise.See also
when
and unless
.> S.ifElse (x => x < 0) (Math.abs) (Math.sqrt) (-1)
1
> S.ifElse (x => x < 0) (Math.abs) (Math.sqrt) (16)
4
when :: (a -> Boolean) -> (a -> a) -> a -> a
Takes a unary predicate, a unary function, and a value of any type, and
returns the result of applying the function to the value if the value
satisfies the predicate; the value otherwise.See also
unless
and ifElse
.> S.when (x => x >= 0) (Math.sqrt) (16)
4
> S.when (x => x >= 0) (Math.sqrt) (-1)
-1
unless :: (a -> Boolean) -> (a -> a) -> a -> a
Takes a unary predicate, a unary function, and a value of any type, and
returns the result of applying the function to the value if the value
does not satisfy the predicate; the value otherwise.See also
when
and ifElse
.> S.unless (x => x < 0) (Math.sqrt) (16)
4
> S.unless (x => x < 0) (Math.sqrt) (-1)
-1
❑ Array
array :: b -> (a -> Array a -> b) -> Array a -> b
Case analysis for the Array a
type.> S.array (S.Nothing) (head => tail => S.Just (head)) ([])
Nothing
> S.array (S.Nothing) (head => tail => S.Just (head)) ([1, 2, 3])
Just (1)
> S.array (S.Nothing) (head => tail => S.Just (tail)) ([])
Nothing
> S.array (S.Nothing) (head => tail => S.Just (tail)) ([1, 2, 3])
Just ([2, 3])
head :: Foldable f => f a -> Maybe a
Returns Just the first element of the given structure if the structure
contains at least one element; Nothing otherwise.> S.head ([1, 2, 3])
Just (1)
> S.head ([])
Nothing
> S.head (Cons (1) (Cons (2) (Cons (3) (Nil))))
Just (1)
> S.head (Nil)
Nothing
last :: Foldable f => f a -> Maybe a
Returns Just the last element of the given structure if the structure
contains at least one element; Nothing otherwise.> S.last ([1, 2, 3])
Just (3)
> S.last ([])
Nothing
> S.last (Cons (1) (Cons (2) (Cons (3) (Nil))))
Just (3)
> S.last (Nil)
Nothing
tail :: (Applicative f, Foldable f, Monoid (f a)) => f a -> Maybe (f a)
Returns Just all but the first of the given structure's elements if the
structure contains at least one element; Nothing otherwise.> S.tail ([1, 2, 3])
Just ([2, 3])
> S.tail ([])
Nothing
> S.tail (Cons (1) (Cons (2) (Cons (3) (Nil))))
Just (Cons (2) (Cons (3) (Nil)))
> S.tail (Nil)
Nothing
init :: (Applicative f, Foldable f, Monoid (f a)) => f a -> Maybe (f a)
Returns Just all but the last of the given structure's elements if the
structure contains at least one element; Nothing otherwise.> S.init ([1, 2, 3])
Just ([1, 2])
> S.init ([])
Nothing
> S.init (Cons (1) (Cons (2) (Cons (3) (Nil))))
Just (Cons (1) (Cons (2) (Nil)))
> S.init (Nil)
Nothing
take :: (Applicative f, Foldable f, Monoid (f a)) => Integer -> f a -> Maybe (f a)
Returns Just the first N elements of the given structure if N is
non-negative and less than or equal to the size of the structure;
Nothing otherwise.> S.take (0) (['foo', 'bar'])
Just ([])
> S.take (1) (['foo', 'bar'])
Just (['foo'])
> S.take (2) (['foo', 'bar'])
Just (['foo', 'bar'])
> S.take (3) (['foo', 'bar'])
Nothing
> S.take (3) (Cons (1) (Cons (2) (Cons (3) (Cons (4) (Cons (5) (Nil))))))
Just (Cons (1) (Cons (2) (Cons (3) (Nil))))
drop :: (Applicative f, Foldable f, Monoid (f a)) => Integer -> f a -> Maybe (f a)
Returns Just all but the first N elements of the given structure if
N is non-negative and less than or equal to the size of the structure;
Nothing otherwise.> S.drop (0) (['foo', 'bar'])
Just (['foo', 'bar'])
> S.drop (1) (['foo', 'bar'])
Just (['bar'])
> S.drop (2) (['foo', 'bar'])
Just ([])
> S.drop (3) (['foo', 'bar'])
Nothing
> S.drop (3) (Cons (1) (Cons (2) (Cons (3) (Cons (4) (Cons (5) (Nil))))))
Just (Cons (4) (Cons (5) (Nil)))
takeLast :: (Applicative f, Foldable f, Monoid (f a)) => Integer -> f a -> Maybe (f a)
Returns Just the last N elements of the given structure if N is
non-negative and less than or equal to the size of the structure;
Nothing otherwise.> S.takeLast (0) (['foo', 'bar'])
Just ([])
> S.takeLast (1) (['foo', 'bar'])
Just (['bar'])
> S.takeLast (2) (['foo', 'bar'])
Just (['foo', 'bar'])
> S.takeLast (3) (['foo', 'bar'])
Nothing
> S.takeLast (3) (Cons (1) (Cons (2) (Cons (3) (Cons (4) (Nil)))))
Just (Cons (2) (Cons (3) (Cons (4) (Nil))))
dropLast :: (Applicative f, Foldable f, Monoid (f a)) => Integer -> f a -> Maybe (f a)
Returns Just all but the last N elements of the given structure if
N is non-negative and less than or equal to the size of the structure;
Nothing otherwise.> S.dropLast (0) (['foo', 'bar'])
Just (['foo', 'bar'])
> S.dropLast (1) (['foo', 'bar'])
Just (['foo'])
> S.dropLast (2) (['foo', 'bar'])
Just ([])
> S.dropLast (3) (['foo', 'bar'])
Nothing
> S.dropLast (3) (Cons (1) (Cons (2) (Cons (3) (Cons (4) (Nil)))))
Just (Cons (1) (Nil))
takeWhile :: (a -> Boolean) -> Array a -> Array a
Discards the first element that does not satisfy the predicate,
and all subsequent elements.See also
dropWhile
.> 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])
[]
dropWhile :: (a -> Boolean) -> Array a -> Array a
Retains the first element that does not satisfy the predicate,
and all subsequent elements.See also
takeWhile
.> 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]
size :: Foldable f => f a -> NonNegativeInteger
Returns the number of elements of the given structure.> S.size ([])
0
> S.size (['foo', 'bar', 'baz'])
3
> S.size (Nil)
0
> S.size (Cons ('foo') (Cons ('bar') (Cons ('baz') (Nil))))
3
> S.size (S.Nothing)
0
> S.size (S.Just ('quux'))
1
> S.size (S.Pair ('ignored!') ('counted!'))
1
all :: Foldable f => (a -> Boolean) -> f a -> Boolean
Returns true
iff all the elements of the structure satisfy the
predicate.See also
any
and none
.> S.all (S.odd) ([])
true
> S.all (S.odd) ([1, 3, 5])
true
> S.all (S.odd) ([1, 2, 3])
false
any :: Foldable f => (a -> Boolean) -> f a -> Boolean
Returns true
iff any element of the structure satisfies the
predicate.See also
all
and none
.> S.any (S.odd) ([])
false
> S.any (S.odd) ([2, 4, 6])
false
> S.any (S.odd) ([1, 2, 3])
true
none :: Foldable f => (a -> Boolean) -> f a -> Boolean
Returns true
iff none of the elements of the structure satisfies
the predicate.Properties:
- `forall p :: a -> Boolean, xs :: Foldable f => f a.
S.none (p) (xs) = S.not (S.any (p) (xs))`
- `forall p :: a -> Boolean, xs :: Foldable f => f a.S.none (p) (xs) = S.all (S.complement (p)) (xs)`
See also all
and any
.> S.none (S.odd) ([])
true
> S.none (S.odd) ([2, 4, 6])
true
> S.none (S.odd) ([1, 2, 3])
false
append :: (Applicative f, Semigroup (f a)) => a -> f a -> f a
Returns the result of appending the first argument to the second.See also
prepend
.> S.append (3) ([1, 2])
[1, 2, 3]
> S.append (3) (Cons (1) (Cons (2) (Nil)))
Cons (1) (Cons (2) (Cons (3) (Nil)))
> S.append ([1]) (S.Nothing)
Just ([1])
> S.append ([3]) (S.Just ([1, 2]))
Just ([1, 2, 3])
prepend :: (Applicative f, Semigroup (f a)) => a -> f a -> f a
Returns the result of prepending the first argument to the second.See also
append
.> S.prepend (1) ([2, 3])
[1, 2, 3]
> S.prepend (1) (Cons (2) (Cons (3) (Nil)))
Cons (1) (Cons (2) (Cons (3) (Nil)))
> S.prepend ([1]) (S.Nothing)
Just ([1])
> S.prepend ([1]) (S.Just ([2, 3]))
Just ([1, 2, 3])
joinWith :: String -> Array String -> String
Joins the strings of the second argument separated by the first argument.Properties:
- `forall s :: String, t :: String.
S.joinWith (s) (S.splitOn (s) (t)) = t`
See also splitOn
(#s