我正在学习Haskell,我决定使用H-99问题集。当然,我已经陷入了第一个问题!
我有以下代码:
module Main where
getLast [] = []
getLast x = x !! ((length x) - 1)
main = do
putStrLn "Enter a list:"
x <- readLn
print (getLast x)
编译此代码会出现以下错误:
h-1.hs:8:14:
No instance for (Read a0) arising from a use of `readLn'
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Read () -- Defined in `GHC.Read'
instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
instance (Read a, Read b, Read c) => Read (a, b, c)
-- Defined in `GHC.Read'
...plus 25 others
In a stmt of a 'do' block: x <- readLn
In the expression:
do { putStrLn "Enter a list:";
x <- readLn;
print (getLast x) }
In an equation for `main':
main
= do { putStrLn "Enter a list:";
x <- readLn;
print (getLast x) }
h-1.hs:9:9:
No instance for (Show a0) arising from a use of `print'
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Show Double -- Defined in `GHC.Float'
instance Show Float -- Defined in `GHC.Float'
instance (Integral a, Show a) => Show (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
...plus 26 others
In a stmt of a 'do' block: print (getLast x)
In the expression:
do { putStrLn "Enter a list:";
x <- readLn;
print (getLast x) }
In an equation for `main':
main
= do { putStrLn "Enter a list:";
x <- readLn;
print (getLast x) }
这是一个很大的错误,但在我看来,Haskell不确定输入类型是什么。这很好,完全可以理解。但是,因为这应该在泛型列表上工作,我不知道如何指定该类型。我试过这个:
x :: [a] <- readLn
...因为[a]
是Haskell为空列表返回的类型(与:t []
一起找到)。这也不会编译。
由于我是初学者,我知道有很多我不知道,但从基本的意义上讲 - 我如何用输入代码满足Haskell的类型系统?我是一名Haskell初学者,正在寻找初学者的答案,如果可能的话。 (另外,请注意我知道有更好的方法来解决这个问题(反向,头部),但这是我首先提出的方式,我想看看我是否可以使它工作。)
答案 0 :(得分:2)
你不能希望写一些这样的东西,它会在运行时检测x
的类型 - 你必须在编译时知道你read
什么样的东西。这就是@ Sibi的答案使用[Int]
的原因。如果无法推断,则会出现编译时错误。
如果要进行多态读取,则必须构建自己的解析器,列出可读类型。
maybeDo :: (Monad m) => Maybe a -> (a -> m b) -> m b
maybeDo f Nothing = return ()
maybeDo f (Just x) = f x
main = do
str <- getLine
maybeDo (maybeRead str :: Maybe Int) $ \i ->
putStrLn $ "Got an Int: " ++ show i
maybeDo (maybeRead str :: Maybe String) $ \s ->
putStrLn $ "Got a String: " ++ show s
有很多方法可以分解这种重复,但在某些时候你必须列出你接受的所有类型。
(查看问题的一种简单方法是定义一个与MyInt
具有相同Read
实例的新类型Int
- 然后我们如何知道{{1}应该返回read "42"
或Int
?)
答案 1 :(得分:1)
这应该有效:
getLast :: Num a => [a] -> a
getLast [] = 0
getLast x = x !! ((length x) - 1)
main = do
putStrLn "Enter a list:"
x <- readLn :: IO [Int]
print (getLast x)
为什么为空列表返回0而不是空列表?
因为它没有成功。因为您为空列表返回[]
,而对于其他情况,您将返回列表中的元素,即a
。现在由于a
类型不等于list,因此它不会成为类型检查。更好的设计是使用Maybe
数据类型捕获此类情况。
此外,由于返回0
,上述函数仅适用于为其创建Num
个实例的类型列表。您可以使用error
函数来缓解该问题。
但是,这应该适用于通用列表,而不仅仅是Ints列表 或数字,对吧?
是的,它应该适用于多态列表。你可以创建一个像getLast
这样的函数,它可以用于所有类型的List。但是当你想从用户那里获得输入时,它应该知道你给出的输入类型。因为类型检查器无法知道您是将其视为Int of List还是Double of List等。