What is the 'best' way to call a parameterless function which only has side effects?

I am calling a function which gets a specific value – which can change over time – from the same record on the same database table each time.

I need a function which I can call to retrieve that value without using any parameters – it always gets the value from the same record so there’s nothing to give it.

At first I tried the Database1 code in the code below (with output below that) but that’s no good because Database1.getTheValue is simply a string which is evaluated before the start of the processing, and it’s always the same.

Then I made the function lazy in Database2 but that’s no good either because the value is ‘fixed’ after the first evaluation, even though the evaluation comes at the correct time (at least for the first use).

Then I added a superfluous parameter to the function (in Database3) but that looks silly.

Then I gave the parameters as () and that (in Database4) looks reasonable.

Then I tried making Database5.getTheValue a function that returns a function, which looks a bit odd and doesn’t look much better than in Database4.

Which of these should I be using, or is there a better way?
Is there a good reason why I shouldn’t be using one of the last two versions for some reason?


module Database1 = 

    let mutable theValue = "abc" 

    let getTheValue = 
        printfn "Getting the value, which is %A" theValue 
        theValue 

    let setTheValue newValue = 
        printfn "Setting the value to %A" newValue 
        theValue <- newValue 

printfn "Start 1" 
let x1 = Database1.getTheValue 
do Database1.setTheValue (x1 + "d")
let y1 = Database1.getTheValue 
do Database1.setTheValue (y1 + "e") 
let z1 = Database1.getTheValue 
printfn "The value should now be 'abcde' : %A " z1 


module Database2 = 

    let mutable theValue = "abc" 

    let getTheValue = 
        lazy (
            printfn "Getting the value, which is %A" theValue 
            theValue 
        )

    let setTheValue newValue = 
        printfn "Setting the value to %A" newValue 
        theValue <- newValue 

printfn "Start 2" 
let x2 = Database2.getTheValue
do Database2.setTheValue (x2.Value + "d")
let y2 = Database2.getTheValue 
do Database2.setTheValue (y2.Value + "e") 
let z2 = Database2.getTheValue 
printfn "The value should now be 'abcde' : %A " z2.Value 


module Database3 = 

    let mutable theValue = "abc" 

    let getTheValue superfluous = 
        printfn "Getting the value, which is %A" theValue 
        theValue 

    let setTheValue newValue = 
        printfn "Setting the value to %A" newValue 
        theValue <- newValue 

printfn "Start 3" 
let x3 = Database3.getTheValue "dummy"
do Database3.setTheValue (x3 + "d")
let y3 = Database3.getTheValue "dummy"
do Database3.setTheValue (y3 + "e") 
let z3 = Database3.getTheValue "dummy"
printfn "The value should now be 'abcde' : %A " z3 


module Database4 = 

    let mutable theValue = "abc" 

    let getTheValue () = 
        printfn "Getting the value, which is %A" theValue 
        theValue 

    let setTheValue newValue = 
        printfn "Setting the value to %A" newValue 
        theValue <- newValue 

printfn "Start 4" 
let x4 = Database4.getTheValue()
do Database4.setTheValue (x4 + "d")
let y4 = Database4.getTheValue()
do Database4.setTheValue (y4 + "e") 
let z4 = Database4.getTheValue()
printfn "The value should now be 'abcde' : %A " z4 


module Database5 = 

    let mutable theValue = "abc" 

    let getTheValue = 
        fun () -> 
            printfn "Getting the value, which is %A" theValue 
            theValue 

    let setTheValue newValue = 
        printfn "Setting the value to %A" newValue 
        theValue <- newValue 

printfn "Start 5" 
let x5 = Database5.getTheValue()
do Database5.setTheValue (x5 + "d")
let y5 = Database5.getTheValue()
do Database5.setTheValue (y5 + "e") 
let z5 = Database5.getTheValue()
printfn "The value should now be 'abcde' : %A " z5

The output is:

Getting the value, which is "abc"
Start 1
Setting the value to "abcd"
Setting the value to "abce"
The value should now be 'abcde' : "abc"
Start 2
Getting the value, which is "abc"
Setting the value to "abcd"
Setting the value to "abce"
The value should now be 'abcde' : "abc"
Start 3
Getting the value, which is "abc"
Setting the value to "abcd"
Getting the value, which is "abcd"
Setting the value to "abcde"
Getting the value, which is "abcde"
The value should now be 'abcde' : "abcde"
Start 4
Getting the value, which is "abc"
Setting the value to "abcd"
Getting the value, which is "abcd"
Setting the value to "abcde"
Getting the value, which is "abcde"
The value should now be 'abcde' : "abcde"
Start 5
Getting the value, which is "abc"
Setting the value to "abcd"
Getting the value, which is "abcd"
Setting the value to "abcde"
Getting the value, which is "abcde"
The value should now be 'abcde' : "abcde"

I think the recommended approach is what you did in the Database4.

One reason to use the technique in Database5 is if you’d like to have some form of memoization, or some code that executes once, before assigning an anonymous function to the getTheValue binding.

But it doesn’t seem relevant to your use case.

1 Like

I think @gauthier’s answer is the correct one, but just to spice up the conversation, I’ll throw out option #6, based on this blog:

module Database6 = 

    let mutable theValue = "abc" 

    let getTheValue = async {
        printfn "Getting the value, which is %A" theValue 
        return theValue
    }

    let setTheValue newValue = async {
        printfn "Setting the value to %A" newValue 
        theValue <- newValue 
    }

let run = async {
  printfn "Start 6" 
  let! x6 = Database6.getTheValue
  do! Database6.setTheValue (x6 + "d")
  let! y6 = Database6.getTheValue
  do! Database6.setTheValue (y6 + "e") 
  let! z6 = Database6.getTheValue
  printfn "The value should now be 'abcde' : %A " z6
}

This is not idiomatic for F#, it won’t perform as well, and doesn’t fit very nicely within the .NET ecosystem, but it does “feel” nice because async becomes the “context” for impure values, and you can drop the superfluous inputs to your function. Instead of getTheValue : unit -> string where the unit is just sort of there to ensure it gives you a different value each time, you have getTheValue : Async<string>, which you can think of as an “impure string” or a string with side-effects/non-determinism attached to it.
Honestly though, this style is an attempt to make F# look and feel more like a pure functional language like Haskell, and is not something you’ll see much in the community/ecosystem. And if you want async to represent “impure” code, you really want every impure thing in your whole codebase to be async which is quite a commitment.

gauthier: Thanks for confirming my suspicions. Good to know I was on the right track.

ntwilson: That’s an interesting method. I’ll have to read that article at some point. Thanks.

I feel the key thing to remember is that a function “takes one input and gives one output”. What you had in Database1 is therefore not a function but a plain value and therefore evaluated only once.

The one input and one output statement is true, a tuple of multiple values becomes ‘one’ thing. Unit () is ‘one thing’ (that handily takes 0 bits to move around). A function must take one input and give one output.

One other option not discussed, you could make a Database class instead of a module and then you can have a property member. That will have a set and get function but at the point of use it will look like you are reading or setting a plain value. Code to call the function will be generated instead of a value read / mutate though. That way the function is used every time even though it reads just like a fixed value being read.

1 Like

Thanks for the extra information realparadyne.