Home > other >  How to check instance of fractional class for zero value in haskell?
How to check instance of fractional class for zero value in haskell?

Time:12-01

If i have a value which is restricted to be fractional, is there a way i can check it either for being a zero value or for some neutral value? I'm trying to implement safe division with signature like this:

safe_idv :: (Fractional q) => q -> q -> Maybe q

I've checked at Hoogle if there is some method in minimal definition which can help, but to no avail. Thanks in advance

Update: Since the question caused some confusion, I want to avoid changing the constraints on q, and pattern matching still requires Eq (i guess implicitly).

For following definition:

safe_div :: (Fractional q) => q -> q -> Result q
safe_div a 0 = Err ["dvision by zero"]
safe_div a b = Ok (a / b)

following error is raised:

    * Could not deduce (Eq q) arising from the literal `0'
      from the context: Fractional q
        bound by the type signature for:
                   safe_div :: forall q. Fractional q => q -> q -> Result q
        at AST2.hs:11:1-48
      Possible fix:
        add (Eq q) to the context of
          the type signature for:
            safe_div :: forall q. Fractional q => q -> q -> Result q
    * In the pattern: 0
      In an equation for `safe_div':
          safe_div a 0 = Err ["dvision by zero"]

CodePudding user response:

With the signature safe_div :: (Fractional q) => q -> q -> Result q, you can only use methods from Fractional or its superclasses on your q values.

(Or other predefined functions that impose no more constraints than Fractional, but those will ultimately have to be implemented by the class methods. So they can't do anything you couldn't do directly with the methods yourself.)

From Fractional itself that gives us:

(/) :: Fractional a => a -> a -> a
recip :: Fractional a => a -> a
fromRational :: Fractional a => Rational -> a 

Well none of those look terribly helpful. But Num is a superclass of Fractional, so we have those methods too:

( ) :: Num a => a -> a -> a
(-) :: Num a => a -> a -> a
(*) :: Num a => a -> a -> a
negate :: Num a => a -> a
abs :: Num a => a -> a
signum :: Num a => a -> a
fromInteger :: Num a => Integer -> a

These also aren't going to help us. abs and signum at first seem like they might, since their "purpose" is telling us certain properties about the number; signum even says this:

For real numbers, the signum is either -1 (negative), 0 (zero) or 1 (positive).

Which sounds exactly the kind of thing we want! The only trouble is that signum communicates the result of its inspection as a value of the same type. If we couldn't tell if our number x is equal to zero, how are we going to tell whether signum x is equal to zero? We're right back at the problem we started with.

The fact is every single method of both Fractional and Num only ever returns a value of the (unknown) type implementing the class. That basically means if you don't know what they type actually is, it's impossible to get any information out of them; the only thing you can do with a value of an unknown Fractional type is pass it to another Fractional (or Num) method, which will also only give you back a value of the same unknown type. There's no way to compare it to anything (which would require something returning a Bool, Ordering, or at least a Maybe, Either, etc). There's no way to convert it to text so it can be shown to a user (which would require something returning a String, Text, etc). You can do further calculations, but the only thing we can ever learn is that the calculation didn't error out, and only by trying it and hoping (which is exactly what you're trying to avoid!).

The only way you can implement your desired function is to add more constraints. Eq is exactly the class of types which can be compared, and you want to compare values of your type, so it just makes sense that you will have to constrain your function to operate within this class of types.

However, anyone calling this function polymorphically is in the same boat. It's very useful for intermediate functions (like this one) to work with unknown Fractional types, so that they can be called with any fractional type. But at the outermost level where someone first decided to call one of these functions, it's only ever useful to call these with a concrete type they actually know something about. Nobody wants to do calculations on numbers where they can't inspect the result in any way! This means that even though your safe_div function (as it is currently written) cannot assume any details of any particular type (such as whether it can be compared for equality), it in fact will only ever realistically be called with specific types like Double, Float, etc, all of which do support Eq. So in practice adding the Eq constraint is hardly limiting who can call it.

I imagine the reason you don't want to change the constraint is that you've already coded up functions where you use this, and they only have Fractional constraints (meaning they can't call safe_div :: (Eq a, Fractional a) => a -> a -> Result a). Unfortunately they'll have to be updated to add the Eq constraint too. The fact is that the interface of just Fractional gives only the ability to do basic arithmetic. To do comparisons and branching calculations you need more. So all your functions that want to do more than basic arithmetic (and that includes any that call anything that does more than basic arithmetic, not just ones that do the comparison and branching themselves) need more constraints than just Fractional. Fortunately the same reasoning as above applies: it is extremely unlikely that you would ever need to call any of these functions with a type that doesn't support Eq, so there really is very little point in resisting the additional constraint.

  • Related