FuctioNaL with OOP
~objectiveが諍いを終演に導く~

2016/4/16 ちゅーん(@its_out_of_tune)

自己紹介

  • 野生のHaskller(28♂)

  • クルージング(スケボー)
  • ボルダリング
  • ポーカー(テキスサス・ホールデム)

  • スプラトゥーン
  • 当たらない方のチャージャー使い

HN: ちゅーん

Twitter:
 @its_out_of_tune
Github:
 tokiwoousaka

自己紹介

  • Takahashi Monad:
     スライド作成用の言語内DSL
     本スライドもこれで作成
  • Sarasvati:
     永遠に開発中のオーディオインターフェイス
     PortAudioのラッパーのラッパー
  • ブログ:http://tune.hateblo.jp/
     ポーカーゲームを作る連載やってます

近状報告

近状報告

尾てい骨
骨折

近状報告

あと東京から引っ越して来て某社に入社しました

OOP vs 関数型?

タイトルについて

インパクトがあれば何でも良いかなって
反省はしていない

この節でやりたかった事

  • 関数型vsOOPでググる

  • 地獄絵図

  • そんな論争は無意味だ!

実際にググってみた

一言二事言いたい事はあるものの
言うほど酷く無かった

なので

次行くよ次

objectiveを使う

オブジェクト指向(OOP)とは

  • モノとモノが云々
  • メッセージがメソッドが
  • 継承関係がどーとかこーとか
  • 動物クラスに猫クラス
  • アラン・ケイがなんたら
  • ストラウストラップがどうたら

オブジェクト指向(OOP)とは

なるほどわからん
(´・ω・`)

実際の所

  • プログラマの数だけOOPがある
  • 歴史/本質の話は数あれど・・・
  • 何か共通のコンテクストはある?

  • 下手なこと言うと論争になるよねorz

実際の所

よし
不毛な話はやめよう

OOPLにありがちな機能

  • クラスとインスタンス
  • メソッド呼び出し
  • 手続き/制御構造
  • 抽象クラスと継承
  • インターフェイスと実装

  • ・・・ってあれ?

OOPLにありがちな機能

オブジェクト指向に
必要なものって
やったら多くね?

今日のお話



我々が「欲しい」と考える
OOPLの機能を得るための
「Haskellらしい」仕組みの紹介

objective?

@fumievalさんによる
Haskell上でOOPを実現するための道具立て

https://hackage.haskell.org/package/objective

CabalなりStackなりで
良い感じにインストールして使おう

実際に使ってみる

サンプルとしてカウンターを作ろう
まずGADTs言語拡張を用いてインターフェイスを定義。

data Counter a where
PrintCount :: Counter ()
Increment :: Counter ()

実際に使ってみる

インターフェイスを実装する
(@~)演算子の左辺に状態の初期値を与える。
右辺は StateT s IO モナドで各メソッドを実装。

counter :: Int -> Object Counter IO
counter i = i @~ \case
PrintCount -> get >>= liftIO . print
Increment -> modify (+1)

実際に使ってみる

new関数でインスタンス作成
(.-)演算子でメソッド呼び出し

main :: IO ()
main = do
cntr <- new $ counter 10
cntr.-PrintCount -- 10が出力される
cntr.-Increment
cntr.-Increment
cntr.-Increment
cntr.-Increment
cntr.-PrintCount -- 14が出力される

実際に使ってみる

ね?簡単でしょう?

オブジェクトの拡張

objectiveにももちろん
既存のオブジェクトを拡張するための
便利な機能が備わっています。

オブジェクトの拡張

その前にobjectiveの型の読み方を説明しておこう
便宜上「インターフェイス」「メソッド」を使ったが
objectiveの基礎的な考え方はメッセージパッシングだ

-- 次のように送受信メッセージを型引数として取る
Object [受信メッセージ] [送信メッセージ]

-- インスタンスを使うにはIOを送信しなくてはいけない
new :: Object f IO -> m (Instance f IO)
(.-) :: Instance f IO -> f a -> IO a

オブジェクトの拡張

【縦の合成】
左辺のオブジェクトの受信メッセージを受け
右辺のオブジェクトの送信メッセージを送る
新しいオブジェクトを作成する。

(@>>@) :: Functor h => Object f g -> Object g h -> Object f h

オブジェクトの拡張

【横の合成】
左辺のオブジェクトの受信メッセージと
右辺のオブジェクトの受信メッセージを纏め上げた
新しいオブジェクトを作成する。

data Sum f g a = InL (f a) | InR (g a)
(@||@) :: Functor m => Object f m -> Object g m -> Object (Sum f g) m

合成による拡張は軽量

二つの合成については、
次のように解釈すると分かりやすい

  • オーバーライド:縦合成
  • メソッドの追加:横合成

上手く組み合わせれば、
継承と概ね同等の拡張を実現出来る

合成による拡張は軽量

実際使いこなすにはOperationalとか
もう少し高度な知見も必要ですが……

objectiveの継承について

  • やりたい事のわりに煩雑になりそう?
  • InLとかInRとかどうにかならないのか
  • これって親子間に多相性無いよね?
  • →Haskellの抽象力で色々できそう
  • →型クラスを上手く使おう
  • →Effの応用でも解決出来るらしい

objectiveの継承について

っていうか
そもそも

objectiveの継承について

  • 継承ってやたら沢山機能があるよね。

  • オーバーライド/メソッド追加/親要素へのアクセス……

  • これ全部が必要な拡張って既に設計に難がありそう

  • 全部ひとからげに1機能で提供してるOOPLって……

結局の所

必要な拡張を必要なだけ
明示的に出来れば良い

HaskellらしいOOP

モナドという例

突然ですが、
手続きプログラミングを
説明できますか?

モナドという例

そもそも

モナドという例

「手続き」って
何なんすか(´・ω・`)

手続きの要件とモナド

  • 何か作用のある「命令」を並べたい -> (>>=)
  • 命令は引数を取ったり結果を返すかも -> 無視/Unit
  • 「何もしない」命令が存在する -> return
  • 「何もしない」命令は消しても影響が無い -> 単位元即
  • 命令は良い感じに関数/メソッド化出来る -> 結合則

形式的であるという事



「数学」という言葉を使うとまた荒れそうなので
「形式的」という言葉に言い換えます

ここでいう「形式的」は
「厳密な定義の組み合わせで曖昧性/矛盾なく説明できる」
くらいの意味です

形式的であるという事

①何か型をつくる
②Monadクラスのインスタンスにする
③ちゃんとモナド則を満たす事を確認する
④その型は例外なくモナドです

形式的であるという事



こう言い切ってしまえば、
「モナドとは何か」なんて議論する余地は、
本来無いはずなのですよっ(`・ω・´)

とにかく

「モナド」とは唯一
手続きを形式的に説明する事に
成功した概念である

とにかく

たぶん

オブジェクトは何か言い切る

①メッセージを送受信出来て
②状態を持っているもの

オブジェクトは何か言い切る

①メッセージの送受信
メッセージを関手として、自然変換が相当する

nat :: m a -> n a

--自然変換は自然性という性質を満たす
nat . fmap f == fmap f . nat

オブジェクトは何か言い切る

②状態を持つ
ミーリ・マシン:入力aを受けると出力bを返し、
自身の状態を書き換えるオートマトンの一種。

newtype Mealy a b = Mealy
{ runMealy :: a -> (b, Mealy a b) }

オブジェクトは何か言い切る

合体!!

オブジェクトは何か言い切る

オブジェクトはミーリ・マシンの性質を持った
ただの自然変換だよ?何か問題でも?

---- Natural ----
forall x. f x -> g x

---- Mealy ----
newtype Mealy a b
= Mealy { runMealy :: a -> (b, Mealy a b) }

---- Object ----
newtype Object f g
= Object { runObject :: forall x. f x -> g (x, Object f g) }

Haskellらしさとは

対象について形式的に述べる事によって
それが何なのか明瞭になるばかりでは無く
その対象の持つ性質を遺憾なく発揮した
高度なプログラミングを実現出来る!

Objectは圏の射

おまけ:
縦合成は単位元としてechoという
特殊なオブジェクトを持ち、結合則を満たします。

つまり、メッセージを対象としてオブジェクトを射とした、
「オブジェクトの圏」を定義できます。

嬉しい✌('ω'✌ )三✌('ω')✌三( ✌'ω')✌

最後に

objectiveの使い所

開発者@fumievalさん曰く
オブジェクトは現実的な問題を解決するには
強すぎる道具立てである、従って……

「ゲームとかGUIくらいしか無いと思います」

objectiveの使い所

元も子もねぇ
(ノ`Д´)ノ彡┻━┻

ちゅーんさんの考え

もっと軽いシチュエーションでも
使い所あるんじゃ無いかなぁ……例えば……

ちゅーんさんの考え

やや複雑な状態を
脳死状態で管理したい時

ちゅーんさんの考え

ちょっとしたツール作る時とかに。

適切に使わないと死ぬかもしれない

まとめ

  • objectiveを使ってHaskellでOOP出来るよ
  • 拡張機能はよくあるOOPLより軽量だよ
  • 複雑な拡張は大変だけどだいたい十分だよ
  • objectiveは形式的なのが強みだよ
  • ゲームとかGUIとか、他にも使い所あるかもね

  • 高度な応用の話は論文を呼んでね!

ありがとうございました
m(__)m