Um fato pouco conhecido é que, se você ativar extensões de idioma suficientes (ghc), Haskell se tornará uma linguagem interpretada de tipo dinâmico! Por exemplo, o programa a seguir implementa a adição.
{-# 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)
Isso realmente não se parece mais com Haskell. Por um lado, em vez de operar sobre objetos, operamos sobre tipos. Cada número é do seu próprio tipo. Em vez de funções, temos classes de tipos. As dependências funcionais nos permitem usá-las como funções entre tipos.
Então, como invocamos nosso código? Nós usamos outra classe
class Test a | -> a
where test :: a
instance (Add (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)
=> Test a
Isso define o tipo de test
como o tipo 4 + 3. Se abrirmos isso em ghci, descobriremos que test
é realmente do tipo 7:
Ok, one module loaded.
*Main> :t test
test :: Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero))))))
Tarefa
Quero que você implemente uma classe que multiplique dois números Peano (números inteiros não negativos). Os números do Peano serão construídos usando os mesmos tipos de dados no exemplo acima:
data Zero
data Succ a
E sua turma será avaliada da mesma maneira que acima. Você pode nomear sua classe como desejar.
Você pode usar as extensões de idioma ghc que desejar, sem nenhum custo para bytes.
Casos de teste
Esses casos de teste assumem que sua classe é nomeada M
; você pode nomear outra coisa, se desejar.
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
Resultados
*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)))))))))))))))))
Inspira-se em Digitando a entrevista técnica