-
Notifications
You must be signed in to change notification settings - Fork 0
03 The prelude
Sibilisp ships with a Fantasy-Land inspired functional programming toolkit, called the prelude. You can use functions and structures defined in the prelude by importing them directly from sibilisp/prelude via Sibilisp's (use).
(use "sibilisp/prelude" identity map)- Logic functions
- Type checking
- Function-manipulating functions
- Generic operations
- List and MSet operations
- Lenses
- Algebraic types
- Algebraic type coercion
Basic functions from combinatorial logic.
Signature
identity :: a -> a
The identity function returns the given argument.
(identity a) ; => aSignature
constantly :: a -> () -> a
The constantly function takes a value and returns a constant function that always returns the value.
(constantly a) ; => (() -> a)Helper functions to check types and collections.
Signature
list-of? :: List L => (L a (a -> Boolean)) -> Boolean
Takes a list and a predicate function and tests if each element in the list passes the predicate. Returns a boolean.
(list-of? ls (#-> (string?))) ; => BooleanSignature
hash-of? :: Hash H => (H a (a -> Boolean)) -> Boolean
Takes a hash and a predicate function and tests if each element in the hash passes the predicate. Returns a boolean.
(hash-of? hs (#-> (string?))) ; => BooleanSignature
dict-of? :: Dict D => (D a (a -> Boolean)) -> Boolean
Takes a dict and a predicate function and tests if each element in the dict passes the predicate. Returns a boolean.
(dict-of? dt (#-> (string?))) ; => BooleanSignature
mset-of? :: MSet M => (M a (a -> Boolean)) -> Boolean
Takes a mset and a predicate function and tests if each element in the mset passes the predicate. Returns a boolean.
(mset-of? ms (#-> (string?))) ; => BooleanSome higher-order-functions
Signature
converge :: ((&rest a) -> b), (list &rest (c -> a)) -> (&rest c) -> b
Takes a multary function and a list of unary functions and returns a unary function, that passes the given argument through the list of unary functions and passes the resulting values as arguments to the multary function.
(defun greet (first-name last-name)
(+ "Hello " first-name " " last-name))
(call
(converge greet (list (#-> (getf 'first))
(#-> (getf 'last))))
(hash :first "John"
:last "Doe")) ; => "Hello, John Doe"Signature
memoize :: ((&rest a) -> b) -> (&rest a) -> b
Takes an n-ary function and returns a function that caches and returns the results of calls with equal arguments.
(defun compute (a b)
(.log console (+ "Got: " a " & " b)) ; side effect for demonstation
(+ a b))
(defconstant mcompute (memoize compute))
(mcompute 1 2) ; => 3, "Got 1 & 2"
(mcompute 1 2) ; => 3Helper functions for working with differnent kinds of structures.
Note
The term 'unknown type' refers to types that are not part of the runtime. In other words, all of the prelude's algebraic types as well as types that are defined by the programmer fall into this category.
Signature
show :: a -> String
Takes a value of any type and returns a string representation of it. Expands the inner fields of structures (if any) into a recursive call to show. If an unknown type is given that implements a to-string method, this is called. Otherwise for unknown types, the constructor's name is used.
(show 1) ; => "(number 1)"
(show "a string") ; => "(string "a string")"
(show (hash :a 1)) ; => "(hash :a (number 1))"
(show (coyo.of 1)) ; => "(coyo (number 1) (function))"
;; --- unknown type
(deftype foo (x))
(show (foo 1)) ; => "(foo)"Signature
equals :: Setoid S => (S a, S a) -> Boolean
Takes two values of any type and compares them for deep structural and value equality. This means that for example a list with the numbers 1, 2, and 3, is considered equal to itself as well as to any list that has the same length and contains the same numbers. If two unknown types are given and the first type implements an equals method, it is used for comparison. If the type is unknown and no equals method is implemented, false is returned.
(equals 1 1) ; => true
(equals "a" "a") ; => true
(equals "a" "A") ; => false
(let ((a (list 1 2 3))
(b (list 1 2 3)))
(equals a b)) ; => true
;; --- unknown type
(deftype foo (x))
(defmethod foo equals (a)
(and (.is foo a)
(eql? (getf this 'x)
(getf a 'x))))
(let ((a (foo 1))
(b (foo 1)))
(equals a b)) ; => trueSignature
concatenate :: Semigroup S => (S a, S a) -> S a
Takes two values of any type that belong to the same Semigroup (string, function, list, mset, hash, dict, future, unknown) and concatenates them together. If unknown types are given and the first type implements a concat method, it is used for concatenation. If a type does not belong to a semigroup, an error is thrown.
(concatenate "a" "b") ; => "ab"
(concatenate (list 1) (list 2 3)) ; => (list 1 2 3)
;; --- unknown type
(deftype sum (x))
(defmethod sum concat (a)
(if (.is sum a)
(sum (+ (getf this 'x)
(getf a 'x)))))
(concatenate (sum 1) (sum 2)) ; => (sum 3) Signature
map :: Functor F => (F a, (a -> b)) -> F b
Takes a value of any type that belongs to the Functor types (function, list, mset, hash, dict, future, unknown) and a function in the shape (a -> b) and projects the function over the value in the functor. If an unknown type is given and it implements a map method, it is used for projecting the function over the value. If a type is not a functor, an error is thrown.
(let ((a (list 1 2 3))
(f (#-> (+ 1))))
(map a f)) ; => (list 2 3 4)
(let ((a (#-> (.split "")))
(b (#-> (.reverse))))
(map a b)) ; => (lambda (x) (.reverse (.split x "")))
;; --- unknown type
(deftype foo (x))
(defmethod foo map (f)
(foo (f (getf this 'x))))
(map (foo 1) (#-> (+ 1))) ; => (foo 2)Signature
ap :: Functor F, Applicative A => (F a, A (a -> b)) -> F b
Takes two values of any type that belong to the Functor types (function, list, mset, hash, dict, future, unknown) and the Applicative types (function, list, mset, hash, dict, future, unknown) and applies the functions in the applicative to the values in the functor. If unknown types are given and the first and the first type implements a ap method, it is used for application. If the first type is not a functor, an error is thrown.
(defun add (n) (#-> (+ n)))
(defun mul (n) (#-> (* n)))
(let ((f (list (add 1) (mul 2))))
(ap (list 1 2 3) f)) ; => (list 4 6 8)
(let ((f (hash :a (add 1) :b (mul 2))))
(ap (hash :a 1 :b 2 :c 3) f)) ; => (hash :a 2 :b 4 :c 3)
;; --- unknown type
(deftype foo (x))
(defmethod foo map (f)
(foo (f (getf this 'x))))
(defmethod foo ap (f)
(foo (call (getf this 'x) (getf f 'x))))
(ap (foo 1) (foo (#-> (+ 1)))) ; => (foo 2)Signature
flat-map :: (M a, (a -> M b)) -> M b
chain :: (M a, (a -> M b)) -> M b
Takes a value of any type that belongs to the Monad types (function, list, future, unknown) and a function in the shape Monad M. (a -> M b) and projects the function over the value in the monad. If an unknown type is given and it implements a flat-map or chain method, it is used for projecting the function over the value. If a type is not a monad, an error is thrown.
(let ((a (list 1 2 3))
(f (#-> (+ 1) (list))))
(chain a f)) ; => (list 2 3 4)
;; --- unknown type
(deftype foo (x))
(defmethod foo flat-map (f)
(f (getf this 'x)))
(chain (foo 1) (#-> (+ 1) (foo))) ; => (foo 2)Signature
contramap :: Contravariant C => (C a, (a -> b)) -> C b
Takes a value of any type that belongs to the Contravariant Functor types (list, mset, future, unknown) and a function in the shape (a -> b) and projects the function over the value in the contravariant functor. If an unknown type is given and it implements a contramap method, it is used for projecting the function over the value. If a type is not a contravariant functor, an error is thrown.
(let ((a (list))
(f (#> 1)))
(contramap a f)) ; => (list 1)
;; --- unknown type
(deftype foo (x))
(defmethod foo contramap (f)
(ternary (not (mod (getf this 'x) 3))
(foo (f))
(foo (getf this 'x))))
(contramap (foo 1) (#> 2)) ; => (foo 1)
(contramap (foo 9) (#> 2)) ; => (foo 2)Signature
bimap :: Bifunctor B => (B a c, (a -> b), (c -> d)) -> B b d
Takes a value of any type that belongs to the Bifunctor types (list, mset, future, unknown) and two functions of which the first one has the shape (() -> a) and the second one has the shape (a -> b). Projects the first function if the type is empty (contravariant) and the second one if the type is not empty (functor). If an unknown type is given and it implements a .bimap method, it is used for projection. If a type is not a bifunctor, an error is thrown.
(let ((a (list))
(f (#> 1))
(g (#> #0)))
(bimap a f g)) ; => (list 1)
(let ((a (list 2))
(f (#> 1))
(g (#> #0)))
(bimap a f g)) ; => (list 2)
;; --- unknown type
(deftype foo (x))
(defmethod foo bimap (f g)
(ternary (mod (getf this 'x) 3)
(foo (f (getf this 'x)))
(foo (g))))
(bimap (foo 1) (#> 3) (#> #0)) ; => (foo 1)
(bimap (foo 9) (#> 3) (#> #0)) ; => (foo 3)Signature
promap :: Profunctor P => (P (b -> c), (a -> b), (c -> d)) -> P (a -> d)
Takes a value of any type that belongs to the Profunctor types (function, unknown) and two functions of which the first one has the shape (a -> b) and the second one has the shape (c -> d), returning a profunctor that contains the projection from the first function into the profunctor and then the result into the second function. If an unknown type is given and it implements a .promap method, it is used for projection. If a type is not a profunctor, an error is thrown.
(let ((f (#> 3))
(g (#-> (* 3)))
(h (#-> (+ 1))))
(promap g f h)) ; (lambda () (+ 1 (* 3 3)))
;; --- unknown type
(deftype foo (x))
(defmethod foo promap (f g)
(let ((x (getf this 'x)))
(foo (#-> (f) (x) (g)))))
(bimap (foo (#-> (+ ", ")))
(#> "Hello")
(#-> (+ "World"))) ; => (foo (lambda () "Hello, World"))Signature
reduce :: Foldable F => (F a, ((b, a) -> b), b) -> b
Takes a value of any type that belongs to the Foldable types (list, mset, unknown), a function of the shape ((a, b) -> a) and a seed value that becomes a. Uses the function to accumulate the value inside the foldable into the seed value. If an unknown type is given and it implements a .reduce method, it is used for reduction. If a type is not a foldable, an error is thrown.
(let ((a (list 1 2 3))
(f (#(s n) (+ s n))))
(reduce a f 0)) ; => 6
;; --- unknown type
(deftype foo (x))
(defmethod foo reduce (f a)
(f a (getf this 'x)))
(reduce (foo 1)
(#(a b) (+ 2 a b))
3) ; => 6Signature
fold-map :: Foldable F, Semigroup S => (F a, (a -> S b)) -> S b
Takes a value of any type that belongs to the Foldable types (list, mset, unknown) and a function in the shape Semigroup S => (a -> S b). Uses the function to accumulate the value inside the foldable into a semigroup returned by the function. If an unknown type is given and it implements a .fold-map method, it is used for reduction, otherwise .reduce is used. If a type is not a foldable, an error is thrown.
(deftype sum (n))
(defmethod sum concat (s)
(sum (+ (getf s 'n) (getf this 'n))))
(fold-map (list 1 2 3) (#-> (+ 1) (sum))) ; => (sum 9)Signature
fold :: Foldable F, Semigroup S => (F a, (a -> S b)) -> S b
Takes a value of any type that belongs to the Foldable types (list, mset, unknown) and a function that lifts a value into a Semigroup. Accumulates the value inside the foldable into the semigroup. If an unknown type is given and it implements a .fold method, it is used for reduction. If a type is not a foldable, an error is thrown.
(deftype sum (n))
(defmethod sum concat (s)
(sum (+ (getf s 'n) (getf this 'n))))
(fold (list 1 2 3) sum) ; => (sum 6)Signature
traverse :: Traversable T, Applicative A => (T a, (b -> A b), (a -> A b)) -> A T b
Takes a value of any type that belongs to the Traversable types (list, mset, unknown), a function that lifts a value into an Applicative and a transformer function of the shape Applicative A. (a -> A b). Traverses the traversable with the transformer function. If an unknown type is given and it implements a .traverse method, it is used for traversal. If a type is not a traversable, an error is thrown.
(deftype foo (n))
(defmethod foo map (f)
(foo (f (getf this 'n))))
(defmethod foo ap (ff)
(foo (call (getf this 'n) (getf ff 'n))))
(traverse (list 1 2 3)
foo.lift
(#(n) (foo (+ n 1)))) ; => (foo (list 2 3 4))Signature
sequence :: Traversable T, Applicative A => (T A a, (b -> A b)) -> A T b
Takes a value of any type that belongs to the Traversable types (list, mset, unknown) and a function that lifts a value into an Applicative. Inverts the shape of the traversable and the values in it. If an unknown type is given and it implements a .sequence method, it is used for traversal. If a type is not a traversable, an error is thrown.
(deftype foo (n))
(defmethod foo map (f)
(foo (f (getf this 'n))))
(defmethod foo ap (ff)
(foo (call (getf this 'n) (getf ff 'n))))
(sequence (list (foo 1) (foo 2) (foo 3))
foo) ; => (foo (list 1 2 3))Signature
alt :: Alt A => (A a, A a) -> A a
Takes two values that belongs to the Alt types (list, mset, unknown) of which the first is the value and the second is the fallback value. Returns the fallback value if the given value is empty. If an unknown type is given and it implements a .alt method, it is used for selection. If a type is not an alt, an error is thrown.
(let ((a (list))
(fallback (list 2)))
(alt a fallback)) ; => (list 2)
(let ((a (list 1))
(fallback (list 2)))
(alt a fallback)) ; => (list 1)
;; --- unknown type
(deftype foo (x))
(defmethod foo alt (f)
(if (nothing? (getf this 'x))
f
this))
(alt (foo nil) (foo 2)) ; => (foo 2)
(alt (foo 1) (foo 2)) ; => (foo 1)Signature
clone :: a -> a
Takes a value of any type that is clonable (string, number, function, boolean, nil, void, list, mset, hash, dict, date, regex, unknown) and returns clone of it. Primitive types are not cloned since they are compared by value, complex types are cloned recursively. If a non clonable type is given, an error is thrown.
(let ((a (list 1 2 3)))
(clone a)) ; => (list 1 2 3)Functions that handle collections
Signature
zip :: List L => (L a, L b) -> L L c
zip :: Mset M, List L => (M a, M b) -> L L c
Takes two collections and packs them together into a list of lists of key-value pairs. Uses the contents of the first list for the keys and the contents of the second list as values. Only produces pairs up to the length of the shortest list.
(let ((a (list 1 2 3))
(b (list 'a 'b 'c)))
(zip a b)) ; => (list (list 1 'a) (list 2 'b) (list 3 'c))Signature
unzip :: List L => L L a -> L L b
Signature
find :: List L, Maybe M => (L a, (a -> Boolean)) -> M a
find :: Mset L, Maybe M => (L a, (a -> Boolean)) -> M a
Takes a collection and a predicate function and returns a maybe.some with the first item that satisfies the predicate or a maye.nothing if no item is found.
(let ((a (list 1 2 3))
(f (#-> (< 2))))
(find a f)) ; => (maybe.some 1)Signature
filter :: List L => (L a, (a -> Boolean)) -> L a
filter :: Mset M => (M a, (a -> Boolean)) -> M a
select :: List L => (L a, (a -> Boolean)) -> L a
select :: Mset M => (M a, (a -> Boolean)) -> M a
Takes a collection and a predicate function and returns a collection of the same type containing all items that pass the predicate function.
(let ((a (list 1 2 3))
(f (#-> (<= 2))))
(filter a f)) ; => (list 1 2)Signature
reject :: List L => (L a, (a -> Boolean)) -> L a
reject :: Mset M => (M a, (a -> Boolean)) -> M a
The opposite of filter/select, takes a collection and a predicate function and returns a collection of the same type containing all items the did not pass the predicate function.
(let ((a (list 1 2 3))
(f (#-> (<= 2))))
(reject a f)) ; => (list 3)Signature
unique :: List L => L a -> L a
unique :: Mset M => M a -> M a
Takes a collection and returns a collection of the same type that contains only unique items. This function is mainly useful for lists, since msets usually only contain unique items by default.
(let ((a (list 1 2 2 3 1 1 2 3)))
(uniqe a)) ; => (list 1 2 3)Signature
union :: List L => (L a, L a) -> L a
union :: Mset M => (M a, M a) -> M a
Takes two collections and returns a collection of the same type that is the union of both collections.
(let ((a (list 1 2 3))
(b (list 1 3 5)))
(union a b)) ; => (list 1 2 3 5)Signature
intersection :: List L => (L a, L a) -> L a
intersection :: Mset M => (M a, M a) -> M a
Takes two collections and returns a collection of the same type that only contains items found in both collections.
(let ((a (list 1 2 3))
(b (list 1 3 5)))
(intersection a b)) ; => (list 1 3)Signature
difference :: List L => (L a, L a) -> L a
difference :: Mset M => (M a, M a) -> M a
Takes two collections and returns a collection of the same type that only contains items found in either the first or second collection but not in both.
(let ((a (list 1 2 3))
(b (list 1 3 5)))
(difference a b)) ; => (list 2 5)Signature
take :: List L => (L a, Number) -> L a
take :: Mset M => (M a, Number) -> M a
Takes a collection and a number and returns a collection of the same type containing as much items from the beginning of the given collection as the second argument indicates.
(let ((a (list 1 2 3)))
(take a 2)) ; => (list 1 2)Signature
drop :: List L => (L a, Number) -> L a
drop :: Mset M => (M a, Number) -> M a
Takes a collection and a number and returns a collection of the same type where as many items from the beginning of the collection as the second argument indicates are dropped.
(let ((a (list 1 2 3)))
(drop a 2)) ; => (list 3)Signature
partition :: List L => (L a, Number) -> L L a
partition :: Mset M => (M a, Number) -> M L a
Takes a collection and a number and returns a collection of the same type which itself contains lists with as many items inside as the second argument defines. If the (rest) collection cannot be partitioned according to the second argument, a shorter partition is created.
(let ((a (list 1 2 3 4 5)))
(partition a 3)) ; => (list (list 1 2 3) (list 4 5))Signature
partition-with :: List L => (L a, (a -> Boolean)) -> L L a
partition-with :: Mset M => (M a, (a -> Boolean)) -> M L a
Works like partition, but takes a predicate function and creates new partitions whenever the predicate changes it's return value.
(let ((a (list 1 2 3 4 5))
(f (#-> (mod 3) (eql? 0))))
(partition-with a f)) ; => (list (list 1 2) (list 3) (list 4 5))Signature
keep :: List L => L a -> L a
keep :: Mset M => M a -> M a
Takes a collection and returns a collection of the same type with all nil or void items removed.
(let ((a (list 1 (nil) 3)))
(keep a)) ; => (list 1 3)Van Laarhoven style lenses for hash and dict structures. Lenses are a non-destructive and pure way to work with data structures like hashes.
Signature
create-lens :: (((k, a k y) -> y), ((k, y, a k y) -> a k y)) -> k -> (a k y, (y -> z)) -> a k z
Takes a getter and a setter function and returns a lens. Useful to create new types of lenses for new structures (also see hash-lens and dict-lens).
Signature
hash-lens :: Hash H => (&rest k) -> (H k a, (a -> b)) -> H k b
Takes a variadic amount of "keys" and returns a lens for each "key" that works on hash structures.
(defconstant **L (hash-lens 'first 'last))Signature
dict-lens :: Dict D => (&rest k) -> (D k a, (a -> b)) -> D k b
Like hash-lens, but the resulting lenses work on dict structures instead.
(defconstant **L (dict-lens 'first 'last))Signature
lget :: (a k y, ((a k y, (y -> z)))) -> z
Takes a structure and a lens and returns the value inside the structure.
(defconstant **L (hash-lens 'first))
(let ((u (hash :first "John" :last "Doe")))
(lget u **L.first)) ; => "John"Signature
lset :: (a k y, ((a k y, (y -> z))), z) -> a k z
Takes a structure, a lens and a value and returns a new structure of the same type with the key set to the new value.
(defconstant **L (hash-lens 'first))
(let ((u (hash :first "John" :last "Doe")))
(lset u **L.first "Adam")) ; => (hash :first "Adam" :last "Doe")Signature
lmap :: (a k y, ((a k y, (y -> z))), (y -> z)) -> a k z
Takes a structure, a lens and a function. Projects the function over the value in the structure and returns a new structure of the same type with the new value.
(defconstant **L (hash-lens 'first))
(let ((u (hash :first "John" :last "Doe")))
(lmap u ; => (hash :first "JOHN" :last "Doe")
**L.first
(#-> (.to-upper-case))))Common algebraic types, ready to use.
Signature
coyo :: a -> coyo a (a -> b)
The coyo structure allows to lift a non-functor value into the realm of functors. It implements the following methods:
c = coyo instance
| Method | Static? | Interface | Example |
|---|---|---|---|
of |
Yes | Pointed | (coyo.of 1) |
lift |
Yes | (coyo.lift 1) |
|
to-string |
Show | (.to-string c) |
|
map |
Functor | (.map c (#-> ... )) |
|
lower |
(.lower c) |
||
reduce |
Foldable | (.reduce c (#(x v) ... ) x) |
(|> (coyo.lift "abcdef")
(.map (#-> (.split "")))
(.map (#-> (.reverse)))
(.map (#-> (.join "")))
(.reduce (#(a v) (+ a v)) ""))Signature
free :: a -> free.result a | free.compute a (free a)
free is a very useful monad that allows to build interpretable EDSL's from custom primitives into a program.
f, ff = free instances
| Method | Static? | Interface | Examples |
|---|---|---|---|
of |
Yes | Pointed | (free.of 1) |
lift |
Yes | (free.lift T) |
|
map |
Functor | (.map f (#-> ... )) |
|
ap |
Applicative | (.ap f ff) |
|
flat-map / chain
|
Monad | (.flat-map f (#-> ... )) |
|
fold-map / interpret
|
Foldable | (.fold-map f (#-> ... ) T) |
;;; --- Primitive operations
(defsum http ((:GET url headers)
(:POST url data headers)))
(defun get (url)
(free.lift
(http.GET url (hash :"Accept" "application/json"))))
(defun post (url data)
(free.lift
(http.POST url data (hash :"Content-Type" "application/json"
:"Accept" "application/json"))))
;;; --- Interpreter function (=> natural transformation)
(defun interpret-task (x)
(match-sum x ((:GET (url headers)
(task (#(rej res)
(|>
(fetch url (hash :method "GET"
:&headers))
(.then map-http-status-to-state)
(.then res)
(.catch rej)))))
(:POST (url data headers)
(task (#(rej res)
(|>
(fetch url (hash :method "POST"
:&headers
:body (json-to-string data)))
(.then map-http-status-to-state)
(.then res)
(.catch rej))))))))
(defun map-http-status-to-state (response)
(with-fields respone (ok json status status-text)
(if ok
(json)
(future-reject (error status ":" status-text))))
;;; --- Program
(defun prog (user-id)
(|>
(get (+ "https://platform.tld/user/" user-id "/settings"))
(.flat-map (#(settings)
(post (+ "https://platform.tld/user/" user-id "/settings/save")
(hash-merge settings
(hash :key "value")))))
(.interpret interpret-task task)))
(|> (prog "jdoe")
(.run-task (#-> (console.warn))
(#> (console.log "Done!"))))Signature
io :: (a -> b) -> io (a -> b)
io encapsulates lazy function composition and application into a structure, useful for operations that have side-effects. It implements the following methods:
i, ii = io instances
| Method | Static? | Interface | Examples |
|---|---|---|---|
of |
Yes | Pointed | (io.of 1) |
lift |
Yes | (io.lift 1) |
|
empty |
Yes | Monoid | (io.empty) |
identity |
Yes | Category | (io.identity) |
to-string |
Show | (.to-string i) |
|
equals |
Setoid | (.equals i ii) |
|
concat |
Semigroup | (.concat i ii) |
|
map |
Functor | (.map i (#-> ... )) |
|
contramap |
Contravariant | (.contramap i (#-> ... )) |
|
promap |
Profunctor | (.promap i (#-> ... ) (#-> ... )) |
|
ap |
Applicative | (.ap i ii) |
|
flat-map / chain
|
Monad | (.flat-map i (#-> ... )) |
|
compose |
Semigroupoid | (.compose i ii) |
|
run-io |
(.run-io i) |
(|> (io.lift (#-> (.query-selector "#my-div")))
(.map (#-> (.get-attribute "title")))
(.map (#-> (.to-upper-case)))
(.run-io document))Signature
task :: ((Error -> _), (b -> c) -> _) -> task (Error -> _) (b -> c)
To model lazy asynchronous actions the task structure is the way to go. Methods implemented by it are:
t, tt = task instances
| Method | Static? | Interface | Examples |
|---|---|---|---|
of |
Yes | Pointed | (task.of 1) |
lift |
Yes | (task.lift 1) |
|
empty |
Yes | Monoid | (task.empty) |
zero |
Yes | Plus | (task.zero) |
resolve |
Yes | (task.resolve 1) |
|
reject |
Yes | (task.reject 1) |
|
to-string |
Show | (.to-string t) |
|
concat |
Semigroup | (.concat t tt) |
|
map |
Functor | (.map t (#-> ...)) |
|
bimap |
Bifunctor | (.bimap t (#-> ...) (#-> ...)) |
|
ap |
Applicative | (.ap t tt) |
|
flat-map / chain
|
Monad | (.flat-map t (#-> ...)) |
|
alt |
Alternative | (.alt t tt) |
|
run-task |
(.run-task t (#(e) ... ) (#(v) ...)) |
(|> (task.lift "https://api.my-site.com/users/")
(.flat-map (#(url)
(task (#(rej res)
(|> (fetch url)
(.then (#-> (.json) (res)))
(.catch rej))))))
(.map (#-> (.find (#-> (getf 'id) (eql? "u12345")))))
(.run-task (#(err)
(.warn console err))
(#(user)
(.log console user))))Signature
maybe :: nil -> maybe.nothing | a -> maybe.just a
The maybe sum type encapsulates the idea of null-checks (and - in JavaScript terms - undefined) and allows to avoid using conditionals. Useful whenever an operation might result in a null/undefined return value. Implements:
m, mm = maybe instances
| Method | Static? | Interface | Examples |
|---|---|---|---|
of |
Yes | Pointed | (maybe.of 1) |
lift |
Yes | (maybe.lift 1) |
|
empty |
Yes | Monoid | (maybe.empty) |
zero |
Yes | Plus | (maybe.zero) |
to-string |
Show | (.to-string m) |
|
equals |
Setoid | (.equals m mm) |
|
concat |
Semigroup | (.concat m mm) |
|
map |
Functor | (.map m (#-> ...)) |
|
bimap |
Bifunctor | (.bimap m (#-> ...) (#-> ...)) |
|
ap |
Applicative | (.ap m mm) |
|
flat-map / chain
|
Monad | (.flat-map m (#-> ...)) |
|
alt |
Alternative | (.alt m mm) |
|
reduce |
Foldable | (.reduce m (#(x v) ...) x) |
|
traverse |
Traversable | (.traverse m T.lift (#-> ...)) |
|
sequence |
Traversable | (.sequence m T.lift) |
(|> (maybe.lift "just a string")
(.map (#-> (.to-upper-case)))
(.map (#-> (.replace " " "-")))
(match-sum ((:nothing ()
(.log console "No value"))
(:just (value)
(.log console value)))))Signature
maybe-transformer :: Monad T, Pointed T =>
T -> (nil -> maybe-t T maybe.nothing) |
T -> (a -> maybe-t T maybe.just a)
Lifts a pointed monad into a maybe-transformer monad (maybe-t). The transformer implements:
| Method | Static? | Interface |
|---|---|---|
of |
Yes | Pointed |
lift |
Yes | |
map |
Functor | |
flat-map / chain
|
Monad |
(defconstant dom-t (maybe-transformer io))
(|> (dom-t.lift document)
(.map (#-> (.query-selector "#my-selector")))
(.map (#-> (getf 'dataset 'foo)))
(getf 'stack)
(.run-io)
(match-sum ((:nothing ()
(.log console "No data"))
(:just (foo-data)
(.log console "Found:" foo-data)))))Signature
either :: Error E =>
nil -> either.left E |
E -> either.left E |
a -> either.right a
Being a structure that is meant to make error handling more managable, either is useful whenever an operation might result in an exception and you want to convey that information instead of throwing the error. Implements these interfaces:
e, ee = either instances
| Method | Static? | Interface | Examples |
|---|---|---|---|
of |
Yes | Pointed | (either.of 1) |
lift |
Yes | (either.lift 1) |
|
empty |
Yes | Monoid | (either.empty) |
zero |
Yes | Plus | (either.zero) |
to-string |
Show | (.to-string e) |
|
equals |
Setoid | (.equals e ee) |
|
concat |
Semigroup | (.concat e ee) |
|
map |
Functor | (.map e (#-> ...)) |
|
bimap |
Bifunctor | (.bimap e (#-> ...) (#-> ...)) |
|
ap |
Applicative | (.ap e ee) |
|
flat-map / chain
|
Monad | (.flat-map e (#-> ...)) |
|
alt |
Alternative | (.alt e ee) |
|
reduce |
Foldable | (.reduce e (#(x v) ...) x) |
|
traverse |
Traversable | (.traverse e T.lift (#-> ...)) |
|
sequence |
Traversable | (.sequence e T.lift) |
(defun ensure-string (value)
(if (string? value)
(either.lift value)
(either.lift (error "The value " value " is not a string"))))
(|> (ensure-string "user input")
(.map (#-> (.to-upper-case)))
(.map (#-> (.replace " " "-")))
(match-sum ((:left (error)
(.log console (getf error 'message)))
(:right (value)
(.log console value)))))Signature
either-transformer :: Monad T, Pointed T, Error E =>
T -> (nil -> either-t T either.left E) |
T -> (E -> either-t T either.left E) |
T -> (a -> either-t T either.right a)
Lifts a pointed monad into a either-transformer monad (either-t). The transformer implements:
| Method | Static? | Interface |
|---|---|---|
of |
Yes | Pointed |
lift |
Yes | |
map |
Functor | |
flat-map / chain
|
Monad |
Signature
seq :: a -> seq List a
A thin layer around native lists, seq provides various methods that make lists traversable, applicatives, etc. The implemented interfaces:
s, ss = seq instances
| Method | Static? | Interface | Examples |
|---|---|---|---|
of |
Yes | Pointed | (seq.of 1 2 3) |
lift |
Yes | (seq.lift 1) |
|
empty |
Yes | Monoid | (seq.empty) |
zero |
Yes | Plus | (seq.zero) |
to-string |
Show | (.to-string s) |
|
length |
(.length s) |
||
equals |
Setoid | (.equals s ss) |
|
concat |
Semigroup | (.concat s ss) |
|
map |
Functor | (.map s (#-> ...)) |
|
ap |
Applicative | (.ap s ss) |
|
flat-map / chain
|
Monad | (.flat-map s (#-> ...)) |
|
alt |
Alternative | (.alt s ss) |
|
reduce |
Foldable | (.reduce s (#(x v) ...) x) |
|
fold-map |
Foldable | (.fold-map s (#-> ...)) |
|
fold |
Foldable | (.fold s maybe.lift) |
|
traverse |
Traversable | (.traverse s maybe.lift (#-> ...)) |
|
sequence |
Traversable | (.sequence s maybe.lift) |
|
filter |
Filterable | (.filter s (#-> ...)) |
|
find |
(.find s (#-> ...)) |
||
cons |
(.cons s 1) |
||
snoc |
(.snoc s 1) |
||
take |
(.take s 3) |
||
take-while |
(.take-while s (#-> ...)) |
||
drop |
(.drop s 3) |
||
drop-while |
(.drop-while s (#-> ...)) |
||
reverse |
(.reverse s) |
(|> (seq.lift (list 1 2 3 4 5))
(.map (#-> (+ 1))
(.filter (#-> (even?)))
(.map (#(a)
(#(b)
(+ a ":" b))))
(.ap (seq.of "A" "B" "C"))
(.fold-map (#(x)
(+ x ", "))))) ; => A:2, B:4, C:6,Signature
proof :: Error E, List L =>
nil -> proof.falsy L E |
E -> proof.falsy L E |
a -> proof.truthy a
Mostly similiar to either, the proof sum type is useful whenever operations might result in exceptions and you want to accumulate them. Usually, proof is used for things like validation. Implements these interfaces:
| Method | Static? | Interface | Examples |
|---|---|---|---|
of |
Yes | Pointed | (proof.of 1) |
lift |
Yes | (proof.lift 1) |
|
empty |
Yes | Monoid | (proof.empty) |
zero |
Yes | Plus | (proof.zero) |
to-string |
Show | (.to-string p) |
|
equals |
Setoid | (.equals p pp) |
|
concat |
Semigroup | (.concat p pp) |
|
map |
Functor | (.map p (#-> ...)) |
|
bimap |
Bifunctor | (.bimap p (#-> ...)) |
|
ap |
Applicative | (.ap p pp) |
|
flat-map / chain
|
Monad | (.flat-map p (#-> ...)) |
|
alt |
Alternative | (.alt p pp) |
(defun is-not-empty (value)
(cond ((not (eql? "" (.trim value)))
(proof.lift value))
:else (proof.lift (error "EMPTY_VALUE"))))
(defun is-numeric (value)
(cond ((not (.test (regex "\\D") value))
(proof.lift value))
:else (proof.lift (error "NUMBERS_ONLY"))))
(defun validate (value)
(|> (is-not-empty value)
(.concat (is-numeric value))
(match-sum ((:falsy (errors)
(each exc errors
(.log console exc))
false)
(:truthy ()
(.log console "All right!")
true)))))Signature
proof-transformer :: Monad T, Pointed T, Error E =>
T -> (nil -> proof-t T proof.falsy (list E)) |
T -> (E -> proof-t T proof.falsy (list E)) |
T -> (a -> proof-t T proof.truthy a)
Lifts a pointed monad into a proof-transformer monad (proof-t). The transformer implements:
| Method | Static? | Interface |
|---|---|---|
of |
Yes | Pointed |
lift |
Yes | |
map |
Functor | |
flat-map / chain
|
Monad |
Operations that transform an algebraic type into another algebraic type.
Signature
coyo-as-io :: coyo a (a -> b) -> io b
Signature
coyo-as-maybe :: coyo a (a -> b) -> maybe b
Signature
coyo-as-either :: coyo a (a -> b) -> either error b
Signature
coyo-as-proof :: coyo a (a -> b) -> proof (list error) b
Signature
coyo-as-task :: coyo a (a -> b) -> task (error -> _) b
Signature
maybe-as-either :: maybe a -> either error a
Signature
maybe-as-proof :: maybe a -> proof (list error) a
Signature
maybe-as-task :: maybe a -> task (error -> _) a
Signature
either-as-maybe :: either error a -> maybe a
Signature
either-as-proof :: either error a -> proof (list error) a
Signature
either-as-task :: either error a -> task (error -> _) a
Signature
proof-as-maybe :: proof (list error) a -> maybe a
Signature
proof-as-either :: proof (list error) a -> either error a
Signature
proof-as-task :: proof (list error) a -> task (error -> _) a
Signature
io-as-task :: io a -> task (error -> _) a