Search code examples
referenceocamlrecordmutable

Is there a way to obtain a 'reference' to a mutable struct field


So I have a record type with mutable field:

type mpoint = { mutable x:int ; mutable y: int };;
let apoint = { x=3 ; y=4};;

And I have a function that expects a 'ref' and does something to its contents. For example:

let increment x = x := !x+1;;
val increment : int ref -> unit = <fun>

Is there a way to get a 'reference' from a mutable field so that I can pass it to the function. I.e. I want to do something like:

increment apoint.x;; (* increment value of the x field 'in place' *)
Error: This expression has type int but an expression was expected of type
         int ref

But the above doesn't work because apoint.x returns the value of the field not its 'ref'. If this was golang or C++ maybe we could use the & operator to indicate we want the address instead of the value of the field: &apoint.x.

(How) can we do this in Ocaml?

PS: Yes, I know its probably more common to avoid using side-effects in this way. But I promise, I am doing this for a good reason in a context where it makes more sense than this simplified/contrived example might suggest.


Solution

  • There's no way to do exactly what you ask for. The type of a reference is very specific:

    # let x = ref 3
    val x : int ref = {contents = 3}
    

    A reference is a record with one mutable field named contents. You can't really fabricate this up from an arbitrary mutable field of some other record. Even if you are willing to lie to the type system, a field of a record is not represented at all the same as a record.

    You could declare your fields as actual references:

    type mpoint = { x: int ref; y: int ref; }
    

    Then there is no problem, apoint.x really is a reference. But this representation is not as efficient, i.e., it takes more memory and there are more dereferences to access the values.

    If an API is designed in an imperative style it will be difficult to use in OCaml. That's how I look at it anyway. Another way to say this is that ints are small. The interface should perhaps accept an int and return a new int, rather than accepting a reference to an int and modifying it in place.