As mentioned in the accepted answer the "args" argument to the entrypoint is an array, not a list, so you cannot use it with the syntax for list matching.
Instead of matching on the array, as suggested above, you could turn the arguments into an actual list and use that for matching. I have found that a very useful way to handlie command line arguments (though it may be overkill for your example case). As an example:
[<EntryPoint>]
let main args =
let arglist = args |> List.ofSeq
match arglist with
| first :: [] ->
// do something with 'first'
| _ -> // catches both the no-argument and multi-argument cases
printfn "Usage : "
// print usage message
Edit:
As for more complicated examples there are two ways to go from here. You can of course add more complicated cases in the match, or you could parse the list of arguments in a recursive way to build an object representing options and arguments. The latter would get a bit too complicated to fit here, but as an example of some more complex match cases, here is some code related to some recent work where the executable accepts a "command" to operate on a target file, and each command has different further arguments (each command calls a function whose implementation I left out for sake of brevity)
[<EntryPoint>]
let main args =
let arglist = args |> List.ofSeq
match arglist with
| target :: "list" :: [] ->
listContent target
| target :: "remove" :: name :: [] ->
removeContent target name
| target :: "add" :: name :: [] ->
addContent target name
| target :: "addall" :: names ->
for name in names do
addContent target name
| _ -> // catches cases not covered above
printfn "Usage : "
// print usage message