什么是IO String和Haskell中的普通字符串之间的区别

时间:2010-03-18 08:23:28

标签: haskell types monads

与IO String到String

之间存在差异

我想从IO中获取一些String值。

任何人都可以告诉我这个问题。我很无能

2 个答案:

答案 0 :(得分:6)

问题是您希望在程序运行时使用String操作导致的IO String值。你写的就好像你已经把手放在绳子上,但你没有。

或者:你写的就好像你已经定义或隔离了一个字符串,但你没有。到目前为止,您已经定义或隔离了一个返回字符串操作。执行该操作的个别情况将返回不同的字符串。

据推测,您正在尝试定义更复杂的操作 - 某种类型为IO Blah - 可能类型IO ()类型可以编译为可执行文件的类型。我们的想法是,在更复杂的操作中,通过执行类型String的操作(即您迄今为止定义的操作)来获得IO String值 - 复杂操作的执行者将继续做一些取决于该价值的东西。这是由String -> IO Blah类型的函数表示的东西 - 也许是String -> IO ()

当然,这样的函数不会将IO String值(即返回字符串的操作)作为参数,而String值作为参数。我们不能直接加入他们。

要从您的操作返回字符串 - 您的IO String值 - 以及'字符串 - > IO Blah的功能,一个新的 - 一个返回一个等等的动作 - 我们通过函数>>=加入它们。专门针对此案例>>=的类型为IO String -> (String -> IO Blah) -> IO Blah - 可能是IO String -> (String -> IO ()) -> IO ()

因此,要考虑一对此类事情的最简单示例,请考虑getLineputStrLn

getLine 找出刚刚输入的字符串的动作 - 它的类型为IO String

您可能会说putStrLn 在屏幕上打印String值,然后返回左边距。但这很肤浅:完成的确切事情取决于String值的规范,因此它的类型为String -> IO()。也就是说:putStrLn没有做任何事情,它是一个将字符串映射到可以完成的事情的函数。像往常一样,你可以在其范围类型中定义一个值(动作,即IO ())通过在函数符号后面跟着域类型中的某些东西的名称(字符串,即String)所以组合putStrLn "Gee whiz"命名一个明确的动作,某事类型为IO ()

ghci将动态执行此类操作,因此,对于任何字符串,例如“Gee whiz”,您可以编写putStrLn "Gee whiz"并立即“执行此操作” - 写作的动作“ Gee whiz“到屏幕并返回左边距。

Prelude> putStrLn "Gee whiz"
Gee whiz
Prelude> 

同样,单字符字符串中只包含Unix响铃字符\BEl,是我们用'\BEl':[]['\BEL']"\BEL"命名的字符串。对于该字符串作为参数,putStrLn对于值有一种听起来完全不同的动作。我们得到

Prelude> putStrLn "\BEL"

Prelude> 

在这里你会听到Unix铃声回到左边距之前。这是一个非常蹩脚的音频节目,但你有。 ghci 执行启动Unix响铃的操作,在按返回之前使用单词putStrLn "\BEL"命名的操作。

所以无论如何getLineIO String类型的值,你想“从IO获取这个字符串值”。当然,它还不存在,它取决于用户输入的内容。但是我们可以考虑一下该程序在得到它时如何处理这样的值。我们可以通过指定从字符串到动作的函数来指定它,例如putStrLn。因此,我们可以通过使用>>=do符号糖组合来定义一个“获取值”的完整操作并以某种方式使用它。

最简单的案例就像echo

echo :: IO ()
echo = getLine >>= putStrLn

或等效

echo = getLine >>= (\x -> putStrLn x)

do符号:

echo = do 
     the_string_i_want_to_take <- getLine
     putStrLn the_string_i_want_to_take

或更少荒谬:

echo = do 
     x <- getLine
     putStrLn x

当然,你想要“拿绳子”并且可能在完成之前弄乱它。

reverseEcho :: IO ()
reverseEcho = getLine >>= (\x -> putStrLn (reverse x))

或更紧凑:

reverseEcho = getLine >>= (putStrLn . reverse)

do符号:

reverseEcho = do 
        the_string_i_want_to_take <- getLine
        putStrLn (reverse the_string_i_want_to_take)

或更少荒谬:

reverseEcho = do 
        x <- getLine
        putStrLn (reverse  x)

如果你想把'扭转字符串'看作是在获取和打印之间用字符串完成的东西,你可以写:

reverseEcho = do 
        the_string_i_want_to_take <- getLine
        the_string_after_i_have_processed_it <- return (reverse the_string_i_want_to_take)
        putStrLn (the_string_after_i_have_processed_it)

reverseEcho = do
        x <- getLine
        y <- return x
        putStrLn y

或等效

reverseEcho = (getLine >>= (return . reverse)) >>= putStrLn 

此处括号不是必需的,因为.>>=的优先级已经过适当优化。但(getLine >>= (return . reverse))只是“返回字符串的操作”的另一个名称,值IO String,而不是字符串本身。您无法直接应用String -> Whatever函数来获取Whatever,但您可以通过>>=将其与字符串中的函数组合到操作

同样

reverseFileAA :: IO ()
reverseFileAA = readFile "AA.txt" >>= writeFile "reversedAA.txt" . reverse

是编写名为“reversedAA.txt”的文件的操作,其文件与AA.txt中的字符串相反,无论它是什么,都可以写成

reverseFileAA = do
     old_file_contents <- readFile "AA.txt"
     new_file_contents <- return (reverse old_file_contents)
     writeFile "reversedAA.txt" old_file_contents

答案 1 :(得分:4)

IO String是IO-Monad中的String。如果IO-Monad中的函数返回IO字符串,则通过执行以下操作获取String:

do str <- ioFunc

当IO-Monad需要IO访问并且必须返回IO类型时,它就在IO-Monad中。