I trying to wrap my head around Haskell type coercion. Meaning, when does can one pass a value into a function without casting and how that works. Here is a specific example, but I am looking for a more general explanation I can use going forward to try and understand what is going on:
Prelude> 3 * 20 / 4
15.0
Prelude> let c = 20
Prelude> :t c
c :: Integer
Prelude> 3 * c / 4
<interactive>:94:7:
No instance for (Fractional Integer)
arising from a use of `/'
Possible fix: add an instance declaration for (Fractional Integer)
In the expression: 3 * c / 4
In an equation for `it': it = 3 * c / 4
The type of (/) is Fractional a => a -> a -> a. So, I’m guessing that when I do “3 * 20” using literals, Haskell somehow assumes that the result of that expression is a Fractional. However, when a variable is used, it’s type is predefined to be Integer based on the assignment.
My first question is how to fix this. Do I need to cast the expression or convert it somehow?
My second question is that this seems really weird to me that you can’t do basic math without having to worry so much about int/float types. I mean there’s an obvious way to convert automatically between these, why am I forced to think about this and deal with it? Am I doing something wrong to begin with?
I am basically looking for a way to easily write simple arithmetic expressions without having to worry about the neaty greaty details and keeping the code nice and clean. In most top-level languages the compiler works for me — not the other way around.
If you just want the solution, look at the end.
You nearly answered your own question already. Literals in Haskell are overloaded:
Since
(*)also has aNumconstraintthis extends to the product:
So, depending on context, this can be specialized to be of type
Int,Integer,Float,Double,Rationaland more, as needed. In particular, asFractionalis a subclass ofNum, it can be used without problems in a division, but then the constraint will becomestronger and be for class
Fractional:The big difference is the identifier
cis anInteger. The reason why a simple let-binding in GHCi prompt isn’t assigned an overloaded type is the dreaded monomorphism restriction. In short: if you define a value that doesn’t have any explicit arguments,then it cannot have overloaded type unless you provide an explicit type signature.
Numeric types are then defaulted to
Integer.Once
cis anInteger, the result of the multiplication isInteger, too:And
Integeris not in theFractionalclass.There are two solutions to this problem.
Make sure your identifiers have overloaded type, too. In this case, it would
be as simple as saying
Use
fromIntegralto cast an integral value to an arbitrary numeric value: