Purescript with Monad

Post on 14-Jun-2015

9.662 views 0 download

description

天下一altJS武闘会(2014/06/08) PureScript枠発表です。

Transcript of Purescript with Monad

なぜMonadが必要なのか、 あるいはPureScriptについて@ruicc 2014-06-08

だれ

@ruicc

Haskeller

サーバサイドエンジニア

興味あること:設計、型理論、圏論

PureScript = Lightweight Haskell

ということで Monadの話をします(2min)

エンジニアから見たMonad (大雑把)

コンストラクタパターン ただし超汎用性高い

ただし超柔軟性高い

Monadは何を構築するのか?

複雑なデータ

関数

プログラム(任意の作用を含む)

リアルワールドにおける 問題テンプレ型A, Bが与えられているとする

solve :: (A -> B) -> IO ()

特定の問題を解く任意の高階関数を想像して下さい

返り値 IO () は単純のため

具体例1. Parser

parse :: Parser a -> String -> Either Errors a

type Parser a = String -> [(a, String)]

--This is a naive definition of parsers.

具体例2. WAI

-- WAI = Web Application Interface

run :: Port -> Application -> IO ()

type Application = Request -> IO Response

リアルワールドにおける ライブラリ提供問題先ほどの関数solveを考える

solve :: (A -> B) -> IO ()

関数(A -> B)は実装に幅をもつ

関数(A -> B)はどこまでも複雑になり得る

ライブラリはユーザへ関数(A -> B)を構築する手段を提供したい

ライブラリ提供問題 どうする?オブジェクト指向言語

例えばクラスをフレームワークとして提供

Haskell, PureScript, etc.

例えばMonadをフレームワークとして提供

具体例1. Parserの場合

Parser構築手段をMonadで提供

Parser Combinatorsと呼ばれる

具体例2. WAIの場合Application構築手段をMonadで提供

Scotty

RouterとControllerをMonadで記述

Yesod

Controllerやクエリ言語をMonadで記述

Monadユースケース

Monadの使いどころ

特定の目的のための値が欲しい

パラメータによる汎用化が困難な問題

目的毎に値を構築する必要がある

構築した値は再利用したい

ユースケース1. Parser

文字列等をパーズするためのパーザがほしい

パラメータによる汎用化が困難な問題

パーズ対象毎にパーザを構築する必要がある

構築したパーザは再利用したい

ユースケース2. EventHandler

イベント処理ためのEventHandlerがほしい

パラメータによる汎用化が困難な問題

イベント毎にEventHandlerを構築する必要がある

構築したEventHandlerは再利用したい

ユースケース3. CSSアニメーション

CSSアニメーションためのCSSビルダがほしい

パラメータによる汎用化が困難な問題

アニメーション毎にCSSビルダを構築する必要がある

構築したCSSビルダは再利用したい

ユースケース4. JavaScript

ブラウザ用動作記述ためのJSビルダがほしい

パラメータによる汎用化が困難な問題

機能毎にJSビルダを構築する必要がある

構築したJSビルダは再利用したい

ユースケース5. DSL

とあるDSLためのDSL記述言語がほしい

パラメータによる汎用化が困難な問題

機能毎にDSL記述言語を構築する必要がある

構築したDSL記述言語は再利用したい

Monad三行まとめ

型で表現可能な任意の値を

構築するための

一手段に過ぎない

そろそろMonadが欲しくなってきた頃と思います

あなたの言語にMonadを取り入れるために

Required primitives

Variables, First class functions, Function application

Required Type system features

Higher Kinded Polymorphism

無くても大丈夫らしい? @khibinoさん情報

Overload(ex. Type Classes)

Do notation

PureScriptの話(出来るところまで)

Haskeller向け説明(15sec)

何が無いのか

GADTs

Type Families

Template Haskell

Concurrent support

Tuple

何が追加されているのかEff

IOを細かく分けて個別に扱える

forall e a. Eff e a == forall a. IO a

Record

Tupleの代わりか

JS側のObjectの表現

Row Polymorphism

RecordとEffで活躍

注意点正格評価

forallが省略出来ない

リテラルが多相化してない

psciの使い方がghciと違う

エラーメッセージ弱い

以下JSer向け説明

JSの問題点

型が弱い

動的型付き言語

要求

強い型がほしい

NullとかAnyとか滅ぼしたい

型安全性がほしい

Q. 強い静的型があればそれでいいのか?

ここでJSの問題領域確認DOM操作

ネットワーク

グラフィックレンダリング

UI関連

音声再生

イベントハンドリング

A. JSやばい、柔軟性が必要。

PureScriptと柔軟性First class functions

高階関数は基本的かつ非常に高い柔軟性を持つ武器

Algebraic Data Type(ADT)

HaxeのEnum、という言い方はどうかと思うけど大体それ。HaxeはGADTになったけど。

直積型、直和型、再帰型

PureScriptと柔軟性 (con’t)

Monad

超強いコンストラクタパターン(前述)

型の表現力が高いほど威力が増す

PureScriptターゲット

任意の規模の開発

複数人によるチームワーク

複雑なロジックの記述

PureScript Pros.Haskellの性質を持っている

テスト容易性

QuickCheck is ready!!

高メンテナンス性

コード再利用性

学習コスト低い(ただしHaskellerに限る)

吐き出すJSコードが小さい

JSエコシステムとの親和性

PureScript Cons.

Haskellそのものではない

superset/subsetどちらでもない

Hackageライブラリが使えない

Concurrent supportがない(GHCJSはある)

PureScriptと パフォーマンスチューニング

mutable変数について

STモナドを使用することで可能。

さらなる最適化

FFIを用いる。

PureScriptとFFI

FFI

Foreign Function Interface

他言語を呼び出す仕組み

今はPS、JS間で交互に呼び出す仕組みを指す

言語間の境界を明確に定める

ぼんやり決める訳ではない

FFIのコスト

PSからJSを呼び出す際、JSのコードにPS側の型を付ける必要がある

FFIの型を適切に記述しないと

PS側の型チェックが通らない

ランタイムエラー発生

FFIのコストはペイ出来るかコミュニティがFFIライブラリを用意してくれればコストゼロ

FFIのコストと対比すべきコストは?

開発コスト

テストコスト

メンテナンスコスト

FFIライブラリ戦略

FFIライブラリ戦略

JavaScriptライブラリをPureScriptで使いたいのだが

ライブラリの解こうとする問題構造から考える

JavaScriptライブラリの構造から考える

ライブラリの解こうとする問題構造から考える

PureScript側で問題を解くために適切なAPIを作成

JS側の構造は無視

PS側で自然に使える

ライブラリのアップデート追随が困難

JavaScriptライブラリの構造から考える

JavaScriptライブラリのAPIをそのままPureScriptへ置き換える

ライブラリ追従が楽

PS側使用時に工夫が必要となる可能性

まあMonadでなんとかなる(多分)

FFIライブラリ戦術

JavaScriptから見たPureScriptPSの関数(Curried function)

JSではNested function

forall e a. Eff e a

引数無しのfunction

Methods

function

PSのRecord

JSのObject

PureScriptから見たJavaScriptライブラリ

JS Objectの型付け問題

FFIでJS側で処理する

Monadで適切に構築する

JSのthis問題

thisの値を運ぶ事で対処

foreign import data Self :: *

method :: forall e a. (Self -> Eff e a) -> Eff e a

PureScriptから中身の見えないJavaScriptの値の扱い

foreign import data Self :: * -- Represent `this`

JavaScript側で生成した値

JavaScript側で扱う値

PureScript側で見える必要はない

しかし運ぶ必要がある

Tips

PS側でどうしてもうまく型が付かない場合は、FFI経由でJS側に押しつけ、JS側でなんとかするという逃げ方がある

JS側の記述は自己責任となるが、それでPS側の型システムが脅かされる訳ではない

FFIはJSに型を付けるという意味合いもある

Try PureScript!

http://tryps.functorial.com/

余談1.

Monadは構築パターン

Comonadは分解パターン、そのうち流行る

余談2.

AltCSSもPureScriptでいいのではないか。モナドで構築。