Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to express immutability guarantees

Tags:

f#

The code below shows a situation where some record is being processed by 2 functions of the same signature (RwA -> RwA). Yet, depending on the implementation of that changer function, the data passed to it might be changed or not.

In a larger project, such things can be costly. Maybe during maintenance or during optimization, the implementation of a function is changed or more changer functions are being added, the program still looks the same, yet suddenly behaves in unexpected ways.

So the question is: Is there any way to render the code in this sample such, that it is obvious (and compiler checked), that a function of type RwAChanger may or may not change the data it is passed as a parameter?

Please do not reply "don't use arrays", as this could be large amounts of data, such as vertices of a 3D mesh or alike, where performance would be an issue. Also, I am sure it is possible to find other examples, which do not involve arrays which produce the same type of problems.

// A record with arrays...
type RwA =
    {
        A : int array
        I : int array
    }

let x = [| for i in 0..10 -> i |]

let init a =
    { A = a; I = Array.map (fun v -> -v ) a }

// This function creates a new array as member A in RwA
let change (o : RwA) : RwA =
    { o with A = Array.map (fun v -> v + 42) o.A }

// This function modifies the value of the array in member A of an RwA instance.
let change2 (o: RwA) : RwA =
    o.A.[0] <- 666
    o

let dump (o : RwA) =
    printfn "{ A = %A; I = %A; }" o.A o.I
    o

let dumpA (a : int array) : int array =
    printfn "x = %A" x
    a

// Is there a way to express a contract about immutability?
type RwAChanger = RwA -> RwA

let transmogrify (changer : RwAChanger) a =
    a
    |> dumpA
    |> init
    |> dump
    |> changer
    |> dump
    |> ignore

let test() = 
    transmogrify change x
    dumpA x |> ignore
    transmogrify change2 x    
    dumpA x |> ignore

do test()
like image 266
BitTickler Avatar asked Feb 14 '26 09:02

BitTickler


1 Answers

I don't think there's a way to tell the compiler that an existing mutable type should be made immutable, nor use Code Contracts as @mydogisbox mentions in a comment.

You may however be able to use an ImmutableArray<T> from the Microsoft.Bcl.Immutable package in your RwA record. The performance difference for array access should be minimal.

like image 128
Honza Brestan Avatar answered Feb 16 '26 00:02

Honza Brestan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!