Dav*_*ave 11 monads haskell composition
我正在玩可组合的失败并设法用签名编写一个函数
getPerson :: IO (Maybe Person)
Run Code Online (Sandbox Code Playgroud)
一个人在哪里:
data Person = Person String Int deriving Show
Run Code Online (Sandbox Code Playgroud)
它的工作原理我用以下的方式编写了它:
import Control.Applicative
getPerson = do
name <- getLine -- step 1
age <- getInt -- step 2
return $ Just Person <*> Just name <*> age
Run Code Online (Sandbox Code Playgroud)
哪里
getInt :: IO (Maybe Int)
getInt = do
n <- fmap reads getLine :: IO [(Int,String)]
case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
Run Code Online (Sandbox Code Playgroud)
我编写此函数的目的是创建可组合的可能失败.虽然我对除了Maybe和IO之外的monad的经验很少,但是如果我有一个更复杂的数据类型以及更多的字段,那么链接计算并不复杂.
我的问题是,如果没有写符号,我将如何重写?由于我无法将值绑定到名称或年龄等名称,因此我不确定从哪里开始.
提问的原因只是为了提高我对(>> =)和(<*>)的理解,并构成失败和成功(不要用难以辨认的单行代码来捣乱我的代码).
编辑:我想我应该澄清一下,"我应该怎么重写getPerson而不用do-notation",我不关心getInt函数的一半.
Joh*_*n L 20
以这种方式处理(>> =)语法的do-notation:
getPerson = do
name <- getLine -- step 1
age <- getInt -- step 2
return $ Just Person <*> Just name <*> age
getPerson2 =
getLine >>=
( \name -> getInt >>=
( \age -> return $ Just Person <*> Just name <*> age ))
Run Code Online (Sandbox Code Playgroud)
在第一个之后的每一行中的每一行被转换为一个lambda,然后绑定到前一行.将值绑定到名称是一个完全机械化的过程.我不知道使用do-notation会如何影响可组合性; 这完全是语法问题.
你的其他功能类似:
getInt :: IO (Maybe Int)
getInt = do
n <- fmap reads getLine :: IO [(Int,String)]
case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
getInt2 :: IO (Maybe Int)
getInt2 =
(fmap reads getLine :: IO [(Int,String)]) >>=
\n -> case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
Run Code Online (Sandbox Code Playgroud)
关于你似乎走向的方向的一些指示:
使用时Control.Applicative,<$>将纯函数提升到monad中通常很有用.在最后一行中有一个很好的机会:
Just Person <*> Just name <*> age
Run Code Online (Sandbox Code Playgroud)
变
Person <$> Just name <*> age
Run Code Online (Sandbox Code Playgroud)
此外,你应该看看monad变形金刚.该MTL包是最普遍的,因为它带有Haskell的平台,但也有其他的选择.Monad变换器允许您创建一个具有底层monad组合行为的新monad.在这种情况下,您正在使用具有该类型的函数IO (Maybe a).mtl(实际上是一个基础库,变换器)定义
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
Run Code Online (Sandbox Code Playgroud)
这与您正在使用的类型相同,m变量实例化为IO.这意味着你可以写:
getPerson3 :: MaybeT IO Person
getPerson3 = Person <$> lift getLine <*> getInt3
getInt3 :: MaybeT IO Int
getInt3 = MaybeT $ do
n <- fmap reads getLine :: IO [(Int,String)]
case n of
((x,""):[]) -> return (Just x)
_ -> return Nothing
Run Code Online (Sandbox Code Playgroud)
getInt3除了MaybeT构造函数之外完全相同.基本上,只要你有一个m (Maybe a)你可以包装它MaybeT来创建一个MaybeT m a.这可以获得更简单的可组合性,正如您可以从新定义中看到的那样getPerson3.该功能根本不担心失败,因为它全部由MaybeT管道处理.剩下的一块是getLine,这只是一块IO String.这个函数被提升到了MaybeT monad中lift.
编辑
newacct的评论表明我也应该提供一个模式匹配示例; 它与一个重要的例外情况基本相同.考虑这个例子(列表monad是我们感兴趣的monad,Maybe只是用于模式匹配):
f :: Num b => [Maybe b] -> [b]
f x = do
Just n <- x
[n+1]
-- first attempt at desugaring f
g :: Num b => [Maybe b] -> [b]
g x = x >>= \(Just n) -> [n+1]
Run Code Online (Sandbox Code Playgroud)
这g与完全相同f,但如果模式匹配失败怎么办?
Prelude> f [Nothing]
[]
Prelude> g [Nothing]
*** Exception: <interactive>:1:17-34: Non-exhaustive patterns in lambda
Run Code Online (Sandbox Code Playgroud)
这是怎么回事?这种特殊情况是Haskell中最大的疣(IMO)之一的原因,这是Monad该类的fail方法.在do-notation中,fail调用模式匹配失败时.实际翻译将更接近:
g' :: Num b => [Maybe b] -> [b]
g' x = x >>= \x' -> case x' of
Just n -> [n+1]
_ -> fail "pattern match exception"
Run Code Online (Sandbox Code Playgroud)
现在我们有
Prelude> g' [Nothing]
[]
Run Code Online (Sandbox Code Playgroud)
fail有用性取决于monad.对于列表,它非常有用,基本上使模式匹配在列表推导中起作用.它在Maybemonad中也非常好,因为模式匹配错误会导致计算失败,这Maybe应该是应该的Nothing.对于IO,也许没有那么多,因为它只是抛出通过用户错误异常error.
这是完整的故事.
| 归档时间: |
|
| 查看次数: |
1671 次 |
| 最近记录: |