使用函数getLine。
它有一个类型
getLine :: IO String
如何从此IO值中提取String。更一般地说,我该如何转换它。
IO a
到此:
a
如果无法做到这一点。为什么我不能这样做?
答案 0 :(得分:10)
在Haskell中,当你想使用一个被困的值时,#34;在IO
中,您不会从IO
中取出值。相反,您也要将要执行的操作放入IO
!
例如,假设您要使用Prelude中的getLine :: IO String
函数检查length
将生成多少个字符。
存在一个名为fmap
的辅助函数,当专用于IO
时,其函数类型为:
fmap :: (a -> b) -> IO a -> IO b
它需要一个适用于"纯粹"值未被IO
捕获,并为您提供了一个函数,该函数可以使用IO
中捕获的值。这意味着代码
fmap length getLine :: IO Int
表示IO
操作,它从控制台读取一行,然后为您提供长度。
<$>
是fmap
的中缀同义词,可以使事情更简单。这相当于上面的代码:
length <$> getLine
现在,有时您想要使用IO
- 陷阱值本身执行的操作会返回IO
- 陷阱值。简单的例子:你想用putStrLn :: String -> IO ()
回写你刚读过的字符串。
在这种情况下,fmap
是不够的。您需要使用(>>=)
运算符,该运算符在专门用于IO
时具有类型IO a -> (a -> IO b) -> IO b
。在以下情况:
getLine >>= putStrLn :: IO ()
使用(>>=)
链接IO
操作具有强制性的顺序风格。有一种叫做"do-notation"的语法糖,它有助于以更自然的方式编写像这样的顺序操作:
do line <- getLine
putStrLn line
请注意,这里的<-
不是运算符,而是由符号提供的语法糖的一部分。
答案 1 :(得分:2)
不进入任何细节,如果你在do
区块中,你可以(非正式地/不准确地)将<-
视为从IO中获取价值。
例如,以下函数从getLine
获取一行,并将其传递给只接受字符串的纯函数
main = do
line <- getLine
putStrLn (wrap line)
wrap :: String -> String
wrap line = "'" ++ line ++ "'"
如果将其编译为wrap
,并在命令行上运行
echo "Hello" | wrap
你应该看到
'Hello'
答案 2 :(得分:1)
如果您了解C,请考虑“如何从gets
获取字符串?” IO String
不是一些很难找到的字符串,它是一个可以返回字符串的过程 - 比如从网络或标准输入读取。您希望运行该过程以获取字符串。
在序列中运行IO操作的常用方法是do
表示法:
main = do
someString <- getLine
-- someString :: String
print someString
在上面的内容中,您运行getLine
操作以获取String
值,然后根据需要使用该值。
答案 3 :(得分:0)
所以&#34;一般&#34;,不清楚为什么你认为你需要这种类型的功能,在这种情况下它会产生重大影响。
应该注意完整性,这是可能的。确实在基础库中存在一个名为unsafePerformIO
的IO a -> a
类型的函数。
但unsafe
部分是有原因的。很少有情况下其使用被认为是合理的。这是一个非常谨慎使用的逃生舱 - 大多数时候你会让怪物进入而不是让自己出局。
为什么通常不能从IO a
转到a
?好吧,它至少可以让你通过一个看起来很纯粹的功能来打破规则 - 哎呀!如果这是一种常见的做法,那么类型签名和编译器为验证它们而完成的所有工作根本就没有意义。所有正确性保证都会消失。
Haskell在某种程度上是有趣的,因为这通常是不可能的。
有关如何处理getLine
问题,请参阅其他答案。