Haskell IO(字符串)和字符串

Posted

技术标签:

【中文标题】Haskell IO(字符串)和字符串【英文标题】:Haskell IO (String) and String 【发布时间】:2011-10-01 12:10:23 【问题描述】:

我想写函数并将结果放到字符串中。

我想要功能:

read' :: FilePath -> String

我用:

:t readFile
readFile :: FilePath -> IO String

我做:

read' :: IO ()
read' = do
     str <- readFile "/home/shk/workspace/src/test.txt" 
     putStrLn str

我想问str是不是字符串?

我们知道:

:t putStrLn
putStrLn :: String -> IO ()

那为什么我不能:

read' :: String
read' = do
     str <- readFile "/home/shk/workspace/lxmpp/src/test.txt" 
     str

我收到以下错误:

 Couldn't match expected type `[t0]' with actual type `IO String'
    In the return type of a call of `readFile'
    In a stmt of a 'do' expression:
        str <- readFile "/home/shk/workspace/lxmpp/src/test.txt"
    In the expression:
      do  str <- readFile "/home/shk/workspace/src/test.txt";
           str 

谢谢。

【问题讨论】:

do-notation 中的 readFile 表示你在 IO monad 中,IO monad 无法转义! @is7s 除非你使用unsafePerformIO! unsafePerformIO 的第一条规则是你不要告诉任何人 unsafePerformIO! @Thomas 哦,喜欢 Real World Haskell 打破了这条规则 @Thomas 除非他理解广义的前组合子态 【参考方案1】:

我想还没有人回答过这个非常重要的问题:

我想问str是不是字符串?

我会努力的。

变量str的类型是String,是的。 但是,此变量的范围非常有限。我认为去除 do-notation 的糖分对于理解是必要的:

read' = readFile "/home/shk/workspace/src/test.txt" >>= (\str -> putStrLn str)

我认为这里更清楚为什么str 不够好。它是您传递给&gt;&gt;= 的函数的参数。它的值只有在有人调用你的函数时才可用,只有在执行包含它的 IO 操作时才会发生。

另外,read' :: IO () 的类型不是由putStrLn str 决定的,而是由运算符&gt;&gt;= 的返回类型决定的。看看它(专门针对IO monad):

(>>=) :: IO a -> (a -> IO b) -> IO b

您可以看到结果始终是 IO b 操作,因此尝试更改任何参数都无济于事。

如果您想了解类型为何如此,您可以阅读一些 monad 教程。其背后的直觉是:不执行动作就无法执行动作。

在问题的实际方面,要使用某些操作返回的值,而不是尝试执行 use (extractValue inputAction),这没有意义,因为 extractValue 是不可能的,如果您的@,请尝试 inputAction &gt;&gt;= use 987654336@ 确实涉及 I/O,或者 fmap use inputAction 如果不涉及。

【讨论】:

【参考方案2】:

只是为了狡辩一点,虽然其他答案完全正确,但我想强调一点:IO String 类型的东西不仅仅是类型系统不会让你直接得到的字符串。这是一种执行 I/O 为您获取字符串的计算。将readFile 应用于文件路径不会返回String 值,就像将牛排放在绞肉机旁边神奇地将它们变成汉堡包一样。

当你有这样的代码时:

foo = do let getStr = readFile "input.txt"
         s1 <- getStr
         s2 <- getStr
         -- etc.

这并不意味着您“将字符串从getStr 中取出两次”。这意味着您执行了两次计算,并且很容易在两者之间得到不同的结果。

【讨论】:

魔法汉堡的好处在于你可以吃它们而不用担心副作用!【参考方案3】:

如果您希望它返回str 而不是(),则应在read' 中使用return str。您不能从read' 的类型中去除IO,因为它不是一个纯函数。为了更好地了解 Haskell 中的输入/输出是如何工作的,我推荐你 to read a tutorial。

【讨论】:

虽然这是正确的,但请注意,与&lt;- 绑定没有意义,只是立即return 它。你可以写read' = readFile "/path/to/test.txt"。这是second monad law。【参考方案4】:

作为更详细的原因:它允许杂质。

您绝对不能在纯操作期间执行 IO,否则会完全破坏引用透明性。从技术上讲,您可以使用unsafePerformIO,但在这种情况下它会破坏引用透明度 - 只有在您可以保证结果始终相同的情况下,您才应该使用它。

【讨论】:

结果的保证是否足以声称纯度?我的意思是,unsafePerformIO (putStrLn "hello!") 可以称为纯粹吗? @Rotsor:不要忘记它也必须是线程安全的。手动保证纯度很困难! @camccanne 我不确定如何解释您的评论。您是在扩展 monadic 用户定义的纯度概念,还是暗示实现它的实际困难?如果不扩展,可以通过safePerformIO = unsafePerformIO :: IO () -&gt; () 来实现,对吗?哪个是坏的......或者不是? @Rotsor:啊,对不起,不清楚。我用“不,这还不够”回答你的问题,并添加了一个额外的要求,即即使它使用的只是ST-style 包含的杂质,它也必须独立于可能的并发访问资源。在unsafePerformIO 面前确保纯度确实非常非常困难。 @Rotsor 我认为打印到屏幕是该功能的结果。但是,是的,这通常表明它不是纯正的^_^。基本上,您不应该关心是否使用先前计算的值。

以上是关于Haskell IO(字符串)和字符串的主要内容,如果未能解决你的问题,请参考以下文章

Haskell:如何将 IO 输入字符串解析为 Float(或 Int 或其他)?

Haskell:在 ByteStrings 和不同的文本编码之间进行转换

在Haskell中使用UTF-8作为IO String读取文件

Haskell“字符串移动”函数

如何获取 Haskell 代码字符串(连同值)

在Haskell中,如何创建具有多种类型的列表?