Search code examples
f#mutablediscriminated-union

Mutable internal value in discriminated union


The task is to create a binary tree with its nodes having a member is_locked that can be true or false and a method lock() that shall set is_locked to true if it's not already.

I tried this

type BinaryTreeNode =
    | Node of BinaryTreeNode * BinaryTreeNode
    | End
    with
        let mutable internalIsLocked = false
        member this.is_locked
            with get() = internalIsLocked 
        member this.lock() = internalIsLocked <- true

but compiler says no due to This declaration element is not permitted in an augmentation at the let binding.

val mutable internalIsLocked : bool instead does not work for the same reason.

How can this problem be solved then? Is it not solvable with discriminated unions?


Solution

  • If you really want to do this, you can move the mutable state into a separate class type, and then use that type in your discriminated union:

    type Lock() =
        let mutable internalIsLocked = false
        member this.is_locked
            with get() = internalIsLocked 
        member this.lock() = internalIsLocked <- true
    
    type BinaryTreeNode =
        | Node of Lock * BinaryTreeNode * BinaryTreeNode
        | End
        with
        static member Create(left, right) =
            Node (Lock(), left, right)
        member this.Lock =
            match this with
                | Node (lock, _, _) -> Some lock
                | End -> None
    

    Note that I've assumed that End nodes are not lockable. Usage:

    let node = BinaryTreeNode.Create(End, End)
    node.Lock.Value.is_locked |> printfn "%A"   // false
    node.Lock.Value.lock()
    node.Lock.Value.is_locked |> printfn "%A"   // true
    

    However, I wouldn't recommend it.