### 1` `Interfaces

#### 1.1` `Functors

(require data/functor) | package: functional-lib |

A functor can be thought of as a kind of “container”. This can be something like a list or hash map, which actually contains values, or something like a channel, which produces values over time. All functors work with map, which allows producing a new functor with the elements “contained” by the functor modified by the mapping function.

For example, using map on lists simply modifies each element of the list, just like map from racket/base.

> (map add1 '(1 2 3)) #<stream>

However, unlike map from racket/base, this more generic map can also map over things like optional values.

> (map add1 (just 2)) (just 3)

> (map add1 nothing) #<nothing>

Functors provide a way to manipulate data in a consistent way without needing to know the data’s underlying structure. To ensure consistency and predictability, all implementations of gen:functor must conform to the functor laws, of which there are two:

Most reasonable definitions of a functor will satisfy these laws already, but it is possible to write an implementation that does not, and there is no guarantee that functions in this library will work predictably on unlawful functors.

procedure

f : procedure? x : functor?

##### 1.1.1` `Implementing new functors

To define your own functors, simply implement the gen:functor generic interface and implement the map method. The only implementation requirements are that methods conform to their associated contracts and that they follow the functor laws.

Here is an example implementation of the most trivial possible functor, the identity functor:

> (struct id (val) #:transparent #:methods gen:functor [(define (map f x) (id (f (id-val x))))]) > (map add1 (id 12)) (id 13)

#### 1.2` `Applicatives

(require data/applicative) | package: functional-lib |

Applicative functors generalize function application to work with any kind of data structure, not just procedures. This is much like prop:procedure, but it is specified via a generic interface. Additionally, all implementations of gen:applicative should also implement gen:functor.

> ((just +) (just 1) (just 2)) (just 3)

> ((just +) nothing (just 2)) #<nothing>

> (sequence->list ((list + *) (list 3 4) (list 10 20))) '(13 23 14 24 30 60 40 80)

In addition to the implementation of apply, the gen:applicative interface must also implement a function called pure. This function “lifts” an ordinary value into the functor. For example, the pure function for lists is just list, but the pure function for optional values is just.

Like functors, applicative functors have their own set of applicative functor laws which all implementations of gen:applicative must conform to:

Most reasonable definitions of an applicative functor will satisfy these laws already, but it is possible to write an implementation that does not, and there is no guarantee that functions in this library will work predictably on unlawful applicative functors.

interface

procedure

(applicative? v) → boolean?

v : any/c

value

procedure

(pure v) → applicative?

v : any/c

##### 1.2.1` `Implementing new applicative functors

Implementing your own applicative functors is somewhat more complicated than implementing plain functors. You must implement two methods, named pure and apply. The former, unlike the pure function exported by data/applicative, should be a function of two arguments, the first of which should be ignored. This is necessary in order to properly perform dynamic dispatch with racket/generic, since some value must exist to be dispatched on. The first argument is therefore the value being used for dispatch, but there is no guarantee about what it is, so you should always ignore it completely.

Implementing the apply method is somewhat more straightforward. It should be a function of two arguments, this first corresponding to the functor in application position and second a list of functors provided as arguments in the application.

Here is an example implementation of the most trivial possible applicative functor, the identity functor:

> (require (prefix-in base: racket/base))

> (struct id (val) #:transparent #:methods gen:functor [(define (map f x) (id (f (id-val x))))] #:methods gen:applicative [(define (pure _ x) (id x)) (define (apply f xs) (base:apply (id-val f) (base:map id-val xs)))]) > ((id +) (pure 2) (pure 3)) 5

#### 1.3` `Monads

(require data/monad) | package: functional-lib |

A monad is a mechanism for sequencing pure values in a context. Monads are an extremely general concept that are notoriously difficult to explain (despite being relatively simple once you understand them), and I will not attempt to explain them here (though perhaps I will try someday).

All monads must also be applicative functors, but they add one more method, called chain. The chain method, much like map, applies a function to a value in a context, but unlike map, the applied function must produce a new monad, not a pure value. Monads can be used to control sequencing of computation in a very flexible way.

Using the chain function directly can become tedious and hard to read beyond a couple of nested applications, so the do form is provided to make sequencing monadic operations more pleasant to read and write.

Like functors and applicative functors, monads have their own set of monad laws which all implementations of gen:monad must conform to:

Most reasonable definitions of a monad will satisfy these laws already, but it is possible to write an implementation that does not, and there is no guarantee that functions in this library will work predictably on unlawful monads.

syntax

(do expr-or-clauses)

expr-or-clauses = monad-expr | do-clause expr-or-clauses do-clause = [match-pattern <- monad-expr] | monad-expr | internal-definition

monad-expr : monad?

In its simplest form, do does nothing at all. Any do block containing only a single expression is equivalent to the expression itself.

> (do 3) 3

> (do "hello, world") "hello, world"

> (do '(1 2 3 4)) '(1 2 3 4)

This is obviously not particularly useful, but do becomes helpful when using multiple sub-forms. Each do-clause may bind the result of a chain operation, which may be used in subsequent computations.

> (sequence->list (do [x <- '(1 2 3)] (pure (* x 2)))) '(2 4 6)

Specifically, any block of the form (do [x <- m] clause ...+) is precisely equivalent to (chain (λ (x) (do clause ...+)) m). More generally, the binding identifier can be replaced with a match pattern, in which case the resulting code uses match-lambda instead.

Not every chain operation has a useful result. In that case, the binding brackets may be omitted, simply leaving the monad-expr. In this case, a chain call will still be produced, but the result will not be bound anywhere.

> (sequence->list (do '(1 2 3) (pure 'hello))) '(hello hello hello)

Finally, arbitrary internal definitions may be interspersed between each do-clause. These definitions do not produce new chain calls, they simply create new bindings.

If a macro used within a do block produces a begin form containing both internal definitions and expressions, the whole form is spliced into the surrounding internal definition context. All expressions will be simply evaluated for side-effects and will not result in any additional calls to chain.

> (sequence->list (do [x <- '(1 2)] (define y (* x 2)) [z <- '(a b)] (define (prettify a b) (~a a ": " b)) (pure (prettify y z)))) '("2: a" "2: b" "4: a" "4: b")

Internal definitions defined within do blocks may refer to all previous bindings, but not subsequent ones. However, multiple internal definitions directly next to one another may be mutually recursive, so long as they are not separated by a chain.

> (do [x <- (just 7)] (define (calls-b) (add1 (b))) (define (b) (- x)) [y <- (just (calls-b))] (pure (* 2 y))) (just -12)

> (sequence->list (join '((1 2) (3 4)))) '(1 2 3 4)

> (sequence->list (join '())) '()

> (join (just (just 'hello))) (just 'hello)

> (join (just nothing)) #<nothing>

> (join nothing) #<nothing>

> (define (ascii->char x) (if (<= 0 x 127) (just (integer->char x)) nothing)) > (map/m ascii->char '(76 33)) (just '(#\L #\!))

> (map/m ascii->char '(76 -5 33)) #<nothing>

##### 1.3.1` `Implementing new monads

Implementing your own monads is no more complicated than implementing your own applicative functors, you just need to provide an implementation of chain that satisfies the monad laws.

Here is an example implementation of the most trivial possible monad, the identity monad:

> (require (prefix-in base: racket/base))

> (struct id (val) #:transparent #:methods gen:functor [(define (map f x) (id (f (id-val x))))] #:methods gen:applicative [(define (pure _ x) (id x)) (define (apply f xs) (base:apply (id-val f) (base:map id-val xs)))] #:methods gen:monad [(define (chain f x) (f (id-val x)))])

> (do [x <- (id 1)] [y <- (id 2)] (pure (+ x y))) (id 3)