简体   繁体   中英

What is the difference between a Functor and a Monad?

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.

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 (ie 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,

  1. 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") .

  2. 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.

(Note that this will be a simplified explanation for category theory concepts)

Functor

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

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))

Monads

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.

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 Monad
  • String 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]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM