3

I am still trying to get the mindset for functional programming, hence I am not sure the question is meaningful: is scoping assignment possible in F# as it is in R?

Let' suppose I am designing a function split x when x = y + 10z for abs(int:y) <= 5 and abs(int:z).

 let split x =
    let z = abs(x / 10)
    let modz = abs(x % 10)
    let y =
            if modz > 5 then
                    let z = z + 1
                    abs(5 - modz)

            else
                    modz
    (y, z);;

Given a module of z bigger then 5, I would like to modify the value of z. I.e., for split 27 I expect as result (2, 3).

split 22;;
val it : int * int = (2, 2)

split 27;;
val it : int * int = (2, 2)
Community
  • 1
  • 1
Worice
  • 3,847
  • 3
  • 28
  • 49

2 Answers2

7

The let keyword creates a name binding, which means "this name means this value" (or this function). It is immutable: in the code you wrote, the name z in the split function will always refer to the value abs(x / 10), and cannot be changed. The let z = z + 1 expression in your if modz > 5 = ... block is not reassigning the value of the outer z. Instead, it's creating a new definition of z, local to the block that it's in. What the code you've written is saying is "within the true part of this if expression, I want a name called z whose value is the value of z that I currently know about, plus 1. Oh, and hide the "outer" z from me: inside this block, I want to use my definition of z." But outside the block, the "outer" z is unchanged. This allows you to redefine names at will, without accidentally causing bugs in other code, and it's usually a good thing.

What you're trying to do is create a mutable variable, and that's done by adding the mutable keyword to let. The syntax for assigning a new value to a mutable variable is also different: it doesn't use let at all. What you wanted is to write:

let mutable z = abs(x / 10)
// Later on...
z <- z + 1

The left-facing arrow operator <- means "change the value of this mutable variable". If you write z = z + 1, which is a common newbie mistake, the compiler will give you a warning that this is a Boolean value that is ignored -- because when it's not part of a let binding, the = operator is comparison. The expression z = z + 1 is asking "is z equal to z + 1?" And that will always be false (unless you've created a really weird numeric type).

I don't really recommend using mutable as a general rule. Try re-writing your logic to avoid it. For example, you could write your function like so:

let split x =
    let z = abs(x / 10)
    let modz = abs(x % 10)
    let increment = if modz > 5 then 1 else 0
    let y = if modz > 5 then abs(5 - modz) else modz
    (y, z + increment)

That would be more functional, and avoid the use of mutable.

rmunn
  • 34,942
  • 10
  • 74
  • 105
  • I am impressed. You just made me clear far more things then expected. Thank you! – Worice Oct 24 '16 at 10:32
  • Note that I just edited my example, because the final line was wrong. I originally wrote `(y + increment, z)`, but what I should have written to match the logic of your function was `(y, z + increment)`. Also, I left off the `;;` because you don't need them if you're writing `.fs` files. You do need them if you're typing code into the F# Interactive interpreter, though, so include the `;;` at the end of your code if (and only if) you're typing it into F# Interactive. – rmunn Oct 24 '16 at 10:44
  • Yes, I noticed, but I got the point anyway. In any case, thanks for your correction. – Worice Oct 24 '16 at 10:48
4

In F# the values (variables) are immutable, you cannot update the value of z.

let z = abs(x / 10)

And

let z = z + 1

Are different declared values, with different scopes.

The scope in F# is identified by indentation, so when you return

(y, z);;

You return the z with same indentation (let z = abs(x / 10))

Stefano Balzarotti
  • 1,760
  • 1
  • 18
  • 35