I want to be able to compose numeric functions in haskell using binary operators. So, for example, with unary numeric functions:
f*g
should translate to:
\x -> (f x)*(g x)
and similarly for addition. Making your own operator to do this is pretty straightforward, but I'd really like to just make Num a => a -> a
functions an instance of Num, but I'm not sure how to do so.
I'd also like to make this arity generic, but that might be too much trouble for how difficult it is to do arity generic functions in Haskell, so it might just be better to define seperate Num a => a -> a -> a
, Num a => a -> a -> a -> a
, etc... instances up to some reasonably large number.
There is an instance of Applicative
for (->) a
, which means that all functions are applicative functors. The modern idiom for composing any functions in the way you're describing is to use Applicative
, like this:
(*) <$> f <*> g
liftA2 (*) f g -- these two are equivalent
This makes the operation clear. Both of these approaches are slightly more verbose but seem to me to express the combination pattern far more clearly.
Furthermore, this is a far more general approach. If you understand this idiom, you will be able to apply it in many other contexts than just Num
. If you're not familiar with Applicative
, one place to start is the Typeclassopedia. If you're theoretically inclined, you may check McBride and Patterson's famous article. (For the record, I'm using "idiom" here in the ordinary sense, but mindful of the pun.)
Num b => Num (a -> b)
The instance you want (and others besides) is available in the NumInstances package. You can copy out the instance @genisage posted; they're functionally identical. (@genisage wrote it out more explicitly; comparing the two implementations may be enlightening.) Importing the library on Hackage has the benefit of highlighting for other developers that you're using an orphan instance.
There's a problem with Num b => Num (a -> b)
, however. In short, 2
is now not only a number but also a function with an infinite number of arguments, all of which it ignores. 2 (3 + 4)
now equals 2
. Any use of an integer literal as a function will almost certainly give an unexpected and incorrect result, and there's no way to warn the programmer short of a runtime exception.
As spelled out in the 2010 Haskell Report section 6.4.1, "An integer literal represents the application of the function fromInteger
to the appropriate value of type Integer
." This means writing 2
or 12345
in your source code or in GHCi is equivalent to writing fromInteger 2
or fromInteger 12345
. Either expression therefore has type Num a => a
.
As a result, fromInteger
is absolutely pervasive in Haskell. Normally this works out wonderfully; when you write a number in your source code, you get a number--of the appropriate type. But with your Num
instance for functions, the type of fromInteger 2
could very well be a -> Integer
or a -> b -> Integer
. In fact, GHC will happily replace the literal 2
with a function, not a number--and a particularly dangerous function too, one that discards any data given to it. (fromInteger n = \_ -> n
or const n
; i.e. throw out all arguments and just give n
.)
Often you can get away with not implementing inapplicable class members, or by implementing them with undefined
, either of which gives a runtime error. This is, for the same reasons, not a fix to the present problem.
Num a => Num (a -> a)
If you're willing to restrict yourself to multiplying and adding unary functions of type Num a => a -> a
, we can ameliorate the fromInteger
problem a bit, or at least have 2 (3 + 5)
equal 16
rather than 2
. The answer is simply to define fromInteger 3
as (*) 3
rather than const 3
:
instance (a ~ b, Num a) => Num (a -> b) where
fromInteger = (*) . fromInteger
negate = fmap negate
(+) = liftA2 (+)
(*) = liftA2 (*)
abs = fmap abs
signum = fmap signum
ghci> 2 (3 + 4)
14
ghci> let x = 2 ((2 *) + (3 *))
ghci> :t x
x :: Num a => a -> a
ghci> x 1
10
ghci> x 2
40
Note that although this is perhaps morally equivalent to Num a => Num (a -> a)
, it has to be defined with the equality constraint (which requires GADTs
or TypeFamilies
). Otherwise we'll get an ambiguity error for something like (2 3) :: Int
. I'm not up to explaining why, sorry. Basically, the equality constraint a ~ b => a -> b
allows the inferred or stated type of b
to propagate to a
during inference.
For a nice explanation of why and how this instance works, see the answer to Numbers as multiplicative functions (weird but entertaining).
Do not expose any module that contains--or imports a module that contains--or imports a module that imports a module that contains...--either of these instances without understanding the problem of orphan instances and/or warning your users accordingly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With