---
# System prepended metadata

title: ppx_fillup

---

# ppx_fillup

## 2022/9/6
- moduleの中身をインスタンスに追加する構文の動作確認
    - Parsetree の印字(examples/parsetree_sample)
- todo 
    - [ ] テストケースを作る
        - 行数の大きいリポジトリに対してPPXが使えそうな場所を書き換え
        - janestreet Coreを使用しているライブラリを探す
        - (2022/10/6 伊藤追記)　テストの流れ:
        OCamlプロジェクト内のmlファイルをなめる(`find`コマンド)
        -> すべてParse 
        -> 以下のマクロに渡す
    - [ ] プログラムをfillup向けに変換するマクロ
        - 例)Format.asprintfにfillupを利用 -> Format.formatter型を探して##に書き換えて出力する
        https://scrapbox.io/ctlab/opam_%E6%8E%A2%E7%B4%A2
        - (2022/10/6 伊藤追記)
        `Ppxlib.Driver.standalone ()`でppxを実行ファイルとして生成
        https://tarides.com/blog/2019-05-09-an-introduction-to-ocaml-ppx-ecosystem#testing-your-ppx
        - (2022/10/11 伊藤追記)マクロの機能
        - [x] `Pprintast._` の変換
        - [ ] リポジトリ全体にマクロを通すための機能(stdinなど？)
            - [ ] mlファイルを検索しファイル名をファイル(txt?)に出力するマクロ(sh)
            - [ ] ファイルの中身を標準入力するマクロ(dune)
            - [ ] 入力されたmlファイルをppxに通すマクロ(ppx,dune)
    - [x] ##が二項演算にしか対応できないことを回避
        - 他の記号(!!とか)を使う
        - (2022/9/29 伊藤追記) `!!0`を使う(やや無理がある？)
            - 実行例
                ```ocaml  
                Format.printf "%a\n" !!0 [%expr 42]
                ```
            - この文法ならインスタンスを制限できる？`print !!show 123`など
        - (2022/10/6 伊藤追記) `__`に修正
            ```ocaml  
            Format.printf "%a\n" __ [%expr 42]
            ```
    - [x] github action を成功させる
        - https://scrapbox.io/ctlab/OCaml_(typed)_PPX_tips

## 2022/07/29
- 多相型の場合
    - option型
    ```ocaml
    show ## None; (* -> エラー *)
    ```
    - list
    ```ocaml 
    show ## [] (* -> エラー *)
    ```
- 流石にこの辺りは明示的なエラーor回避手段を用意して対応すべき？

## 2022/07/24
- インスタンスの扱い
    - トップレベルで宣言したインスタンスは普通の式としても使える
    ```ocaml
    let show_int[@instance] = ...
    
    (* どっちもOK *)
    print show_int 123
    print ## 123 
    ```
- モジュール展開の時どうするか
    - 既存ライブラリのモジュール(X)の中身をすべてインスタンスとみなして openする構文
    ```ocaml
    module M = struct
        let show_int = ...
    end
    
    open%fillup M
    print ## 123  (* もちろんOK *)
    print show_int 123  (* これを認めるか *)
    ```
    - 普通の open ではなく、インスタンスのみスコープに導入する open 構文
    ```ocaml
    module M = struct
        let show_int[@instance] = ...
        let show_float = ...
    end
    
    open%instance M
    print ## 123  (* もちろんOK *)
    print show_int 123  (* これを認めるか *)
    
    (* 以下はどちらも認めない 
    print show_float 1.23  
    print ## 1.23  
    *)
    ```

## 2022/05/23
- 新機能の [プルリク](https://github.com/masaki110/ppx_fillup/pull/3) を切った 


- ppx_fillupの改善方針
    - [`resolve_instance`](https://github.com/masaki110/ppx_fillup/blob/future/src/main.ml#L73-L109) では環境内の全要素を探索している
    --> `Env.fold_values` を利用して環境内の変数だけを探索させる
    - `Env.fold_modules` を利用してfillup用のモジュール(ダミーのモジュール)名を探索し、その中身も探索する
        - インスタンスのリストに加える
        - ダミーのモジュール以外から探索したあと、ダミーのモジュール内を探索する
- (伊藤) 
    - [make_instances](https://github.com/masaki110/ppx_fillup/blob/ca2265b/src/main.ml#L74-L105) の修正
    ```ocaml
    let make_instances env =
      (* Path.t -> Ident.t の変換 *)
      let ident_of_path =
        Path.(
          function
          | Pident id -> id (* --> そのまま *)
          | Pdot (_, s) -> Ident.create_local s (* --> openされたモジュール内の式をIdent.t型に変換 *)
          | _ -> assert false)
      in
      Env.fold_values
        (fun _ path desc acc ->
          if is_instance desc then (ident_of_path path, desc) :: acc else acc)
        None env []
    ```

## 2022/05/16

- ホワイトボード議論ログ
    - どうやって特殊open (インスタンスのみスコープに入れるopen) を実現するか？
    - ![](https://i.imgur.com/SwWfijX.png)
- 特殊openはグローバル?ローカル？両方?
    - 両方あってもいい気がする

- どういう構文になるか？
    - 以下のようであるっぽい

```ocaml
open%fill M;;

let f x =
    let open%fill M2 in
    ...
```

==>

```
[%%fill open M]

let f x =
    [%fill let open M2 in ...]
```

(伊藤)PPXメモ
ppxlibのドライバ関数で以下のようにextensionを定義するとき、
```ocaml
let () =
    Driver.register_transformation 
    ~extensions:[ hole; open_instance ]
    "ppx_fillup"

```
extensionの宣言

```ocaml

let hole =
  Extension.declare 
    "HOLE" 
    Extension.Context.expression
    Ast_pattern.(pstr nil) (* <- これ *)
    (fun ~loc ~path:_ -> ...)
```
Ast_patternの参考 : https://github.com/ocaml-ppx/ppxlib/blob/main/metaquot/ppxlib_metaquot.ml#L122-L142
## 2022/05/12 (伊藤)
- 問題
    インナー引数を取るようなインスタンスで、
    例えばOption型の場合、
    ```ocaml
    let (show_option [@instance]) =
     fun inst -> function
      | None -> "None"
      | Some i -> "Some " ^ inst i
    ```
    このように定義すると、`show ## None;`で`Instance not found:'a -> string`でエラー。
    - 解決法
        - インスタンス定義で工夫
        - `instance not found`の条件を考える
- ソースコードは変えてないのにローカル宣言できるようになってる！
    - openもできる
    - 前後で変わったこと → ocaml のソースツリーを見られるようにした
    - (今井) OCamlのバージョンを変えた？ → 変わってない

## 2022/05/09

- GitHub Actions を試してみよう！
    - マニュアル：https://docs.github.com/ja/actions
    - 今井が使っているもの: 
        - https://github.com/keigoi/ocaml-mpst/tree/main/.github/workflows
        - これみたいな設定を書く
    - runs-on:
        - https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job
        - ubuntu-latest など
    - steps, uses:
        - GitHub Marketplace で action を探す
        - 例: actions/checkout は https://github.com/marketplace/actions/checkout
        - 例: ocaml/setup-ocaml は https://github.com/marketplace/actions/set-up-ocaml

- これの問題
    - let open したものが exp_env に入っていない。
        - できていない。`Typedtree.expression` のフィールド `exp_env : Env.summary` には `let .. in` や `let open` で導入した値の情報が入っていない？
        - `Env_value(s, ident, desc) : Env.summary` に入っていることを期待していた
    - どうやって追いかけたものか？何かうまい方法はないか？
    - ppx_implicits のコードを追ってみる？ https://github.com/Pitometsu/ppx_implicits
        - あまり参考にならないっぽい
    - OCaml のコードを追ってみた
    - Env_open を探せばよさそう⇒あまり参考にならず…
    - env.mli に find_values という関数がある！
        - 現在の最新: https://github.com/ocaml/ocaml/blob/0374f64//typing/env.ml#L1066
        - 4.12: https://github.com/ocaml/ocaml/blob/4.12/typing/env.ml#L923
        - env.values を引いている！
        - この方法は参考になりそう
- OCaml のソースツリーを VSCode で快適にブラウジングするには？
    - discuss.ocaml.org で聞く
    - https://discuss.ocaml.org/
    - 質問に使えそうな単語/フレーズ: 
        - Browsing OCaml source tree with VSCode/merlin?
        - Hello, is there any proper way to browsing/exploring OCaml compiler source tree itsel? I tried browsing the tree (having `make world.opt` done) with VSCode, but I get a lot of type errors in ...
- Next:
    - [ ] GitHub Actions がエラーなく走るように
    - [ ] let open したやつも fill できるように
    - [x] ↑OCamlの質問 
        - 追記:投稿しました → https://discuss.ocaml.org/t/browsing-ocaml-source-tree-with-vscode-merlin/9819
- 次回
    - 5/16(月) 9:30-
- 論文紹介は？
    - Very Gentle MPST か Gentle MPST



## 2022/05/02

- Camler workshop で GADT の例をやった
- `[%HOLE] sum /. [%HOLE] count` のように二項演算子でも ad hoc poly の恩恵を得たい！
    - 変換元が int, 変換先が float と分かっているならうれしさが少ない


## 2022/04/26

- GitHub Flow の方法をやってみた (https://github.com/masaki110/ppx_fillup/pull/1)
- ローカルインスタンスができていない件 https://github.com/masaki110/ppx_fillup/issues/2
- 次：「Task 2. 既存ライブラリのあるモジュール(X)の中身をすべてインスタンスとみなして openする構文を追加する」


- そのほか
    - OCaml の型検査器のコードを読んで知見を得たい
    - OCamlコンパイラのソースコードを VSCode ブラウズする方式を知りたい

- 次: 2022/5/2 10:00-

## 2022/04/21

TODO

- [x] 追加機能を設計する。
- [ ] 卒論を PROフォーマットに流し込む。
- [x] テストを書く。 ppx_deriving の OUnit を使ったテストが参考になりそう。
- [ ] ↓ の追加機能を実装していく。
    - [ ] 既存ライブラリのあるモジュール(X)の中身をすべてインスタンスとみなして openする構文を追加する
    - [ ] 普通の open ではなく、インスタンスのみスコープに導入する open 構文を追加する
    - [x] インスタンスをローカルに宣言できるようにする 
        - できていない。`Typedtree.expression` のフィールド `exp_env : Env.summary` には `let .. in` や `let open` で導入した値の情報が入っていない？
        - `Env_value(s, ident, desc) : Env.summary` に入っていることを期待していた
- [ ] 利用例 (examples/) を充実させる。
- [ ] caml-list で公開する。


----

# 議論と設計

## 何が本質か？

- 型クラスやimplicitsとはどう違うのか？
    - `##` を書き込んだところにインスタンスが渡される
        - Haskell では？
            - 暗黙的にインスタンスが渡される

- インスタンスの定義方式は Scalaの implicits と同様
    - クラス名がない / スコープで制御

- インスタンスの使用方式にオリジナリティがある
    - Haskell / Scala / modular implicits: 
        - 関数定義の型にインスタンス挿入箇所を明示することで挿入を自動化
    - ppx_fillup:
        - 関数の使用場所にインスタンス挿入箇所を明示



## 他のやり方はないか？

- 例： `##` を書かせつつ、インスタンスの宣言は Haskell方式 / Scala方式

- 型クラスにおける「クラス名」は必要ないのか？
    - 必要なさそう。例： Scala の implicits はスコープで制御

## 追加機能 (was: ppx_fillupをどのように良くできるか？)

- Scala の implicits でのプログラミング技法を使えるようにする

### 1. インスタンスをローカルに宣言できるようにする
例：
```ocaml
let print_int =
 let show_int[@instance] = ... in
 let print showable x = ... in
 print ## x (* ## -> show_int *)
```
-> ローカルに定義した識別子は exp_env で拾えない - 要検討 
    -> なぜかいけた(5/12)
### 2. 既存ライブラリのあるモジュール(X)の中身をすべてインスタンスとみなして openする構文を追加する
例：
```ocaml
module Show = struct 
 let show_int = ...
 let show_float = ...
end

(* add all variable (show_int and show_float) in module X *)
open Show[@@open_as_instance]
(*もしくは*)
[%%include_instances Show]


(*これに加えて, 以下の let open みたいな構文も欲しいが…*)
let () =
    let open Show[@open_as_instance] in ...
    
--> ローカルに定義した識別子が texp_env で拾えない問題にぶちあたりそう

```

### 3. 普通の open ではなく、インスタンスのみスコープに導入する open 構文を追加する

例：
```ocaml
module X = struct 
 let foo[@instance] = ...
 let bar = ...
end

(* add only foo[@instance] in module X *)
open X[@@instance]
```


## 比較

### Haskell + type classes

```haskell 
class Show a where
  show :: a -> String
instance Show Int where
  show = showInt

print x = putStrLn (show a)

main = print 123
```

### OCaml + ppx_fillup

```ocaml 
let (show_int[@instance]) = string_of_int

(* Haskellっぽく書くなら *)
(* instance (int -> string) = string_of_int *)
(* instance (float -> string) = string_of_float *)

let print inst v = print_endline (inst v)

let () = print ## 123 
```

### OCaml + modular implicits

```ocaml
module type SHOW = sig
  type t
  val show : t -> string
end
implicit module ShowInt = struct
  type t = int
  let show = string_of_int
end
let print {X : Show} x = print_endline (X.show x)

let () = print 123
```

## PROS (利点)

- 既存の定義 (ここでは `print` 関数) に手を加えずに擬似的な多重定義が可能
    - 既存のライブラリを簡単に多重定義関数のように使える
        - Modular implicitsだとimplicits module

- Modular implicitsと違いモジュールの定義が必要ないので記述量が少ない

## CONS (欠点)

- 関数呼び出しごとに`##`を記述する必要がある


## implicits で既存のライブラリをラップしようとするとどうなるか

```ocaml
(* 既存のライブラリ *)
let print inst v = print_endline (inst v)

(* 既存のライブラリのラッパー *)
(* たくさん書かないといけない *)
module type SHOW = sig
  type t
  val show : t -> string
end
implicit module ShowInt = struct
  type t = int
  let show = string_of_int
end

let print2 {X : Show} x = print X.show x

let () = print2 123
```
