与Haskell混淆阻塞

时间:2014-10-16 05:27:24

标签: haskell monads

我有以下代码:

doSomething :: [Int] -> [Int]
doSomething arg = arg ++ [1]

afterThreeTurns = do
    first <- ["test"]
    doSomething [1] -- COMMENT THIS
    return first

返回:

*Main> afterThreeTurns
["test","test"]

如果我拿出标记为COMMENT THIS的行,则按预期返回[“test”]。为什么?我看待它的方式做的事情首先应该没有影响吗?

3 个答案:

答案 0 :(得分:6)

由于doSomething [1][2,1],因此您的代码相当于:

afterThreeTurns = do
   first <- ["test"]
   x <- [2,1]
   return first

这与列表推导[ first | first <- ["test"], x <- [2,1] ]相同,这解释了为什么要获得长度为2的列表。

请注意,变量x未在任何地方引用,因此也可以这样写:

afterThreeTurns = do
   first <- ["test"]
   _ <- [2,1]
   return first

以下是使用IO monad的类似情况。代码:

thirdLine = do
  getLine
  getLine
  x <- getLine
  putStrLn $ "The third line is: " ++ x

与:

相同
thirdLine = do
  _ <- getLine
  _ <- getLine
  x <- getLine
  putStrLn $ "The third line is: " ++ x

你可以使用-fwarn-unused-do-bind编译器标志来获取ghc来标记这些monadic语句。在您的示例中,ghc将发出警告:

...: Warning:
    A do-notation statement discarded a result of type ‘Int’
    Suppress this warning by saying ‘_ <- doSomething [1]’
    or by using the flag -fno-warn-unused-do-bind

答案 1 :(得分:3)

让我们将其转换为对>>=

的等效调用
["test"] >>= (\first ->
                 doSomething [1] >>= (\_ -> return first))

编译器始终在do内部执行此操作。两种写作方式完全相同。

现在,>>= []concatMap的{​​{1}}相同,其参数会被翻转,所以让我们继续进行转换(并应用定义{{1} })和reduce:

return x = [x]

直观地,concatMap (\first -> concatMap (\_ -> [first]) (doSomething [1])) ["test"] concatMap (\first -> concatMap (\_ -> [first]) ([1] ++ [1])) ["test"] concatMap (\first -> concatMap (\_ -> [first]) [1, 1]) ["test"] concatMap (\first -> concat [[first], [first]]) ["test"] concatMap (\first -> [first, first]) ["test"] concat [["test"], ["test"]] ["test", "test"] Monad可以被认为是表示“非确定性”计算(即,可以采用几种不同可能结果之一的计算)。当您以这种方式组合两个非确定性计算时,结果的数量会相乘。这是由于可以采取的不同“路径”,每个分支的每种可能性都有一个。

供参考,以下是[]注释与do>>=来电之间的转换(请注意,>>必须始终等同于m1 >> m2):

m1 >>= (\_ -> m2)

答案 2 :(得分:0)

List monad(或[])中,“效果”返回多个结果而不是一个。因此,您的doSomething函数对结果没有贡献,但确实会影响效果,从而改变最终列表的长度。