4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

MLAdvent Calendar 2020

Day 15

OCaml Upcoming Changes 2020: __FUNCTION__

Posted at

この記事はML Advent Calendar 2020 15日目の記事です。

OCamlにそろそろ入りそうな機能を紹介します。

今回は、次のOCaml 4.12で入る Add primitive __FUNCTION__ that returns the name of current scope by nojb · Pull Request #9554 · ocaml/ocaml の紹介をします。


OCamlのStdlibには__FILE__, __LINE__, __POS__, __LOC__, __MODULE__ といった、現在のソースコード上の位置や、現在のモジュール名を取得するための値が用意されている。

このPRではさらに、 __FUNCTION__ という、現在のスコープ名を文字列として取得する値を導入する。このとき得られる文字列は、OCaml 4.11.0以降でバックトレースに表示される関数名と同じ形式だ(Print function names in backtraces by stedolan · Pull Request #9096 · ocaml/ocaml を参照)。ちなみに、 __FUNCTION__ という名前にもかかわらず、現在定義中の変数の名前や、メソッドの名前も取ることができる。

具体的にどのような名前が得られるかを見てみよう。

locs.ml
let f = __FUNCTION__

module Mod1 = struct
  module Nested = struct
    let f () = __FUNCTION__
  end
end

let anon () =
  Fun.id (fun () -> __FUNCTION__) ()

let local_module () =
  let module N = struct
    let foo () = __FUNCTION__
  end
  in N.foo ()

class klass = object
    method meth () = __FUNCTION__
end

let () =
  print_endline @@ f;
  (*
    Locs.f
    変数定義の右辺の場合は、現在定義中の変数名が取れる。
    先頭のLocsはこのファイルがLocsモジュールになることに由来する。以下同じ
   *)

  print_endline @@ Mod1.Nested.f ();
  (*
    Locs.Mod1.Nested.f
    モジュールを入れ子にするとそれがパス風に付加される
   *)

  print_endline @@ anon ();
  (*
    Locs.anon.(fun)
    関数anon内の無名関数。無名関数は (fun) で表現する
   *)

  print_endline @@ local_module ();
  (*
    Locs.local_module.N.foo
    関数 local_module 内のローカルモジュールN、さらにその中の関数foo
   *)

  print_endline @@ (new klass)#meth ();
  (*
    Locs.klass#meth
    klassクラスのメソッドmeth
   *)

  ()

モジュール内の要素を参照するときのモジュールパスと似たような形式になっている。ocaml/testsuite/tests/translprim/locs.ml, ocaml/testsuite/tests/translprim/locs.reference にはより複雑な例がある。

ちなみに、この機能はコンパイラプリミティブとして提供されているので、ユーザーレベルで同じような機能を実現することはできない。おおまかな流れとしては以下のように Typedtree から Lambda に致る段階で識別子の参照から文字列定数へ置き換えられている(ParsetreeTypedtreeLambda はコンパイラの中間表現の名前)。

  1. ソースコード: __FUNCTION__
    ↓ 構文解析
  2. Parsetree: Pexp_ident "__FUNCTION__"
    ↓ 型推論・型検査
  3. Typedtree: Texp_ident "Stdlib!.__FUNCTION__"
    ↓ パターンマッチのコンパイル。モジュール、クラスの除去。 __FUNCTION__ 等プリミティブの変換
  4. Lambda: Lconst (Const_immstring "Locs.f")
    ↓ バイトコード/ネイティブコード生成へ

各ステップの様子は ocamlc -dparsetree -dtypedtree -dlambda のようにしてコンパイラを実行すると見ることができる。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?