vesii
vesii

Reputation: 3128

Reference to a reference in SML

I was studying references in SML.

I wrote the following code:

let
    val f = (fn (s) => s := ref((!(!s)) + 2))
    val x = ref (5)
    val y = ref x
in
    (f y ; !x)
end;

I'm trying to get to val it = 7 : int, although my program prints val it = 5 : int. I can't understand why. I am sure the problem is in the f function but can't understand why.

What I'm trying to do: f function should update the argument y to be ref(ref(7)) so x could be ref(7). but for some reason it doesn't work. What is the problem?

Upvotes: 1

Views: 398

Answers (2)

sshine
sshine

Reputation: 16135

As Andreas Rossberg suggests, val f = fn r => !r := 7 could be one way to update the int of an int ref ref to 7. But instead of 7 you could write anything. If, instead, you want to increase by two the int being pointed indirectly to, a hybrid between your attempt and Andreas'es suggestion could be

fun f r = !r := !(!r) + 2

Here, !r := ... means "dereference r to get the int ref it points to, and update that int ref so that it instead points to ...", and !(!r) + 2 means "dereference r twice to get the int it indirectly points to, and add two to it." At this point, you have not changed what r points to (like you do with s := ref ...), and you're using the value it points to indirectly using the double-dereference !(!r).

A test program for this could be:

val x = ref 5
val y = ref x
fun f r = !r := !(!r) + 2

fun debug str =
    print ( str ^ ": x points to " ^ Int.toString (!x) ^ " and "
          ^ "y points indirectly to " ^ Int.toString (!(!y)) ^ ".\n" )

val _ = debug "before"
val _ = f y
val _ = debug "after"

Running this test program yields:

before: x points to 5 and y points indirectly to 5.
after: x points to 7 and y points indirectly to 7.

Upvotes: 0

Andreas Rossberg
Andreas Rossberg

Reputation: 36118

Updating y to point to a new ref does not update x. There's a new reference created during the call to f, let's call it z. Before the call we have:

x -> 5
y -> x

where -> is "points to". After the call it is:

x -> 5
y -> z
z -> 7

Edit: One possible way to actually update x is by defining f as follows:

val f = fn r => !r := 7

When invoking f y, this updates the reference pointed to by y, which is x. But whether that is the "right" solution depends on what you actually want to achieve.

Upvotes: 1

Related Questions