cat/functor
Functor
type {minimal implementation - fmap
}.
Default implementation for: replace
(<$ operator).
Functor composition and various functor instances: Option, List, Reader, Const, Tuple, Triple, Pair, Either.
Types
Functor
type in gleam.
// Haskell type class
class Functor f where
fmap :: (a -> b) -> f a -> f b
Functor laws
- Preservation of
identity
: fmap id = id - Preservation of
composition
: fmap (g ∘ h) = (fmap g) ∘ (fmap h)
Since gleam does not have Higher Kinded Types
, we cannot pass the type constructor to the Functor type.
We would like pass Option as f and then use f(a) as a type in the fmap definition.
Compromise
The user will follow this convention:
- f: the first parameter of the Functor type is a
phantom type
used to differentiate between Functor instances - a, b: the second and third parameters are
generic types
- fa, fb: the fourth and fifth parameters are the
constructed types
f(a) and f(b) respectively
Examples
// The type that will be an instance of Functor:
type Identity(a) {
Identity(a)
}
// The phantom type that will be the first parameter:
type IdentityF
// Instance of Identity for Functor (defined as a function to keep it generic over a and b)
let id_functor = fn() -> Functor(IdentityF, a, b, Identity(a), Identity(b)){
Functor(fmap: fn(f) {
fn(idx) {
let Identity(x) = idx
Identity(f(x))
}
})
}
// Use: the function id_functor needs to be called twice
// Each time it binds a and b (first to Int and Bool, second to Float and String)
let f = fn(x: Int) -> Bool { x % 2 == 0 }
identity_functor().fmap(f)(Identity(5))
// -> Identity(False)
let g = fn(x: Float) -> String { float.to_string(x) }
identity_functor().fmap(g)(Identity(6.0))
// -> Identity("6.0")
pub type Functor(f, a, b, fa, fb) {
Functor(fmap: fn(fn(a) -> b) -> fn(fa) -> fb)
}
Constructors
-
Functor(fmap: fn(fn(a) -> b) -> fn(fa) -> fb)
Functions
pub fn const_functor() -> Functor(
ConstF(a),
b,
c,
Const(a, b),
Const(a, c),
)
Const Functor Instance
.
// Haskell instance
instance Functor (Const c) where
fmap :: (a -> b) -> Const c a -> Const c b
fmap _ (Const v) = Const v
pub fn either_functor() -> Functor(
EitherF(a),
b,
c,
Either(a, b),
Either(a, c),
)
Either Functor
.
Examples
either_functor().fmap(bool.negate)(Left(27))
// -> Left(27)
either_functor().fmap(bool.negate)(Right(False))
// -> Right(True)
pub fn function_functor() -> Functor(
FunctionF(a),
b,
c,
fn(a) -> b,
fn(a) -> c,
)
(->) Functor Instance
.
// Haskell instance
instance Functor ((->) r) where
fmap :: (a -> b) -> (r -> a) -> (r -> b)
fmap f g = f ∘ g
Examples
let f = fn(x) { x % 2 == 0 }
let g = bool.to_string
function_functor().fmap(g)(f)(19)
// -> "False"
pub fn functor_compose(
g: Functor(a, b, c, d, e),
f: Functor(f, g, h, b, c),
) -> Functor(Pair(a, f), g, h, d, e)
Functor composition
.
Is simply the composition of their fmaps.
Examples
Some([1, 2, 3])
|> functor_compose(option_functor(), list_functor()).fmap
( fn(x) { int.to_string(x + 1) } )
// -> Some(["2", "3", "4"])
pub fn identity_functor() -> Functor(
IdentityF,
a,
b,
Identity(a),
Identity(b),
)
Identity Functor Instance
.
// Haskell implementation
instance Functor Maybe where
fmap :: (a -> b) -> Identity a -> Identity b
fmap f (Identity x) = Identity(f x)
Examples
let f = fn(x: Int) -> Bool { x % 2 == 0 }
identity_functor().fmap(f)(Identity(5))
// -> Identity(False)
pub fn list_functor() -> Functor(ListF, a, b, List(a), List(b))
List Functor Instance
.
// Haskell instance
instance Functor [] where
fmap :: (a -> b) -> [a] -> [b]
fmap _ [] = []
fmap f (x:xs) = (f x):(fmap f xs)
pub fn option_functor() -> Functor(
OptionF,
a,
b,
Option(a),
Option(b),
)
Option Functor Instance
(generic over a and b).
// Haskell instance
instance Functor Maybe where
fmap :: (a -> b) -> Maybe a -> Maybe b
fmap _ Nothing = Nothing
fmap f (Just x) = Just (f x)
Examples
let double = fn(x) { x * 2 }
option_functor().fmap(double)(None)
// -> None
Some(2)
|> option_functor().fmap(double)
// -> Some(4)
pub fn pair_functor() -> Functor(
PairF(a),
b,
c,
Pair(a, b),
Pair(a, c),
)
Pair Functor
.
Examples
pair_functor().fmap(bool.negate)(Pair(9, False))
// -> Pair(9, True)
pub fn reader_functor() -> Functor(
ReaderF(a),
b,
c,
Reader(a, b),
Reader(a, c),
)
Reader Functor Instance
.
// Haskell implementation
instance Functor (Reader r) where
fmap :: (a -> b) -> Reader r a -> Reader r b
fmap f g = f ∘ g
Examples
let ra = Reader(fn(x) { x % 2 == 0 })
let f = bool.to_string
reader_functor().fmap(f)(ra).apply(19)
// -> "False"
pub fn replace(functor: Functor(a, b, c, d, e)) -> fn(c, d) -> e
Haskell (<$)
operator.
(<$) :: a -> f b -> f a
x <$ m = fmap (const x) m
Examples
replace(option_functor())("a", Some(2))
// -> Some("a")
replace(option_functor())("a", None)
// -> None
pub fn triple_functor() -> Functor(
TripleF(a, b),
c,
d,
#(a, b, c),
#(a, b, d),
)
Triple Functor
.
Examples
triple_functor().fmap(bool.negate)(#("abc", 9, False))
// -> #("abc", 9, True)
pub fn tuple_functor() -> Functor(
TupleF(a),
b,
c,
#(a, b),
#(a, c),
)
Tuple Functor
.
Examples
tuple_functor().fmap(bool.negate)(#(9, False))
// -> #(9, True)
pub fn writer_functor() -> Functor(
WriterF,
a,
b,
Writer(a),
Writer(b),
)
Writer Functor Instance
.
// Haskell Instance
fmap :: (a -> b) -> Writer a -> Writer b
fmap f = id >=> (\x -> return (f x))
Examples
monad.Writer(16, "message")
|> writer_functor().fmap(fn(x) { x % 4 == 0 })
// -> monad.Writer(True, "message")