2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

存在量化がやっとわかった

Posted at

はじめに

存在量化の記事はたくさんあって既に二番煎じとなっていますが、
ようやく存在量化がわかったのでメモとして残しておきます。 1

間違った知識があると思うので、その時は訂正お願いします

optparse-applicative

Haskellでcli toolを作る場合まずはこれを使う事が多いと思います。

optparse-applicativesub 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等を使いこなせるようにしたいです

  1. https://qiita.com/sparklingbaby/items/fa80df035cff85eb0960

    https://kazu-yamamoto.hatenablog.jp/entry/20080828/1219888005

    https://ja.wikibooks.org/wiki/Haskell/存在量化された型

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?