简体   繁体   English

了解Haskell中的类型

[英]Understanding types in Haskell

I am trying to map a function, h, to a list of primes. 我正在尝试将函数h映射到素数列表。 Both are given below: 两者都在下面给出:

f k x = floor ( log k / log x )

h = f 20

primes = 2 : sieve [3,5..]
  where
    sieve (p:xs) = p:[ x | x <- xs, x `mod` p > 0 ]

However when I try map h primes I get the following error: 但是,当我尝试map h primes ,出现以下错误:

Ambiguous type variable `a0' in the constraints:
  (Enum a0)
    arising from the arithmetic sequence `3, 5 .. '

and so on... 等等...

Both the function f and primes seem to work as expected but I cannot apply f to the numbers in primes??? 函数f和素数似乎都能按预期工作,但我无法将f应用于素数中的数字??? What am I misunderstanding here? 我在这里误会什么?

Your function primes is of type [Integer] . 您的函数primes是类型[Integer]

Your function h is of type Double -> Integer . 您的函数hDouble -> Integer类型的。

The type of map is like this: map的类型如下:

ghci> :t map
map :: (a -> b) -> [a] -> [b]

Or when specialized to a list of Integer it's type signatures becomes: 或者,当专门处理Integer列表时,其类型签名将变为:

map :: (Integer -> b) -> [Integer] -> [b]

But your h function which you are passing to map is of type Double -> Integer and hence it doesn't typecheck because it is expecting something of Integer -> b and not a function accepting Double . 但是要传递给map h函数的类型为Double- Double -> Integer ,因此不会进行类型检查,因为它期望的是Integer- Integer -> b而不是接受Double的函数。

Always try to write type signature before function, that will make your life easier. 始终尝试在功能之前编写类型签名,这将使您的生活更轻松。

The problem 问题

primes :: Integral a => [a] and h :: (RealFrac a, Integral b, Floating a) => a -> b . primes :: Integral a => [a]h :: (RealFrac a, Integral b, Floating a) => a -> b Now, there aren't any types that are instances of both RealFrac and Integral but GHC doesn't know this, so the error message it gives is a bit confusing. 现在,没有任何类型同时是RealFracIntegral实例,但是GHC不知道这一点,因此它给出的错误消息有点令人困惑。

The reason 原因

log :: Floating a => a -> a , (/) :: Fractional a => a -> a -> a and floor :: (RealFrac a, Integral b) => a -> b , so when we compose them we get the above type signature. log :: Floating a => a -> a floor :: (RealFrac a, Integral b) => a -> b log :: Floating a => a -> a(/) :: Fractional a => a -> a -> a floor :: (RealFrac a, Integral b) => a -> b (/) :: Fractional a => a -> a -> a floor :: (RealFrac a, Integral b) => a -> b (/) :: Fractional a => a -> a -> afloor :: (RealFrac a, Integral b) => a -> b ,所以当我们撰写他们我们得到上面的类型签名。 Haskell has strong number types and this means that there are no implicit conversions between integer and floating point types. Haskell具有强大的数字类型,这意味着整数和浮点类型之间没有隐式转换。

The solution 解决方案

Use the explicit conversion function fromIntegral :: (Integral a, Num b) => a -> b : 使用显式转换函数fromIntegral :: (Integral a, Num b) => a -> b

map (h . fromIntegral) primes

What type does primes have? primes有什么类型? That completely depends on the circumstances. 那完全取决于情况。 It could be 它可能是

primes :: [Integer]

or something else. 或者是其他东西。 It isn't clear, since literals like 3 have the type 3 :: Num a => a . 目前尚不清楚,因为像3这样的文字的类型为3 :: Num a => a The only thing we can be sure about, is that whatever it is need to be an instance of Enum , otherwise [3,5..] couldn't work. 我们唯一可以确定的是,无论它是Enum的实例是什么,否则[3,5..]将无法工作。

Now what's the type of f ? 现在f是什么类型? f uses log on it's arguments, and then floors the result, so we can expect something like f使用log作为参数,然后对结果取底,因此我们可以期待类似

f :: (RealFrac a, Floating a, Integral b) => a -> a -> b

However, this already hints that you cannot use it with map , since map expects (a -> b) as first parameter. 但是,这已经暗示您不能将其与map一起使用,因为map需要将(a -> b) a- (a -> b)作为第一个参数。 And more important, the elements of primes don't fulfill the Floating constraint: 更重要的是, primes元素不满足Floating约束:

*Main> let (p1:p2:_) = primes
*Main> f p1 p2

<interactive>:29:1:
    No instance for (RealFrac Integer) arising from a use of `f'
    Possible fix: add an instance declaration for (RealFrac Integer)
    In the expression: f p1 p2
    In an equation for `it': it = f p1 p2

So we either need to change primes type or f 's type. 因此,我们需要更改primes类型或f的类型。 We change f 's type: 我们更改f的类型:

f k x = floor ( log (fromIntegral k) / log (fromIntegral x) )

Now we can use f p1 p2 as expected: 现在我们可以按预期使用f p1 p2

*Main> let (p1:p2:_) = primes
*Main> f p1 p2
0

But what about the map issue? 但是map问题呢? map expects (a -> b) as first parameter, and f is still (a -> b -> c) *. map期望(a -> b) a- (a -> b)作为第一个参数,而f仍然是(a -> b -> c) *。 From your current code it seems like you would like to use two consecutive primes and apply f . 从您当前的代码看来,您似乎想使用两个连续的素数并应用f For this, we first use uncurry : 为此,我们首先使用uncurry

uncurry :: (a -> b -> c) -> (a, b) -> c

Now uncurry f has (a, b) -> c . 现在, uncurry f具有(a, b) -> c But now the list ( primes ) isn't a list of pairs. 但是现在列表(质primes )不再是配对列表。 However, we can fix this easily, we zip primes with its tail: 但是,我们可以很容易地解决此问题,我们用尾部zip素数:

map (uncurry f) (zip primes $ tail primes)

And that's how you can map a function with two parameters on consecutive elements of a single list. 这样便可以在单个列表的连续元素上映射具有两个参数的函数。


*k or x could be of different Integral types * k或x可以是不同的积分类型

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM