frege, what a non-strict language
TRANSCRIPT
Frege,What a Non-strict Language
チェシャ猫 (@y_taka_23)NL 名古屋 (2016/04/16)
自己紹介
● 名前 : チェシャ猫
○ Twitter: @y_taka_23
○ GitHub: y-taka-23
● 好きなもの
○ Haskell
○ 形式手法 (Coq, Alloy, SPIN, etc...)
● 自称 Frege エバンジェリスト
本日の内容
● JVM 言語 Frege の概要
○ 基本的な特徴
○ Haskell との比較
● Java コード生成と非正格評価
○ コンパイルの仕組み
○ 評価戦略のマッピング
1. JVM 言語 Frege の概要
“Frege is a Haskell for JVM”
Frege のエッセンス
● 純粋・非正格評価な関数型言語
○ 本日のメイントピック
● 強い静的型付け
○ Hindley-Milner 型推論 + Rank-N types
● モナドによる Java の呼び出し
○ 歌舞伎座.tech #9 での発表スライド参照
○ 「すごい Frege たのしく学ぼう!」で検索
Q: どんな文法?
Hello, Frege
module Hello where
greeting :: String -> Stringgreeting name = “Hello, “ ++ name
main :: [String] -> IO ()main args = do putStrLn $ greeting “World”
Hello, Frege
module Hello where
greeting :: String -> Stringgreeting name = “Hello, “ ++ name
main :: [String] -> IO ()main args = do putStrLn $ greeting “World”
A: ほぼ Haskell
『すごい Haskell』翻訳実験
● 全サンプルコードを Frege に○ https://github.com/y-taka-23/learn-you-a-frege
● だいたい丸写しでコンパイルが通る
○ 構文論的には Haskell 2010 互換
2. Java コード生成と非正格評価
Frege のコンパイル
● コンパイラ自身も Frege 実装
● JVM 系ビルドツールが利用可
○ Gradle, Maven, sbt. Leiningen, Bazel
● コンパイルすると Java ソースコードに
○ あくまでも中間生成コードで可読性低
○ 最新版 v3.24 で生成ロジックが変更
Hello again, Frege
module Hello where
greeting :: String -> Stringgreeting name = “Hello, “ ++ name
main :: [String] -> IO ()main args = do putStrLn $ greeting “World”
Hello again, Frege
module Hello where
greeting :: String -> Stringgreeting name = “Hello, “ ++ name
main :: [String] -> IO ()main args = do putStrLn $ greeting “World”
Hello again, Frege
module Hello where
greeting :: String -> Stringgreeting name = “Hello, “ ++ name
main :: [String] -> IO ()main args = do putStrLn $ greeting “World”
public static void main(String[] args) {...}
Frege の型 = Java の型?
Frege によるたらい回し関数
tarai :: Int -> Int -> Int -> Inttarai x y z = if x <= y then y else tarai (tarai (x - 1) y z) (tarai (y - 1) z x) (tarai (z - 1) x y)
予想される Java コード
static int tarai(int x, int y, int z) { if (x <= y) { return y; } else { return tarai( tarai(x - 1, y, z), tarai(y - 1, z, x), tarai(z - 1, x, y)); }}
コンパイルしてみる
生成されるコードの骨子
static int tarai(int x, int y, Lazy<Integer> z) { if (x <= y) { return y; } else { return tarai( tarai(x - 1, y, z), tarai(y - 1, (int)z.call(), Thunk.<Integer>lazy(x)), Thunk.<Integer>shared( (Lazy<Integer>)(() -> tarai( (int)z.call() - 1, x, Thunk.<Integer>lazy(y)))) ); }}
意外と複雑だった
評価戦略のマッピング
● Haskell の非正格評価を Java 上で再現
● ここで関連する要素は主に 3 つ
○ frege.run8.Lazy<T>
○ frege.run8.Box<T>
○ frege.run8.Thunk<T>
frege.run8.Lazy<T>
● Callable のサブインタフェース
○ call() メソッドで値を取得
● R 型の式の評価を遅延させる
○ 役割は Supplier<R> に類似
● Frege の代数的データ型や関数は
デフォルトで Lazy の実装クラスに
frege.run8.Lazy<T>
static int tarai(int x, int y, Lazy<Integer> z) { if (x <= y) { return y; } else { return tarai( tarai(x - 1, y, z), tarai(y - 1, (int)z.call(), Thunk.<Integer>lazy(x)), Thunk.<Integer>shared( (Lazy<Integer>)(() -> tarai( (int)z.call() - 1, x, Thunk.<Integer>lazy(y)))) ); }}
frege.run8.Lazy<T>
static int tarai(int x, int y, Lazy<Integer> z) { if (x <= y) { return y; } else { return tarai( tarai(x - 1, y, z), tarai(y - 1, (int)z.call(), Thunk.<Integer>lazy(x)), Thunk.<Integer>shared( (Lazy<Integer>)(() -> tarai( (int)z.call() - 1, x, Thunk.<Integer>lazy(y)))) ); }}
frege.run8.Box<T>
● Lazy の実装クラスその 1
● Thunk.<T>lazy(x) で生成
● Lazy になっていない型のラッパー
○ 役割は IntSupplier などと同様
○ call() されるとラップされている値を返す
frege.run8.Box<T>
static int tarai(int x, int y, Lazy<Integer> z) { if (x <= y) { return y; } else { return tarai( tarai(x - 1, y, z), tarai(y - 1, (int)z.call(), Thunk.<Integer>lazy(x)), Thunk.<Integer>shared( (Lazy<Integer>)(() -> tarai( (int)z.call() - 1, x, Thunk.<Integer>lazy(y)))) ); }}
frege.run8.Box<T>
static int tarai(int x, int y, Lazy<Integer> z) { if (x <= y) { return y; } else { return tarai( tarai(x - 1, y, z), tarai(y - 1, (int)z.call(), Thunk.<Integer>lazy(x)), Thunk.<Integer>shared( (Lazy<Integer>)(() -> tarai( (int)z.call() - 1, x, Thunk.<Integer>lazy(y)))) ); }}
frege.run8.Thunk<T>
● Lazy の実装クラスその 2
● Thunk.<T>shared(x)で生成
● Call-by-need を実現
○ x が共有可能なら shared(x) は x を返す
○ 初めて call() されると内部に値を保持
○ 次に call() された際にはその値を返す
frege.run8.Thunk<T>
static int tarai(int x, int y, Lazy<Integer> z) { if (x <= y) { return y; } else { return tarai( tarai(x - 1, y, z), tarai(y - 1, (int)z.call(), Thunk.<Integer>lazy(x)), Thunk.<Integer>shared( (Lazy<Integer>)(() -> tarai( (int)z.call() - 1, x, Thunk.<Integer>lazy(y)))) ); }}
McCarthy による変種
tak :: Int -> Int -> Int -> Inttak x y z = if x <= y then z else tak (tak (x - 1) y z) (tak (y - 1) z x) (tak (z - 1) x y)
McCarthy による変種
tak :: Int -> Int -> Int -> Inttak x y z = if x <= y then z else tak (tak (x - 1) y z) (tak (y - 1) z x) (tak (z - 1) x y)
McCarthy 版から生成されるコード
static int tak(int x, int y, int z) { if (x <= y) { return z; } else { return tak( tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y)); }}
McCarthy 版から生成されるコード
static int tak(int x, int y, int z) { if (x <= y) { return z; } else { return tak( tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y)); }}
まとめ
● Frege は JVM のための Haskell○ 構文は Haskell そのまま
○ JVM 系ビルドツールが使用可能
● 非正格評価の Java へのマッピング
○ Lazy インタフェースによる評価の遅延
○ Box クラスによるプリミティブ型の Lazy 化
○ Thunk クラスによる結果の使いまわし
Thunk You for Listening!Presented by
チェシャ猫 (@y_taka_23)