Nio*_*ium 11 haskell traversal lazy-evaluation strictness io-monad
以下代码有点神秘.在非玩具版本的问题中,我试图在monad Result中进行monadic计算,其值只能在IO中构造.似乎IO背后的魔力使这样的计算严格,但我无法弄清楚究竟是怎么发生的.
代码:
data Result a = Result a | Failure deriving (Show)
instance Functor Result where
fmap f (Result a) = Result (f a)
fmap f Failure = Failure
instance Applicative Result where
pure = return
(<*>) = ap
instance Monad Result where
return = Result
Result a >>= f = f a
Failure >>= _ = Failure
compute :: Int -> Result Int
compute 3 = Failure
compute x = traceShow x $ Result x
compute2 :: Monad m => Int -> m (Result Int)
compute2 3 = return Failure
compute2 x = traceShow x $ return $ Result x
compute3 :: Monad m => Int -> m (Result Int)
compute3 = return . compute
main :: IO ()
main = do
let results = mapM compute [1..5]
print $ results
results2 <- mapM compute2 [1..5]
print $ sequence results2
results3 <- mapM compute3 [1..5]
print $ sequence results3
let results2' = runIdentity $ mapM compute2 [1..5]
print $ sequence results2'
Run Code Online (Sandbox Code Playgroud)
输出:
1
2
Failure
1
2
4
5
Failure
1
2
Failure
1
2
Failure
Run Code Online (Sandbox Code Playgroud)
chi*_*chi 10
不错的测试用例.这是发生了什么:
mapM compute我们像往常一样在工作中看到懒惰.这里不足为奇.
在mapM compute2我们的IO单子,其内部工作mapM的定义将要求整个列表:不像Result其尽快跳过列表的尾部Failure被发现,IO总是会扫描整个表.注意代码:
compute2 x = traceShow x $ return $ Result x
Run Code Online (Sandbox Code Playgroud)
因此,一旦访问IO动作列表的每个元素,上面将打印调试消息.一切都是,所以我们打印一切.
在mapM compute3我们现在使用的,大致有:
compute3 x = return $ traceShow x $ Result x
Run Code Online (Sandbox Code Playgroud)
现在,因为return在IO中是懒惰的,所以在返回IO动作时它不会触发traceShow.因此,mapM compute3运行时,不会看到任何消息.相反,我们只在sequence results3运行时看到消息,这会强制Result- 不是全部,而是仅仅需要.
最后一个Identity例子也很棘手.请注意:
> newtype Id1 a = Id1 a
> data Id2 a = Id2 a
> Id1 (trace "hey!" True) `seq` 42
hey!
42
> Id2 (trace "hey!" True) `seq` 42
42
Run Code Online (Sandbox Code Playgroud)
当使用a时newtype,在运行时没有涉及装箱/拆箱(AKA提升),因此强制某个Id1 x值会导致x被强制.对于data类型,这不会发生:值被包装在一个框中(例如Id2 undefined,不等于undefined).
在您的示例中,您添加了一个Identity构造函数,但这是来自newtype Identity!! 所以,在打电话时
return $ traceShow x $ Result x
Run Code Online (Sandbox Code Playgroud)
在return这里不换任何东西,而traceShow一旦被立即触发mapM运行.