简体   繁体   中英

Understanding types in Haskell

I am trying to map a function, h, to a list of primes. 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:

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??? What am I misunderstanding here?

Your function primes is of type [Integer] .

Your function h is of type Double -> Integer .

The type of map is like this:

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

Or when specialized to a list of Integer it's type signatures becomes:

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 .

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 . 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.

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. Haskell has strong number types and this means that there are no implicit conversions between integer and floating point types.

The solution

Use the explicit conversion function fromIntegral :: (Integral a, Num b) => a -> b :

map (h . fromIntegral) primes

What type does primes have? 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 . 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.

Now what's the type of f ? f uses log on it's arguments, and then floors the result, so we can expect something like

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. And more important, the elements of primes don't fulfill the Floating constraint:

*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. We change f 's type:

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

Now we can use f p1 p2 as expected:

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

But what about the map issue? map expects (a -> b) as first parameter, and f is still (a -> b -> c) *. From your current code it seems like you would like to use two consecutive primes and apply f . For this, we first use uncurry :

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

Now uncurry f has (a, b) -> c . But now the list ( primes ) isn't a list of pairs. However, we can fix this easily, we zip primes with its tail:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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