为什么我不能这样做?
genList :: Num a => Int -> [a]
genList m_size = [1..m_size]
它说:
Couldn't match expected type `a' with actual type `Int'
`a' is a rigid type variable bound by
the type signature for genList :: Num a => Int -> [a]
at uloha1.hs:105:12
Relevant bindings include
genList :: Int -> [a] (bound at uloha1.hs:106:1)
In the expression: m_size
In the expression: [1 .. m_size]
为什么它不能隐式重新输入Int到Num? Int是Num的例子不是吗? 我找不到任何相关内容。 我正在使用最新的Haskell平台Ghci 我也是哈斯克尔的新手。
答案 0 :(得分:3)
Haskell永远不应该“重新输入”任何东西。您可能会将此与多态数字文字混淆。但是,m_size
不是文字,而是明确Int
。你可以写
[1 .. fromIntegral m_size]
但是你仍然存在这个构造还需要Enum
约束的问题。
另一种选择是
genList :: Enum a => Int -> [a]
genList m_size = [toEnum 1 .. toEnum m_size]
由于这适用于任何枚举类型,因此它也适用于所有数字枚举类型。
答案 1 :(得分:2)
如果你需要这个签名,你仍然可以使它工作 - 你只是不能使用[x..y]
枚举 - 一个简单的实现就是这样:
genList :: Num a => Int -> [a]
genList = reverse . genList'
genList' :: Num a => Int -> [a]
genList' 0 = []
genList' n = fromIntegral n : genList' (n-1)
它应该按预期工作:
λ> genList 5 :: [Int]
[1,2,3,4,5]
λ> genList 5 :: [Double]
[1.0,2.0,3.0,4.0,5.0]
答案 2 :(得分:1)
我四处询问。我意识到,我无法理解的是,哈斯克尔不能"演员"任何东西。它只能使类型更具体,如果它之前太一般。
因此1
的类型为Num
,但通过1 + 1::Int
,结果类型变为Int
。但它无法从Int
返回Num
。
所以[1..m_size]
的类型为[Int]
,它比[Num]
更具体,而且haskell无法投射它,因为我认为这是因为我习惯使用OO语言。
所以解决这个问题的方法与@Carsten所说的类似。我将首先生成包含Int
类型元素的列表,然后重新键入'它以后。所以更精简的解决方案看起来像这样:
genList :: Num a => Int -> [a]
genList n = map fromIntegral [1..n]
有人告诉我,感谢haskell延迟实现,列表元素在被map
过程时只会迭代一次。
还要感谢@Ingo @Kiraa @chepner的贡献,我基本上试着在这里总结一下。
答案 3 :(得分:0)
问题不在于将Int重新命名为Num,而是使用[1..m_size]表示法,而不是让a
正在实现Enum
的编译器放心。函数的类型应为
genList :: (Num t, Enum t) => t -> [t]
答案 4 :(得分:0)
仅仅因为Int
是Num
的实例并不意味着它等同于任何类型,可以声明为Num
的实例。考虑
genList 1000 :: [Int8]
Int8
是Num
的一个实例,但通常Int
(特别是在这种情况下,1000)不是Int8
类型的值。
假设我们不打算更改函数的定义(请参阅其他答案,了解如何执行此操作),我们可以执行以下两项操作之一:
将参数类型设置为Int
,并意识到我们必须返回Int
的列表,因为我们尚未指定创建另一种类型列表的方法。
genList :: Int -> [Int]
允许使用较少的常规输出类型,该类型指示允许的输入类型。与其他答案类似,ghci
可以根据您的定义推断出有效的函数类型:genList :: (Enum t, Num t) => t -> [t]
。也就是说,只要输入类型也是[t]
并且该类型是t
和{{的实例,您就可以为任何类型t
返回Enum
。 1}}。