Skip to content

03 The prelude

David Hofmann edited this page Nov 13, 2022 · 22 revisions

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)

Contents

Logic functions

Basic functions from combinatorial logic.

identity

Signature
identity :: a -> a

The identity function returns the given argument.

(identity a)                    ; => a

constantly

Signature
constantly :: a -> () -> a

The constantly function takes a value and returns a constant function that always returns the value.

(constantly a)                  ; => (() -> a)

Type checking

Helper functions to check types and collections.

list-of?

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?)))   ; => Boolean

hash-of?

Signature
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?)))   ; => Boolean

dict-of?

Signature
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?)))   ; => Boolean

mset-of?

Signature
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?)))   ; => Boolean

Function-manipulating functions

Some higher-order-functions

converge

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"

memoize

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)                  ; => 3

Generic operations

Helper 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.

show

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)"

equals

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))                 ; => true

concatenate

Signature
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)   

map

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)

ap

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)

flat-map / chain

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)

contramap

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)

bimap

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)

promap

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"))

reduce

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)                 ; => 6

fold-map

Signature
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)

fold

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)

traverse

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))

sequence

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))

alt

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)

clone

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)

List and MSet operations

Functions that handle collections

zip

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))

unzip

Signature
unzip :: List L => L L a -> L L b

find

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)

filter / select

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)

reject

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)

unique

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)

union

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)

intersection

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)

difference

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)

take

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)

drop

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)

partition

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))

partition-with

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))

keep

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)

Lenses

Van Laarhoven style lenses for hash and dict structures. Lenses are a non-destructive and pure way to work with data structures like hashes.

create-lens

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).

hash-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))

dict-lens

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))

lget

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"

lset

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")

lmap

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))))

Algebraic types

Common algebraic types, ready to use.

coyo

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)) ""))

free

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!"))))

io

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))

task

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))))

maybe

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)))))

maybe-transformer

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)))))

either

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)))))

either-transformer

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

seq

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,

proof

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)))))

proof-transformer

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

Algebraic type coercion

Operations that transform an algebraic type into another algebraic type.

coyo-as-io

Signature
coyo-as-io :: coyo a (a -> b) -> io b

coyo-as-maybe

Signature
coyo-as-maybe :: coyo a (a -> b) -> maybe b

coyo-as-either

Signature
coyo-as-either :: coyo a (a -> b) -> either error b

coyo-as-proof

Signature
coyo-as-proof :: coyo a (a -> b) -> proof (list error) b

coyo-as-task

Signature
coyo-as-task :: coyo a (a -> b) -> task (error -> _) b

maybe-as-either

Signature
maybe-as-either :: maybe a -> either error a

maybe-as-proof

Signature
maybe-as-proof :: maybe a -> proof (list error) a

maybe-as-task

Signature
maybe-as-task :: maybe a -> task (error -> _) a

either-as-maybe

Signature
either-as-maybe :: either error a -> maybe a

either-as-proof

Signature
either-as-proof :: either error a -> proof (list error) a

either-as-task

Signature
either-as-task :: either error a -> task (error -> _) a

proof-as-maybe

Signature
proof-as-maybe :: proof (list error) a -> maybe a

proof-as-either

Signature
proof-as-either :: proof (list error) a -> either error a

proof-as-task

Signature
proof-as-task :: proof (list error) a -> task (error -> _) a

io-as-task

Signature
io-as-task :: io a -> task (error -> _) a


GitHub license NPM version Github stars NPM downloads