Haskell 试图在ghci输出中设置一些顺序

Haskell 试图在ghci输出中设置一些顺序,haskell,ghci,Haskell,Ghci,出于教学目的,我试图遵循并实施 其思想是表示一个类型mioa(“我的IO”),它是RW->(a,RW) RW是真实世界,为了简单起见,它只是一个整数;我不能使用realRealWorld,因为周围没有构造函数 我想到的第一句话是: type RW = Integer putString :: String -> RW -> ((), RW) putString str world = (unsafePerformIO $ putStrLn str, world + 1) getS

出于教学目的,我试图遵循并实施

其思想是表示一个类型
mioa
(“我的IO”),它是
RW->(a,RW)

RW
是真实世界,为了简单起见,它只是一个整数;我不能使用real
RealWorld
,因为周围没有构造函数

我想到的第一句话是:

type RW = Integer

putString :: String -> RW -> ((), RW)
putString str world = (unsafePerformIO $ putStrLn str, world + 1)

getString :: RW -> (String, RW)
getString world = let input = unsafePerformIO getLine
  in (input, world + 1)
之后,我尝试定义几种与用户交互的方式,比如问一个或多个问题。整个代码是

getString
开始,我得到了一个非常有趣的输出:

*Main> getString 0
("This is my input
This is my input",1)
*Main>
我看到
getString
的结果是正确的,但是屏幕变得凌乱。让事情变得更加混乱

getString
的情况下,我希望最初的
(“
与最终结果一起出现。这在ghci中可能吗?(我怀疑,但无论如何我都会问)

当我学习的时候,事情变得容易了,因为我可以从浏览器中免费获得弹出窗口。有没有什么Haskell环境可以让我插入代码


我知道,通过使用unsafePerformIO,我已经诅咒我的灵魂遭受永恒的痛苦,但提醒我这与我的观点无关。

你的问题很容易通过一些
解决

getString :: RW -> (String, RW)
getString world =
  let input = unsafePerformIO getLine
  in input `seq` (input, world + 1)
瞧:

λ getString 0
Hello!
("Hello!",1)
那有多酷

但我想让你稍微超出你的问题:你能把元组变成单子吗?(提示:你需要以某种方式修改它。)然后你可以使用
do
符号,你的
RW
IO看起来就像真的一样


p.S.正如@chi在一篇评论中指出的,当你希望确保秩序时,你应该更喜欢
pseq

那么
unsafePerformIO
的一个主要原因是,嗯…,不安全,正是因为评估的顺序是不可预测的。哈斯克尔的懒惰,很可能会首先打印
(“
,然后查询输入。存在
IO
monad的主要原因是为了保证
IO
操作的求值顺序。因此,
unsafePerformIO
s可能永远不会求值、多次求值,并且以不同的顺序进行。Haskell旨在防止您执行实际的IO,除非使用IO monad。您可以通过读取/写入RW类型来“模拟”IO。与其让它成为单个整数,不如将其设置为
([String],[String])
第一个字符串列表是将来作为输入读取的字符串,而后者是过去打印的字符串。当
putString
附加到第二个列表时,当
getString
从第一个列表中删除一个字符串(如果为空,则返回“EOF”或其他内容)@WillemVanOnsem-每次返回一个修改过的世界,并确保最后一个世界在最终结果中,这是我确保所有操作都被执行的保障。它们按顺序执行的事实是另一回事。到目前为止,一切都很好;但我在继续的过程中一直在寻找麻烦。你能找到一种方法让编译器更清楚地了解我的序列吗我想尝试定义我自己的
bind
,但我不知道这是否会有帮助…@chi-你的建议很有趣。我会遵循它。谢谢!这也显示了“IO的纯实现”(有点)。这确实应该“有效”在实践中,让我补充一点,
seq
并不能保证排序。理论上,编译器有权将
x`seq`y`seq`z
重写为
y`seq`x`seq`z
(但我不知道这是否真的发生)。相反,
pseq
保证排序。(即使这样,也很难对
不安全的
原语进行推理,因此,正如OP已经理解的那样,通常应该避免使用它们。)