Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding `~` with 2 Functions

Tags:

haskell

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?

like image 948
Kevin Meredith Avatar asked Apr 27 '17 13:04

Kevin Meredith


People also ask

What does it mean for two functions to commute?

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.

What does it mean to compose 2 functions?

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.


1 Answers

{-# 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)
like image 83
leftaroundabout Avatar answered Sep 29 '22 07:09

leftaroundabout