はじめに
存在量化の記事はたくさんあって既に二番煎じとなっていますが、
ようやく存在量化がわかったのでメモとして残しておきます。 1
間違った知識があると思うので、その時は訂正お願いします
optparse-applicative
Haskellでcli toolを作る場合まずはこれを使う事が多いと思います。
optparse-applicative
でsub command
を作成する時は
data Command
= Add AddOptions
| Commit CommitOptions
...
のようなdataを定義します。
続いて実行関数を定義します。
run :: Command -> IO()
run (Add a) = ...
run (Commit c) = ...
これで全然問題ないのですが、sub command
が多くなってくるとコマンドをモジュールで管理したくなります。
型クラスで定義する
run
は共通の振る舞いをするので、型クラスを定義します。
class Runner a where
run :: a -> IO()
次に、対応するdataとinstance
を定義します。
newtype AddRunner = AddRunner AddOption
instance Runner AddRunner where
run (AddRunner a) = ...
newtype CommitRunner = CommitRunner AddOption
instance Runner CommitRunner where
run (CommitRunner a) = ...
対応するdataで変換する
ここで代数的データ構造から対応するdata
に変換したいのですがここで問題が発生します。
converter :: Runner r => Command -> r
converter (Add a) = AddRunner a -- コンパイルエラー
...
のように書きたいのですが、コンパイルエラーとなってしまいます。
ExistentialQuantification
これを解決するのが、ExistentialQuantification
拡張です。
Runner
クラスを内包するWrapRunnerを新たに定義します。
{-# LANGUAGE ExistentialQuantification #-}
data WrapRunner = forall a . Runner a => WrapRunner a
WrapRunner
は代数的データ構造から変換かけることができます。
converter :: Cmd -> WrapRunner
converter (Add a) = WrapRunner $ AddRunner a
converter (Commit c) = WrapRunner $ CommitRunner c
さらにRunner
のinstanceにもできます
instance Runner WrapRunner where
run (WrapRunner wr) = run wr
これでようやくデータの振る舞いを型で定義する事ができました。
GADTs
GADTs
で定義されているとします
data GCmd a where
GAdd :: AddOption -> GCmd String
GCommit :: CommitOption -> GCmd ()
run :: GCmd a -> IO a
その時対応する型クラスは以下のようになると思います
{-# LANGUAGE TypeFamilies #-}
class Runner a where
type RType a
run :: a -> IO (RType a)
instance Runner AddRunner where
type RType AddRunner = String
run (AddRunner a) = ...
instance Runner CommitRunner where
type RType CommitRunner = ()
run (CommitRunner c) = ...
これに対応するWrapデータをするには
type RType a に対応するデータを定義すればできます
data WrapRType = forall a . WrapRType a
instance Runner WrapRunner where
type RType WrapRunner = WrapType
run (WrapRunner wr) = WrapType <$> run wr
最後に
型クラスを使っての動的解析の仕方がわかったあまりに記事にしましたが、これが正しいやり方であったり、ベストプラクティスであるかは正直分かりません。
ただ今回のおかげで存在量化の理解が進みました。次はDataKinds
等を使いこなせるようにしたいです