あまり知られていない事実は、十分な言語拡張(ghc)をオンにすると、Haskellが動的に型付けされたインタープリター型言語になるということです。たとえば、次のプログラムは加算を実装しています。
{-# 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)
これは本当にHaskellのようには見えません。オブジェクトを操作する代わりに、タイプを操作します。各番号は独自のタイプです。関数の代わりに、型クラスがあります。機能的な依存関係により、型間の関数として使用できます。
では、コードをどのように呼び出すのでしょうか。別のクラスを使用します
class Test a | -> a
where test :: a
instance (Add (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)
=> Test a
これによりtest
、タイプが4 + 3に設定されます。これをghciで開くとtest
、タイプ7であることがわかります。
Ok, one module loaded.
*Main> :t test
test :: Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero))))))
仕事
2つのペアノ数値(負でない整数)を乗算するクラスを実装してほしい。ペアノ数値は、上記の例と同じデータ型を使用して構築されます。
data Zero
data Succ a
そして、あなたのクラスも上記と同じ方法で評価されます。クラスには任意の名前を付けることができます。
バイトに費用をかけずに、必要なghc言語拡張機能を使用できます。
テストケース
これらのテストケースでは、クラスの名前がM
であることを想定していますが、必要に応じて別の名前を付けることができます。
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
結果
*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)))))))))))))))))
テクニカルインタビューの入力からインスピレーションを得ます