Matching command line args - f#

Starting out learning F#. Want to make a simple program that just tells me what it found in the command line args. I have:
[<EntryPoint>]
let main argv =
printfn "%A" argv
match argv with
| [] -> 42
| _ -> 43
But this gives errors. If I hover over argv I see:
val argv : string[]
which is what I would have expected (a list of strings). However the first match expression has an error:
Error 1 This expression was expected to have type
string [] but here has type
'a list
Basically I just want to match on an empty argument list (an empty list of strings). What's the right way to do that?
I should add: I don't just want a solution (though that would be nice). I also want to understand what the compiler is looking for here that I'm not giving it.

It might be confusing since [] literal is used to signify an empty list, but type string [] is an array of strings rather than a list.
You can pattern match against an array like this:
[<EntryPoint>]
let main argv =
printfn "%A" argv
match argv with
| [||] -> 42
| _ -> 43
Like many seemingly inconsistent things in F#, this is a result of its dual heritage.
In OCaml, you'd use int list and int array for the types, [1;2;3] and [|1;2;3|] for the values respectively. But in C#/.NET, square brackets as in int[] are the way to indicate you're dealing with an array.
Probably in an attempt to be more approachable for the .NET crowd, in type names F# uses [] as an alias for array, so both forms are usable. It's rather unfortunate that this coincides with the empty list literal, but leaving that 'as is' was another constraint - one of early goals in F# design was to make it compatible with OCaml code, so that porting from that language to F# is as friction-less as possible.

If you want to implement some command-line utility I suggest using a wonderful library https://github.com/JordanMarr/FSharp.SystemCommandLine or https://github.com/cannorin/FSharp.CommandLine is also good.
But if you want to do that by hand, try
[<EntryPoint>]
let main (argv : string list) =
printfn "%A" argv
match argv with
| [] -> 42
| _ -> 43
or
[<EntryPoint>]
let main argv =
printfn "%A" argv
match argv |> List.ofArray with
| [] -> 42
| _ -> 43

Related

Why does F# not like the type ('a list list) as input?

*I edited my original post to include more info.
I'm working on an F# assignment where I'm supposed to create a function that takes an "any list list" as input and outputs an "any list". It should be able to concatenate a list of lists into a single list.
Here's what my function looks like:
let llst = [ [1] ; [2;3] ; ['d';'e';'f'] ]
let concat (llst:'a list list) : 'a list =
List.concat llst
List.iter (fun elem -> printf "%d " elem) concat
This solution more or less copied directly from microsofts example of using the List.concat function, the only exception being the specification of input/output types.
When i run the code i get this error:
concat.fsx(7,43): error FS0001: This expression was expected to have type
''a list'
but here has type
''b list list -> 'b list'
So it appears that concat is turning my llst into a character list, which i don't understand.
Can anyone help me understand why I'm getting this type error and how I can write a function that takes the types that I need?
The problem is somewhere in your implementation of the concat function. It is hard to say where exactly without seeing your code, but since this is an assignment, it is actually perhaps better to explain what the error message is telling you, so that you can find the issue yourself.
The error message is telling you that the F# type inference algorithm found a place in your code where the actual type of what you wrote does not match the type that is expected in that location. It also tells you what the two mismatching types are. For example, say you write something like this:
let concat (llst:'a list list) : 'a list =
llst
You will get the error you are getting on the second line, because the type of llst is 'a list list (the compiler knows this from the type annotation you give on line 1), but the expected type is the same as the result type of the function which is 'a list - also specified by your type annotation.
So, to help you find the issue - look at the exact place where you are getting an error and try to infer why compiler thinks that the actual type is 'a list list and try to understand why it expects 'a list as the type that should be in this place.
This is correct:
let concat (llst:'a list list) : 'a list =
List.concat llst
However, it's really equivalent to let concat = List.concat
This, however, doesn't compile, the elements of the lists need to be of the same type:
let llst = [ [1] ; [2;3] ; ['d';'e';'f'] ]
This also is problematic:
List.iter (fun elem -> printf "%d " elem) concat
List.iter has two arguments and the second one needs to be a List. However in your case you are (as per compiler error) providing your concat function which is a a' List List -> a' List.
What I suspect you meant to do, is apply the concat function to your llist first:
List.iter (fun elem -> printf "%d " elem) (concat llist)
// or
llist
|> concat
|> List.iter (fun elem -> printf "%d " elem)
However, all of this is perhaps missing the point of the exercise. What perhaps you need to do is implement some simple recursion based on the empty / non-empty state of your list, ie. fill in the blanks from here:
let rec myconcat acc inlist =
match inlist with
| [] -> ??
| elt :: tail -> ??

Slow conversion of F# discriminated union case to string

I have around 100k discriminated union cases I have to convert to strings, but it seems to be extremely slow.
As a comparison, the following executes (in F# interactive) in 3seconds on average :
open System
let buf = Text.StringBuilder()
let s = DateTime.Now
for i in 1 .. 100000 do
Printf.bprintf buf "%A" "OtherFinancingInterest" //string
buf.Length <- 0
printfn "elapsed : %0.2f" (DateTime.Now - s).TotalMilliseconds
While the following executes (also in F# interactive) in over a minute...
open System
let buf = Text.StringBuilder()
let s = DateTime.Now
for i in 1 .. 100000 do
Printf.bprintf buf "%A" OtherFinancingInterest //DU
buf.Length <- 0
printfn "elapsed : %0.2f" (DateTime.Now - s).TotalMilliseconds
The discriminated union has 25 values (the result is still extremely slow, around 16 seconds with two cases, but less so than with 25). Any idea if that is "normal" or if I may be doing something wrong ?
Many thanks
The %A format specifier pretty prints any F# value. It uses reflection to do so. It should only really be used for debugging purposes, and not in normal application code.
Note that using %s in your first example using a string makes it a lot faster because there is no type checking needed at runtime.
For the DU, there is a hack you could use to make the reflection only happen once on application load:
type FinancingInterest =
| OtherFinancingInterest
open FSharp.Reflection
let private OtherFinancingInterestStringMap =
FSharpType.GetUnionCases typeof<FinancingInterest>
|> Array.map (fun c -> FSharpValue.MakeUnion(c, [||]) :?> FinancingInterest)
|> Array.map (fun x -> x, sprintf "%A" x)
|> Map.ofArray
type FinancingInterest with
member this.AsString = OtherFinancingInterestStringMap |> Map.find this
You would also use this with the %s format specifier:
Printf.bprintf buf "%s" OtherFinancingInterest.AsString
I had similar timings to yours in your example, and now this one comes down to 40ms.
This only works as long as all of the DU cases don't have an arguments. You will get an exception on application load as soon as you try anything like this:
type FinancingInterest =
| Foo of string
| OtherFinancingInterest
Having said all this, I think you're better off writing a simple function that explicitly converts your type into a string value, writing out the names in full with repetition if necessary. The names of discriminated union cases should not generally be thought of as data that affects your program. You would usually expect to be able to safely rename case names without affecting runtime behaviour at all.

F# Seq.choose() Error FS0001

I have tried MSDN's example for the Seq.choose function (written below) in both a .fsx file and the interactive window for Visual Studio, but it repeatedly returns an error FS0001, stating that the "None" option is a PageExt type rather than the abstract option type 'a option.
I have searched in vain for an explanation of the PageExt type or why this could be returning an error when the None keyword should just represent the "no value" option in the match expression.
let numbers = seq {1..20}
let evens = Seq.choose(fun x ->
match x with
| x when x%2=0 -> Some(x)
| _ -> None ) numbers
printfn "numbers = %A\n" numbers
printfn "evens = %A" evens
;;
| _ -> None ) numbers
---------------------------------------^^^^
>
C:Path\stdin(38,40): error FS0001: This expression was expected to have type
'a option
but here has type
PageExt
Thanks for any help anyone can offer!
The PageExt type is likely something that you've pulled into your current FSI session previously which bound something to None, essentially blocking FSI from recognizing the normal option types.
In F#, you can reuse names, which "shadows" the original value. For example, in FSI, if you type:
let a = 1;;
let a = 2.3;;
a;;
You'll notice that it shows:
val a : int = 1
Then
val a : float = 2.3
Finally
val it : float = 2.3
This isn't changing the definition of a, but rather defining a new a name that shadows (or "hides") the original bound value.
In your case, you have a None name that's bound to something with a PageExt type that's shadowing Option.None, preventing it from being usable.
The easiest way to fix this is to reset your FSI session. Right click in the F# Interactive window, and choose "Reset iteractive session". If you do that, then run the code you pasted, it will work fine.

Does F# naming conventions forbids having the same name inside different disc. union types?

I have a common type in one module and that type is used inside two other discriminated union types.
I named them with the same name because of convenience. Other names are different. Next thing, I am trying to make a helper that prints types in console.
The first line fails to compile because of the type mismatch inside the match-case. I've tried a few things and it still fails with or without opening modules, forcing type and so on.
The other thing is, if I change the manes to Common1 and Common2 it works without any problems.
I remember reading that types with the same signatures are stored inside the same internal type, but my real-life example has different signatures and still fails.
Am I missing a point somewhere?
Example:
Example fails to compile with error:
Error This expression was expected to have type OneA but here has type OneB
module commonThings =
type CommonThing =
| Comm1 of int
| Comm2 of int
module thingA =
open commonThings
type OneA =
| A1 of string
| Common of CommonThing
| A2 of string
module thingB =
open commonThings
type OneB =
| B1 of string
| Common of CommonThing
| B2 of string
module printAB =
open commonThings
open thingA
open thingB
let printA (msg:OneA) =
match msg with
| A1 v -> printfn "A1"
| Common v -> printfn "Common"
| A2 v -> printfn "A2"
module main =
[<EntryPoint>]
let main argv =
printfn "%A" argv
0 // return an integer exit code
When you open thingB module, type OneB comes into scope and the Common case label from type OneA gets shadowed with the one from type OneB.
When name clashes between types or union/active pattern cases occur, the most recent one wins. Reordering the opens would make it work by chance:
open thingB
open thingA
The right solution is to prefix the case name. There's also RequireQualifiedAccess attribute you can use to force a type (or module) to always require prefixes for its internals.
You can disambiguate by prefixing the type name:
let printA (msg:OneA) =
match msg with
| A1 v -> printfn "A1"
| OneA.Common v -> printfn "Common"
| A2 v -> printfn "A2"

How to print a bigint in F#?

I am trying to find out how to print out the value of a bigint or System.Numerics.BigInteger in F#. I have found a site here that attempts to explain how to do this, but in the Visual Studio editor, it is flagged as an error. So far, I am just trying something simple like:
printfn "bigInt: %A " 123456789I
But that gets flagged as:
Why does this not work? How can I print out a bigint?
Code:
[<EntryPoint>]
let main =
printfn "bigInt: %A " 123456789I
If you are using an explicit entry point, main needs to accept a single string[] argument and return int. Your sample code is missing both of those requirements. Your printfn line is fine.
[<EntryPoint>]
let main (argv : string[]) =
printfn "bigint %A" 12345I
0

Resources