答えを確認した後、私は最も邪魔にならないように見えるモデルを使用することにしました。変更されたオブジェクトを使用して、少し複雑なシナリオでどのように機能するかを示しました。
type StackDef<'a>(v : 'a, s : Stack<'a>) =
member val Length = s.Length + 1
member val Inner = v, s
and Stack<'a> =
| Empty
| Stack of StackDef<'a>
member this.Length =
match this with
| Empty -> 0
| Stack(def) -> def.Length
let Stack (v, s) = Stack(StackDef(v, s))
let (|Stack|Empty|) = function | Empty -> Empty | Stack(sd) -> Stack(sd.Inner)
//...
let example = Stack(1, Stack(2, Stack(3, Empty))).Length
- 外部の可変状態は含まれていません。
- 識別された共用体
Dish
(または例ではStack
)は引き続き存在します。
- このフィールド
length
は、ユニオン定義にはまったく表示されません。また、コンストラクターによって提供されることもありません。
- メモ化されたデータは、必要に応じてインスタンスに関連付けられます。
ただし、考えてみると、Afterthoughtなどの静的ウィーバーを使用することで、次のような方法を置き換えることができる可能性があります。
Stack<'a> =
| Empty
| Stack of 'a * Stack<'a>
[<Lazy>] //custom attribute that would work with a static weaver
member this.Length =
match this with
| Empty -> 0
| Stack(_, s) -> s.Length + 1
private readonly Lazy<int> __length
上記のコードを実行するデリゲートを使用してコンストラクターで初期化され、メソッドの実際のコンテンツを単純に呼び出すように変更します__length.Value
。F#では、おそらく非常に正当な理由で、共用体型にフィールドを含めることはできませんが、ILにそのような制限があるかどうかは非常に疑わしいです。
実際、IL操作を使用して多くのことを行うことが可能です。多分それは考えるべきことです。