Haskell: Double constraint function only works with integers

Suppose I write a function that takes a list of integers and returns only those integers in the list that are less than 5.2. I could do something like this:

belowThreshold = filter (< 5.2) 

Easy, right? But now I want to limit this function only to working with input lists like [Int] for my own reasons. This seems like a reasonable request. Unfortunately no. An declaration that restricts types as follows:

 belowThreshold :: [Integer] -> [Integer] belowThreshold = filter (< 5.2) 

Causes a type error. So what is this story? Why does the filter (<5.2) seem to convert the input list into pairs? How can I make a version of this function that only accepts whole lists and returns only whole lists? Why does the type system hate me?

+4
source share
4 answers

Check the added type of the lower threshold value in ghci before adding the annotation:

 > :t belowThreshold belowThreshold :: [Double] -> [Double] 

It looks like you were expecting Num a => [a] -> [a] when you said "restrain this function." In fact, you change the type of function when adding the [Integer] -> [Integer] annotation.

To do this work, use an explicit conversion:

 belowThreshold = filter ((< 5.2) . fromIntegral) 

Now belowThreshold :: [Integer] -> [Integer] as you like. But integers are converted to doubles before being compared with 5.2.

So why do you need to convert? You probably misled the type error: the list of integers has not been converted to doubles compared to 5.2, the real problem is that only paired pairs can be compared to paired ones, so you should pass the list of paired numbers belowThreshold . Haskell has no implicit conversions, even between numbers. If you want conversions, you must write them yourself.

I want this function to work only with input lists like [Int] for my own reasons. This seems like a reasonable request.

Well, from the point of view of the type system, no. Is this reasonable code?

 'c' < "foo" 

How about this?

 12 < "bar" 

All of these values ​​are instances of Ord , but you cannot use them with (<) . Haskell has no implicit conversions. Therefore, even if two values ​​are instances of Num , as well as Ord , you will not be able to compare them with (<) if they are of different types.

+8
source

You are trying to compare Integer with double (5.2). Haskell doesn't like this. Try using

 filter (< 6) 
+3
source

If you should use double (let's say this is an argument), I would use ceiling :

filter (< (ceiling 5.2))

Now, if you want a function that takes a boundary value as any (corresponding) numeric value, you can make your own class type a ceiling for you.

 class Ceilingable a where ceil :: (Integral b) => a -> b instance (RealFrac a) => Ceilingable a where ceil = ceiling instance (Integral a) => Ceilingable a where ceil = fromIntegral belowThreshold :: (Ceilingable a) => a -> [Integer] -> [Integer] belowThreshold threshold = filter (< ceil threshold) 
+1
source

Syntax 5.2 valid for any Fractional . Int not an instance of Fractional , and cannot or should not be. How to do what when converting an arbitrary Rational to Int is not indicated.

Converting to Double from an arbitrary fraction, however, makes perfect sense (within the type range).

Your expectation is due to the presence of implicit coercion in many languages.

However, they have a cost. You must manually ensure that the entire coercive system is confluent. Haskell does not, instead of allowing the numeral literal syntax to use a type system. To convert them between you, you need to use fromIntegral to explicitly indicate the need for coercion, this avoids merging dependency and allows programmers to define new numeric types.

 belowThreshold = filter (\x -> fromIntegral x < 5.2) 

This is similar to using an explicit conversion in C ++, for example ((double)x < 5.2) . Although this statement only works because of default, since 5.2 can be used as a member of any Fractional , and the result from fromIntegral x is any Num , a superclass from Fractional , so fromIntegral x < 5.2 is underspecified, it just knows that it needs to compare two Fractional values ​​are of the same type, and it selects Double as a reasonable default value based on the default instruction.

Also note that Int not the only Integral type, so the above method works in any list of Integral values:

 belowThreshold :: Integral a => [a] -> [a] 
+1
source

Source: https://habr.com/ru/post/1304073/


All Articles