What is the difference between a Functor and a Monad?
Swift Functor, Applicative, Monad
Functor, Applicative, Monad:
- solve the same problem - working with a wrapped value into context(class)
- using closure[About]
- return a new instance of context(class)
The difference is in parameters of closure
Pseudocode:
class SomeClass<T> {
var wrappedValue: T //wrappedValue: - wrapped value
func foo<U>(function: ???) -> Functor<U> { //function: - function/closure
//logic
}
}
where ???
function: (T) -> U == Functor
function: SomeClass< (T) -> U > == Applicative
function: (T) -> SomeClass<U> == Monad
Functor
Functor applies a function
to a wrapped value
Pseudocode:
class Functor<T> {
var value: T
func map<U>(function: (T) -> U) -> Functor<U> {
return Functor(value: function(value)) //<- apply a function to value
}
}
Applicative or applicative functor
Applicative applies wrapped function
to a wrapped value
.
The diff with Functor is wrapped function
instead of function
Pseudocode:
class Applicative<T> {
var value: T
func apply<U>(function: Applicative< (T) -> U >) -> Applicative<U> {
return Applicative(value: unwrappedFunction(value))
}
}
Monad
Monad applies a function
(which returns a wrapped value
) to a wrapped value
Pseudocode:
class Monad<T> {
var value: T
func flatMap<U>(function: (T) -> Monad<U>) -> Monad<U> { //function which returns a wrapped value
return function(value) //applies the function to a wrapped value
}
}
Swift:
Optional
,Collection
,Result
is Functor and MonadString
is Functor
Optional as an example
enum CustomOptional<T> {
case none
case some(T)
public init(_ some: T) {
self = .some(some)
}
//CustomOptional is Functor
func map<U>(_ transform: (T) -> U) -> CustomOptional<U> {
switch self {
case .some(let value):
let transformResult: U = transform(value)
let result: CustomOptional<U> = CustomOptional<U>(transformResult)
return result
case .none:
return .none
}
}
//CustomOptional is Applicative
func apply<U>(transformOptional: CustomOptional<(T) -> U>) -> CustomOptional<U> {
switch transformOptional {
case .some(let transform):
return self.map(transform)
case .none:
return .none
}
}
//CustomOptional is Monad
func flatMap<U>(_ transform: (T) -> CustomOptional<U>) -> CustomOptional<U> {
switch self {
case .some(let value):
let transformResult: CustomOptional<U> = transform(value)
let result: CustomOptional<U> = transformResult
return result
case .none:
return .none
}
}
}
[Swift Optional map vs flatMap]
Let me explain my understanding without going into category theory:
Functors and monads both provide some tool to wrapped input, returning a wrapped output.
Functor = unit + map (i.e. the tool)
where,
unit
= something which takes raw input and wraps it inside a small context.
map
= the tool which takes a function as input, applies it to raw value in wrapper, and returns wrapped result.
Example: Let us define a function which doubles an integer
// doubleMe :: Int a -> Int b
const doubleMe = a => 2 * a;
Maybe(2).map(doubleMe) // Maybe(4)
Monad = unit + flatMap (or bind or chain)
flatMap
= the tool which flattens the map
, as its name implies. It will be clear soon with the example below.
Example: Let us say we have a curried function which appends two strings only if both are not blank.
Let me define one as below:
append :: (string a,string b) -> Maybe(string c)
Let's now see the problem with map
(the tool that comes with Functor
),
Maybe("a").map(append("b")) // Maybe(Maybe("ab"))
How come there are two Maybe
s here?
Well, that's what map
does; it applies the provided function to the wrapped value and wraps the result.
Let's break this into steps,
Apply the mapped function to the wrapped value ; here the mapped function is
append("b")
and the wrapped value is"a"
, which results inMaybe("ab")
.Wrap the result, which returns
Maybe(Maybe("ab"))
.
Now the value we are interested in is wrapped twice. Here comes flatMap
to the rescue.
Maybe("a").flatMap(append("b")) // Maybe("ab")
Of course, functors and monads have to follow some other laws too, but I believe this is not in the scope of what is asked.