Active Pattern Matching with Discriminated Unions - f#

Is there any way to use a discriminated union of the following form with active pattern matching? I haven't been able to find any examples.
This is what I'm trying to do:
type c = a | b
type foo =
| bar1
| bar2 of c
//allowed
let (|MatchFoo1|_|) aString =
match aString with
| "abcd" -> Some bar1
| _ -> None
//not allowed
let (|MatchFoo2|_|) aString =
match aString with
| "abcd" -> Some (bar2 of a)
| _ -> None
Why can "Some" not be used in the second way? Is there another way to achieve the same thing?

You only need to use of when declaring the type, so you can just construct values with the bar2 constructor like:
bar2 a
Your second function should work if you change it to:
let (|MatchFoo2|_|) aString =
match aString with
| "abcd" -> Some (bar2 a)
| _ -> None

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 access "base" in Discriminated Union method override?

I have a DU and I'm overriding the Equals method. Based on the current DU value, I would like to call the base equality method or my custom one. However, it's not letting me access "base". Any idea on how to work around this?
type Test =
| A of string
| B of int64
override this.Equals(other) =
let other' = other :?> Test
match other' with
| A str -> str = "a"
| B i -> base.Equals this other //how do I do this?
First, any F# discriminated union will have obj as base class, so just use obj.Equals.
Second, Equals is a .NET method, not an F# function, so its arguments must be given in a tupled form - i.e. Equals(x,y) instead of Equals x y.
Finally, if you implement a custom Equals, you also need to add [<CustomEquality; NoComparison>]
So:
[<CustomEquality; NoComparison>]
type Test =
| A of string
| B of int64
override this.Equals(other) =
let other' = other :?> Test
match other' with
| A str -> str = "a"
| B i -> obj.Equals(this, other)

Construct generic function

I have a type:
type DictionaryCache<'a, 'b when 'a :comparison>()
And I have another type which contains some of this DictionaryCache:
type Cache() =
let user = new DictionaryCache<int, User>()
let userByLogin = new DictionaryCache<string, User>()
member this.User = user
member this.UserByLogin = userByLogin
In the last type I want to create generic function which will return one of the members based on input parameter:
member this.CacheNameToDictionary (cacheName: string) : DictionaryCache<'a, 'b> option =
match cacheName with
| "userByAutoincrementedId" -> Some(this.User)
| "userByLogin" -> Some(this.UserByLogin)
| _ -> None
But it doesn't work because of type mismatch.
Is there any way to rewrite this function ?
Update: here is a full code what I need to do:
type Cache() =
let user = new DictionaryCache<int, User>()
let userByLogin = new DictionaryCache<string, User>()
static let mutable instance = lazy(new Cache())
static member Instance with get() = instance.Value
member this.User = user
member this.UserByLogin = userByLogin
member this.Get (useCache: string) (cacheName: string) (id: 'a) longFunction exceptionFunction : 'b option =
let nameToDictionary() : DictionaryCache<'a, 'b> option =
match cacheName with
| "userByAutoincrementedId" -> Some(this.User)
| "userByLogin" -> Some(this.UserByLogin)
| _ -> None
let foo() : 'b option =
try
longFunction()
with
| exn -> exceptionFunction exn
None
match (useCache, nameToDictionary()) with
| "true", Some(dictionary) ->
match dictionary.Get id with
| Some(result) -> Some(result)
| _ -> match foo() with
| Some(result) -> dictionary.Put id result
Some(result)
| _ -> None
| _ -> foo()
This is not possible - the problem is that the return type of the method would depend on the string that it gets as the input argument. The input string is only known at run-time, but the type needs to be known at compile-time.
You could use the Choice type which lets you return one of multiple different types:
member this.CacheNameToDictionary (cacheName: string) =
match cacheName with
| "userByAutoincrementedId" -> Choice1Of3(this.User)
| "userByLogin" -> Choice2Of3(this.UserByLogin)
| _ -> Choice3Of3()
This works, but the return type lists all three alternatives and is pretty ugly:
Choice<DictionaryCache<int,User>, DictionaryCache<string,User>,unit>
Also, the consumer of this method will have to pattern match on the result and handle the two different dictionaries in different ways, so this might not make your code particularly beautiful.
Honestly, I think that you are adding a level of abstraction that you do not need. If there are two different keys, then you need different code to handle that and it's unlikely that you'll be able to write code that is extensible and adds third kind of dictionary.

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# How to have a value's type determined by a match statement?

Here is my problem:
let foo =
match bar with
| barConfig1 -> configType1(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
| barConfig2 -> configType2(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
| barConfig3 -> configType3(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
| barConfig4 -> configType4(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
I'd like to have the type of foo be determined by the match statement, but it always sets foo to the first type.
type bar =
|barConfig1
|barConfig2
|barConfig3
|barConfig4
In F#, there are no statements, only expressions, and each expression has to have a single concrete type. A match block is an expression as well, meaning that it has to have a single concrete type. What follows from that is that each case of the match has to have the same type as well.
That is, something like this is not valid F#:
let foo = // int? string?
match bar with // int? string?
| Int -> 3 // int
| String -> "Three" // string
In this case, the type inference mechanism will expect the type of the match to be the same as the type of the first case - int, and end up confused when it sees the string in the second. In your example the same thing happens - type inference expects all the cases to return a configType1.
A way around it would be by casting the values into a common supertype or interface type. So for your case, assuming the configTypes implement a common IConfigType interface:
let foo = // IConfigType
let arg = (devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
match bar with
| barConfig1 -> configType1(arg) :> IConfigType
| barConfig2 -> configType2(arg) :> IConfigType
| barConfig3 -> configType3(arg) :> IConfigType
| barConfig4 -> configType4(arg) :> IConfigType
If the output type has a limited number of cases, you can make that a discriminated union as well:
type ConfigType =
| ConfigType1 of configType1
| ConfigType2 of configType2
| ConfigType3 of configType3
| ConfigType4 of configType4``
let foo =
match bar with
| barConfig1 -> ConfigType1 <| configType1(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
| barConfig2 -> ConfigType2 <| configType2(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
| barConfig3 -> ConfigType3 <| configType3(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)
| barConfig4 -> ConfigType4 <| configType4(devices:DeviceEntities,DeviceStartIndex,inputStartIndex,outputStartIndex)``
Alternately, if they all implement an interface or inherit some base class, you can upcast to that, as with scrwtp's answer.

Resources