Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 与应用程序相比,Monad给了我们什么优势?_Haskell_Functional Programming_Monads_Applicative - Fatal编程技术网

Haskell 与应用程序相比,Monad给了我们什么优势?

Haskell 与应用程序相比,Monad给了我们什么优势?,haskell,functional-programming,monads,applicative,Haskell,Functional Programming,Monads,Applicative,我读过,但不明白最后一节 作者说Monad提供了上下文敏感度,但仅使用一个应用实例就可以实现相同的结果: let maybeAge = (\futureYear birthYear -> if futureYear < birthYear then yearDiff birthYear futureYear else yearDiff futureYear birthYear) <$> (readMay futureYearString) <*>

我读过,但不明白最后一节

作者说Monad提供了上下文敏感度,但仅使用一个应用实例就可以实现相同的结果:

let maybeAge = (\futureYear birthYear -> if futureYear < birthYear
    then yearDiff birthYear futureYear
    else yearDiff futureYear birthYear) <$> (readMay futureYearString) <*> (readMay birthYearString)
let maybeAge=(\futureYear birthy->if futureYear

没有do语法肯定更难看,但除此之外,我不明白为什么我们需要Monad。有人能帮我澄清一下吗?

对于单子,后续效果可能取决于先前的值。例如,您可以有:

main = do
    b <- readLn :: IO Bool
    if b
      then fireMissiles
      else return ()
main=do
b,这个例子实际上没有使用上下文敏感性。考虑上下文敏感性的一种方法是,它允许用户根据一元值选择要采取的操作。在某种意义上,应用计算必须始终具有相同的“形状”,无论涉及的值如何;一元计算不需要。我个人认为用一个具体的例子更容易理解,所以让我们来看一个。下面是一个简单程序的两个版本,它要求您输入密码,检查您是否输入了正确的密码,并根据您是否输入了密码打印出响应

import Control.Applicative

checkPasswordM :: IO ()
checkPasswordM = do putStrLn "What's the password?"
                    pass <- getLine
                    if pass == "swordfish"
                      then putStrLn "Correct.  The secret answer is 42."
                      else putStrLn "INTRUDER ALERT!  INTRUDER ALERT!"

checkPasswordA :: IO ()
checkPasswordA =   if' . (== "swordfish")
               <$> (putStrLn "What's the password?" *> getLine)
               <*> putStrLn "Correct.  The secret answer is 42."
               <*> putStrLn "INTRUDER ALERT!  INTRUDER ALERT!"

if' :: Bool -> a -> a -> a
if' True  t _ = t
if' False _ f = f
到目前为止,一切顺利。但如果我们使用应用程序版本:

*Main> checkPasswordA
What's the password?
hunter2
Correct.  The secret answer is 42.
INTRUDER ALERT!  INTRUDER ALERT!
我们输入了错误的密码,但我们仍然得到了秘密!还有入侵者警报!这是因为
,或者等效地
liftAn
/
liftMn
,总是执行其所有参数的效果。应用程序版本在
do
符号中转换为

do pass  <- putStrLn "What's the password?" *> getLine)
   unit1 <- putStrLn "Correct.  The secret answer is 42."
   unit2 <- putStrLn "INTRUDER ALERT!  INTRUDER ALERT!"
   pure $ if' (pass == "swordfish") unit1 unit2
或相当于

liftAN f app1 app2 ... appN

考虑这个问题,考虑<代码>应用程序< /C>的方法:

pure  :: a -> f a
(<$>) :: (a -> b) -> f a -> f b
(<*>) :: f (a -> b) -> f a -> f b
(请记住,您只需要其中一个。)

手工操作很多,如果你仔细想想的话,我们能把应用程序函数组合在一起的唯一方法就是构造
f app1形式的链。。。appN
,并可能嵌套这些链(例如,
f(gxy)z
)。然而,
(==)
)允许我们获取一个值,并根据该值生成不同的一元计算,可以动态构造。这就是我们用来决定是计算“打印出秘密”还是计算“打印出入侵者警报”的原因,以及为什么我们不能单独使用应用程序函子来做出决定;应用程序函数的任何类型都不允许使用普通值

您可以以类似的方式考虑
join
fmap
协同工作:,您可以执行以下操作

checkPasswordFn :: String -> IO ()
checkPasswordFn pass = if pass == "swordfish"
                         then putStrLn "Correct.  The secret answer is 42."
                         else putStrLn "INTRUDER ALERT!  INTRUDER ALERT!"

checkPasswordA' :: IO (IO ())
checkPasswordA' = checkPasswordFn <$> (putStrLn "What's the password?" *> getLine)
这与以前的monadic版本做了相同的事情(只要我们先导入Control.Monad
以获得
join
):


这里有两个使用
Monad
接口的函数

ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM c x y = c >>= \z -> if z then x else y

whileM :: Monad m => (a -> m Bool) -> (a -> m a) -> a -> m a
whileM p step x = ifM (p x) (step x >>= whileM p step) (return x)
您不能使用
Applicative
接口实现它们。但为了开悟,让我们试着看看哪里出了问题。那

import Control.Applicative

ifA :: Applicative f => f Bool -> f a -> f a -> f a
ifA c x y = (\c' x' y' -> if c' then x' else y') <$> c <*> x <*> y
这是你对差异的第一个暗示。您不能仅使用复制
ifM
Applicative
接口编写函数

如果您将其分为将
fa
形式的值看作是关于“效果”和“结果”(这两个术语都是非常模糊的近似术语,是可用的最佳术语,但不是很好),您可以在这里改进您的理解。对于类型为
的值,可能是a
,作为计算,“效果”是成功还是失败。“结果”是一个类型为
a
的值,在计算完成时可能会出现该值。(这些术语的含义在很大程度上取决于具体的类型,因此不要认为这是除了作为类型的
可能
之外的有效描述。)

在这样的背景下,我们可以更深入地观察差异。
Applicative
界面允许“结果”控制流是动态的,但它要求“效果”控制流是静态的。如果表达式包含3个可能失败的计算,则其中任何一个的失败都会导致整个计算失败。
Monad
界面更加灵活。它允许“效果”控制流取决于“结果”值<代码>ifM
根据第一个参数选择要包含在其自身“效果”中的参数的“效果”。这就是
ifA
ifM
之间巨大的根本区别

whileM
发生了更严重的事情。让我们试着做
whileA
,看看会发生什么

whileA :: Applicative f => (a -> f Bool) -> (a -> f a) -> a -> f a
whileA p step x = ifA (p x) (whileA p step <*> step x) (pure x)
whileA::Applicative f=>(a->f Bool)->(a->f a)->a->f a
whileA p step x=ifA(px)(whileA p step x)(纯x)
嗯。。发生的是编译错误<代码>()的类型不正确<代码>而p步骤的类型为
a->fa
步骤x
的类型为
fa
<代码>()的形状不适合将它们组合在一起。要使其工作,函数类型必须是
f(a->a)

你可以尝试更多的东西,但是你最终会发现
whileA
没有一个实现可以像
whileM
那样工作。我的意思是,您可以实现该类型,但无法使其同时循环和终止


要使其正常工作,需要加入
(>>=)
。(好的,或者是其中一个的众多等价物之一)以及从
Monad
界面中获得的额外内容。

如果您尝试将Monad的
bind
和Applicative
的类型签名转换为自然语言,您会发现:

ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM c x y = c >>= \z -> if z then x else y

whileM :: Monad m => (a -> m Bool) -> (a -> m a) -> a -> m a
whileM p step x = ifM (p x) (step x >>= whileM p step) (return x)
bind
I将为您提供包含的值,您将返回一个新的打包值

你给我一个打包的函数
checkPasswordM' :: IO ()
checkPasswordM' = join checkPasswordA'
*Main> checkPasswordM'
What's the password?
12345
INTRUDER ALERT!  INTRUDER ALERT!
ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM c x y = c >>= \z -> if z then x else y

whileM :: Monad m => (a -> m Bool) -> (a -> m a) -> a -> m a
whileM p step x = ifM (p x) (step x >>= whileM p step) (return x)
import Control.Applicative

ifA :: Applicative f => f Bool -> f a -> f a -> f a
ifA c x y = (\c' x' y' -> if c' then x' else y') <$> c <*> x <*> y
*Main> ifM (Just True) (Just 1) (Just 2)
Just 1
*Main> ifM (Just True) (Just 1) (Nothing)
Just 1
*Main> ifA (Just True) (Just 1) (Just 2)
Just 1
*Main> ifA (Just True) (Just 1) (Nothing)
Nothing
whileA :: Applicative f => (a -> f Bool) -> (a -> f a) -> a -> f a
whileA p step x = ifA (p x) (whileA p step <*> step x) (pure x)
Left e1 >> Left e2    ===   Left e1
data AllErrors e a = Error e | Pure a deriving (Functor)

instance Monoid e => Applicative (AllErrors e) where
  pure = Pure
  (Pure f) <*> (Pure x) = Pure (f x)
  (Error e) <*> (Pure _) = Error e
  (Pure _) <*> (Error e) = Error e
  -- This is the non-Monadic case
  (Error e1) <*> (Error e2) = Error (e1 <> e2)
> Left "a" <*> Left "b"
Left 'a'

> Error "a" <*> Error "b"
Error "ab"
do
   r1 <- act1
   if r1
        then act2
        else act3