Real World OCaml読んでみた感想

Lisp Meet Up Presented by Shibuya.lisp #14
κeen(@blackenedgold)
#lispmeetup
2014-02-26
自己紹介
●

Twitter: κeen(@blackenedgold)

●

Github: KeenS

●

ブログ: κeenのHappy Hacκing Blog

●

東大数学科の三年生

●

就活中!

●

Lisp, Ruby, シェルスクリプトが好き

●

CIMの開発してます
話の流れ
●

OCamlと周りの環境の紹介(軽く)

●

Real World OCamlの紹介

●

OCamlの機能をLispに取り込んでみた ボツ編

●

OCamlの機能をLispに取り込んでみた
OCamlと周りの環境
の紹介
OCamlについて
名古屋の方は黙って見てて下さい

ML方言のCamlにオブジェクト指向を加えたもの
● 強い静的片付け
● 強力な型推論
● フランスのINRIA研究所で開発されている
●
OCamlについて
名古屋の方は黙って見てて下さい

●

関数型

●

値は基本変更不可

●

関数はカリー化されている
OCaml

サンプルコード

let rec quicksort = function
| [] -> []
| pivot :: rest ->
let is_less x = x < pivot in
let left, right = List.partition is_less rest in
quicksort left @ [pivot] @ quicksort right

Racket
(define quicksort
(match-lambda
['() '()]
[(cons pivot rest)
(let ([lessp (lambda (x) (< x pivot))])
(let-values ([(left right) (partition lessp rest)])
(append (quicksort left) (list pivot) (quicksort right))))]))
OCaml

サンプルコード

再帰関数定義

リストリテラル

let rec quicksort = function
コンス演算子によるマッチング
| [] -> []
| pivot :: rest ->
let is_less x = x < pivot in
let left, right = List.partition is_less ローカル関数定義
rest in
quicksort left @ [pivot] @ quicksort right

Racket

タプルリテタルによる
(define quicksort
変数の分配束縛

モジュール関数呼び出し

(match-lambda
['() '()]
append演算子
[(cons pivot rest)
(let ([lessp (lambda (x) (< x pivot))])
(let-values ([(left right) (partition lessp rest)])
(append (quicksort left) (list pivot) (quicksort right))))]))
OCamlをとりまく環境
●

Jane Streetという金融会社が業務で使用

●

標準ライブラリにツッコみどころがある
–
–

●

Jane StreetのライブラリCoreで補う
標準ライブラリを上書きしてる

ライブラリマネージャ、バージョンマネージャ完
備

●

推奨エディタがEmacs

●

日本だと名古屋が拠点
Real World OCamlの紹介
Real World OCamlの紹介
http://realworldocaml.org
● ベータ版は↑から無料で読める
● 英語版はオライリーより$44くらい
● 510p (cf. Land of Lispは480p)
● 著者は
OMakeの開発者
XenをOCamlで開発した人
Jane Streetの中の人
●
Real Wold OCamlの紹介
●

Jane StreetのCoreを前提

●

言語の基礎からコンパイラプラグインまで

●

●

プログラミングの知識はあるが関数型は初めて
の人対象
一冊読めば入門からReal Worldまでいけると思
います
目次
●

はじめに

●

第1部 言語の概念

●

1 マップ及びハッシュテーブル
3.
1 コマンドラインのパース
4.

1 ひとめぐり
.

15.
JSONデータの扱い

2. 変数と関数

16.
OCamllex及びMenhirによるパース

3. リストとパターン

17.
S式によるデータシリアライゼーション

4. ファイル、モジュール、プログラム
5. レコード

18.
Asyncによる並行プログラミング
●

6. バリアント

第3部 ランタイムシステム
1 外部関数の呼出し(FFI)
9.

7. エラーハンドリング

20.
値のメモリ表現

8. 命令型プログラミング

21
ガーベジコレクタを理解する
.

9. ファンクタ

22.
コンパイラフロントエンド:パース及び
型チェック

1 第一級​モジュール
0.

23.
コンパイラバックエンド:バイトコード
及びネイティブコード

1 .オブジェクト
1
1 クラス
2.

第2部 ツール及びテクニック

●

索引
目次
●

はじめに

●

第1部 言語の概念

●

1 マップ及びハッシュテーブル
3.
1 コマンドラインのパース
4.

1 ひとめぐり
.

15.
JSONデータの扱い

2. 変数と関数

16.
OCamllex及びMenhirによるパース

3. リストとパターン

17.
S式によるデータシリアライゼーション

4. ファイル、モジュール、プログラム
5. レコード

18.
Asyncによる並行プログラミング
●

6. バリアント

第3部 ランタイムシステム
1 外部関数の呼出し(FFI)
9.

7. エラーハンドリング

20.
値のメモリ表現

8. 命令型プログラミング

21
ガーベジコレクタを理解する
.

9. ファンクタ

22.
コンパイラフロントエンド:パース及び
型チェック

1 第一級​モジュール
0.

23.
コンパイラバックエンド:バイトコード
及びネイティブコード

1 .オブジェクト
1
1 クラス
2.

第2部 ツール及びテクニック

●

索引
ボツ編
モジュール分割
let rec quicksort = function
| [] -> []
| pivot :: rest ->
let is_less x = x < pivot in
let left, right = List.partition is_less rest in
quicksort left @ [pivot] @ quicksort right
モジュール分割
let rec quicksort = function
| [] -> []
| pivot :: rest ->
let is_less x = x < pivot in
let left, right = List.partition is_less rest in
quicksort left @ [pivot] @ quicksort right

List.map
Array.map
String.map
…など
モジュール分割
原案

(list:map #'1+ '(1 2 3)) ;=> (1 2 3)
(vector:map #'1+ #(1 2 3)) ;=> #(2 3 4)
(string:map (lambda (x) (code-char (1+(char-code x))))
"abc")
;=> "bcd"
モジュール分割
原案

(list:map #'1+ '(1 2 3)) ;=> (1 2 3)
(vector:map #'1+ #(1 2 3)) ;=> #(2 3 4)
(string:map (lambda (x) (code-char (1+(char-code x))))
"abc")
;=> "bcd"
CLer「名前空間は機能を分割するためのもので型を
ディスパッチするものではない」
CLer「ハンガリアン記法で区別可能。よって却下」
モジュール分割
原案

(list:map #'1+ '(1 2 3)) ;=> (1 2 3)
(vector:map #'1+ #(1 2 3)) ;=> #(2 3 4)
(string:map (lambda (x) (code-char (1+(char-code x))))
"abc")
;=> "bcd"
CLer「名前空間は機能を分割するためのもので型を
ディスパッチするものではない」
CLer「ハンガリアン記法で区別可能。よって却下」

ボツ
local use-package
let average x y =
x Int64.(+) y Int64.(/) Int64.of_int 2;;
open Int64;;
let average x y =
x + y / of_int 2;;
let average x y =
let open Int64 in
x + y / of_int 2;;
let average x y =
let module I = Int64 in
x I.(+) y I.(/) I.of_int 2;;
local use-package
(defun average (x y)
let average x y =
(int64:/ (int64:+ x y)
x Int64.(+) y Int64.(/) Int64.of_int 2;;
(int64:of-int 2)))

open Int64;;
let average x y =
x + y / of_int 2;;
let average x y =
let open Int64 in
x + y / of_int 2;;
let average x y =
let module I = Int64 in
x I.(+) y I.(/) I.of_int 2;;

(use-package :int64)
(defun average (x y)
(/ (+ x y) (of-int 2)))
local use-package
(defun average (x y)
let average x y =
(int64:/ (int64:+ x y)
x Int64.(+) y Int64.(/) Int64.of_int 2;;
(int64:of-int 2)))

open Int64;;
let average x y =
x + y / of_int 2;;

(use-package :int64)
(defun average (x y)
(/ (+ x y) (of-int 2)))

(defun average (x y)
let average x y =
(with-use-packages (:int64)
let open Int64 in
(/ (+ x y) (of-int 2))))
x + y / of_int 2;;
let average x y =
(defun average (x y)
(with-package-nicknames
let module I = Int64 in
x I.(+) y I.(/) I.of_int 2;; ((i int64))
(i:/ (i:+ x y) (i:of-int 2))))
local use-package
●

シンボルの解決はリード時

●

マクロの展開はコンパイル時
local use-package
●

シンボルの解決はリード時

●

マクロの展開はコンパイル時

オワタ(^o^)/
local use-package
●

シンボルの解決はリード時

●

マクロの展開はコンパイル時

オワタ(^o^)/
ボツ
普通編
Variantとパターンマッチ
let rec quicksort = function
| [] -> []
| pivot :: rest ->
let is_less x = x < pivot in
let left, right = List.partition is_less rest in
quicksort left @ [pivot] @ quicksort right

type 'a list = 'a :: 'a list | []
Variantとパターンマッチ
let rec quicksort = function
| [] -> []
コンパイルエラー
| pivot :: rest ->
let is_less x = x < pivot in
let left, right = List.partition is_less rest in
quicksort left @ [pivot] @ quicksort right

type 'a list = 'a :: 'a list | []
Variantとパターンマッチ
let rec quicksort = function
コンパイルエラー
| left , right -> left :: [right]
| [] -> []
| pivot :: rest ->
let is_less x = x < pivot in
let left, right = List.partition is_less rest in
quicksort left @ [pivot] @ quicksort right

type 'a list = 'a :: 'a list | []
Common Lispでやってみる
パターンマッチはoptimaがある
型を(defstruct)で一々定義するのは面倒
(defvariant)的な何かを…
Common Lispでやってみる
パターンマッチはoptimaがある
型を(defstruct)で一々定義するのは面倒
(defvariant)的な何かを…

やってみました
Common Lispでやってみた
赤黒木の例
type 'a tree =
| Leaf
| Red of tree * 'a * tree
| Black of tree * 'a * tree
(defvariant tree
leaf
(red tree t tree)
(black tree t tree))
gist https://gist.github.com/9225141 にサンプルコードあります
Common Lispでやってみた
赤黒木の例
type 'a tree =
| Leaf
| Red of tree * 'a * tree
| Black of tree * 'a * tree
(defvariant tree
leaf
(red tree t tree)
(black tree t tree))

(PROGN
(DEFTYPE TREE () '(OR (MEMBER LEAF) BLACK RED))
(DEFSTRUCT
(BLACK (:CONSTRUCTOR BLACK (#:TREE1014 #:T1015
#:TREE1016))
(:PRINT-OBJECT
(LAMBDA (OBJ STREAM)
(FORMAT STREAM "(~a ~{~a~^ ~})" 'BLACK
(LIST-SLOTS OBJ '(#:TREE1014 #:T1015 #:TREE1016)
(FORMAT NIL "~a-" 'BLACK))))))
(#:TREE1014 NIL :TYPE TREE)
(#:T1015 NIL :TYPE T)
(#:TREE1016 NIL :TYPE TREE))
(DEFPATTERN BLACK
(#:TREE1014 #:T1015 #:TREE1016)
(LIST 'BLACK- :TREE1014 #:TREE1014 :T1015 #:T1015 :TREE1016
#:TREE1016))
(DEFSTRUCT
(RED (:CONSTRUCTOR RED (#:TREE1017 #:T1018 #:TREE1019))
(:PRINT-OBJECT
(LAMBDA (OBJ STREAM)
(FORMAT STREAM "(~a ~{~a~^ ~})" 'RED
(LIST-SLOTS OBJ '(#:TREE1017 #:T1018 #:TREE1019)
(FORMAT NIL "~a-" 'RED))))))
(#:TREE1017 NIL :TYPE TREE)
(#:T1018 NIL :TYPE T)
(#:TREE1019 NIL :TYPE TREE))
(DEFPATTERN RED
(#:TREE1017 #:T1018 #:TREE1019)
(LIST 'RED- :TREE1017 #:TREE1017 :T1018 #:T1018 :TREE1019
#:TREE1019)))

gist https://gist.github.com/9225141 にサンプルコードあります
Common Lispでやってみた
やってること
●

型の定義

●

構造体の定義

●

プリティプリント

●

コンストラクタの定義

●

●

構造体のスロットの型制
限
optimaのパターン定義

できなかったこと
●

match節が必要十分
でないときのエラー
|>
let (|>) data f = f data;;

()で囲まれてるのは中置演算子
● 関数適用より優先度が低い
● 左結合
●
|>
let (|>) data f = f data;;

()で囲まれてるのは中置演算子
● 関数適用より優先度が低い
● 左結合
●

# String.split ~on:':' "/usr/bin:/usr/local/bin:/bin:/sbin"
|> List.dedup ~compare:String.compare
|> List.iter ~f:print_endline
;;
/bin
/sbin
/usr/bin
/usr/local/bin

unixのパイプみたいに使える!
Lispでやってみた
Clojure
->>がある
Common Lisp
(defmacro ->> (data &rest funs)
(reduce (lambda (x y)
`(funcall ,y ,x)) funs
:initial-value data))
Scheme
(define (->> data . funs)
(fold (lambda (x y) (x y)) data funs))
以上

質問あればどうぞ

Real World OCamlを読んでLispと協調してみた