My understanding of Sum
and Product
newtypes is that they serve as monoidial wrappers for numeric types. I would understand Functor
instance on them, but why there are also Applicative
, Monad
any many other seemingly useless instances? I understand that they are mathemathically OK (isomorphic to Identity
modad, right?) But what is the use case? If there is an Applicative Sum
instance, for example, I would expect to encounter a value of type Sum (a -> b)
somewhere. I can't imagine where this could possibly be useful.
Such instances are convenient for lifting arbitrary functions to work on things that happen to currently be living inside a Sum
or Product
. For example, one might imagine wanting to do some bitwise operations on something that is nevertheless more convenient in a Sum
than bare; then liftA2 (.&.) :: Sum Int -> Sum Int -> Sum Int
(for example).
One could also provide this operation by giving a Bits
instance for Sum
, but generalizing that technique would require the implementors of Sum
to predict every operation one might ever want to do, which seems like a tall order. Providing Applicative
and Monad
instances give a once-and-for-all translation for users to lift any function they like -- including ones the implementors of Sum
did not predict being useful.
Values like this typically result from partial application of binary operators. Assuming Functor
and Applicative
instances like
import Control.Applicative
import Data.Monoid
instance Functor Sum where
fmap f (Sum x) = Sum (f x)
instance Applicative Sum where
pure = Sum
(Sum f) <*> (Sum x) = Sum (f x)
then you can see how a value of Sum (a -> b)
would arise.
> :t (*) <$> (Sum 5)
(*) <$> (Sum 5) :: Num a => Sum (a -> a)
> (*) <$> (Sum 5) <*> (Sum 10)
Sum {getSum = 50}
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