Functor 和 Monad 有什么区别?

What is the difference between a Functor and a Monad?

这里有类似的问题,但它们与特定的编程语言有关,我正在寻找概念层面的答案。

据我所知,函子本质上是不可变的容器,它公开 map() API 派生另一个函子。哪个加法使得将特定函子称为 monad 成为可能?

据我了解,每个 monad 都是一个 functor,但并非每个 functor 都是一个 monad。

(请注意,这将是对范畴论概念的简化解释)

函子

Functor 是从一组值 a 到另一组值的函数:a -> b。对于编程语言,这可能是来自 String -> Integer 的函数:

function fn(text: string) : integer

作文

组合是指将一个函数的值用作下一个函数值的输入:fa(fb(x))。例如:

hash(lowercase(text))

单子

Monad 允许组合不可组合的 Functors,通过在组合中添加额外功能来组合 Functors,或两者兼而有之。

  • 第一个例子是 Functor 的 Monad String -> (String, Integer)

  • 第二个例子是一个 Monad,它计算在一个值上调用的函数的数量

一个 Monad 包含一个 Functor T,它负责您想要的功能以及另外两个功能:

  • input -> T(input)
  • T(T(input)) -> T(input)

第一个函数允许将您的输入值转换为我们的 Monad 可以组合的一组值。第二个函数允许合成。

所以总而言之,每个 Monad 都不是 Functor,而是使用 Functor 来完成它的目的。

让我在不涉及范畴论的情况下解释一下我的理解:

Functors 和 monad 都提供了一些工具 来包装输入,返回包装输出。

Functor = unit + map(即工具)

其中,

unit = 接受原始输入并将其包装在一个小上下文中的东西。

map = 将函数作为输入的工具,将其应用于包装器中的原始值,然后 returns 包装结果。

示例:让我们定义一个将整数加倍的函数

// doubleMe :: Int a -> Int b
const doubleMe = a => 2 * a;
Maybe(2).map(doubleMe)  // Maybe(4)

Monad = unit + flatMap (or bind or chain)

flatMap = 压平 map 的工具,顾名思义。通过下面的示例很快就会清楚。

示例:假设我们有一个柯里化函数,它仅在两个字符串都不为空时才附加两个字符串。

让我定义一个如下:

append :: (string a,string b) -> Maybe(string c)  

现在让我们看看mapFunctor自带的工具)的问题,

Maybe("a").map(append("b")) // Maybe(Maybe("ab"))  

这里怎么有两个Maybe

嗯,这就是 map 所做的;它将提供的函数应用于包装值并包装结果。

让我们把它分成几个步骤,

  1. 将映射函数应用于包装值 ;这里的映射函数是 append("b"),包装值是 "a",结果是 Maybe("ab").

  2. 包装结果,returns Maybe(Maybe("ab")).

现在我们感兴趣的值被包裹了两次。 flatMap 来拯救了。

Maybe("a").flatMap(append("b")) // Maybe("ab")

当然,functor 和 monad 也必须遵循一些其他的规律,但我相信这不在要求的范围内。

Swift Functor, Applicative, Monad

仿函数、应用函数、单子:

  • 解决同样的问题 - 将包装值处理到上下文中(class)
  • 使用闭包[About]
  • return 上下文的新实例(class)

区别在于闭包的参数

伪代码:

class SomeClass<T> {
    var wrappedValue: T //wrappedValue: - wrapped value
    func foo<U>(function: ???) -> Functor<U> { //function: - function/closure
        //logic
    }
}

在哪里???

function: (T) -> U == Functor
function: SomeClass< (T) -> U > == Applicative
function: (T) -> SomeClass<U> == Monad

函子

Functor 将 function 应用于 wrapped value

伪代码:

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 将 wrapped function 应用于 wrapped value

与 Functor 的区别是 wrapped function 而不是 function

伪代码:

class Applicative<T> {
    var value: T
    func apply<U>(function: Applicative< (T) -> U >) -> Applicative<U> {
        return Applicative(value: unwrappedFunction(value))
    }
}

Monad

Monad 将 function(其中 return 是 wrapped value)应用于 wrapped value

伪代码:

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是Functor和Monad
  • String 是 Functor

作为示例可选

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