# let x = [];; val x : 'a list = [] # let y = ref [];; val y : '_a list ref = {contents = []} # let y' = !y;; val y' : '_a list = [] # x;; - : 'a list = [] # y';; - : '_a list = [] #
in the above definitions, x is of type 'a list
and y is of type '_a list
this subtle distinction essentially means that once an element has been added to y
its type hardens meaning it no longer behaves as a generic 'a
but as the type of whatever was inserted.
# y';; - : '_a list = [] # 1 :: y';; - : int list = [1] # y';; - : int list = [] # 'a' :: y';; Characters 7-9: 'a' :: y';; ^^ Error: This expression has type int list but an expression was expected of type char list Type int is not compatible with type char
whereas with x of type 'a list
# x;; - : 'a list = [] # 1 :: x;; - : int list = [1] # 'a' :: x;; - : char list = ['a']
To avoid this issue you can simple stipulate all variables being when defining functions. This process is called an eta-expansion or -expansion
given :
# let f x = x;; val f : 'a -> 'a = <fun>
This creates the difference between:
# let g = f f;; val g : '_a -> '_a = <fun>
and
# let g x = f f x;; val g : 'a -> 'a = <fun>
Particularly important in Haskell as without them Haskell is devoid of interaction (in an attempt to make it pure)
The general pattern is to take functions defined on normal (un-instrumented ) types 'a
and to lift them to work with instrumented types, 'a t
.
A simple example of an event where we may want to lift computation (in this case to make the function pure) is the following
# 1/0;; Exception: Division_by_zero
# let ( / ) x y = if y = 0 then None else Some (x/y) - : val ( / ) : int -> int -> int option = <fun>
/
) to work with it, we render all other arithmetic operators useless# 1/0;; - : int option = None # 4/2;; - : int option = Some 2 # 4/2 + 2;; Characters 0-3: 4/2 + 2;; ^^^ Error: This expression has type int option but an expression was expected of type int
/
)? Or can we design machinary to do it for us? Such as:let ( + ) = lift ( + ) let ( * ) = lift ( * ) let ( - ) = lift ( - ) let ( < ) = lift ( < ) ...
'a
to work on our instrumented type 'a t
in this case 'a option
we do this using Monads.module type F = sig type _ t val map : ('a -> 'b) -> ('a t -> 'b t) end
Here one can see that the map
operation produces instrumented (lifted) types 'a t
.
The map
function is a requirement for a methematical functor, which is defined as "maps between types". The map
function allows a function to work on an instrumented type of 'a t
However, we cannot lift all computation with this. We require 2 extra operators which the Monad pattern provides. (unit
and mult
)
Monads are just functors with 2 extra operations
The monad extension adds a further 2
module type M = sig include F val unit : 'a -> 'a t val mult : ('a t) t -> 'a t end
To produce
module type M = sig type _ t val map : ('a -> 'b) -> 'a t -> 'b t val unit : 'a -> 'a t val mult : 'a t t -> 'a t end
Once compiled.
Unit
'a
up to 'a t
Mult
'a t t
down to 'a t
module Option_m : (M with type 'a t = 'a option) = struct type 'a t = 'a option let map (f : 'a -> 'b) = function | None -> None | Some a -> Some (f a) let unit a = Some a let mult = function | None -> None | Some x -> x end
This is the condensed form using the predefined Monad (and by proxy, functor) signatures, simply stipulating the instrumented type as 'a option
The full, explicit signature is given by:
module Option_m : sig type 'a t = 'a option val map : ('a -> 'b) -> 'a t -> 'b t val unit : 'a -> 'a t val mult : 'a t t -> 'a t end
With the same resulting implementation, simply taking the above to be its explicit signature.
With the above machinary we could lift all our functions uniformly. However, the monadic extension, as mentioned earlier, adds a futher 2 functions useful when lifting many functions.
module Monad_e (Monad : M) = struct include Monad (* bind *) let ( >>= ) at f = at |> (map f) |> mult (* lift *) let lift f at bt = at >>= fun a -> bt >>= fun b -> unit (f a b) end module Monad_e : functor (Monad : M) -> sig type 'a t = 'a Monad.t val map : ('a -> 'b) -> 'a t -> 'b t val unit : 'a -> 'a t val mult : 'a t t -> 'a t val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t val lift : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t end
Bind
bind
(>>=)
takes a monadic value at : 'a t
and processes it via a function of type f : 'a -> 'b t
by lifting the function to map f : 'a t -> 'b t t
. However, we have to use our mult
function to squash the resulting double instrumentation.
The monadic extension now features our theorised lift
method which can lift any 2 valued (curried) function of type 'a -> 'b -> 'c
and lift it to 'a t -> 'b t -> 'c t
. The arguments ('a
and 'b
) are lifted using bind
and the result ('c
) via unit
.
module Id_m : M = struct type 'a t = 'a let map f = f let unit a = a let mult a = a end
module Writer_m : (M with type 'a t = 'a * string) = struct type 'a t = 'a * string let map f (a, s) = (f a, s) let unit a = (a, "") let mult ((a, s), s') = (a, s' ^ s) end module Writer_m : sig type 'a t = 'a * string val map : ('a -> 'b) -> 'a t -> 'b t val unit : 'a -> 'a t val mult : 'a t t -> 'a t end
Logger
modulemodule Logger = struct module M = Monad_e (Writer_m) open M let (<) (x, s) (y, s') = (x < y, (if x < y then ((string_of_int x) ^ "<" ^ (string_of_int y) ^ "\n") else ((string_of_int x) ^ ">=" ^ (string_of_int y) ^ "\n")) ^ s ^ s') end module Logger : sig module M : sig type 'a t = 'a Writer_m.t val map : ('a -> 'b) -> 'a t -> 'b t val unit : 'a -> 'a t val mult : 'a t t -> 'a t val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t val lift : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t val lift2 : ('a -> 'b -> 'c -> 'd) -> 'a t -> 'b t -> 'c t -> 'd t end val ( < ) : int * string -> int * string -> bool * string end
run: 'a m -> 'a
removes instrumentation which is unusual
represents a simple singleton state
set
and get
do not require parametersThe state is typed therefore, we need to parameterise the state monad by a state module outlining the state of a singleton cell.
The type of the state monad is type 'a m = State.s -> ('a * State.s)
. Better referred to as a State transformer as it is a function of a state to a state and a value
unit
acts as identity on the state
mult
composes the state transformers associated with the inner and outer monad, in order to reflect sequential execution
run
executes (evaluates) the beginning to end composition for the monadic value, starting with the default state defined in the State module
module type STATE = sig type s val emp : s end module type MONAD = sig type 'a m val map : ('a -> 'b) -> ('a m -> 'b m) val mult : 'a m m -> 'a m val unit : 'a -> 'a m end module type STATE_MONAD = functor(State : STATE) -> sig include MONAD (* Special operations *) val run : 'a m -> 'a val set : State.s -> unit m val get : State.s m val ( >>= ) : 'a m -> ('a -> 'b m) -> 'b m end module StateMonad : (STATE_MONAD) = functor (State : STATE) -> struct type 'a m = State.s -> ('a * State.s) let (map : ('a -> 'b) -> ('a m -> 'b m)) = fun f a s -> let (a', s') = a s in (f a', s') let (mult : 'a m m -> 'a m) = fun att s0 -> let (at, s1) = att s0 in at s1 let (unit : 'a -> 'a m) = fun a s -> (a, s) let (run : 'a m -> 'a ) = fun m -> m State.emp |> fst let (set : State.s -> unit m) = fun s _ -> ((), s) let (get : State.s m) = fun s -> (s, s) (* Bind *) let (>>=) at f = at |> (map f) |> mult end
An example usage:
module IntState : (STATE with type s = int) = struct type s = int let emp = 0 end module Stateful = struct module SM = StateMonad(IntState) open SM let inc s = get >>= fun n -> set (n + 1) >>= fun () -> get end