Haskell 输入返回元组
我想知道 IO() 函数是否可以返回元组,因为我想从这个函数中获取这些元组作为另一个函数的输入。
investinput :: IO()->([Char], Int)
investinput = do
putStrLn "Enter Username : "
username <- getLine
putStrLn "Enter Invest Amount : "
tempamount <- getLine
let amount = show tempamount
return (username, amount)
请帮忙。
谢谢。
i wonder can a IO() function return tuple because i would like to get these out of this function as input for another function.
investinput :: IO()->([Char], Int)
investinput = do
putStrLn "Enter Username : "
username <- getLine
putStrLn "Enter Invest Amount : "
tempamount <- getLine
let amount = show tempamount
return (username, amount)
Please help.
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
Haskell 中的 IO 与您习惯的语言中的 IO 不同。 Haskell 中的所有函数都必须是纯函数:也就是说,如果使用参数 x 调用函数 f ,则调用它一次、两次或一次之间一定没有区别。百倍。不过,考虑一下这对 IO 意味着什么。天真地说,
getLine
应该具有getLine :: String
类型,或者可能getLine :: () ->;字符串
。 (()
是单位类型,其唯一值是()
;它有点像类 C 语言中的 void 类型,但有一个值)但这意味着每次您编写getLine
时,它都必须返回相同的字符串,这不是您想要的。这就是IO
类型的目的:封装动作。这些动作与功能不同;它们代表不纯计算(尽管它们本身是纯的)。IO a
类型的值表示执行时返回a
类型的值的操作。因此,getLine
的类型为getLine :: IO String
:每次评估操作时,都会生成一个String
(通过从用户读取) 。类似地,putStr 的类型为 putStr :: String -> IO();它是一个函数,它接受一个字符串并返回一个操作,该操作在运行时不会返回任何有用的信息……但是,作为副作用,会在屏幕上打印一些内容。您正在尝试编写
IO () -> 类型的函数([字符],整数)
。这将是一个将操作作为输入并返回一个元组的函数,这不是您想要的。您需要一个 IO (String, Int) - 一个操作,运行时会生成一个由字符串(这是[Char]
的同义词)和一个元组组成的元组。整数。您当前的代码也快完成了!这就是您需要的:请注意,我只进行了两项更改(并删除了一个空行)。首先,我改变了函数的类型,就像我上面所说的那样。其次,我将
show
更改为read
。show
函数的类型为Show a =>;一个-> String
:它是一个函数,它接受任何可以显示的内容并生成一个表示它的字符串。 您想要read
,其类型为Read a =>;字符串 -> a:给定一个字符串,它解析它并返回一些可读的值。
您询问的另一件事是返回一个元组
(String, Int)
而不是操作IO (String, Int)
。没有纯粹的方法可以做到这一点;换句话说,不存在纯函数IO a ->一个。这是为什么呢?因为
IO a
代表了一个依赖于现实世界的不纯粹的动作。如果我们有这样一个函数impossibleRunIO :: IO a -> a
,那么我们希望出现impossibleRunIO getLine == irresibleRunIO getLine
的情况,因为该函数必须是纯函数。但这是没有用的,因为我们希望impossibleRunIO
能够真正与现实世界交互!因此,这个纯函数是不可能的。进入IO
的所有东西都永远无法离开。这就是return
的作用:它是一个函数,在本例中为1,类型为return :: a ->; IO a
,它使您能够将纯值放入IO
中。对于任何x
,return x
是一个在运行时始终生成x
的操作。这就是为什么您必须以return
结束do
块:username
是您从操作中提取的纯值,因此是仅在do
块内可见。您需要将其提升到 IO 中,然后外界才能看到它。amount
/tempamount
也是如此。为了完整起见:这背后有一些总体理论将其联系在一起。但对于开始 Haskell 编程来说根本没有必要。我建议你将大部分代码构建为纯函数,这些函数会折叠、旋转和破坏你的数据。然后构造一个薄(尽可能薄)的
IO
前层,与所述函数交互。您会惊讶地发现您需要的 IO 如此之少!1:它实际上有一个更通用的类型,但目前不相关。
IO in Haskell doesn't work like IO in the languages you're used to. All functions in Haskell must be pure: that is, if a function
f
is called with the argumentx
, there must be no difference between calling it once, twice, or a hundred times. Consider what this means for IO, though. Naïvely,getLine
should have the typegetLine :: String
, or perhapsgetLine :: () -> String
. (()
is the unit type, whose only value is()
; it's sort of like a void type in a C-like language, but there is a single value of it.) But this would mean that every time you wrotegetLine
, it would have to return the same string, which is not what you want. This is the purpose of theIO
type: to encapsulate actions. These actions are distinct from functions; they represent impure computation (though they themselves are pure). A value of typeIO a
represents an action which, when executed, returns a value of typea
. Thus,getLine
has typegetLine :: IO String
: every time the action is evaluated, aString
is produced (by reading from the user). Similarly,putStr
has typeputStr :: String -> IO ()
; it is a function which takes a string and returns an action which, when run, returns no useful information… but, as a side effect, prints something to the screen.You are attempting to write a function of type
IO () -> ([Char], Int)
. This would be a function which took as input an action and returned a tuple, which is not what you want. You want anIO (String, Int)
—an action which, when run, produces a tuple consisting of a string (which is a synonym for[Char]
) and an integer. You're almost there with your current code, too! This is what you'll need instead:Notice that I've only made two changes (and removed a blank line). First, I've changed the type of the function, like I said above. Second, I changed
show
intoread
. Theshow
function has the typeShow a => a -> String
: it is a function which takes anything which can be shown and produces a string representing it. You wantedread
, which has the typeRead a => String -> a
: given a string, it parses it and returns some readable value.The other thing you asked about is returning a tuple
(String, Int)
instead of an actionIO (String, Int)
. There is no pure way to do this; in other words, there is no pure functionIO a -> a
. Why is this? BecauseIO a
represents an impure action which depends on the real world. If we had such a functionimpossibleRunIO :: IO a -> a
, then we would want it to be the case thatimpossibleRunIO getLine == impossibleRunIO getLine
, since the function must be pure. But this is useless, as we would wantimpossibleRunIO
to be able to actually interact with the real world! Thus, this pure function is impossible. Everything that entersIO
can never leave. This is whatreturn
does: it is a function with, in this case1, the typereturn :: a -> IO a
, which enables you to place pure values intoIO
. For anyx
,return x
is an action which, when run, always producesx
. This is why you have to end yourdo
block with thereturn
:username
is a pure value you extracted from an action, and as such is only visible within thedo
block. You need to lift it intoIO
before the outside world can see it. The same is true ofamount
/tempamount
.And just for completeness's sake: there is some overarching theory behind this which ties it together. But it's not necessary at all for beginning Haskell programming. What I would recommend doing is structuring most of your code as pure functions which fold, spindle, and mutilate your data. Then construct a thin (as thin as possible)
IO
front layer which interacts with said functions. You'll be surprised how little IO you need!1: It actually has a more general type, but that's not relevant for the moment.
是的,你已经快到了,但我认为你想要签名:
...然后从调用函数中你可以做类似的事情:
我认为你想读取 tempamount 而不是显示。
Yeah, you're almost there but I think you want the signature:
... then from the calling function you can do something like:
I think you want to read tempamount rather than show though.
生成元组的 IO 函数的类型为 IO (a, b),在本例中:
IO () -> 的签名([Char], Int) 意味着该函数采用 IO () 类型的参数并从中生成一个元组,这不是您想要的。
一般来说,IO 函数(或不同 monad 中的函数)可以返回的类型没有限制,您可以选择您喜欢的类型。
An IO function that produces a tuple would have type
IO (a, b)
, in this case:A signature of
IO () -> ([Char], Int)
would mean that the function takes a parameter of typeIO ()
and produces a tuple from that, which is not what you want.Generally there are no restrictions on the types an IO function (or a function in a different monad) can return, you can chose the types however you like.
关于返回
(String, Int)
而不是IO (String, Int)
的问题的答案很简单:你不能。一旦进入IO
,您就会被困在那里。这就是人们说 Haskell 是一种“纯粹”语言时的部分含义。您想要做的事情与您已经在此处使用
getLine
所做的事情类似。getLine
的类型是IO String
。当您编写username <- getLine
时,您实际上是从IO String
中取出String
,但这只是可能的,因为您位于do
表达式内。您可以使用
investinput
执行与使用getLine
完全相同的操作。以下是如何在main
函数中使用investinput
的示例:由于您在评论中提到了
liftM
,因此这是一个完整的工作版本使用liftM
和反向绑定运算符 (=<<
) 而不是do
表示法实现同样的效果:这显示了如何使用 < code>investinput 与“另一个需要元组的函数”。
The answer to your question about returning
(String, Int)
rather thanIO (String, Int)
is simple: you can't. Once you're inIO
you're stuck there. That's part of what it means when people say that Haskell is a "pure" language.What you want to do is similar to what you're already doing here with
getLine
. The type ofgetLine
isIO String
. When you writeusername <- getLine
, you're in effect taking theString
out of theIO String
, but this is only possible because you're inside thedo
expression.You can do exactly the same kind of thing with
investinput
as withgetLine
. Here's an example of how you could useinvestinput
in yourmain
function:Since you mention
liftM
in a comment, here's a full working version that does the same thing usingliftM
and the reverse bind operator (=<<
) instead ofdo
notation:This shows how you could use
investinput
with "another function which expects a tuple".