Function 单子与函数的区别

Function 单子与函数的区别,function,functional-programming,monads,Function,Functional Programming,Monads,好的,关于Monad,我知道已经问了足够多的问题。我不想再打扰任何人去问什么是单子 事实上,我读过,这是非常有帮助的。我觉得我离真正理解它很近了 我在这里提出这个问题只是为了描述我对单子和函数的一些想法,希望有人能纠正我或确认它是正确的 那篇文章中的一些答案让我觉得monad有点像函数 Monad接受一个类型,返回一个包装类型(return),也可以接受一个类型,对它执行一些操作,并返回一个包装类型(bind) 在我看来,它有点像函数。函数获取某些内容并执行某些操作,然后返回某些内容 那我们为

好的,关于Monad,我知道已经问了足够多的问题。我不想再打扰任何人去问什么是单子

事实上,我读过,这是非常有帮助的。我觉得我离真正理解它很近了

我在这里提出这个问题只是为了描述我对单子和函数的一些想法,希望有人能纠正我或确认它是正确的


那篇文章中的一些答案让我觉得monad有点像函数

Monad接受一个类型,返回一个包装类型(
return
),也可以接受一个类型,对它执行一些操作,并返回一个包装类型(
bind

在我看来,它有点像函数。函数获取某些内容并执行某些操作,然后返回某些内容

那我们为什么还要monad?我认为其中一个关键原因是monad为初始数据/类型的顺序操作提供了更好的方式或模式

例如,我们有一个初始整数
i
。在我们的代码中,我们需要一步一步地应用10个函数
f1,f2,f3,f4,…,f10
,也就是说,我们首先对
i
应用
f1
,得到一个结果,然后对该结果应用
f2
,然后我们得到一个新的结果,然后应用
f3

我们可以通过函数rawly实现这一点,就像
f1 i |>f2 |>f3…
。然而,这些步骤的中间结果并不一致;此外,如果我们不得不在中间的某个地方处理可能的失败,事情就会变得糟糕。如果我们不希望整个过程在异常时失败,那么无论如何都必须构造一个
选项。所以很自然地,
monad
出现了

Monad统一并强制所有步骤中的返回类型。这大大简化了代码的逻辑和可读性(这也是那些
设计模式的目的,不是吗)。此外,它对错误或bug更具防弹性。例如,
Option Monad
强制每个中间结果都是
options
,并且很容易实现
快速失败
范例

正如许多关于monad的文章所描述的那样,monad是一种设计模式,是组合函数/步骤以构建流程的更好方式



我理解正确吗?

我觉得你似乎在发现类比学习的局限性。Monad在Haskell中被精确地定义为一个类型类,在范畴论中被定义为一个代数对象;任何使用“…像…”的比较都是不精确的,因此是错误的

所以不,因为Haskell的单子与函数不同,因为它们1)是作为类型类实现的,2)旨在以不同于函数的方式使用

这个答案可能并不令人满意;你在寻找直觉吗?如果是这样的话,我建议做大量的例子,尤其是通读。如果没有坚实的实例和经验基础,很难直观地理解像单子这样的抽象事物

为什么我们甚至需要单子?这是一个好问题,也许这里有不止一个问题:

  • 为什么我们甚至需要Monad类型类?原因与我们需要任何类型类相同

  • 为什么我们甚至需要monad概念?因为它很有用。而且,它不是一个函数,所以它不能被函数替换。(您的示例似乎不需要Monad(相反,它需要一个应用程序)

    例如,您可以使用Applicative type类实现上下文无关的解析器组合器。但是,请尝试在不使用Monad的情况下为包含相同符号字符串的语言实现两次解析器(用空格分隔),即:

    a a   -> yes
    a b   -> no
    ab ab -> yes
    ab ba -> no
    
    所以这是单子提供的一件事:使用先前的结果来“决定”做什么的能力。下面是另一个例子:

    f :: Monad m => m Int -> m [Char]
    f m = 
        m >>= \x -> 
        if x > 2 
            then return (replicate x 'a') 
            else return []
    
    f (Just 1)  -->>  Just ""
    f (Just 3)  -->>  Just "aaa"
    f [1,2,3,4] -->>  ["", "", "aaa", "aaaa"]
    
  • 单子(和函子,和应用函子)可以看作是关于“广义函数应用”:它们都创建
    fa类型的函数⟶ f b
    不仅涉及“上下文中的值”,如类型
    a
    b
    ,而且还涉及“上下文”——相同类型的上下文——由
    f
    表示

    因此,“正常”函数应用涉及
    (a)类型的函数⟶ b) 
    ,“通用”函数应用程序的函数类型为
    (f a⟶  f b)
    。由于更统一的类型结构:
    fa,这样的函数也可以在正常的函数组合下组合⟶ f b;f-b⟶ f c==>f a⟶ f c

    三个中的每一个都以不同的方式创建它们,从不同的事情开始:

    Functors:               fmap  :: Functor     f =>   (a ⟶   b) ⟶ (f a  ⟶  f b)
    
    Applicative Functors:   (<*>) :: Applicative f => f (a ⟶   b) ⟶ (f a  ⟶  f b)
    
    Monadic Functors:       (=<<) :: Monad       f =>   (a ⟶ f b) ⟶ (f a  ⟶  f b)
    以及他们的类型,

    "liftF1"   :: (Functor     f) => (a ⟶ b)      ⟶ f a ⟶       f b           -- fmap
    
    liftA2     :: (Applicative f) => (a ⟶ b ⟶ c) ⟶ f a ⟶       f b  ⟶ f c
    
    "liftBind" :: (Monad       f) => (a ⟶ b ⟶ c) ⟶ f a ⟶ (a ⟶ f b) ⟶ f c
    
    "liftF1"   :: (Functor     f) => (a ⟶ b)      ⟶ f a ⟶       f b           -- fmap
    
    liftA2     :: (Applicative f) => (a ⟶ b ⟶ c) ⟶ f a ⟶       f b  ⟶ f c
    
    "liftBind" :: (Monad       f) => (a ⟶ b ⟶ c) ⟶ f a ⟶ (a ⟶ f b) ⟶ f c