Mało znanym faktem jest to, że jeśli włączysz wystarczającą liczbę rozszerzeń języka (ghc), Haskell stanie się dynamicznie pisanym językiem interpretowanym! Na przykład poniższy program implementuje dodawanie.
{-# Language MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances #-}
data Zero
data Succ a
class Add a b c | a b -> c
instance Add Zero a a
instance (Add a b c) => Add (Succ a) b (Succ c)
To już tak naprawdę nie wygląda jak Haskell. Dla jednego zamiast działania na obiektach, działamy na typach. Każda liczba ma własny typ. Zamiast funkcji mamy klasy typów. Zależności funkcjonalne pozwalają nam wykorzystywać je jako funkcje między typami.
Jak więc wywołać nasz kod? Używamy innej klasy
class Test a | -> a
where test :: a
instance (Add (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)
=> Test a
To ustawia typ test
na typ 4 + 3. Jeśli otworzymy to w ghci, okaże się, że test
rzeczywiście jest to typ 7:
Ok, one module loaded.
*Main> :t test
test :: Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero))))))
Zadanie
Chcę, abyś zaimplementował klasę, która zwielokrotnia dwie liczby Peano (nieujemne liczby całkowite). Liczby Peano zostaną zbudowane przy użyciu tych samych typów danych w powyższym przykładzie:
data Zero
data Succ a
Twoja klasa będzie również oceniana w taki sam sposób jak powyżej. Możesz nazwać swoją klasę, jak chcesz.
Możesz używać dowolnych rozszerzeń języka GHC bez bajtów.
Przypadki testowe
Te przypadki testowe zakładają, że twoja klasa ma nazwę M
, możesz nazwać ją inną nazwą, jeśli chcesz.
class Test1 a| ->a where test1::a
instance (M (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)=>Test1 a
class Test2 a| ->a where test2::a
instance (M Zero (Succ (Succ Zero)) a)=>Test2 a
class Test3 a| ->a where test3::a
instance (M (Succ (Succ (Succ (Succ Zero)))) (Succ Zero) a)=>Test3 a
class Test4 a| ->a where test4::a
instance (M (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))) (Succ (Succ (Succ Zero))) a)=>Test4 a
Wyniki
*Main> :t test1
test1
:: Succ
(Succ
(Succ
(Succ
(Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))))))
*Main> :t test2
test2 :: Zero
*Main> :t test3
test3 :: Succ (Succ (Succ (Succ Zero)))
*Main> :t test4
test4
:: Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ
(Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))))))))))))
Inspiracje czerpie z pisania wywiadu technicznego