33

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.

Grzegorz Piwowarek
  • 13,172
  • 8
  • 62
  • 93
  • 6
    A functor takes a pure function (and a functorial value) whereas a monad takes a Kleisli arrow, i.e. a function that returns a monad (and a monadic value). Hence you can chain two monads and the second monad can depend on the result of the previous one. You cannot do this with functors. –  Jul 22 '17 at 10:23
  • 2
    Did you have a look at https://en.wikipedia.org/wiki/Monad_(functional_programming)#Formal_definition? The important addition to the Functor API is `flatMap` (or `bind` or `chain` or however you want to call it). If you chose a particular language, it would be easier to explain, as the *purely conceptual* answer is [category theory](https://en.wikipedia.org/wiki/Monad_(category_theory)). – Bergi Jul 22 '17 at 13:30
  • As @Bergi says the simple answer to your question (in the second paragraph) is the presence of a bind/flatMap/chain/whatever (the monadic function). Assuming your Functor/Monad obeys the monad laws, of course. But I don't think that is a very helpful *conceptual* answer. And, it is true that monads are functors because all it takes to transform a monad into a functor is a trivial application of the monadic function to create map/select/etc. – melston Jul 25 '17 at 03:56
  • If your type constructor has no value-level inhabitants, you'll never be able to make a `unit` injection function despite be able to make an admittedly vacuous `map` function. Similar tricks can be played if the type parameter of the type constructor is not used at the term-level. – Alec Sep 01 '17 at 18:15
  • In the fairly comprehensive design of my large-scale composable pipeline system, the difference is simple: a monad ***has encapsulated state*** and a functor may not. For systems that compose them together, this entails that functors are therefore (obviously) reusable, while monads ***must be*** single-use. Although functors can consume and emit other functors, in practice, most composed operations emit monads. For example, fan-out involves the functor or monad emitting on a 1:many basis, an operation which, for functors, only makes sense in the context of composing the pipeline itself. – Glenn Slayden Jun 06 '22 at 02:21

3 Answers3

18

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

Bondolin
  • 2,793
  • 7
  • 34
  • 62
3

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]

yoAlex5
  • 29,217
  • 8
  • 193
  • 205
0

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

  • 6
    This definition of functor is strange. I have always read that it's something that can be mapped over, like an optional or a list. In Haskell, it's something that defines `fmap` for this purpose. You're saying that it's any function that maps one type into another. Aren't these definitions incompatible? What am I missing? – User Mar 11 '18 at 10:36
  • A definition of a Functor in Haskell: "class Functor f where fmap :: (a -> b) -> f a -> f b". As you see it provides a way to map from f a -> f b. The notion that you can "map over it" is not the definition of a Functor but it's usage outside mathematical scope. – Alexandros Panagiotakis Mar 20 '18 at 09:08
  • Ok, I'm very new to all this so I apologize for the ignorance. Just one question more (if it's possible to explain this in a newbie-friendly way), you say a functor is "a function from a set of values a to another set of values" - what's the difference with a function then? Don't all functions map from one set of values (`x`) to another (`f(x)`)? It's probably related with the different types? – User Mar 21 '18 at 20:40
  • From my understanding a pure function is functor but I am very new to this. If I define a `square x = x * x` 2 will always map to 4. Functors should be composable and I can compose `plus2 x = x + 2` with the square above. that returns me a new functor that I can fmap over `(fmap plus2 square) 4` – Jakub May 07 '19 at 08:43
  • 3
    This answer is not quite right. I was initially confused about this as well. In the context of computer science, a `Functor` refers to the "data structure" that supports a `map` operation (not just the map function). The `map` function has a requirement that it takes 1 parameter and returns 1 parameter. When the input and output types are the same it can also be called an `endofunctor`. – Brennan Cheung Jun 03 '19 at 23:57
  • A functor is a pattern which forces you to implement a map function that returns another functor without changing the original object. In javascript, functors might be arrays, promises, and any other objects with such patterns. – Andy Nov 12 '20 at 12:44
  • This definition of functor is indeed very strange. It's sound like a simplified definition of what a function is. – Good Night Nerd Pride Nov 13 '21 at 12:48
  • 2
    There are languages that define functors as callable instances of other classes; these functors have nothing to do with the functors of category theory. – chepner Jul 29 '22 at 21:15