我有以下代码:
doSomething :: [Int] -> [Int]
doSomething arg = arg ++ [1]
afterThreeTurns = do
first <- ["test"]
doSomething [1] -- COMMENT THIS
return first
返回:
*Main> afterThreeTurns
["test","test"]
如果我拿出标记为COMMENT THIS的行,则按预期返回[“test”]。为什么?我看待它的方式做的事情首先应该没有影响吗?
答案 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
函数对结果没有贡献,但确实会影响效果,从而改变最终列表的长度。