F# pattern match directly against let binding - f#

Is it possible in F# to pattern match directly against a let binding?
For example, this compiles without any warnings:
let value =
match arg with
| 1 -> "value1"
| 2 -> "value2"
| _ -> failwith "key not found"
Whereas the following gives the warning "This rule will never be matched" against the lines matching key2 and _:
let key1 = 1
let key2 = 2
let value =
match arg with
| key1 -> "value1"
| key2 -> "value2"
| _ -> failwith "key not found"
Is this because although they're immutable, the let bindings are unlike C# const variables?

just use capital letters and [<Literal>] them and it works as expected.
let [<Literal>] X = 0
let [<Literal>] Y = 1
let bla arg =
match arg with
| X -> "zero"
| Y -> "one"
| somethingelse -> somethingelse.ToString()
the lower case name by convention typically means a wildcard that is bound to the name.

The reason you're getting that error is because of what F# is doing when you use a variable name in the pattern clause of a match expression.
Let's say I have
match arg with
| x when x = 0 -> "zero"
| y when y = 1 -> "one"
| _ -> "other"
I think it's key here to note that, despite not defining x or y prior to the match, this code will still work. This is because x and y are just short codes which makes writing match expressions easier. Behind the scenes, the F# compiler is actually converting that x when x = 0 into "let binding" where x is bound to arg. x can then be used in the x = 0 expression and in the expression after the ->.
Going back to the problem you ran into:
let key1 = 1
let key2 = 2
let value =
match arg with
| key1 -> "value1"
| key2 -> "value2"
| _ -> failwith "key not found"
The reason this won't work, is because in the match expression, F# is rebinding key1 to the value of arg, so key1 -> "value1" is equivalent toif arg1 = arg1 then "value1". The first pattern will always be matched; so, key2 and _ will never be reached.
I'm not sure how clear my explanation is, so I'll also throw in a second approach to explaining what's happened:
if you translate the match expression into an if-else it would look like this:
let key1 = 1
let key2 = 2
let value =
if let key1 = arg in arg = key1 then
"value1"
else if let key2 = arg in arg = key2 then
"value2"
else
failwith "key not found"
(why yes, F# will let you throw let bindings into if expressions)
This if/else expression is equivalent to your match expression. In this form it becomes clear that the first condition will always evaluate to true.
I won't put it in here, but it may help to look at the code quotation of a match expression. I didn't really get what was going on with match expressions until I saw what the abstract syntax tree they generated looked like.

You can only use literals if you want to match against a particular value in a pattern matching case. An identifier means binding -- i.e. the actual value in this pattern matching case will be bound to the identifier that will be visible in the scope of this case.
As #DanielFabian has shown, you can define your own literals and give them names.
If the value you need to match against isn't known at compile time, you can use guards like so:
match arg with
| x when x = key1 -> "value1"
| x when x = key2 -> "value2"
| _ -> // etc
See the MSDN article on pattern matching for more details.

There are two main issues raised by the code you are trying to use.
Firstly pattern matching:
match arg with
| _ -> "value"
matches arg with anything and then returns "value"
match arg with
| a -> "value"
matches arg with anything, calls it "a", and then returns "value". You can think of the match having it's own little namespace, the a only exists in the match, and the match only 'sees' the things that have been named in it.
Secondly, if you want to match to a set of predefined values, then you probably want to use discriminated unions. You can define one like this:
type Keys=
| Key1
| Key2
Then match like this:
match arg with
| Key1 -> "value1"
| Key2 -> "value2"
In this case it matches against the type Keys, not a value called Key1 or Key2.

Related

How can I get values with discriminited union type from recursive function

I am trying to obtain a specific value from one list with a multiple recursion so I have :
type PowerSystem =
| System of string * int
| Junction of string * List<PowerSystem>
let Starship =
Junction("Core",
[
Junction("Users",
[
System("Main Computer",-10);
System("Library Computer",-10);
Junction("Defence",)]
let rec JunctionPath (pSystem:PowerSystem) =
match pSystem with
| Junction(name,aList) -> SplitList2 aList
| System(name,aNumber) -> [name]
and SplitList2 list =
match list with
| [] -> printfn "%A" []
| head::tail -> printfn "%A" List.filter (fun e->e="Port Phasers" (JunctionPath head)#(SplitList2 tail))
JunctionPath Starship
I get error FS0001: The type ''a -> string list' does not match the type 'unit'
I want to get the name of junctions when the system is called Main Computer but I can't call other functions. I tryed in different ways to get those values but I can't found the way. Thanks in advance
SplitList2 only has the side effect of printfn hence returns unit ().
In JunctionPath the first branch returns a list of string - [name] - and the second a unit due to calling SplitList2. This is not allowed as both (in general all) pattern match branches should return the same type. This is why you get your error "error FS0001: The type ''a -> string list' does not match the type 'unit'"
(It would be clearer if you, in future, note the line references in the error as that points to the problematic line).
Now in answer to your last point:
I want to get the name of junctions when the system is called Main
Computer but I can't call other functions. I tryed in different ways
to get those values but I can't found the way.
this does not make sense since what is "Port Phasers"? Anyway, without testing, I have modified SplitList2 to return a list on either branch. Then we pipe the result of JunctionPath into a printfn. However the logic of the line List.filter (fun e->e="Port Phasers" (JunctionPath head)#(SplitList2 tail)) still does not make sense, so I updated it as I think you meant it (although you should have had another error for this?). (This is not tail recursive and can be improved but one step at a time)
type PowerSystem =
| System of string * int
| Junction of string * List<PowerSystem>
let Starship =
Junction("Core",
[
Junction("Users",
[
System("Main Computer",-10);
System("Library Computer",-10);
Junction("Defence",)]
let rec JunctionPath (pSystem:PowerSystem) =
match pSystem with
| Junction(name,aList) -> SplitList2 aList
| System(name,aNumber) -> [name]
and SplitList2 list =
match list with
| [] -> []
| head::tail -> List.filter (fun e->e="Port Phasers") (JunctionPath head)#(SplitList2 tail))
JunctionPath Starship |> printfn "%A%

Pattern matching against a string property

I'm de-serializing some mappings from JSON and later on I need to pattern match based on a string field of the de-serialized types like this:
let mappings = getWorkItemMappings
let result =
workItemMappings
|> Seq.find (fun (m: WorkItemMapping) -> m.Uuid = workTime.workItemUuid)
match mapping.Name with
Even if I complete the pattern match for all cases I still get Incomplete pattern matches on this expression.. Which is obvious to me due to the string type of the Name field.
Is there a way tell the compiler which values for the Name field are available?.
I think I could create a union type for the possible mapping types and try to de-serialize the JSON to this union type but I would like to if there's another option.
If you are pattern matching on a string value, the compiler has no static guarantee that it will only have certain values, because it is always possible to construct a string of a different value. The fact that it comes from JSON does not help - you may always have an invalid JSON.
The best option is to add a default case which throws a custom descriptive exception. Either one that you handle somewhere else (to indicate that the JSON file was invalid) or (if you check the validity elsewhere) something like this:
let parseFood f =
match f with
| "burger" -> 1
| "pizza" -> 2
| _ -> raise(invalidArg "f" $"Expected burger or pizza but got {f}")
Note that the F# compiler is very cautious. It does not even let you handle enum values using pattern matching, because under the cover, there are ways of creating invalid enum values! For example:
type Foo =
| A = 1
let f (a:Foo) =
match a with
| Foo.A -> 0
warning FS0104: Enums may take values outside known cases. For example, the value 'enum (0)' may indicate a case not covered by the pattern(s).
Very hard to understand what you're asking. Maybe this snippet can be of help. It demos how literal string constants can be used in pattern matching, and reused in functions. This gives some added safety and readability when adding and removing cases. If you prefer not to serialize a DU directly, then perhaps this is useful as part of the solution.
type MyDu =
| A
| B
| C
let [<Literal>] A' = "A"
let [<Literal>] B' = "B"
let [<Literal>] C' = "C"
let strToMyDuOption (s: string) =
match s with
| A' -> Some A
| B' -> Some B
| C'-> Some C
| _ -> None
let strToMyDu (s: string) =
match s with
| A' -> A
| B' -> B
| C'-> C
| s -> failwith $"MyDu case {s} is unknown."
let myDuToStr (x: MyDu) =
match x with
| A -> A'
| B -> B'
| C -> C'
// LINQPad
let dump x = x.Dump()
strToMyDuOption A' |> dump
strToMyDuOption "x" |> dump
myDuToStr A |> dump

Simply parse command line args

I want to use the following code to restrict there is only one argument. However, I got the following error at first :: NIL?
Error 1 This expression was expected to have type
string []
but here has type
'a list
[<EntryPoint>]
let main argv =
match argv with
| first :: NIL ->
.... do something with first
| _ -> failwith "Must have only one argument."
The command line arguments are passed as an array, not a list.
Do something like this if you expect exactly one argument:
match argv with
| [|first|] ->
// .... do something with first
| _ -> failwith "Must have only one argument."
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
How about Active Patterns for parsing individual commands, returning an Option indicating None on failure and Some with the content of recovered parameters, possibly nestled in their own dedicated type. Converting "argv" to "string list" is merely a convenience act due to the convenience of list syntax in F#. Note: there are a lot of type annotations here that are typically unnecessary.
type Cmd1Parms = ....
type Cmd2Parms = ....
let performCmd1 cmd1Parms = ...
let performCmd2 cmd2Parms = ...
let commandNotFound argL = ...
let (|ParseForCmd1|_|) argL : Cmd1Parms option = ....
let (|ParseForCmd2|_|) argL : Cmd2Parms option = ....
[<EntryPoint>]
let main argv =
let argL = List.ofSeq<string> argv
match argL with
| ParseForCmd1 cmd1Parms -> performCmd1 cmd1Parms
| ParseForCmd2 cmd2Parms -> performCmd2 cmd2Parms
| _ -> commandNotFound argL

F# match with ->

I want to make something like it (Nemerle syntax)
def something =
match(STT)
| 1 with st= "Summ"
| 2 with st= "AVG" =>
$"$st : $(summbycol(counter,STT))"
on F# so is it real with F#?
There is no direct support for that but you can mimic the effect like this as well:
let 1, st, _ | 2, _, st = stt, "Summ", "AVG"
sprintf "%s %a" st summbycol (counter, stt)
If I understand you correctly, you'd like to assign some value to a variable as part of the pattern. There is no direct support for this in F#, but you can define a parameterized active pattern that does that:
let (|Let|) v e = (v, e)
match stt with
| Let "Summ" (st, 1)
| Let "AVG" (st, 2) -> srintf "%s ..." st
The string after Let is a parameter of the pattern (and is passed in as value of v). The pattern then returns a tuple containing the bound value and the original value (so you can match the original value in the second parameter of the tuple.

F# Pattern Matching with lambdas

I want to set the value of IsParentRoot "0" if the input is "0" or execute some code if else:
let isParentRoot parVal =
match parVal with
| "0" -> "0"
| x -> (fun x ->
"something")
I'm trying this but this does not compile with the error "This function takes too many arguments, or is used in a context where a function is not expected". Any idea?
Thanks
Wouldn't you have to supply an argument to your function for that to compile?
Something like
let isParentRoot parVal =
match parVal with
| "0" -> "0"
| x -> (fun y -> "something") x
because otherwise the last match would try returning a function that returns a string, while the first one would return a string. Mixing both isn't allowed.
But I think your approach may likely be wrong here. Functions return values (or unit). If you want to explicitly change something the general functional idiom is to return a new value where you changed what you wanted to change. Programming with side-effects (which your "set something to "0" if foo and do something entirely else if not" would be) is pretty much non-FP-like. But I'm still an F# beginner and just started two days ago to look into the language, so I might be mistaken here.
All possible return values of a function must be of the same type. So your case does not work since in one branch you are returning a String and in the other branch you are returning a function.
This would work:
let isParentRoot parVal =
match parVal with
| "0" -> (fun _ -> "0")
| x -> (fun _ -> "something")
If you really want your case to work you could upcast both return values to Object.
You don't need to define a lamba at this point, I believe you can just write:
let isParentRoot parVal =
match parVal with
| "0" -> "0"
| x -> CODE BLOCK
GOES HERE
i.e. to append x to a list and read the list out (just so there's meaningful code in here) you could just write:
let isParentRoot parVal =
match parVal with
| "0" -> "0"
| x -> let roots = List.append oldroots x
List.iter (fun r -> printfn "root: %s" r.ToString()) roots
Assuming you've previously defined your oldroots list that is.

Resources