Pattern matching casting types - f#

I am a newbie in F# and have been following guides to try to make a piece of code work but it hasn't.
I create types of single and coop sports through inheritance.
Then I use pattern matching to know the type and, if it is a coop sport, get also the number of players. Then rank each accordingly.
However, I have been getting errors. I followed Microsoft examples on this and I don't really understand the errors. I don't have a functional programming background.
type Sport (name: string) =
member x.Name = name
type Individual(name: string) =
inherit Sport(name)
type Team(name: string, numberOfPlayers : int) =
inherit Sport(name)
member x.numberOfPlayers = numberOfPlayers
let MK = new Individual("Combate Mortal")
let SF = new Individual("Lutadores de Rua")
let Tk = new Individual("Tekken Chupa")
let MvC = new Team("Marvel Contra Capcom", 3)
let Dbz = new Team("Bolas do Dragao", 3)
let interpretSport (sport:string) (players:int) =
match sport with
| "Combate Mortal" -> printfn "Rank1"
| "Lutadores de Rua" -> printfn "Rank2"
| "Tekken Chupa" -> printfn "Rank3"
| "Bolas do Dragao" -> printfn "Rank4. No of players: %d " players
| "Marvel Contra Capcom" -> printfn "Rank5. No of players: %d" players
| _ -> printfn "not a sport in our list..."
let matchSport (sport:Sport) =
match sport with
| :? Individual -> interpretSport(sport.Name)
| :? Team as teamSport -> interpretSport(teamSport.Name,teamSport.numberOfPlayers)
| _ -> printfn "not a sport"
matchSport(MK)
matchSport(SF)
matchSport(Tk)
matchSport(MvC)
matchSport(Dbz)
1st error when calling function with more than 1 argument:
2nd error when printing:

The question has already been answered, but because the asker says he is a newby in F#, maybe it's worth to iterate a little.
To begin, you define a function with two parameters:
let interpreteSport (sport:string) (player:int) =
In F#, there is no notion of optional parameters in the same sense that they exist in C#, so if you declare a function with two parameters, and you want to invoke it, and get its return value, you must supply all the parameters you put in its definition.
So in the first branch of your match expression, when you write:
:? Individual -> interpretSport(sport.Name)
you are making an error, passing only one parameter to a function that takes two.
But wait! Why the compiler don't alert you with an error saying you are calling a function with one parameter when it expects two?
Because it turns out that what you write, even if it does not call the interpreteSport function as you believed, it's a perfect valid expression in F#.
What it returns is an expression called "partially applied function", that is, a function that has received its first parameter, and is waiting for another one.
If you assign the result of such an expression to a value, let's say:
let parzFun = interpretSport sport.Name
you can then pass this value around in your code and, when you are ready to supply the missing parameter, evaluate it like this:
let result = parzFun 1
That's what the compiler is telling you when it talks about 'int -> unit': function signatures in F# are given in this form:
a -> b -> c -> d -> retval, where a, b, c, d etc. are the types of the parameters, and retVal the return value.
Your interpreteSport function has a signature of: string -> int -> unit, where unit is the special type that means 'no value', similar to C# void, but with the big difference that unit is an expression that you can correctly assign to a value, while void is just a keyword, and you cannot assign a variable to void in C#.
OK, so, when you call your function passing only the first parameter (a string), what you obtain is an expression of type int -> unit, that is another function that expects and integer and returns unit.
Because this expression is in a branch of a match expression, and because all the branches of a match expression must return the same type, the other 2 branches are also expected to return an int -> unit function, what it's not, and that explain your second error.
More on this in a moment, but before, we must look at the first error reported by the compiler, caused by this line of code:
:? Team as teamSport -> interpretSport(teamSport.Name,teamSport.numberOfPlayers)
Here, you are thinking your are calling your function with 2 parameters, but your are actually not: when you put 2 values in parenthesis, separated by a comma, you are creating a tuple, that is, a single value composed of two or more values. It's like your are passing again only the first parameter, but now with the wrong type: the first parameter of you function is a string, and you are instead passing a tuple: ('a * 'b) is how F# represents tuples: that means a single value composed of a value of type 'a (generic, in your case string) and another of type 'b (generic, in your case integer).
To call your function correctly you must call it so:
:? Team as teamSport -> interpretSport teamSport.Name teamSport.numberOfPlayers
But even if you limit yourself to this correction you will have all the same the second error because, remember, the first expression of your match returns a partially applied funcion, so int -> unit (a function that expects an integer and returns a unit) while your second and your third expressions are now of type unit, because they actually call two functions that return unit (interpreteSport and printfn). To completely fix your code, as has already been said in other answers, you must supply the missing integer parameter to the first call, so:
let matchSport (sport:Sport) =
match sport with
| :? Individual -> interpretSport sport.Name 1
| :? Team as teamSport -> interpretSport teamSport.Name teamSport.numberOfPlayers
| _ -> printfn "not a sport"

If this is a F# learning exercise then it's best to avoid classes and inheritance completely. The fundamental idiomatic F# types are records and discriminated unions.
The intent of your code is not clear to me at all, but I have attempted to refactor to remove the use of classes:
type Players =
| Individual
| Team of numberOfPlayers:int
type Sport = { Name : string; Players : Players }
let MK = { Name = "Combate Mortal"; Players = Individual }
let SF = { Name = "Lutadores de Rua"; Players = Individual }
let Tk = { Name = "Tekken Chupa"; Players = Individual }
let MvC = { Name = "Marvel Contra Capcom"; Players = Team 3 }
let Dbz = { Name = "Bolas do Dragao"; Players = Team 3 }
let interpretSport (sport:Sport) =
let players =
match sport.Players with
| Individual -> ""
| Team numberOfPlayers -> sprintf ". No of players: %d" numberOfPlayers
let rank =
match sport.Name with
| "Combate Mortal" -> Some 1
| "Lutadores de Rua" -> Some 2
| "Tekken Chupa" -> Some 3
| "Bolas do Dragao" -> Some 4
| "Marvel Contra Capcom" -> Some 5
| _ -> None
match rank with
| Some r -> printfn "Rank%d%s" r players
| None -> printfn "not a sport in our list..."

Your function interpretSport has two arguments but your first call to it has only one. Try calling it like so:
| :? Individual -> interpretSport sport.Name 1
Also, the second call uses tupled parameters but the function is declared to take curried parameters. Try calling it like so:
| :? Team as teamSport -> interpretSport teamSport.Name teamSport.numberOfPlayers

There are several small problems with your code. The most obvious is in matchSport, you are calling interpretSport in an uncurried style, with an argument tuple. The call should look like:
Team as teamSport -> interpretSport teamSport.Name teamSport.numberOfPlayers
However, that's a problem, because on the first case of the pattern matching you call interpretSport with only one argument, so you partially apply it and you get a type int -> unit, but when you fully apply it on the second case you get unit, and of course all types of the pattern matching cases must match. The cheapest solution would be to add a 1 to your call on the first case like this:
Individual -> interpretSport sport.Name 1
But you probably want to use the sports you bound before (maybe in a list you give as a parameter) to do the checking. It is in general a bad idea (in functional programming and elsewhere) to hard-code that many strings, you probably want to do some kind of association List, or Map of the Sports to the Ranks, then fold over the sports List and match when found with individual or team and then print whatever the Map gives you for that case. This would be shorter and more extensible.

Related

How to check if list contains discriminated union case with type?

Given the follwing code:
type Creature =
{ Strength: int
Toughness: int }
type CardType =
| Creature of Creature
| Land
| Instant
type Card =
{ Types: CardType list }
module Card =
let isType t card = List.contains t card.Types
I am able to write
Card.isType Land
When trying to check if card is a creature, i get the following error:
This expression was expected to have type
'CardType'
but here has type
'Creature -> CardType'
Is it even possible to have a "isType" function like this or am I stuck with pattern matching on a separate "isCreature" function instead?
Unless you want to resort to various reflection-based hacks, you are stuck with pattern matching. I would probably define a bit more general function using List.exist rather than List.contains (taking a predicate). Then you can easily define three functions for your specific card types:
module Card =
let isType t card =
List.exists t card.Types
let isCreature =
isType (function Creature _ -> true | _ -> false)
let isLand = isType ((=) Land)
let isInstant = isType ((=) Instant)
For Land and Instant, you can just check if the value equals the specific one you're looking for. For Creature, this requires pattern matching - but can be done quite nicely using function.

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

Is there a generic function that identifies the cases of a nested discriminated union?

I created a nested Discriminated Union (DU) as follows:
type OptimizationPeriod = | All
| Long
| Short
type OptimizationCriterion = | SharpeRatio of OptimizationPeriod
| InformationRatio of OptimizationPeriod
| CalmarRatio of OptimizationPeriod
and also a non-nested DU:
type Parallelism = Sequential | PSeq
I have a JSON configuration file with strings that define the DU cases. The following function manages to identify the case of the non-nested Parallelism DU :
let stringToDUCase<'t> (name: string) : 't =
let dUCase =
Reflection.FSharpType.GetUnionCases( typeof<'t> )
|> Seq.tryFind (fun uc -> uc.Name = name)
|> Option.map (fun uc -> Reflection.FSharpValue.MakeUnion( uc, [||] ) :?> 't)
match dUCase with
| Some x -> x
| _ -> let msg = sprintf "config.json - %s is not a case in DU %A" name typeof<'t>
failwith msg
Note: I certainly copied it from somewhere as the function is a bit over my head, apologies to the author for not remembering where it came from.
Unfortunately this function fails to identify the case for the nested DU:
stringToDUCase<OptimizationCriterion> config.Trading.Criterion
System.Exception: config.json - SharpeRatio All is not a case in DU FractalTypes.OptimizationCriterion
Two questions:
1) I was able to write a function that deals specifically with the OptimizationCriterion DU and is able to identify the case. Is there a generic function along the lines of stringToDUCase that could do the same?
2) Would it be better to use a tuple of type OptimizationCriterion*OptimizationPeriod instead of a nested DU? (I probably would have to call stringToDUCase twice, but that is not a problem)
An "empty" DU case like All is just a value, but a "non-empty" DU case like SharpeRatio is actually a function that takes one value and returns the type. In this case, SharpeRatio has the type OptimizationPeriod -> OptimizationCriterion.
Your existing stringToDUCase function always passes an empty array into MakeUnion (implying an empty DU case). So here's a modified version of the function that works for any DU case:
let stringToParamDUCase<'t> (name: string) =
Reflection.FSharpType.GetUnionCases(typeof<'t>)
|> Seq.tryFind (fun uc -> uc.Name = name)
|> Option.map (fun uc ->
fun (parameters:obj []) -> Reflection.FSharpValue.MakeUnion(uc, parameters) :?> 't)
|> Option.defaultWith (fun () ->
failwith (sprintf "config.json - %s is not a case in DU %A" name typeof<'t>))
Note that it returns a function of obj [] -> 't. I've also simplified the error handling a little bit.
This is how you might use it:
let myOptimizationPeriod = stringToParamDUCase<OptimizationPeriod> "All" [||]
let f = stringToParamDUCase<OptimizationCriterion> "SharpeRatio"
let myOptimizationCriterion = f [|All|]
I think the existing answer should answer your question directly. However, I think it is worth making two additional points. First, it might be easier if you represented your OptimizationCriterion as a record, because all your DU cases contain the same value:
type OptimizationPeriod =
| All | Long | Short
type OptimizationRatio =
| SharpeRatio | InformationRatio | CalmanRatio
type OptimizationCriterion =
{ Ratio : OptimizationRatio
Period : OptimizationPeriod }
This happens to solve your problem too, because now you only need DUs without parameters, but I think it is also better design, because you avoid duplicating the second parameter.
Second, I don't think you really need to go with a fancy custom reflection-based function for deserialization. If you want to store your data in a JSON, you should either use standard library (Newtonsoft.JSON or Chiron will do just fine), or you can write this directly using something like JsonValue from F# Data, but using custom reflection code is a quick way leading to unmaintainable code.

Type test pattern matching for DUs

With DU (Discriminated Union types), how do I perform a type test pattern matching ?
I have this following running code :
type IU =
|Int of int
|Unit of Unit
let x = IU.Int(3)
let y = IU.Unit(())
let z = [3.14]
let showI (v) =
match box v with
| :? IU ->
match v with
| Int(_) -> "an IU int"
|_ -> "not a IU.int"
|_ -> "not a IU.int"
But I am not happy with the inner match in the showI function. I would have preferred something like :
let showI (v) =
match box v with
| :? IU.Int -> "an int"
|_ -> "not a IU.int"
which doesn't compile (error : the type Int is not defined).
Is there an obvious syntax I missed ? Thanks.
Note : showI function accepts a variable with an unknowned type ; that is the reason for the smelly box v.
As others have pointed out, I don't think there's any built-in language feature that lets you do this. However, you could define an active pattern that performs the type test:
let (|IsIU|_|) (candidate : obj) =
match candidate with
| :? IU as iu -> Some iu
| _ -> None
This active pattern has the type obj -> IU option.
You can compose your own custom active pattern with standard patterns, like this:
let showI = function
| IsIU (IU.Int i) -> "an IU int"
| _ -> "not a IU.int"
In this example, the custom IsIU active pattern has been composed with a standard identifier pattern that matches on the IU.Int case.
Here's a sample FSI session showing usage with the x, y, and z values given in the OP:
> showI x;;
val it : string = "an IU int"
> showI y;;
val it : string = "not a IU.int"
> showI z;;
val it : string = "not a IU.int"
Staying within the context of your question I believe what you are missing is that IU.Int is not a type, but a case Int of discriminated union type IU. When you write
let x = IU.Int(3)
the type of value x is IU, not IU.Int. That's why compiler barks upon your attempt to match obj to UI.Int with :? pattern.
In a broader context, it seems you try approaching F# a-la dynamic language of Javascript kind, which it is not. Exaggerating a bit, you seemingly try using functions operating upon arguments of only one type obj and hence spending substantial run-time effort on dynamic discovery of specific argument types with wide opportunities for making mistakes on the way.
Such approach misses the whole point of F# idiomatic DU use case, which is disassembling of a value that is known to be statically typed as IU by pattern match machinery to specific union case (IU.Int or IU.Unit):
let showI (v : IU) = // explicit argument type is added to illuminate the point
match v with
| IU.Int(x) -> sprintf "a IU.Int(%i) value" x
| _ -> "a IU.Unit"
So, if you by mistake try calling showI with argument that is not of type IU, compiler will catch the erroneous use of your function with argument of wrong type right away and simply will not build the executable form of your code until the mistake is corrected.
EDIT: Idiomatic use aside you may get away with a single match, indeed, with the help of when guard, like in a snippet below, although this is a nasty hack:
open Microsoft.FSharp.Reflection
let showI (v) =
match box v with
| :? IU as x when (fst(FSharpValue.GetUnionFields(x, typeof<IU>))).Name.Equals("Int")
-> "an IU.Int"
| _ -> "not an IU.Int"

F# Pattern-matching by type

How pattern-matching by type of argument works in F#?
For example I'm trying to write simple program which would calculate square root if number provided or return it's argument otherwise.
open System
let my_sqrt x =
match x with
| :? float as f -> sqrt f
| _ -> x
printfn "Enter x"
let x = Console.ReadLine()
printfn "For x = %A result is %A" x (my_sqrt x)
Console.ReadLine()
I get this error:
error FS0008: This runtime coercion or type test from type
'a
to
float
involves an indeterminate type based on information prior
to this program point. Runtime type tests are not allowed
on some types. Further type annotations are needed.
Since sqrt works with float I check for float type, but guess there could be better solution - like check if input is number (in general) and if so, cast it to float?
The problem here is that the type of x is actually a string. Adding that it comes from Console.ReadLine, what kind of information is stored in that string is only possible to determine at runtime. This means that you can't use neither pattern matching, nor pattern matching with coercion here.
But you can use Active Patterns. As what actual data is stored in x is only known at runtime, you have to parse the string and see what is contains.
So suppose you are expecting a float, but you can't be sure since user can input whatever they want. We are going to try and parse our string:
let my_sqrt x =
let success, v = System.Single.TryParse x // the float in F# is represented by System.Single in .NET
if success then sqrt v
else x
But this won't compile:
This expression was expected to have type float32 but here has type string
The problem is that the compiler inferred the function to return a float32, based on the expression sqrt (System.Single.Parse(x)). But then if the x doesn't parse to float, we intend to just return it, and as x is a string we have an inconsistency here.
To fix this, we will have to convert the result of sqrt to a string:
let my_sqrt x =
let success, v = System.Single.TryParse x
if success then (sqrt v).ToString()
else x
Ok, this should work, but it doesn't use pattern matching. So let's define our "active" pattern, since we can't use regular pattern matching here:
let (|Float|_|) input =
match System.Single.TryParse input with
| true, v -> Some v
| _ -> None
Basically, this pattern will match only if the input can be correctly parsed as a floating point literal. Here's how it can be used in your initial function implementation:
let my_sqrt' x =
match x with
| Float f -> (sqrt f).ToString()
| _ -> x
This looks a lot like your function, but note that I still had to add the .ToString() bit.
Hope this helps.
Just quoting the one and only Scott Wlaschin's 'F# for fun and profit' site:
Matching on subtypes You can match on subtypes, using the :? operator,
which gives you a crude polymorphism:
let x = new Object()
let y =
match x with
| :? System.Int32 ->
printfn "matched an int"
| :? System.DateTime ->
printfn "matched a datetime"
| _ ->
printfn "another type"
This only works to find subclasses of a parent class (in this case,
Object). The overall type of the expression has the parent class as
input.
Note that in some cases, you may need to “box” the value.
let detectType v =
match v with
| :? int -> printfn "this is an int"
| _ -> printfn "something else"
// error FS0008: This runtime coercion or type test from type 'a to int
// involves an indeterminate type based on information prior to this program point.
// Runtime type tests are not allowed on some types. Further type annotations are needed.
The message tells you the problem: “runtime type tests are not allowed
on some types”. The answer is to “box” the value which forces it into
a reference type, and then you can type check it:
let detectTypeBoxed v =
match box v with // used "box v"
| :? int -> printfn "this is an int"
| _ -> printfn "something else"
//test
detectTypeBoxed 1
detectTypeBoxed 3.14
In my opinion, matching and dispatching on types is a code smell, just
as it is in object-oriented programming. It is occasionally necessary,
but used carelessly is an indication of poor design.
In a good object oriented design, the correct approach would be to use
polymorphism to replace the subtype tests, along with techniques such
as double dispatch. So if you are doing this kind of OO in F#, you
should probably use those same techniques.

Resources