2017-04-27 17 views
5

Tło: Nie rozumiem ~ i żądam użycia przypadku.Zrozumienie `~` z 2 funkcjami

Dane:

{-# LANGUAGE GADTs #-} 

f :: a ~ b => a -> b -> b 
f a b = a 

g :: a -> a -> a 
g a b = a 

Wydaje mi się, że obie funkcje są równe:

Prelude> :r 
[1 of 1] Compiling Main    (TypeEq.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> f 10 20 
10 
*Main> g 10 20 
10 

W jakich okolicznościach byłoby użyć f nad g?

+4

Należy podkreślić, że nie jest korzystne w takim przypadku: te funkcje są równe. Potrzebujesz czegoś takiego jak rodziny typów lub GADT, żeby zobaczyć jakieś przydatne rzeczy, jak sądzę. A może $ Data.Typeable.eqT $ może być dobrym przykładem. – chi

+3

Jeśli zastanawiasz się, w jaki sposób można go używać w bardzo pomocny sposób, przeczytaj ten świetny wpis na blogu od Chris Done: http://chrisdone.com/posts/haskell-constraint-trick – Shersh

Odpowiedz

9
{-# 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 

Następnie

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 

Możesz teraz sprzeciw, że powinno to lepiej było zrobić z Foldable przymusu, zamiast brzydkiego konwersji na liście. W rzeczywistości nie można tego zrobić, ponieważ nieskasowane wektory nie mają składanej instancji z powodu ograniczenia Unbox!

To może jednak równie dobrze zostały zrobione z non-equational przymusu, mianowicie

fizzbuzz :: (IsList l, Num (Item l), Eq (Item l), Show (Item l)) 
    => l -> IO() 

To jest bardziej ogólne, ale prawdopodobnie również bardziej niewygodne. Kiedy w praktyce potrzebujesz tylko jednego typu zamkniętego, wymuszenie więzów równań może być dobrym wyborem.

Rzeczywiście, czasami wygodniej wrzucić w equational ograniczenie tylko do podpisu typ bardziej zwięzłe, jeśli jest to trochę monotonne: podpis

complicatedFunction :: Long (Awkward (Type a) (Maybe String)) 
       -> [Long (Awkward (Type a) (Maybe String))] 
       -> Either String (Long (Awkward (Type a) (Maybe String))) 

można zastąpić

complicatedFunction :: r ~ Long (Awkward (Type a) (Maybe String)) 
      => r -> [r] -> Either String r 

, które mogą być lepsze niż inne sucho możliwością

type LAwkTS a = Long (Awkward (Type a) (Maybe String)) 

complicatedFunction :: LAwkTS a -> [LAwkTS a] -> Either String (LAwkTS a)