为什么在monad中使用这种特殊的函数类型?

ssm*_*ssm 17 monads haskell types typeclass

Haskell的新手,我正试图找出这个Monad的东西.monadic绑定运算符 - >>=具有非常奇特的类型签名:

(>>=) :: Monad m => m a -> (a -> m b) -> m b
Run Code Online (Sandbox Code Playgroud)

为了简化,我们替换Maybem:

(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Run Code Online (Sandbox Code Playgroud)

但请注意,该定义可以用三种不同的方式编写:

(>>=) :: Maybe a -> (Maybe a -> Maybe b) -> Maybe b
(>>=) :: Maybe a -> (      a -> Maybe b) -> Maybe b
(>>=) :: Maybe a -> (      a ->       b) -> Maybe b
Run Code Online (Sandbox Code Playgroud)

在这三个中,中心的一个是最不对称的.但是,据我所知,如果我们想避免(LYAH称为样板代码),第一个有点无意义.但是,接下来的两个,我更喜欢最后一个.对于Maybe,这看起来像:

当这被定义为:

(>>=) :: Maybe a -> (a -> b) -> Maybe b

instance Monad Maybe where
   Nothing  >>= f = Nothing
   (Just x) >>= f = return $ f x
Run Code Online (Sandbox Code Playgroud)

这里,a -> b是一个普通的功能.此外,我没有立即看到任何不安全的东西,因为在函数应用程序之前Nothing捕获异常,因此除非获得a,否则不会调用该函数.a -> bJust a

那么也许有些事情对我来说并不明显,这导致(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b定义比更简单的(>>=) :: Maybe a -> (a -> b) -> Maybe b定义更受欢迎?是否有一些固有的问题与(我认为是)更简单的定义相关联?

Gab*_*lez 22

如果用下面的派生函数(from Control.Monad)来思考它会更加对称:

(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
(f >=> g) x = f x >>= g
Run Code Online (Sandbox Code Playgroud)

这个函数很重要的原因是它遵循三个有用的方程式:

-- Associativity
(f >=> g) >=> h = f >=> (g >=> h)

-- Left identity
return >=> f = f

-- Right identity
f >=> return = f
Run Code Online (Sandbox Code Playgroud)

这些是类别法,如果你翻译它们(>>=)而不是使用(>=>),你会得到三个monad法则:

(m >>= g) >>= h = m >>= \x -> (g x >>= h)

return x >>= f = f x

m >>= return = m
Run Code Online (Sandbox Code Playgroud)

所以它不是(>>=)优雅的运算符,而是(>=>)您正在寻找的对称运算符.然而,我们通常认为的原因(>>=)是因为这是do符号的荒谬.


ick*_*fay 8

让我们考虑Maybemonad的一个常见用途:处理错误.说我想安全地划分两个号码.我可以写这个函数:

safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv n d = n `div` d
Run Code Online (Sandbox Code Playgroud)

然后使用标准Maybemonad,我可以做这样的事情:

foo :: Int -> Int -> Maybe Int
foo a b = do
  c <- safeDiv 1000 b
  d <- safeDiv a c  -- These last two lines could be combined.
  return d          -- I am not doing so for clarity.
Run Code Online (Sandbox Code Playgroud)

请注意,在每个步骤中,safeDiv都可能失败,但在两个步骤中都safeDiv需要Ints,而不是Maybe Ints.如果>>=有这个签名:

(>>=) :: Maybe a -> (a -> b) -> Maybe b
Run Code Online (Sandbox Code Playgroud)

你可以将函数组合在一起,然后给它一个Nothing或一个Just,或者它将打开它Just,遍历整个管道,并重新包装它Just,或者它只是Nothing通过基本上未触及的.这可能有用,但它不是monad.为了它有用,我们必须能够在中间失败,这就是这个签名给我们的东西:

(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Run Code Online (Sandbox Code Playgroud)

顺便说一下,你设计的签名确实存在:

flip fmap :: Maybe a -> (a -> b) -> Maybe b
Run Code Online (Sandbox Code Playgroud)

  • @ssm:Monads不是*更好*,你可以用它们做更多的东西(在某种意义上).所有Monads都是Functors,所以Monad是一个带有一些额外东西的Functor.所以适用于此事.事实上,所有Monads都是Applicative,所有Applicials都是Functors. (4认同)
  • +1.实际上,`fmap ::(Functor f)=>(a - > b) - > fa - > fb`*是非常有用的,但在上面解释的意义上,它比`(>> =)`强大得多. (3认同)