1と7のリビジョン間の差分 (その間の編集: 6回)
2013-06-23 00:21:17時点のリビジョン1
サイズ: 2781
編集者: grafi
コメント:
2013-06-23 01:55:44時点のリビジョン7
サイズ: 4775
編集者: grafi
コメント:
削除された箇所はこのように表示されます。 追加された箇所はこのように表示されます。
行 9: 行 9:
    ::: Haskell     :::Haskell
行 17: 行 17:
    ::: Haskell     :::Haskell
行 24: 行 24:
    ::: Haskell     :::Haskell
行 29: 行 29:
心眼で余計な演算子を消す(Lisperが括弧を消すようなもの)と、 とりあえずこのコードでなぜうまくいくのかはすっ飛ばして、心眼で余計な演算子を消す(Lisperが括弧を消すようなもの)と、
行 31: 行 31:
    ::: Haskell     :::Haskell
行 38: 行 38:
    ::: Haskell     :::Haskell
行 45: 行 45:
----
`<$>`および、`<*>`は何者かというと、

    :::Haskell
    (<$>) :: Applicative m => (a -> b) -> m a -> m b
    (<*>) :: Applicative m => m (a -> b) -> m a -> m b

です。

`<$>`は、ある関数を、mの中の値に対して適用できるように「持ち上げる(liftする)」関数です。listに対するmapを一般化した概念ともいえます(ある関数を、listの中の値に対して適用できるように持ち上げる)。一方、`<*>`は、既にmの中に入ったある関数を、mの中の別の値に対して適用してやる関数です。`<*>`が定義されているのはApplicativeであるための一番重要な条件なので、「適用的(Applicative)」という名前もしっくり来ます。

また、二引数を受け取る関数は、実際にはカリー化されていて、「まず一引数を受け取って別の一引数を受け取る関数を返す」関数として表現されていることが重要です。
行 48: 行 61:
言語内DSLに便利 もうちょっとまともに書く予定
行 50: 行 63:
言語内DSLでの関数の表現が大変 ## 言語内DSLに使う
<http://www.haskell.org/haskellwiki/GADT>
行 52: 行 66:
# PHOAS
Parameterのkindは\*->*だよ
## 関数をどうするか
* Stringと任意のExpにしちゃうと、型の情報が消えちゃう
* Haskellの関数にエンコードすると、evalがしにくくなったり、任意の関数が書けてしまったり、pretty printできなかったり

## PHOAS
* pに対して多相的なExpしか許さないことで任意の関数を書かせない
    * 引数は必ずEVarを使って導入される
* pのkindは\*->*、要はpに入るのはパラメタライズされる前の型。で、EVarはp a型の値を受け取る。ppt関数の中でのpは、保持する値は実は変数を表示するために割り振るStringであるものの、形式的にどんな型でもパラメタライズされ得るようにしておけば、関数のpretty print可能。
* Coqでプログラム変換が型を保持することを示すのに便利

Applicativeのこと少し

スライドに「Monadのような計算(の一部)を applicative style(要は普通の関数型言語の書き方)で書けるようにする」と書いていますが、ちょっと何を言っているのか分からないかもしれません。

pricesにくだものの名前と値段の連想リストが入っているとして、二つの果物を買うのに必要な値段は

buy2 fruit1 fruit2 =
  lookup fruit1 prices >>= \pri1 ->
  lookup fruit2 prices >>= \pri2 ->
  return pri1 + pri2

と書けますが、もしlookupの返り値がMaybeでなければ

buy2' fruit1 fruit2 = (lookup fruit1 prices) + (lookup fruit2 prices)

と書くところです。Maybeモナドの文脈で計算させたいだけなのに、余計なpri1とかpri2とかいう変数が出てきて、ちょっと気持ち悪いと思いませんか?

Applicativeスタイルで書くと以下のようになります。

buy2 fruit1 fruit2 = (+) <$> lookup fruit1 prices <*> lookup fruit2 prices

Haskellでは二項演算子はただの関数で、演算子っぽい名前がついた関数は中置記法で書くことができるものの、(+)という風に括弧で括って使うと、普通の関数と全く同じように前置記法で適用できることに注意が必要です。

とりあえずこのコードでなぜうまくいくのかはすっ飛ばして、心眼で余計な演算子を消す(Lisperが括弧を消すようなもの)と、

buy2' fruit1 fruit2 = (+) (lookup fruit1 prices) (lookup fruit2 prices)

と読むことができます。

つまりは

buy2' fruit1 fruit2 = (lookup fruit1 prices) + (lookup fruit2 prices)

ですね。

読みにくいだけだと思うかもしれないですが、この例くらいなら慣れれば読みやすくなる、という感じですかね。Applicativeに限った話ではないですが、気持ち悪い見た目には現実問題として慣れるしかないものの、本来は言語とかライブラリとかを作った側の責任であって、読みづらいと感じる自分は無能だとか感じる必要はないと思います。


<$>および、<*>は何者かというと、

(<$>) :: Applicative m => (a -> b) -> m a -> m b
(<*>) :: Applicative m => m (a -> b) -> m a -> m b

です。

<$>は、ある関数を、mの中の値に対して適用できるように「持ち上げる(liftする)」関数です。listに対するmapを一般化した概念ともいえます(ある関数を、listの中の値に対して適用できるように持ち上げる)。一方、<*>は、既にmの中に入ったある関数を、mの中の別の値に対して適用してやる関数です。<*>が定義されているのはApplicativeであるための一番重要な条件なので、「適用的(Applicative)」という名前もしっくり来ます。

また、二引数を受け取る関数は、実際にはカリー化されていて、「まず一引数を受け取って別の一引数を受け取る関数を返す」関数として表現されていることが重要です。

Monadよりも力が限定されている分、Applicativeには無駄に混みいった制御構造を作ってしまいにくいというメリットがあります。Applicativeすごいんだよという話はApplicative Programming with Effectsに載っています(読んでません)。

GADT

もうちょっとまともに書く予定

言語内DSLに使う

http://www.haskell.org/haskellwiki/GADT

関数をどうするか

  • Stringと任意のExpにしちゃうと、型の情報が消えちゃう
  • Haskellの関数にエンコードすると、evalがしにくくなったり、任意の関数が書けてしまったり、pretty printできなかったり

PHOAS

  • pに対して多相的なExpしか許さないことで任意の関数を書かせない
    • 引数は必ずEVarを使って導入される
  • pのkindは*->*、要はpに入るのはパラメタライズされる前の型。で、EVarはp a型の値を受け取る。ppt関数の中でのpは、保持する値は実は変数を表示するために割り振るStringであるものの、形式的にどんな型でもパラメタライズされ得るようにしておけば、関数のpretty print可能。
  • Coqでプログラム変換が型を保持することを示すのに便利

FLProgClassCategory

grafi/Haskell課題(GADTなど) (最終更新日時 2013-07-01 19:11:35 更新者 alpicola)