从IO a写一个函数 - >一个?

时间:2017-02-26 13:44:13

标签: haskell

使用函数getLine。

它有一个类型

getLine :: IO String

如何从此IO值中提取String。更一般地说,我该如何转换它。

IO a

到此:

a

如果无法做到这一点。为什么我不能这样做?

4 个答案:

答案 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;,不清楚为什么你认为你需要这种类型的功能,在这种情况下它会产生重大影响。

应该注意完整性,这是可能的。确实在基础库中存在一个名为unsafePerformIOIO a -> a类型的函数。

unsafe部分是有原因的。很少有情况下其使用被认为是合理的。这是一个非常谨慎使用的逃生舱 - 大多数时候你会让怪物进入而不是让自己出局。

为什么通常不能从IO a转到a?好吧,它至少可以让你通过一个看起来很纯粹的功能来打破规则 - 哎呀!如果这是一种常见的做法,那么类型签名和编译器为验证它们而完成的所有工作根本就没有意义。所有正确性保证都会消失。

Haskell在某种程度上是有趣的,因为这通常是不可能的。

有关如何处理getLine问题,请参阅其他答案。