SMLのファンクタに少し踏み込んだ
κeenです。SmlSharpContribにコントリビュートしてます。そこでファンクタを使う用事があったのですが少し踏み込んだ使い方をしようとしたらハマったのでメモ。
ファンクタおさらい
SMLのfunctor
はstructure
に引数がついたもので、モジュールを引数にとり、モジュールを返します。
functor List (Args : sig type elem end) =
struct
type elem = Args.elem
datatype list = Nil | Cons of elem * list
fun length Nil = 0
| length (Cons (x, xs)) = 1 + length xs
end
structure IntList = List(struct type elem = int end)
複雑なファンクタ
以前mlyaccを使った時にJoin
なる3つのモジュールを引数にとるファンクタが登場したのでした。
structure PrologParser =
Join(structure LrParser = LrParser
structure ParserData = PrologParserLrVals.ParserData
structure Lex = PrologLex)
これの定義を覗いてみます。
functor Join(structure Lex : LEXER
structure ParserData: PARSER_DATA
structure LrParser : LR_PARSER
sharing ParserData.LrTable = LrParser.LrTable
sharing ParserData.Token = LrParser.Token
sharing type Lex.UserDeclarations.svalue = ParserData.svalue
sharing type Lex.UserDeclarations.pos = ParserData.pos
sharing type Lex.UserDeclarations.token = ParserData.Token.token)
: PARSER =
...
複数のモジュールの他にsharing
なるキーワードも出てきています。それにstructure
キーワードもプリフィクスされています。
先程の例とは大分離れてますね。何があったのでしょう。structure
を付けとけば複数書ける…?
省略記法
実はファンクタの引数の中では省略記法が使えます。引数のモジュール名とsig ... end
が省略可能なのです。さらに適用の時もstruct ... end
も省略可能なのです。
つまり、最初の例はこうも書けるのです。
functor List (type elem) =
struct
type elem = Args.elem
datatype list = Nil | Cons of elem * list
fun length Nil = 0
| length (Cons (x, xs)) = 1 + length xs
end
structure IntList = List(type elem = int)
モジュール内モジュールと省略記法
そうです。複雑怪奇なJoin
ファンクタは省略記法で書かれていたのでした。省略せずに書くと
structure PrologParser =
Join(struct
structure LrParser = LrParser
structure ParserData = PrologParserLrVals.ParserData
structure Lex = PrologLex
end)
functor Join(X: sig
structure Lex : LEXER
structure ParserData: PARSER_DATA
structure LrParser : LR_PARSER
sharing ParserData.LrTable = LrParser.LrTable
sharing ParserData.Token = LrParser.Token
sharing type Lex.UserDeclarations.svalue = ParserData.svalue
sharing type Lex.UserDeclarations.pos = ParserData.pos
sharing type Lex.UserDeclarations.token = ParserData.Token.token
end)
: PARSER =
...
となります。形式的には引数のモジュールは1つでありながら事実上複数のモジュールを渡していたのです。structure
が付いていたのはモジュール内モジュールだったから、sharing
はモジュール内モジュールに対する制約宣言です。
なぜこれでハマったかというとSML#のインターフェースファイルでは省略記法が使えなかったからです。地雷の数だけ強くなれるよ♪