静的に解決された型パラメーターを持つインライン関数を使用して、コンテキストに応じて異なる型を生成できます。
let inline pcdata (pcdata : string) : ^U = (^U : (static member MakePCData : string -> ^U) pcdata)
let inline a (content : ^T) : ^U = (^U : (static member MakeA : ^T -> ^U) content)
let inline br () : ^U = (^U : (static member MakeBr : unit -> ^U) ())
let inline img () : ^U = (^U : (static member MakeImg : unit -> ^U) ())
let inline span (content : ^T) : ^U = (^U : (static member MakeSpan : ^T -> ^U) content)
br関数を例にとってみましょう。タイプ^Uの値が生成され、コンパイル時に静的に解決されます。これは、^Uに静的メンバーMakeBrがある場合にのみコンパイルされます。以下の例では、A_Content.BrまたはSpan_Content.Brのいずれかを生成できます。
次に、合法的なコンテンツを表す一連のタイプを定義します。それぞれが、受け入れるコンテンツの「Make」メンバーを公開します。
type A_Content =
| PCData of string
| Br
| Span of Span_Content list
static member inline MakePCData (pcdata : string) = PCData pcdata
static member inline MakeA (pcdata : string) = PCData pcdata
static member inline MakeBr () = Br
static member inline MakeSpan (pcdata : string) = Span [Span_Content.PCData pcdata]
static member inline MakeSpan content = Span content
and Span_Content =
| PCData of string
| A of A_Content list
| Br
| Img
| Span of Span_Content list
with
static member inline MakePCData (pcdata : string) = PCData pcdata
static member inline MakeA (pcdata : string) = A_Content.PCData pcdata
static member inline MakeA content = A content
static member inline MakeBr () = Br
static member inline MakeImg () = Img
static member inline MakeSpan (pcdata : string) = Span [PCData pcdata]
static member inline MakeSpan content = Span content
and Span =
| Span of Span_Content list
static member inline MakeSpan (pcdata : string) = Span [Span_Content.PCData pcdata]
static member inline MakeSpan content = Span content
その後、値を作成できます...
let _ =
test ( span "hello" )
test ( span [pcdata "hello"] )
test (
span [
br ();
span [
br ();
a [span "Click me"];
pcdata "huh?";
img () ] ] )
そこで使用されるテスト関数はXMLを出力します...このコードは、値が適切に機能することを示しています。
let rec stringOfAContent (aContent : A_Content) =
match aContent with
| A_Content.PCData pcdata -> pcdata
| A_Content.Br -> "<br />"
| A_Content.Span spanContent -> stringOfSpan (Span.Span spanContent)
and stringOfSpanContent (spanContent : Span_Content) =
match spanContent with
| Span_Content.PCData pcdata -> pcdata
| Span_Content.A aContent ->
let content = String.concat "" (List.map stringOfAContent aContent)
sprintf "<a>%s</a>" content
| Span_Content.Br -> "<br />"
| Span_Content.Img -> "<img />"
| Span_Content.Span spanContent -> stringOfSpan (Span.Span spanContent)
and stringOfSpan (span : Span) =
match span with
| Span.Span spanContent ->
let content = String.concat "" (List.map stringOfSpanContent spanContent)
sprintf "<span>%s</span>" content
let test span = printfn "span: %s\n" (stringOfSpan span)
出力は次のとおりです。
span: <span>hello</span>
span: <span><br /><span><br /><a><span>Click me</span></a>huh?<img /></span></span>
エラーメッセージは妥当なようです...
test ( div "hello" )
Error: The type 'Span' does not support any operators named 'MakeDiv'
Make関数と他の関数はインラインであるため、生成されたILは、型安全性を追加せずにこれを実装した場合に手動でコーディングするものとおそらく似ています。
同じアプローチを使用して属性を処理できます。
ブライアンが曲芸師の解決策がそうするかもしれないと指摘したように、私はそれが継ぎ目で劣化するかどうか疑問に思います。(これは曲芸師としてカウントされますか?)または、すべてのXHTMLを実装するまでにコンパイラーまたは開発者を溶かしてしまうかどうか。