请耐心等待我,因为我对函数式编程和Haskell都很陌生。我试图在Haskell中编写一个函数,它接受一个Integers列表,打印所述列表的头部,然后返回列表的尾部。该函数需要是[Integer]类型 - > [整数]。为了给出一些上下文,我正在编写一个解释器,当在关联列表中查找其各自的命令时调用此函数(键是命令,值是函数)。
这是我写的代码:
dot (x:xs) = do print x
return xs
编译器提供以下错误消息:
forth.hs:12:1:
Couldn't match expected type `[a]' against inferred type `IO [a]'
Expected type: ([Char], [a] -> [a])
Inferred type: ([Char], [a] -> IO [a])
In the expression: (".", dot)
我怀疑点函数中的打印调用是导致推断类型为IO [a]的原因。有什么方法可以忽略打印的返回类型,因为我需要返回的是列表的尾部被传递到点。
提前致谢。
答案 0 :(得分:8)
在大多数功能语言中,这都可行。但是,Haskell是一种纯函数式语言。您不能在函数中执行IO,因此该函数可以是
[Int] -> [Int]
未执行任何IO或[Int] -> IO [Int]
与IO 编译器推断的dot
类型为dot :: (Show t) => [t] -> IO [t]
,但您可以将其声明为[Int] -> IO [Int]
:
dot :: [Int] -> IO [Int]
请参阅IO monad:http://book.realworldhaskell.org/read/io.html
我没有提到System.IO.Unsafe.unsafePerformIO
应该谨慎使用并且对其后果有充分的了解。
答案 1 :(得分:8)
不,你的功能会导致副作用(也就是IO,在这种情况下是在屏幕上打印),或者它没有。 <{1}}会执行IO,因此会在print
中返回一些内容,但这不能撤消。
如果编译器被欺骗忘记IO
,那将是一件坏事。例如,如果在程序中使用相同的参数(例如IO
)多次调用[Integer] -> [Integer]
函数,则编译器可能只执行一次函数并使用其中的结果函数被“调用”的所有地方。即使您在多个地方调用了该功能,您的“隐藏”打印也只会执行一次。
但类型系统可以保护您,并确保使用[]
的所有函数(即使只是间接地)具有IO
类型来反映这一点。如果您想要纯函数,则不能在其中使用IO
。
答案 2 :(得分:6)
您可能已经知道,Haskell是一种“纯粹的”函数式编程语言。出于这个原因,副作用(例如在屏幕上打印值)不是偶然的,因为它们是更主流的语言。这个事实为Haskell提供了许多不错的属性,但是当你所做的只是试图在屏幕上打印一个值时,你会因为不关心它而被原谅。
由于该语言没有引起副作用的直接工具,因此策略是函数可能会产生一个或多个“IO动作”值。 IO操作封装了一些副作用(打印到控制台,写入文件等)以及可能产生的值。您的dot
函数正在产生这样的操作。你现在遇到的问题是你需要能够引起IO副作用的东西,以及展开值并可能将它传回你的程序。
如果不诉诸黑客,这意味着您需要将IO操作备份回main
功能。实际上,这意味着main
和dot
之间的所有内容都必须位于“IO Monad”中。 “IO Monad”中发生的事情可以说是“IO Monad”。
修改强>
这是关于在有效的Haskell程序中使用dot
函数可以想象的最简单的示例:
module Main where
main :: IO ()
main =
do
let xs = [2,3,4]
xr <- dot xs
xrr <- dot xr
return ()
dot (x:xs) =
do
print x
return xs