I have a function f op = (op 1 2, op 1.0 2.0), which is required to work like this:
f (+)
(3, 3.0)
But without declaring the type of f it works like this:
f (+)
(3.0, 3.0)
And I'm struggling with declaring the type of f. It should take an operator which works with all instances of Num. Is it even possible in Haskell?
The problem is that you forced the operator to work on
Fractionaltypes by applying it to fractional numbers such as1.0and2.0. Your code typechecks becauseFractionalis a subtype ofNum(meaning that each instance ofFractionalis also an instance ofNum).Following experiment in GHCi should make it clear:
So, if you want to make it work on
Nums entirely, you just need to get rid of those ".0"s:However
If you really need the behavior where the second element of returned tuple is
Fractionaland the first is some more generalNum, the things get a bit more complicated.The operator (or actually, function) you pass to
fhas to appear as with two different types at the same time. This is normally not possible in plain Haskell, as each type variable gets a fixed assignment with each application – that means that(+)needs to decide if it isFloatorIntor what.This can be, however, changed. The thing you will need to do is to turn on the
Rank2Typesextension by writing:set -XRank2Typesin GHCi or adding{-# LANGUAGE Rank2Types #-}at the very top of the.hsfile. This will allow you to writefin a manner in which the type of its argument is more dynamic:Now the typechecker won't assign any fixed type to
op, but instead leave it polymorphic for further specializations. Therefore it may be applied to both1 :: Intand1.0 :: Floatin the same context.Indeed,
Protip from comments: the type constraint may be relaxed to make the function more general:
It will work fine in all cases the previous version of
fwould do plus some more where thesndof the returned pair could be for instance anInt: