Functors in SML
Table of Contents
1. Basic Idea
A functor in ML means a "parametrized module". It's an unfortunate choice of words, because "functor" has come to mean something completely different in functional programming. I will discuss that "something completely different" implemented in Standard ML.
In Haskell, the functor class consists of the fmap
function and the
<$
infix operator. I do not believe the <$
infix operator is
necessary. We can then implement this as a signature:
signature FUNCTOR = sig type 'a t; val map : ('a -> 'b) -> 'a t -> 'b t; end;
There are two constraints to a functor:
map id = id
it preserves the identity function, andmap (f o g) = (map f) o (map g)
it preserves composition.
Note: I will refer to a module adhering to this signature
generically as a FUNCTOR or a FUNCTOR
, whereas the Standard ML
parametrized structure is "functor" (all lower case) and Category
Theoretic notions will use functor (or "Functor" if it starts a
sentence).
1.1. Usefulness as AST
The basic use for a FUNCTOR
is to encode an abstract syntax
tree. Then the map
function just transforms the leafs somehow.
For example, the syntax tree for propositional logic:
structure Formula = struct datatype 'a t = Verum | Falsum | Atom of 'a | Not of 'a t | And of ('a t) * ('a t) | Or of ('a t) * ('a t) | Implies of ('a t) * ('a t) | Iff of ('a t) * ('a t); fun map f Verum = Verum | map f Falsum = Falsum | map f (Atom a) = Atom (f a) | map f (Not a) = Not (map f a) | map f (And (a1,a2)) = And (map f a1, map f a2) | map f (Or (a1,a2)) = Or (map f a1, map f a2) | map f (Implies (a1,a2)) = Implies (map f a1, map f a2) | map f (Iff (a1,a2)) = Iff (map f a1, map f a2); end;
Observe that we did not ascribe the FUNCTOR
signature to this,
because it would "hide" the constructors And
, Or
, Verum
, and so
on. But we can still use Formula
wherever a FUNCTOR
is needed.
1.2. Example 2: Operations for a Free Monad
Another use for functors is to provide the "menu" of possible operations for some effect, which will be handled by a free monad.
For example, IO with "just" reading or writing a string to the screen can be handled simple as:
(* A functor describing the operations with side-effects in IO *) structure IO_Op = struct datatype 'a t = Print of (string * 'a) | Read of (string -> 'a); fun map f (Print (s,k)) = Print (s, f k) | map f (Read k) = Read (fn s => f (k s)); end;
2. References
- Implicit Functors in OCaml
- Joel Björnson, More type classes which implements functors, monoids, and applicatives in OCaml