First-time F#: A simple Rock Paper Scissors This is my first F# program. It's a simple Rock-Paper-Scissors implementation.
I am looking for feedback of all sorts, e.g. the choice of types (e.g. List vs Array), whether there are parts that could be solved more elegantly / idiomatic, formatting, etc.
open System

let allowedActions = [ &quot;r&quot;; &quot;p&quot;; &quot;s&quot; ]

let randomItem list (rng: Random) =
    List.item (rng.Next(List.length list)) list

let computerAction () = randomItem allowedActions (Random())
let rec playerAction () =
    printfn &quot;Write 'r' for Rock, 'p' for Paper, 's' for Scissors. Type 'q' to Quit.&quot;

    let input = Console.ReadLine()

    let isInputAllowed = List.contains input allowedActions || input = &quot;q&quot;
    match isInputAllowed with
    | true -&gt; input
    | _ -&gt; printfn &quot;invalid input '%s'&quot; input; playerAction()

let computerWin playerAction computerAction =
    printfn &quot;The computer did win. '%s' beats '%s'.&quot; computerAction playerAction
let playerWin playerAction computerAction =
    printfn &quot;The player did win. '%s' beats '%s'.&quot; playerAction computerAction
let tie anAction = printfn &quot;It's a tie. '%s'&quot; anAction

let ruling playerAction computerAction =
    match (playerAction, computerAction) with
    | (&quot;r&quot;, &quot;p&quot;)
    | (&quot;p&quot;, &quot;s&quot;)
    | (&quot;s&quot;, &quot;r&quot;) -&gt; computerWin playerAction computerAction
    | (&quot;r&quot;, &quot;s&quot;)
    | (&quot;p&quot;, &quot;r&quot;)
    | (&quot;s&quot;, &quot;p&quot;) -&gt; playerWin playerAction computerAction
    | (_, _) -&gt; tie playerAction

let playGame playerAction computerAction =
    printfn &quot;You've chosen '%s'.&quot; playerAction
    printfn &quot;The computer chose '%s'.&quot; computerAction
    ruling playerAction computerAction
    ()

let game () =
    let playerAction = playerAction ()
    let computerAction = computerAction ()
    match playerAction with
    | &quot;q&quot; -&gt; false
    | _ -&gt;
        playGame playerAction computerAction
        true

[&lt;EntryPoint&gt;]
let rec main arg =
    printfn &quot;Hello World to Rock Paper Scissors&quot;
    match game () with
    | true -&gt; main arg
    | false -&gt; 0
```
As a first attempt it looks in many ways ok.
I have three &quot;complaints&quot;:
a) You rely too much on string literals as identifiers for input. That is considered bad practice in - I think - every programming language and should be avoided because it's error prone (typos and illegal input etc.) hence not very robust.
Instead of &quot;r&quot;, &quot;p&quot; and &quot;s&quot; you could use a discriminated union - which is a very important part of the F#-toolbox to be familiar with:
type Weapon =
    | Rock
    | Paper
    | Scissors

This will for instance make the match-statement in ruling much more readable and type safe:
let ruling playerWeapon computerWeapon =
    match (playerWeapon, computerWeapon) with
    | (Rock, Paper)
    | (Paper, Scissors)
    | (Scissors, Rock) -&gt; computerWin playerWeapon computerWeapon
    | (Rock, Scissors)
    | (Paper, Rock)
    | (Scissors, Paper) -&gt; playerWin playerWeapon computerWeapon
    | (_, _) -&gt; tie playerWeapon


b) Don't repeat yourself. In the functions where you print the results, you do the same thing, and therefore you should extract the common parts to a generalized function, so that:
let computerWin playerAction computerAction =
    printfn &quot;The computer did win. '%s' beats '%s'.&quot; computerAction playerAction
let playerWin playerAction computerAction =
    printfn &quot;The player did win. '%s' beats '%s'.&quot; playerAction computerAction

changes to:
let showWinner name winnerWeapon looserWeapon = 
    printfn &quot;The %s did win. '%A' beats '%A'.&quot; name winnerWeapon looserWeapon
let computerWin playerWeapon computerWeapon = showWinner &quot;computer&quot; computerWeapon playerWeapon
let playerWin playerWeapon computerWeapon = showWinner &quot;player&quot; playerWeapon computerWeapon


c) You recursively call the main function. I don't know if you actually violate any formal or informal rules but it just looks &quot;ugly&quot; to me. I would make a dedicated function that runs the game recursively.

FYI - I have below refactored your program while incorporating my suggestions above:
// HH: Instead of string literals you should use discriminated unions as identifiers for &quot;weapons&quot;
//     This is much more robust in respect to typos etc.
type Weapon =
    | Rock
    | Paper
    | Scissors
    static member Abbreviation w = (w.ToString().[0]).ToString().ToLower()
    static member ToWeapon ch =
        match ch with
        | &quot;r&quot; -&gt; Rock
        | &quot;p&quot; -&gt; Paper
        | &quot;s&quot; -&gt; Scissors
        | _ -&gt; failwith &quot;Invalid Weapon char&quot;

let weapons = [ Rock; Paper; Scissors ]

// HH: You should only instantiate a single random object - used throughout the session.
let rand = new Random()
let computerAction () = weapons.[rand.Next(weapons.Length)]


// HH: This now returns an optional value of None if the user wants to quit
//     or Some (Weapon) if a valid weapon is chosen
let rec playerAction () =
    let allowedActions = weapons |&gt; List.map Weapon.Abbreviation
    let choices = weapons |&gt; List.map (fun w -&gt; sprintf &quot;'%s' = %A&quot; (Weapon.Abbreviation w) w)
    printfn &quot;Enter:\n%s.\n'q' to Quit.&quot; (String.Join(&quot;\n&quot;, choices))
    let input = Console.ReadLine()

    let validWeapon w = List.contains w allowedActions
    match input with
    | &quot;q&quot; -&gt; None
    | w when validWeapon w -&gt; Some (Weapon.ToWeapon w)
    | _ -&gt; printfn &quot;invalid input '%s'&quot; input; playerAction()

//HH: Never repeat yourself: extract a function to print the winner...
let showWinner name winnerWeapon looserWeapon = 
    printfn &quot;The %s did win. '%A' beats '%A'.&quot; name winnerWeapon looserWeapon
// HH: ... and call that from the dedicated winner functions
let computerWin playerWeapon computerWeapon = showWinner &quot;computer&quot; computerWeapon playerWeapon
let playerWin playerWeapon computerWeapon = showWinner &quot;player&quot; playerWeapon computerWeapon
let tie anAction = printfn &quot;It's a tie. '%A'&quot; anAction

let ruling playerWeapon computerWeapon =
    // HH: By using discriminated unions this match
    //     expression is much more readble and robust
    match (playerWeapon, computerWeapon) with
    | (Rock, Paper)
    | (Paper, Scissors)
    | (Scissors, Rock) -&gt; computerWin playerWeapon computerWeapon
    | (Rock, Scissors)
    | (Paper, Rock)
    | (Scissors, Paper) -&gt; playerWin playerWeapon computerWeapon
    | (_, _) -&gt; tie playerWeapon

let playGame playerWeapon computerWeapon =
    printfn &quot;You've chosen '%A'.&quot; playerWeapon
    printfn &quot;The computer chose '%A'.&quot; computerWeapon
    ruling playerWeapon computerWeapon
    ()

let runGame () =
    let playerAction = playerAction ()
    match playerAction with
    | Some playerWeapon -&gt;
        let computerWeapon = computerAction ()
        playGame playerWeapon computerWeapon
        true
    | None -&gt; false

// HH: Personally I don't like,that you call main recursively.
//     You probably don't violate any formal or informal rule, but it just look wrong to me
//     So make a dedicated function to start the game by
let rec play () =
    match runGame() with
    | true -&gt; play()
    | false -&gt; ()

I have changed some of the names so that they matches my naming. For the example I show that you can extent a discriminated union with members (static and instance). You could chose to have these methods as normal function instead.