我仍然很难将我的大脑缠绕在Haskell及其类型系统上。我发布了另一个问题:Haskell Converting Int to Float 这肯定有帮助,但我仍然坚持。我认为这是一个更简单的例子:
ymod2 :: Integral b => b -> [b]
ymod2 n = map (\y->(y `mod` 2)) [0..n]
powersOfx :: (Enum b, Floating b) => b -> b -> [b]
powersOfx n x = map (\y->(x**y)) [0..n]
thirdLst :: (Integral a, Enum a, Enum b, Floating b) => a -> b -> [b]
thirdLst n x = zipWith (*) (ymod2 n) (powersOfx (fromIntegral n) x)
正如你所看到的,我有一个函数,它接受一个积分(ymod2)和一个带有Enum,Floating的函数。这些都在thirdLst中使用(通过zipWith)。我通过thirdLst积分和浮动。我确实尝试将Integral更改为浮动以在powersOfx中使用。当我把它加载到WinGHCi中时,我得到:
• Couldn't match type ‘b’ with ‘a’
‘b’ is a rigid type variable bound by
the type signature for:
thirdLst :: forall a b.
(Integral a, Enum a, Enum b, Floating b) =>
a -> b -> [b]
// and quite a bit more which I can post if desired
我尝试了各种排列。我已经删除了fromIntegral,我尝试了不同的类型,但似乎没有任何效果。 我认为我的核心问题可能是如何转换类型。我想从整体上就可以做到。我意识到,很可能,在这个人为的例子中,powersOfx方法可以被重写以使用不同的类型。我的问题仍然存在。这就是如何在压缩它们时使这两种方法很好地协同工作。
谢谢,戴夫
更新。基于阅读Willem的回答,我能够通过将thirdLst替换为:
来使我的代码正常工作thirdLst :: (Integral a, Floating b, Enum b) => a -> b -> [b]
thirdLst n x = zipWith (*) (map fromIntegral (ymod2 n)) (powersOfx (fromIntegral n) x)
我相信Willem的例子远非优越,未来的读者应该研究他的答案。我将他的答案标记为正确答案。谢谢Willem!
答案 0 :(得分:3)
我仍然很难将我的大脑缠绕在Haskell及其类型系统上。
我认为Haskell中的数值类型有一些方面:
Int
隐式转换为Float
; (+) :: Num a => a -> a -> a
,这意味着如果其中一个操作数是Int
,那么另一个操作数和结果都是Int
s;和(+)
,(-)
,(**)
)来自类型。例如,(**) :: Fractional a => a -> a -> a
来自Fractional
类型类。现在让我们模拟Haskell编译器。您定义了一个函数:
thirdLst n x = zipWith (*) (ymod2 n) (powersOfx (fromIntegral n) x)
所以我们在这里看到该函数有两个参数n
和x
,所以首先我们假设thirdLst
有类型:
thirdLst :: a -> b -> c
但我们仍然需要通过查看表达式来分析类型。我们看到ymod2 :: Integral d => d -> [d]
(我们这里使用另一个名称,因为那些基本上是不同的变量)函数被调用,这意味着n
的类型是a
以及{{ 1}},这意味着d
和a
属于同一类型,d
生成列表ymod2 n
。我们还必须添加[a]
类型,现在我们的函数类型是:
Integral a
我们还在表达式中看到fromIntegral :: (Integral e, Num f) => e -> f
以thirdLst :: Integral a => a -> b -> c
ymod2 n :: Integral a => [a]
作为参数调用,因此我们得出结论n
(a ~ e
和a
是相同的类型),e
的类型为fromIntegral n
。我们再次向Num f => f
添加Integral
类型约束,但由于它已经受到约束,因此a
上的约束保持不变:
a
thirdLst :: Integral a => a -> b -> c
ymod2 n :: Integral a => [a]
fromIntegral n :: Num f => f
表达式只是fromIntegral
的子表达式,因为(powersOfx (fromIntegral n) x)
的类型为powersOfx
。因此,我们知道powersOfx :: (Enum g, Floating g) => g -> g -> [g]
和f ~ g
,因此我们知道表达式的结果是b ~ g
,我们必须添加[b]
和Enum b
作为约束:
Floating b
到目前为止还没有问题,thirdLst :: (Integral a, Enum b, Floating b) => a -> b -> c
ymod2 n :: Integral a => [a]
fromIntegral n :: Num b => b
powersOfx (fromIntegral n) x :: (Enum b, Floating b) => [b]
,thirdLst
和n
的两个参数有不同的类型,但现在我们使用zipWith :: (h -> i -> j) -> [h] -> [i] -> [j]
,我们使用as第一个参数(*) :: Num k => k -> k -> k
,因此我们知道x
,因此我们的k ~ h ~ i ~ j
具有类型:
zipWith (*)
现在出现了问题,我们发现zipWith (*) :: Num k => [k] -> [k] -> [k]
的两个参数需要具有相同的类型。由于zipWith (*)
和ymod2 n :: Integral a => [a]
是我们用于powersOfx (fromIntegral n) x :: (Enum b, Floating b) => [b]
的参数,因此zipWith (*)
表示k ~ a ~ b
,k
和a
都是相同的类型。所以现在我们得出结论:
b
这意味着这两个参数需要具有相同的类型。现在,这已经导致一个重要问题:数字通常不会同时为thirdLst :: (Integral a, Enum a, Floating a, Num a) => a -> a -> [a]
和Integral
。在最常见的数值库中,没有同时具有Floating
和Floating
的数字类型。所以这使得这个功能毫无用处。
因此,我们需要找到更多通用函数,以及更多自由。您的Integral
功能太限制:由于列表的长度powersOfx
需要具有相同的类型。我们可以构建一个两个类型独立的区域:
x
对于import Data.List(genericTake)
powersOfx :: (Num a, Integral i) => i -> a -> [a]
powersOfx n x = genericTake (n+1) (iterate (x*) 1)
,您基本上构建了一个长度类似于ymod2
的列表。因此,我们可以使用cycle :: [a] -> [a]
,再使用[0, 1, 0, 1, ...]
:
genericTake (n+1)
然后我们的功能是:
ymod2 :: (Num a, Integral i) => i -> [a]
ymod2 n = genericTake (n+1) (cycle [0, 1])