Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pattern matching casting types

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:

like image 466
Kevin Wright II Avatar asked Mar 21 '18 10:03

Kevin Wright II


People also ask

What is C# pattern matching?

Pattern matching is a technique where you test an expression to determine if it has certain characteristics. C# pattern matching provides more concise syntax for testing expressions and taking action when an expression matches.

Which operator does pattern matching?

LIKE operator is used for pattern matching, and it can be used as -. % – It matches zero or more characters.

Is Vs as C#?

Differences Between As and Is The is operator is used to check if the run-time type of an object is compatible with the given type or not, whereas the as operator is used to perform conversion between compatible reference types or nullable types. The is operator is of Boolean type, whereas the as operator is not.

What is cast operator in C#?

Cast, in the context of C#, is a method by which a value is converted from one data type to another. Cast is an explicit conversion by which the compiler is informed about the conversion and the resulting possibility of data loss.


1 Answers

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" 
like image 198
Francesco Blue Avatar answered Oct 03 '22 19:10

Francesco Blue