我正在学习Haskell,作为练习,我编写了一个读取整数并打印下一个整数的程序:
main = do
line_with_n_in_it <- getLine
putStrLn $ show $ (read line_with_n_in_it :: Int) + 1
然而,对我而言,我必须明确指出我读过的那一行似乎很傻。
如果我写
main = do
putStrLn $ show $ (read getLine :: Int) + 1
runghc
抱怨read
期望String
,getLine
提供IO String
。因此,根据我的理解,似乎这个神奇的<-
运算符会将IO String
转换为String
。
是否有一个不同的运算符或函数可以让我简单地内联<-
运算符?因此,如果magic
是我的神奇功能,我将能够将我的新程序编写为
main = do
putStrLn $ show $ (read $ magic getLine :: Int) + 1
答案 0 :(得分:4)
你应该阅读monads(和IO monad)
一个好的开始here
问题是你必须从monad中“提取”这个值,这不是“完全”的函数调用。
您的第一个代码是正确的,您从monad中提取了一些值
readedString <- getLine
然后,使用它
putStrLn $ "Readed: " ++ readedString
你可以避免“命名行”,但一般来说,这是一个很好的写名。
为了避免命名,你必须编写一些monadic函数然后绑定
getLine >>= putStrLn . show . (+1) . read
但是,我再次建议您阅读monads(和IO monad)。
顺便说一句,<-
运算符“等于”>>=
运算符,详情为here。
答案 1 :(得分:1)
Haskell显然不允许这种行为的事实正是使它如此有价值的事实 - 你不能混合使用纯粹的和IO混乱的代码。
神奇的<-
运算符是monadic绑定的语法糖。例如,您的原始函数可以使用>>=
运算符(发音为“bind”)显式重写:
main = do
n <- getLine
putStrLn $ show $ (read n :: Int) + 1
===
main = getLine >>= \n -> putStrLn $ show $ (read n :: Int) + 1
<-
对你来说似乎是神奇的事实是一件好事 - 它不是标准的haskell语法,它是糖。后一种编写函数的方式应该更有意义。
至于为什么这个有用,我可以给你一个特定于IO monad的手写解释,并建议你阅读Learn You a Haskell for Great Good!关于monad(或整本书)的章节,如果你真的是新的!)以加强你的理解。
我们走了。 getLine
从String
读取IO
,对吗?但这意味着调用的结果是“用IO标记”;它是String
包裹在IO
上下文中,我们无法操纵,因为我们可以正常String
。执行此类操作会违反referential transparency,因为IO
上下文(即您正在阅读的内容)可能会发生变化。基本上,魔术<-
运算符所做的就是从String
上下文“拉出”纯IO
值并允许您对其进行操作。更明确地,>>=
运算符执行IO
操作(getLine
),以及将String
转换为新IO
操作的函数(lambda表达式)在已翻译的示例中),并返回新的IO
操作。这是一种将IO
表达式链接在一起,并“挖掘”其内在值,对其进行操作,并在IO
中重新包装它们的方法。 Monads一般不是我(或任何人)可以在SO答案中详细解释给你的,但我强烈建议你读LYAH,如果你打算继续学习Haskell - 这是一本好书并且会让你快速上手。