Snippets

Cameron Presley F# - User needs to make a selection based from a list

Created by Cameron Presley last modified
open System
let rec userMakesAChoice prompt selectionValidator options =
        match options with
        | [] -> None
        | _ -> 
            prompt options
            let result = Console.ReadLine()
            match selectionValidator result options with
            | None -> userMakesAChoice prompt selectionValidator options
            | Some x -> Some x

// Defining the prompt
let promptUserForHowManyPlayers numbers = numbers |> List.iter (fun x -> printfn "%A" x)

// Defining valid options
let validNumPlayerChoices = [2; 3; 4; 5;]

// Defning selection validator
let validateNumPlayers selection options =
    match selection |> Int32.TryParse with
    | (false,_) -> None
    | (true, value) -> options |> List.tryFind (fun x -> x = value)
    
// Example usage
userMakesAChoice promptUserForHowManyPlayers validateNumPlayers validNumPlayerChoices

Comments (5)

  1. Vasily Kirichenko

    Using HOFs so wildly makes the code very hard to read. Is it necessary to pass validation and prompt logic as functions?

    1. Cameron Presley

      First off, thanks for looking at the snippet, we all do better if we learn together.

      For the problem I was trying to solve, I believe so. In this card game simulation I'm working on, I'm asking the user to choose an opponent and/or a card as well. I originally wrote the logic for these two cases separate, but I noticed that they were very similar to one another, so I wanted to see if there were some generic way I can define how a user makes a choice and then for when asking for a card, I can load the certain functions whereas for when I'm asking for a player, I can pass different ones.

      I can see your point that using HOFs can be a bit confusing, but I'm not sure what else I could've have done. I'm completely open to suggestions, so any feedback would be great.

  2. Vasily Kirichenko
    open System
    
    let rec userMakesAChoice selectionValidator = function
        | [] -> None
        | options -> 
            options |> List.iter (printfn "%A")
            Console.ReadLine()
            |> selectionValidator options
            |> Option.tryFillWith (fun _ -> userMakesAChoice selectionValidator options)
    
    let parseInt s = match Int32.TryParse s with true, x -> Some x | _ -> None
    let validateOptions options value = options |> List.tryFind ((=) value)
    let (>=>) f g = f >> Option.bind g
    
    let validateNumPlayers options = parseInt >=> validateOptions options
    
    userMakesAChoice validateNumPlayers [ 2..5 ]
    
    1. Cameron Presley

      Some Questions if you have the time:

      1. I've never seen the function keyword before, but it looks like a combination of match with a single param. That's definitely interesting.
      2. I've also never seen Option.tryFillWith before, but it looks like it'll take the value if it's some, otherwise, it looks like it recurses, is that correct?
      3. Also, I'm not sure why you defined (>=>) and why that's being sued, could you elaborate a bit more on that?

      Thanks

      1. chosen breed

        Hi Cameron. Thanks for sharing the code. Yeah readability could be an issue but that is can be a subjective quality ;-). I think F# gives us the power to be really expressive. With that power comes some responsibility. I think I understand what's going on. With regards to Vasily's implementation I'll let him comment :-)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.