Removing nested matches in F#? - f#

Suppose I have types like this:
type C =
| W of int
| Z of int
type B =
{
C : C
D : int
}
type A =
| X of int
| Y of B
And I would like to do something for the Z case only:
let a =
Y
{
C = Z 123
D = 456
}
match a with
| X _ -> ()
| Y b ->
match b.C with
| W _ -> ()
| Z z -> printfn "%i" z
Is there a way to achieve this in a single match?
Can this be made more concise?

Yes, there is indeed! Patterns can be nested, that's their primary point. You can match on Y, and inside that match on the fields of B, and inside that match on C.
Like this:
match a with
| X _ -> ()
| Y { C = Z z } -> printfn "%i" z
| Y { C = W _ } -> ()
And since you're returning unit in both non-Y.C.Z cases, you can combine them in a catch-all pattern:
match a with
| Y { C = Z z } -> printfn "%i" z
| _ -> ()

Related

Possible to optimize pattern-matching code segment in one line?

I am wondering if there is a way to write this line without piping h to calcVol function twice?
| h :: t when (h |> calcVol) > maxVol -> maxLoop t (h |> calcVol)
Where h is a tuple containing three dimensions, and calcVol returns a float value.
I know that I could explicitly define a vol value as:
| h :: t ->
let vol = calcVol h
if vol > maxVol then...
I am wondering if there is a way to do this nicely in one line?
If all the uses of vol were before the arrow, you could do this:
| h :: t when let vol = (h |> calcVol) in vol > maxVol -> // Something
But let assignments in the when clause left of the arrow do not carry over to the right-hand side. Demonstration:
let f x = x + 5
let l = [1; 2]
match l with
| a :: b when let y = f a in y = 6 -> "Six"
| _ -> "Other"
This works, and returns "Six". But:
let f x = x + 5
let l = [1; 2]
match l with
| a :: b when let y = f a in y = 6 -> sprintf "Six = %d" y
| _ -> "Other"
This does not work, producing the error:
error FS0039: The value or constructor 'y' is not defined.
So unfortunately, you can't have the one-line version you want and you'll have to go with the longer approach (with a let followed by an if, as you demonstrate in the second half of your answer).
Using active patterns a solution could look like this:
let calcVol v = v
let (|MaxVol|) maxVol = function
| [] -> (maxVol, [])
| h :: t -> ((max (calcVol h) maxVol), t)
let rec maxLoop list m =
match list with
| [] -> m
| MaxVol m (c, t) -> maxLoop t c
let vs = [ -1; 42; 3 ]
maxLoop vs System.Int32.MinValue // 42
Another possibility with better readability might be to first calculate the volumes (e.g. by mapping) and then find the maximum. Difficult to tell without the complete code...

Could the dictionary of operations be eliminated from this generalized Set definition?

I am trying to generalize the concept of a Set in F#. Among other things I want to define sets using inequalities. This would help me simplifying some sections of my code. So I created a type MySet as follows:
type Comparison = | GE
| GT
| LE
| LT
| EQ
type ComparisonOps<'t> = { gt: 't->'t->bool
ge: 't->'t->bool
eq: 't->'t->bool
le: 't->'t->bool
lt: 't->'t->bool }
type MySet<'t when 't : comparison> =
| List of list<'t>
| Sequence of seq<'t>
| Array of 't []
| String of string
| Set of Set<'t>
| Compare of (ComparisonOps<'t>*Comparison*'t)
Note: I intend to make MySet recursive later, allowing for unions and intersections, but for the purposes of this question this is not necessary.
The whole point of the new MySet type is to allow checking if elements of different types belong to sets of different cases. This is implemented by this function:
let elementOf<'t when 't : comparison> (st: MySet<'t>) (x: 't) : bool =
match st with
| List xs -> List.contains x xs
| Sequence s -> Seq.contains x s
| Array a -> Array.contains x a
| Set st -> Set.contains x st
| String str -> match box str with
| :? string as s -> match box x with
| :? string as z -> s.Contains z
| _ -> false
| _ -> false
| Compare (comp: ComparisonOps<'t>*Comparison*'t) ->
let compOps, cmp, y = comp
match cmp with
| GT -> compOps.gt x y
| GE -> compOps.ge x y
| EQ -> compOps.eq x y
| LE -> compOps.le x y
| LT -> compOps.lt x y
Note: I also plan to generalize elementOf allowing for function application, but again this is not needed here.
The function works:
let myStringSet = MySet.String("XYZ")
let strb = "X" |> elementOf<string> myStringSet
printfn "strb = %b" strb // strb = true
let myListSet = MySet.List([0..10])
let listb = 5 |> elementOf<int> myListSet
printfn "listb = %b" listb // listb = true
let myCompSet = MySet.Compare((ComparisonFloat, GT, 0.0))
let compb = -1.0 |> elementOf<float> myCompSet
printfn "compb = %b" compb // compb = false
let myCompSet2 = MySet.Compare((ComparisonString, LT, "XYZ"))
let compb2 = "XA" |> elementOf<string> myCompSet2
printfn "compb2 = %b" compb2 // compb2 = true
That is great, but I wonder if I really need to create the dictionary of operations ComparisonOps, since operations like < are polymorphic on the types int, float and string anyway.
Eliminating ComparisonOps could considerably simplify the code. Is that possible?
As Fyodor Soikin notes, it sounds like maybe what you want is to define a set as all elements satisfying a predicate:
type MySet<'t> = | MySet of ('t -> bool)
Then set operations are easy to define:
let intersect (MySet p1) (MySet p2) = MySet(fun t -> p1 t && p2 t)
And all of your specific constructors can just be turned into simple functions:
let ofList l = MySet(fun t -> List.contains t l)
let lt x = MySet(fun t -> t < x)

Built-in "< > compare" doesn't work with "IComparable<T>"?

I have a Discriminated Union, and I hope to use built in operators like > < compare max for it.
[<CustomComparison>]
type SymbolType =
| A
| B
| C
| D
interface IComparable<SymbolType> with
member x.CompareTo y =
match x, y with
| A, A-> 0
| A, _ -> 1
| _, A-> -1
| _, _ -> 0
I understand I can use IComparable, but then i have to do a null check, what's worse is that I have to cast it like (SymbolType) y which I assume would be time consuming.
You can already use standard comparison operators on the type. The built-in implementation uses the order of declarations of the individual cases, so:
type SymbolType = A | B | C | D
// Behavior of built-in comparison
A < B = true
D <= C = false
max B D = D
This looks very fragile, so maybe it is not the best thing to rely on. If you have cases that do not contain other values, you can use enum instead of discriminated union and define the ordering you wish:
type SymbolType =
| A = 1
| B = 2
| C = 4
| D = 3
// The order is now defined by your code
SymbolType.C < SymbolType.D = false
You can just implement the required methods with thin wrappers:
[<CustomComparison>]
[<CustomEquality>]
type SymbolType =
| A
| B
| C
| D
override x.Equals y =
match y with
| :? SymbolType as t -> (((x :> IComparable<_>).CompareTo) t)=0
| _ -> false
interface IComparable with
member x.CompareTo y =
match y with
| :? SymbolType as t -> ((x :> IComparable<_>).CompareTo) t
| _ -> failwith "bad comparison"
interface IComparable<SymbolType> with
member x.CompareTo y =
match x, y with
| A, A-> 0
| A, _ -> 1
| _, A-> -1
| _, _ -> 0
This way does avoid any duplicate typing.
On CLR, operators are static functions, so you can't define them in an interface. But boxing can also be avoided if your use the interface as a constraint of type parameter of a generic function.
int Compare<T>(T lhs, T rhs) where T : IComparable<T>
{
return lhs.CompareTo(rhs) // no boxing
}
Sorry, I'm not familiar with F#, so I wrote the example in C#.

OR pattern matching

I'm trying to use an OR pattern, as described here:
let foo = function
| Some (0, x) when x > 0 | None -> "bar"
| _ -> "baz"
However, this gives a compiler error:
error FS0010: Unexpected symbol '|' in pattern matching. Expected '->'
or other token.
What am I doing wrong? Does it have to do with the when guard?
A when guard refers to a single case, regardless of how many patterns are combined. The cases need to be separated:
let foo = function
| Some (0, x) when x > 0 -> "bar"
| None -> "bar"
| _ -> "baz"
For that reason, it may be better to factor out the return value, so a possibly complex expression isn't repeated:
let foo value =
let ret = "bar"
match value with
| Some (0, x) when x > 0 -> ret
| None -> ret
| _ -> "baz"
Using an active pattern is another way to avoid such repetition:
let (|Bar|_|) = function
| Some(0, x) when x > 0 -> Some()
| None -> Some()
| _ -> None
let foo = function
| Bar -> "bar"
| _ -> "baz"
You'll need two separate match cases there because the two cases bind different sets of variables (x and nothing, respectively):
| Some(0, x) when x>0 -> "bar"
| None -> "bar"
A nice trick I sometime use when you want to guard only specific bindings of a label, in a very complex pattern, is to use my own active patterns and the & (and) pattern operator:
let (|GreaterThan|_|) lowerLimit n =
if n > lowerLimit then Some () else None
let (|LesserThan|_|) upperLimit n =
if n < upperLimit then Some () else None
let (|GreaterOETo|_|) lowerLimit n =
if n >= lowerLimit then Some () else None
let (|LesserOETo|_|) upperLimit n =
if n <= upperLimit then Some () else None
let (|InRange|_|) (lowerLimit, upperLimit) n =
if n >= lowerLimit && n <= upperLimit then Some () else None
let (|Even|Odd|) n =
if n % 2 = 0 then
Even (n / 2)
else
Odd (n / 2)
type Union =
| A of int
| B of int
| A' of int
let getSpecialCases = function
| A (Even (x & GreaterThan 4 & LesserOETo 16))
| A (Odd (x & GreaterThan 0))
| B (x & LesserOETo 0)
| A' (Even (x & InRange (5, 16)))
| A' (Odd (x & GreaterThan 0)) -> Some x
| _ -> None
And of course you can just make a function to active pattern wrapper:
let (|P|_|) pred x =
if pred x then Some () else None
let ``match`` = function
| Even (x & pred (fun x -> x >= 7 && x <= 54)) -> Some x
| _ -> None

F# Map Trouble

I am having trouble groking F#'s Map class. I created a simple, naive lambda calculus evaluation function,
type Name = string
type Term =
| Var of Name
| Lit of int
| App of Term * Term
| Lam of Name * Term
let rec lookup(v, e) =
match e with
| (v1, t)::tl -> if v1 = v then t else lookup(v, tl)
| [] -> failwith "unknown variable %s" v
let rec eval(x, e) =
match x with
| Var x -> lookup(x, e)
| Lit x -> Lit x
| App (Lam(v, f), t2) -> eval(f, ((v, t2)::e))
| _ -> failwith "Invalid"
The obvious optimization to this is to change the list to a Map so I came up with,
let rec eval2(x, e: Map<Name,Term>) =
match x with
| Var v -> e.[v]
| Lit l -> x
| App (Lam (v, f), t) -> eval2(f, e.Add(v, t))
| _ -> failwith "Invalid term"
Given the values,
let ident = Lam ("x", Var "x")
let prog = App (ident, Lit 3)
why does,
let x = eval(prog, [])
succeed but,
let x2 = eval2(prog, Map [])
throw a key not found exception?
I don't repro this behavior (using F# 1.9.6.2, it works for me):
#light
type Name = string
type Term =
| Var of Name
| Lit of int
| App of Term * Term
| Lam of Name * Term
let rec eval2(x, e: Map<Name,Term>) =
match x with
| Var v -> e.[v]
| Lit l -> x
| App (Lam (v, f), t) -> eval2(f, e.Add(v, t))
| _ -> failwith "Invalid term"
let ident = Lam ("x", Var "x")
let prog = App (ident, Lit 3)
let x2 = eval2(prog, Map [])
printfn "%A" x2 // Lit 3

Resources