F# match with -> - f#

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.

Related

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

How to do pattern matching in Rx. Where and Select in a single operator?

Suppose I have this type:
type T = int option
and an observable of that type:
let o : IObservable<T> = // create the observable
I'm looking for a better way to express this:
o.Where(function | None -> false | Some t -> true)
.Select(function | Some t -> t)
An observable that only propagates the Some case.
There are several things that I don't like.
I'm using 2 operators
I'm pattern matching twice
The second pattern matching isn't exhaustive (makes visual studio show a warning and feels odd)
Too much code. The pattern repeats every time I need pattern matching.
Can't you use Observable.choose ? something like this :
let o1 : IObservable<int option> = // ...
let o2 = Observable.choose id o1
If you have a type that is not an option, say:
type TwoSubcases<'a,'b> = | Case1 of 'a | Case2 of 'b
and a partial active pattern:
let (|SecondCase|_|) = function
| Case1 _ -> None
| Case2 b -> Some b
then you can do:
let o1 : IObservable<TwoSubcases<int, float>> = // ...
let o2 : IObservable<float> = Observable.choose (|SecondCase|_|) o1
Thanks to #Lee I came up with a nice solution.
o.SelectMany(function | None -> Observable.Empty() | Some t -> Observable.Return t)
This works for any union type, not only Option.

F# pattern match directly against let binding

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.

How do I use Some/None Options in this F# example?

I am new to F# and I have this code:
if s.Contains("-") then
let x,y =
match s.Split [|'-'|] with
| [|a;b|] -> int a, int b
| _ -> 0,0
Notice that we validate that there is a '-' in the string before we split the string, so the match is really unnecessary. Can I rewrite this with Options?
I changed this code, it was originally this (but I was getting a warning):
if s.Contains("-") then
let [|a;b|] = s.Split [|'-'|]
let x,y = int a, int b
NOTE: I am splitting a range of numbers (range is expressed in a string) and then creating the integer values that represent the range's minimum and maximum.
The match is not unnecessary, the string might be "1-2-3" and you'll get a three-element array.
Quit trying to get rid of the match, it is your friend, not your enemy. :) Your enemy is the mistaken attempt at pre-validation (the "if contains" logic, which was wrong).
I think you may enjoy this two-part blog series.
http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!180.entry
http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!181.entry
EDIT
Regarding Some/None comment, yes, you can do
let parseRange (s:string) =
match s.Split [|'-'|] with
| [|a;b|] -> Some(int a, int b)
| _ -> None
let Example s =
match parseRange s with
| Some(lo,hi) -> printfn "%d - %d" lo hi
| None -> printfn "range was bad"
Example "1-2"
Example "1-2-3"
Example "1"
where parseRange return value is a Some (success) or None (failure) and rest of program can make a decision later based on that.

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