Reputation: 13773
There are similar questions here but they are attached to a particular programming language and I am looking for an answer on the conceptual level.
As I understand, Functors are essentially immutable containers that expose map() API which derives another functor. Which addition makes it possible to call a particular functor a monad?
As I understand, every monad is a functor but not every functor is a monad.
Upvotes: 36
Views: 18002
Reputation: 34175
Swift Functor, Applicative, Monad
Functor, Applicative, Monad:
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 FunctorOptional 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]
Upvotes: 4
Reputation: 407
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 in Maybe("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.
Upvotes: 23
Reputation: 928
(Note that this will be a simplified explanation for category theory concepts)
A Functor is a function from a set of values a to another set of values: a -> b
. For a programming language this could be a function that goes from String -> Integer
:
function fn(text: string) : integer
Composition is when you use the value of one function as input to the value of the next: fa(fb(x))
. For example:
hash(lowercase(text))
A Monad allows to compose Functors that either are not composable otherwise, compose Functors by adding extra functionality in the composition, or both.
An example of the first is a Monad for a Functor String -> (String, Integer)
An example of the second is a Monad that counts the Number of functions called on a value
A Monad includes a Functor T
that is responsible for the functionality you want plus two other functions:
input -> T(input)
T(T(input)) -> T(input)
The first function allows to transform your input values to a set of values that our Monad can compose. The second function allows for the composition.
So in conclusion, every Monad is not a Functor but uses a Functor to complete it's purpose.
Upvotes: -1