In Ocaml, is there a way to implement the function pop of a stack? - stack

Instead of using the module Stack, I want to build a pop function by myself.
The function I implemented is:
let pop (stack_lst:stack) = match stack_lst with
| [] -> None
| [x] -> x
| hd::tl -> hd
Soon I realize that my function only gives the top frame, however, my function does not remove the top frame from the stack. In such a sense, the frame still remains. Since OCaml gives me immutable data structure, what should I do?
In addition to my question, my data type is defined as:
location = Obj of int | Null
and
environment = (var * location) list
and
frame = Decl of environment | Call of environment * stack
and
stack = frame list

You'd just have to return a new stack without the popped element as well. Additionally, you're returning an option if the stack is empty, but not in the other branches, which I've also fixed here:
let pop (stack_lst: stack) = match stack_lst with
| [] -> (None, [])
| [x] -> (Some x, [])
| hd::tl -> (Some hd, tl)
or
let pop (stack_lst: stack) = match stack_lst with
| [] -> None
| [x] -> Some (x, [])
| hd::tl -> Some (hd, tl)

Ignoring for the moment whether it's idiomatic, or the complexity implications - OCaml does support mutable state, which could be used to address something like a stack.
# type 'a stack = { mutable lst : 'a list };;
type 'a stack = { mutable lst : 'a list; }
# let a = { lst = [1; 3; 4] };;
val a : int stack = {lst = [1; 3; 4]}
# let pop = function
| {lst=[]} -> None
| {lst=(x::xs)} as s -> s.lst <- xs; Some x;;
val pop : 'a stack -> 'a option = <fun>
# pop a;;
- : int option = Some 1
# a;;
- : int stack = {lst = [3; 4]}
# pop a;;
- : int option = Some 3
# a;;
- : int stack = {lst = [4]}
#

Related

How to avoid stack overflow in this F# program (recursive tree search)?

I've got a discriminated union tree like this:
type rbtree =
| LeafB of int
| LeafR of int
| Node of int*rbtree*rbtree
And what I have to do is to search for every LeafB present in the tree, so I came with a this recursive function:
let rec searchB (tree:rbtree) : rbtree list =
match tree with
| LeafB(n) -> LeafB(n)::searchB tree
| LeafR(n) -> []
| Node(n,left,right) -> List.append (searchB left) (searchB right)
But when I try to test it I get stack overflow exception and I have no idea how to modify it to work properly.
As #kvb says your updated version isn't truely tail-rec and might cause a stackoverflow as well.
What you can do is using continuations essentially using heap space instead of stack space.
let searchB_ tree =
let rec tail results continuation tree =
match tree with
| LeafB v -> continuation (v::results)
| LeafR _ -> continuation results
| Node (_, lt, rt) -> tail results (fun leftResults -> tail leftResults continuation rt) lt
tail [] id tree |> List.rev
If we looks at the generated code in ILSpy it looks essentially like this:
internal static a tail#13<a>(FSharpList<int> results, FSharpFunc<FSharpList<int>, a> continuation, Program.rbtree tree)
{
while (true)
{
Program.rbtree rbtree = tree;
if (rbtree is Program.rbtree.LeafR)
{
goto IL_34;
}
if (!(rbtree is Program.rbtree.Node))
{
break;
}
Program.rbtree.Node node = (Program.rbtree.Node)tree;
Program.rbtree rt = node.item3;
FSharpList<int> arg_5E_0 = results;
FSharpFunc<FSharpList<int>, a> arg_5C_0 = new Program<a>.tail#17-1(continuation, rt);
tree = node.item2;
continuation = arg_5C_0;
results = arg_5E_0;
}
Program.rbtree.LeafB leafB = (Program.rbtree.LeafB)tree;
int v = leafB.item;
return continuation.Invoke(FSharpList<int>.Cons(v, results));
IL_34:
return continuation.Invoke(results);
}
So as expected with tail recursive functions in F# it is tranformed into a while loop. If we look at the non-tail recursive function:
// Program
public static FSharpList<int> searchB(Program.rbtree tree)
{
if (tree is Program.rbtree.LeafR)
{
return FSharpList<int>.Empty;
}
if (!(tree is Program.rbtree.Node))
{
Program.rbtree.LeafB leafB = (Program.rbtree.LeafB)tree;
return FSharpList<int>.Cons(leafB.item, FSharpList<int>.Empty);
}
Program.rbtree.Node node = (Program.rbtree.Node)tree;
Program.rbtree right = node.item3;
Program.rbtree left = node.item2;
return Operators.op_Append<int>(Program.searchB(left), Program.searchB(right));
}
We see the recursive call at the end of the function Operators.op_Append<int>(Program.searchB(left), Program.searchB(right));
So the tail-recursive function allocates continuations functions instead of creating a new stack frame. We can still run out of heap but there's lot more heap than stack.
Full example demonstrating a stackoverflow:
type rbtree =
| LeafB of int
| LeafR of int
| Node of int*rbtree*rbtree
let rec searchB tree =
match tree with
| LeafB(n) -> n::[]
| LeafR(n) -> []
| Node(n,left,right) -> List.append (searchB left) (searchB right)
let searchB_ tree =
let rec tail results continuation tree =
match tree with
| LeafB v -> continuation (v::results)
| LeafR _ -> continuation results
| Node (_, lt, rt) -> tail results (fun leftResults -> tail leftResults continuation rt) lt
tail [] id tree |> List.rev
let rec genTree n =
let rec loop i t =
if i > 0 then
loop (i - 1) (Node (i, t, LeafB i))
else
t
loop n (LeafB n)
[<EntryPoint>]
let main argv =
printfn "generate left leaning tree..."
let tree = genTree 100000
printfn "tail rec"
let s = searchB_ tree
printfn "rec"
let f = searchB tree
printfn "Is equal? %A" (f = s)
0
Oh, I might came with an solution:
let rec searchB (tree:rbtree) : rbtree list =
match tree with
| LeafB(n) -> LeafB(n)::[]
| LeafR(n) -> []
| Node(n,left,right) -> List.append (searchB left) (searchB right)
Now it looks like working properly when I try it.

How do I separate case ids from case values on a Discriminated Union?

I want to build a dictionary from a list of items.
An item has the following definition:
type Item =
| A of TotalPrice * Special
| B of TotalPrice * Special
| C of TotalPrice
| D of TotalPrice
I want the keys of the dictionary to map to the case ids:
| A
| B
| C
| D
I would then have the values for the case id be a list.
How do I separate the case ids from the case values?
Example:
let dictionary = items |> List.map (fun item -> item) // uh...
Appendix:
module Checkout
(*Types*)
type UnitPrice = int
type Qty = int
type Special =
| ThreeForOneThirty
| TwoForFourtyFive
type TotalPrice = { UnitPrice:int ; Qty:int }
type Item =
| A of TotalPrice * Special
| B of TotalPrice * Special
| C of TotalPrice
| D of TotalPrice
(*Functions*)
let totalPrice (items:Item list) =
let dictionary = items |> List.map (fun item -> item) // uh...
0
(*Tests*)
open FsUnit
open NUnit.Framework
[<Test>]
let ``buying 2 A units, B unit, A unit = $160`` () =
// Setup
let items = [A ({UnitPrice=50; Qty=2} , ThreeForOneThirty)
B ({UnitPrice=30; Qty=1} , TwoForFourtyFive)
A ({UnitPrice=50; Qty=1} , ThreeForOneThirty)]
items |> totalPrice |> should equal 160
Your data is badly defined for your use case. If you want to refer to the kinds of items by themselves, you need to define them by themselves:
type ItemKind = A | B | C | D
type Item = { Kind: ItemKind; Price: TotalPrice; Special: Special option }
Then you can easily build a dictionary of items:
let dictionary = items |> List.map (fun i -> i.Kind, i) |> dict
Although I must note that such dictionary may not be possible: if the items list contains several items of the same kind, some of them will not be included in the dictionary, because it can't contain multiple identical keys. Perhaps I didn't understand what kind of dictionary you're after.
If you want to create the dictionary with keys like A, B, C and D you will fail because A and B are constructors with type TotalPrice * Special -> Item and C and D are constructors of type TotalPrice -> Item. Dictionary would have to make a decision about type of keys.
Getting DU constructor name should be doable by reflection but is it really necessary for your case?
Maybe different type structure will be more efficient for your case, ie. Fyodor Soikin proposal.
Maybe the following will clarify somewhat why datastructure and code is no good, and as such also clarify that this mainly is not related to FP as indicated in some of the comments et al.
My guess is that the question is related to "how can this be grouped", and lo and behold, there is in fact a groupBy function!
(*Types*)
type UnitPrice = int
type Qty = int
type Special =
| ThreeForOneThirty
| TwoForFourtyFive
type TotalPrice = { UnitPrice:int ; Qty:int }
type Item =
| A of TotalPrice * Special
| B of TotalPrice * Special
| C of TotalPrice
| D of TotalPrice
let items = [A ({UnitPrice=50; Qty=2} , ThreeForOneThirty)
B ({UnitPrice=30; Qty=1} , TwoForFourtyFive)
A ({UnitPrice=50; Qty=1} , ThreeForOneThirty)]
let speciallyStupidTransformation =
function
| ThreeForOneThirty -> 34130
| TwoForFourtyFive -> 2445
let stupidTransformation =
function
| A (t,s) -> "A" + (s |> speciallyStupidTransformation |> string)
| B (t,s) -> "B" + (s |> speciallyStupidTransformation |> string)
| C (t) -> "C"
| D(t) -> "D"
let someGrouping = items |> List.groupBy(stupidTransformation)
val it : (string * Item list) list =
[("A34130",
[A ({UnitPrice = 50;
Qty = 2;},ThreeForOneThirty); A ({UnitPrice = 50;
Qty = 1;},ThreeForOneThirty)]);
("B2445", [B ({UnitPrice = 30;
Qty = 1;},TwoForFourtyFive)])]
Yeah its still a bad idea. But its somewhat grouped uniquely, and may be misused further to aggregate some sums or whatever.
Adding some more code for that, like the following:
let anotherStupidTransformation =
function
| A(t,_) -> (t.UnitPrice, t.Qty)
| B(t,_) -> (t.UnitPrice, t.Qty)
| C(t) -> (t.UnitPrice, t.Qty)
| D(t) -> (t.UnitPrice, t.Qty)
let x4y x y tp q =
if q%x = 0 then y*q/x else tp/q*(q%x)+(q-q%x)/x*y
let ``34130`` = x4y 3 130
let ``2445`` = x4y 2 45
let getRealStupidTotal =
function
| (s, (tp,q)) ->
(s|> List.ofSeq, (tp,q))
|> function
| (h::t, (tp,q)) ->
match t |> List.toArray |> System.String with
| "34130" -> ``34130`` tp q
| "2445" -> ``2445`` tp q
| _ -> tp
let totalPrice =
items
|> List.groupBy(stupidTransformation)
|> List.map(fun (i, l) -> i,
l
|> List.map(anotherStupidTransformation)
|> List.unzip
||> List.fold2(fun acc e1 e2 ->
((fst acc + e1) * e2, snd acc + e2) ) (0,0))
|> List.map(getRealStupidTotal)
|> List.sum
val totalPrice : int = 160
might or might not yield some test cases correct.
For the above testdata as far as I can read the initial code at least is ok. The sum does get to be 160...
Would I use this code anywhere? Nope.
Is it readable? Nope.
Is it fixable? Not without changing the way the data are structured to avoid several of the stupid transformations...

How to write code in F# for what functors do in OCaml?

I have many programs written in OCaml, some of them use functors. Now, I am considering of writing and re-writing a part of code in F# (to benefit some advantages that OCaml does not have). One thing I am afraid of is to write code in F# for what functors do in OCaml.
For instance, how could we emulate this example from OCaml manual in F#?
type comparison = Less | Equal | Greater
module type ORDERED_TYPE = sig
type t
val compare: t -> t -> comparison
end
module Set =
functor (Elt: ORDERED_TYPE) -> struct
type element = Elt.t
type set = element list
let empty = []
let rec add x s =
match s with
[] -> [x]
| hd::tl ->
match Elt.compare x hd with
Equal -> s (* x is already in s *)
| Less -> x :: s (* x is smaller than all elements of s *)
| Greater -> hd :: add x tl
end
module OrderedString = struct
type t = string
let compare x y = if x = y then Equal else if x < y then Less else Greater
end
module OrderedInt = struct
type t = int
let compare x y = if x = y then Equal else if x < y then Less else Greater
end
module StringSet = Set(OrderedString)
module IntSet = Set(OrderedInt)
let try1 () = StringSet.add "foo" StringSet.empty
let try2 () = IntSet.add 2 IntSet.empty
Here is a bit different approach that achieves same outcome using a generic class and one object per type.
type Comparison = Less | Equal | Greater
type Set<'a>(compare : 'a -> 'a -> Comparison) =
member this.Empty : 'a list = []
member this.Add x s =
match s with
| [] -> [x]
| hd::tl ->
match compare x hd with
| Equal -> s (* x is already in s *)
| Less -> x :: s (* x is smaller than all elements of s *)
| Greater -> hd :: this.Add x tl
let compare x y = if x = y then Equal else if x < y then Less else Greater
let compareFloats (x : float) (y : float) = if x = y then Equal else if x < y then Less else Greater
// Note that same generic compare function can be used for stringSet and intSet
// as long as the type parameter is explicitly given
let stringSet = Set<string>(compare)
let intSet = Set<int>(compare)
// Type parameter not needed, because compareFloats is not generic
let floatSet = Set(compareFloats)
let try1 () = stringSet.Add "foo" stringSet.Empty // -> ["foo"]
let try2 () = intSet.Add 2 intSet.Empty // -> [2]
let try3 () = floatSet.Add 3.0 floatSet.Empty // -> [3.0]
As you noticed, F# doesn't have functors - F# modules cannot be parameterized by types. You can get similar results in F# using the object oriented parts of the language - interfaces, generic classes and inheritance.
Here's a heavy handed approach at emulating your example.
type Comparison = Less | Equal | Greater
/// Interface corresponding to ORDERED_TYPE signature
type IOrderedType<'a> =
abstract Value: 'a
abstract Compare: IOrderedType<'a> -> Comparison
/// Type that implements ORDERED_TYPE signature, different instantiations
/// of this type correspond to your OrderedInt/OrderedString modules.
/// The 't: comparison constraint comes from the fact that (<) operator
/// is used in the body of Compare.
type Ordered<'t when 't: comparison> (t: 't) =
interface IOrderedType<'t> with
member this.Value = t
member this.Compare (other: IOrderedType<'t>) =
if t = other.Value then Equal else if t < other.Value then Less else Greater
/// A generic type that works over instances of IOrderedType interface.
type Set<'t, 'ot when 't: comparison and 'ot :> IOrderedType<'t>> (coll: IOrderedType<'t> list) =
member this.Values =
coll |> List.map (fun x -> x.Value)
member this.Add(x: 't) =
let rec add (x: IOrderedType<'t>) s =
match coll with
| [] -> [x]
| hd::tl ->
match x.Compare(hd) with
| Equal -> s (* x is already in s *)
| Less -> x :: s (* x is smaller than all elements of s *)
| Greater -> hd :: add x tl
Set<'t, 'ot>(add (Ordered(x)) coll)
static member Empty = Set<'t, 'ot>(List.empty)
/// A helper function for Set.Add. Useful in pipelines.
module Set =
let add x (s: Set<_,_>) =
s.Add(x)
/// Type aliases for different instantiations of Set
/// (these could have easily been subtypes of Set as well)
type StringSet = Set<string, Ordered<string>>
type IntSet = Set<int, Ordered<int>>
let try1 () = Set.add "foo" StringSet.Empty
let try2 () = Set.add 2 IntSet.Empty
try1().Values
try2().Values
The functional way in F# would rely mostly on type inference avoiding OOP structures like interface or types with member.
type Comparison = Less | Equal | Greater
type OrderedSet<'t> = 't list // type alias, not really necessary
module OrderedSet =
let empty : OrderedSet<_> = List.empty // just an empty list
let values (s : OrderedSet<_>) : OrderedSet<_> = s // identity function
let add compare x (s : OrderedSet<_>) : OrderedSet<_> =
let rec addR s =
match s with
| [] -> [x]
| hd::tl ->
match compare x hd with
| Equal -> s (* x is already in s *)
| Less -> x :: s (* x is smaller than all elements of s *)
| Greater -> hd :: addR tl
addR s
let compare x y = if x = y then Equal else if x < y then Less else Greater
let compareFloats (x : float) y = if x = y then Equal else if x < y then Less else Greater
let addGeneric v = add compare v
let addFloat v = add compareFloats v
And it is used like this:
let try1 () = OrderedSet.addGeneric "foo" OrderedSet.empty |> OrderedSet.addGeneric "bar"
let try2 () = OrderedSet.addGeneric 2 OrderedSet.empty |> OrderedSet.addGeneric 3
let try3 () = OrderedSet.empty
|> OrderedSet.addFloat 3.0
|> OrderedSet.addFloat 1.0
|> OrderedSet.addFloat 2.0
try1() |> printfn "%A" // OrderedSet<string> = ["bar"; "foo"]
try2() |> printfn "%A" // OrderedSet<int> = [2; 3]
try3() |> printfn "%A" // OrderedSet<float> = [1.0; 2.0; 3.0]
The type alias type OrderedSet<'t> = 't list and the functions empty and values are not really necessary but they help to mask the actual implementation (in case that is desirable).

What is wrong in F# code?

In F# I am trying to get last element of give list. I wrote below code
let rec findLast t =
match t with
| hd :: [] -> hd
| hd :: tl -> findLast tl
| _ -> -1
printfn "%A" (findLast [1,2,3,4,5])
But when I tried to execute it in F# Interactive it complain as below
error FS0001: This expression was expected to have type
int but here has type
'a * 'b * 'c * 'd * 'e
I just want to know what is wrong in above code. I know there are different smart and elegant ways to get last element from list in F#. But I am interested to know what is wrong in above code ?
1,2,3,4,5 is a tuple. 'a * 'b * 'c * 'd * 'e is a tuple definition. Create a list with semicolons [1;2;3;4;5]. [1,2,3,4,5] is a list of tuples with one item which is a quintuple.
let rec findLast t =
match t with
| hd :: [] -> hd
| hd :: tl -> findLast tl
| _ -> -1
printfn "%A" (findLast [1;2;3;4;5])
Try this one:
let rec lastElem = function
| [] -> None
| [x] -> Some x
| x::xs -> lastElem xs
You can try it in the REPL:
> lastElem [1;2;3];;
val it : int option = Some 3
> lastElem ["a";"b";"c"];;
val it : string option = Some "c"
As #Phillip-Scott-Givens pointed out, you've likely made a totally common (especially for C#'ers), error and used a comma to separate a list instead of a semi-colon.
This results in a tuple list [(1, 2, 3, 4, 5)] and not an integer list [1;2;3;4;5]. Getting unexpected asterisks in your type definitions is a symptom of this :)
That said, here a few different functions that get the last value from your tuple, list, and tuple list (ref: https://stackoverflow.com/a/1175123/5470873):
// Data:
let tuples = [ (1,2,3,4,5); ] // = [1,2,3,4,5]
let firstListElement = tuples.[0]
// Access:
let rec lastItemInList = function
| hd :: [] -> hd
| hd :: tl -> lastItemInList tl
| _ -> failwith "Empty list."
let lastValueOfFirstItem = function
| (_, _, _, _, last) :: _ -> last
| _ -> -1
let lastValueOfTuple = function _, _, _, _, last -> last
// same as: let lastValueOfTuple myTuple =
// match myTuple with
// | (_, _, _, _, last) -> last
// Examples:
tuples |> lastItemInList // val it : int * int * int * int * int = (1, 2, 3, 4, 5)
tuples |> lastValueOfFirstItem // val it : int = 5
tuples |> List.map lastValueOfTuple // val it : int list = [5]
firstListElement |> lastValueOfTuple // val it : int = 5

Strictly compute and memoize in discriminated unions, in F#

I have a discriminated union, such as
type Dish =
| Eggs
| Spam of Dish
This is basically a linked list, without any content, e.g. Spam(Spam(Spam(Eggs))). I want to strictly perform a computation on this structure, such as counting the length, and memorize the result. In a normal type, I'd use class-local let bindings, but those aren't available in discriminated unions.
One way to do this would be,
type Count = int
type Dish =
| Eggs
| Spam of Dish * Count
But this is really messy, when the data I need is easily computable, but I still hope there is a better way (without using external mutable constructs).
One option is making the union cases private to hide the cached length.
//the 'guts' of Dish -- entirely hidden
type private DishImpl =
| Eggs
| Spam of DishImpl
// Dish wrapper type -- implementation hidden
type Dish =
private
| Dish of DishImpl * int
with
// O(1), just get the 'length' field
member x.Length = let (Dish(_, len)) = x in len
static member Eggs() = Dish(Eggs, 1)
static member Spam(Dish(dish, len)) = Dish(Spam dish, len + 1)
let eggs = Dish.Eggs()
let spam = Dish.Spam(eggs)
printfn "%d" eggs.Length //outputs: 1
printfn "%d" spam.Length //outputs: 2
To do it up right, create an accompanying module with let-bound functions and active patterns for destructuring.
If you tolerate a bit internal mutable state, here is a memoize function which creates a dictionary per function:
let memoize f =
let dict = Dictionary()
fun n ->
match dict.TryGetValue(n) with
| (true, v) -> v
| _ ->
let res = f(n)
dict.Add(n, res)
res
// This function results in a warning though
let rec length = memoize (function Eggs -> 0 | Spam d -> 1 + length d)
The approach isn't that bad since the mutable dictionary is hidden.
A purely functional approach could be using Map to hold values and a kind of State computation expression to hide Map values passing around. Please refer to this snippet to see how a memoize computation expression looks like.
There is also Memo Functions, Polytypically! by Ralph Hinze (2000). Adapting to F#:
type Dish =
| Eggs
| Spam of Dish
type DishTable<'T> =
{
Eggs : Lazy<'T>
Spam : Lazy<DishTable<'T>>
}
let rec tabulate (f: Dish -> 'T) : DishTable<'T> =
{
Eggs = lazy f Eggs
Spam = lazy tabulate (f << Spam)
}
let rec lookup (table: DishTable<'T>) (dish: Dish) : 'T =
match dish with
| Eggs -> table.Eggs.Value
| Spam x -> lookup table.Spam.Value x
let memo (f: Dish -> 'T) : (Dish -> 'T) =
lookup (tabulate f)
let rec len x =
match x with
| Eggs -> 0
| Spam x -> 1 + len x
let l2 = memo len
This is what I came up with. It's not true memoization because it counts eagerly when you call mem, but might work for your needs.
type Dish =
| Eggs
| Spam of Dish
| Memo of Dish * int
with
member d.length =
match d with
| Eggs -> 1
| Spam d -> 1 + d.length
| Memo (d, i) -> i
member d.mem =
match d with
| Eggs -> Memo(d, d.length)
| Spam d2 -> Memo(d, d.length)
| Memo(d2, i) -> d // no need to memo it again
let x = Spam (Spam(Spam Eggs))
let m = x.mem
x.length // val it : int = 4
m.length // val it : int = 4
Note that in your case, literally the only interesting property of a value of your type is its length, so you might as well just use integers as your representation instead:
let Eggs = 0
let Spam n = 1 + n
let (|Eggs|Spam|) = function
| 0 -> Eggs
| n -> Spam(n-1)
let length = id
// example usage
let dish = Spam(Spam(Eggs))
let l = length dish
let kind =
match dish with
| Eggs -> "Eggs"
| Spam(Eggs) -> "One Spam"
| Spam(Spam _) -> "At least two Spams"
If your real question is how to do this for a more interesting type, then one approach would be to create mutually recursive types, one of which is annotated:
type 'a AnnotatedDish = { dish : 'a Dish; value : 'a }
and 'a Dish =
| Eggs
| Spam of 'a AnnotatedDish
// "smart" constructors, given that you want to annotate with length
let eggs = { dish = Eggs; value = 0 }
let spam d = { dish = Spam d; value = 1 + d.value }
let length { value = l } : int = l
// active patterns
let (|Eggs|Spam|) = function
| { dish = Eggs } -> Eggs
| { dish = Spam d } -> Spam d
// example usage
let dish = spam(spam(eggs))
let l = length dish
let kind =
match dish with
| Eggs -> "Eggs"
| Spam(Eggs) -> "One Spam"
| Spam(Spam _) -> "At least two Spams"
After reviewing the answers, I've decided to go with a model that seems the least obtrusive to me. I've used a modified object to demonstrate how it would work in a slightly more complex scenario.
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
It doesn't contain any external mutable state.
The discriminated union Dish (or in the example, Stack) continues to exist.
The field length doesn't appear in the union definition at all, nor is it provided by any constructor, just as it should be.
The memoized data is associated with the instance, as it should be.
However, having thought about it, by using a static weaver such as Afterthought it might be possible to replace any method such as:
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
With a private readonly Lazy<int> __length initialized in the constructor with a delegate that executes the above code, and change the actual content of the method to simply invoking __length.Value.
While F# doesn't allow union types to contain fields, possibly for very valid reasons, I highly doubt the IL would have such restrictions.
In fact, it would be possible to do a lot of things using some IL manipulation. Maybe it's something to think about.

Resources