Background: I don't understand ~ and am requesting a use case.
Given:
{-# LANGUAGE GADTs #-}
f :: a ~ b => a -> b -> b
f a b = a
g :: a -> a -> a
g a b = a
It seems to me that both functions are equal:
Prelude> :r
[1 of 1] Compiling Main ( TypeEq.hs, interpreted )
Ok, modules loaded: Main.
*Main> f 10 20
10
*Main> g 10 20
10
Under what circumstances would it be useful to use f
over g
?
At many layers of the mathematics curriculum, students learn about that various functions can essentially commute with each other. In other words, the order in which the operations is performed doesn't affect the final answer.
In Maths, the composition of a function is an operation where two functions say f and g generate a new function say h in such a way that h(x) = g(f(x)). It means here function g is applied to the function of x. So, basically, a function is applied to the result of another function.
{-# LANGUAGE TypeFamilies #-}
import GHC.Exts (IsList(..))
fizzbuzz :: (IsList l, Item l ~ Int) => l -> IO ()
fizzbuzz = go . toList
where go [] = return ()
go (n:m)
| n`mod`3==0 = putStrLn "fizz" >> go m
| n`mod`5==0 = putStrLn "buzz" >> go m
| otherwise = print n >> go m
Then
Prelude> fizzbuzz [1..7]
1
2
fizz
4
buzz
fizz
7
Prelude> import Data.Vector.Unboxed as UA
Prelude UA> fizzbuzz (UA.fromList[1..7] :: UA.Vector Int)
1
2
fizz
4
buzz
fizz
7
You may now object that this should better have been done with a Foldable
constraint, instead of the ugly conversion to a list. Actually this couldn't be done, because unboxed vectors do not have a foldable instance due to the Unbox
constraint!
It could, however, just as well have been done with a non-equational constraint, namely
fizzbuzz :: (IsList l, Num (Item l), Eq (Item l), Show (Item l))
=> l -> IO ()
That is more general, but arguably also more awkward. When you need, in practice, only one contained-type anyway, an equational constraint may be a good choice.
Indeed, I sometimes find it convenient to toss in an equational constraint just to make a type signature more concise, if it's a bit repetitive: the signature
complicatedFunction :: Long (Awkward (Type a) (Maybe String))
-> [Long (Awkward (Type a) (Maybe String))]
-> Either String (Long (Awkward (Type a) (Maybe String)))
can be replaced with
complicatedFunction :: r ~ Long (Awkward (Type a) (Maybe String))
=> r -> [r] -> Either String r
which may be better than the other DRY-possibility of
type LAwkTS a = Long (Awkward (Type a) (Maybe String))
complicatedFunction :: LAwkTS a -> [LAwkTS a] -> Either String (LAwkTS a)
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