3

I have been trying to re-write one of my simple C# console app into purely functional F# code (if at all possible). Up until now I have managed to re-write while blocks into "immutable" F# with the help of Seq (something like Seq.initInfinite sequenceGenerator |> Seq.takeWhile condition |> Seq.iter bodyOfWhileCycle) - the website "F# for Fun and Profit" has been my inspiration.
However, this time I came across a simple while block that in "mutable" F# looks like follows (and it works):

printfn "Type low OP number"
let mutable lowLimit = parseMe (Console.ReadLine())                    
printfn "Type high OP number"
let mutable highLimit = parseMe (Console.ReadLine())                                            
let mutable myCondition = true
if highLimit > lowLimit then myCondition <- false            
while myCondition do 
      printfn "Type low OP number again"
      lowLimit <- parseMe (Console.ReadLine())                                  
      printfn "Type high OP number again"
      highLimit <- parseMe (Console.ReadLine())
      if highLimit > lowLimit then myCondition <- false

Can anyone help me to figure out how to refactor this "mutable" while-do block into a functional style? My attempts to refactor it do not work the way I want - I do not know how to get the lowLimit1/highLimit1 values out of the bodyOfWhileCycle function. One of my unsuccessfull attempts is here:

printfn "Type low OP number"
let lowLimit = parseMe (Console.ReadLine())                       
printfn "Type high OP number"
let highLimit = parseMe (Console.ReadLine())
let verifyingInputValues: unit = 
    let bodyOfWhileCycle _=    
                            printfn "Type low OP number again"
                            let lowLimit1 = parseMe (Console.ReadLine())                                  
                            printfn "Type high OP number again"
                            let highLimit1 = parseMe (Console.ReadLine())
                            ()                
    fun _ -> highLimit - lowLimit 
    |>  Seq.initInfinite 
    |>  Seq.takeWhile ((>) 0)
    |>  Seq.iter bodyOfWhileCycle   
verifyingInputValues
2
  • Noticing you are a newbie here, if you are happy with our answers could you please mark one of them as accepted? Mine, I think, directly shows you an idiomatic way to implement a while loop immutably with also a simple refactor of your other parse code. @ChaosPandion's solution directly debugs your Seq approach and also does a larger (and better) refactor on your parse code. You decide. Commented Apr 12, 2021 at 8:10
  • As both first two answers are very helpful to my learning process, it is tricky to mark one of them as better than the other one (but if stackoverflow requires marking, I'll be forced to do so in some time). I really appreciate the help of both of ChaosPandion and @Martin Freedman. I slightly adapted Martin Freedman's answer ("match" instead of "if") and I will use both principles in my code. Commented Apr 12, 2021 at 16:32

2 Answers 2

2

It looks like you want to have the while loop finish when highLimit > lowLimit and otherwise repeat requests, and presumably, do something with them later.

In which case you want a function that returns a tuple of ( highLimit, lowLimit ) and not unit (). A recursive function can handle the apparent change in state or IO between calls, without mutability, by passing the new state in as an argument.

let fullParse: () -> int *int =
    let parseHighLow again = 
       printfn "Type low OP number %s" again
       let lowLimit = parseMe (Console.ReadLine())                                  
       printfn "Type high OP number %s" again
       let highLimit = parseMe (Console.ReadLine())
       highLimit, lowLimit
        
    let rec verify (high, low) = 
       if high > low  then high, low else verify (parseHighLow "again")
    verify (parseHighLow "")

let (high, low) = fullParse ()
Sign up to request clarification or add additional context in comments.

Comments

2

Take notice of the skip rather than take because we are waiting for what we want to be received from a user. What we want is one thing so we apply the head function to the sequence. Overall your attempt was close but your sequence wasn't producing values.

open System

let rec p name = 
    printfn "Input %s Value" name
    let text = Console.ReadLine()
    let (ok, i) = Int32.TryParse text
    if ok then i else p name
     
let ps () = 
    Seq.initInfinite (fun _ -> p "High", p "Low")
    |> Seq.skipWhile (fun (high, low) -> high <= low)
    |> Seq.head

let (high, low) = ps ()

1 Comment

I did notice the "skip" and "head" in your code. Thanx. It seems like "takeWhile" and "iter" can only be used for replacing "while-do" cycles resulting in "unit" (at least it worked in my code), but not for yielding values.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.