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 -> String
greeting name = “Hello, “ ++ name
main :: [String] -> IO ()
main args = do
putStrLn $ greeting “World”
Hello, Frege
module Hello where
greeting :: String -> String
greeting 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 -> String
greeting name = “Hello, “ ++ name
main :: [String] -> IO ()
main args = do
putStrLn $ greeting “World”
Hello again, Frege
module Hello where
greeting :: String -> String
greeting name = “Hello, “ ++ name
main :: [String] -> IO ()
main args = do
putStrLn $ greeting “World”
Hello again, Frege
module Hello where
greeting :: String -> String
greeting 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 -> Int
tarai 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 -> Int
tak 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 -> Int
tak 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)

Frege, What a Non-strict Language

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