Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Match with complex type

I'm new to F# and I'm trying to do a simple pattern matching with a complex type but I cannot find the way of doing it. See the pseudo code below to explain the pattern matching that i want to do.

type Vector= {X:int; Y:int}

let calculateDirection vector = 
match vector with
| vector.X=0 && vector.Y>0 -> "N"    // pseudo code 
| vector.X>0 && vector.Y>0 -> "NE"   // pseudo code 
| vector.X>0 && vector.Y=0 -> "E"    // pseudo code 
| vector.X>0 && vector.Y<0 -> "SE"   // pseudo code 
| vector.X=0 && vector.Y<0 -> "S"    // pseudo code 
| vector.X<0 && vector.Y<0 -> "SW"   // pseudo code 
| vector.X<0 && vector.Y=0 -> "W"    // pseudo code 
| vector.X<0 && vector.Y>0 -> "NW"   // pseudo code 
| _ -> "Error"

I read a few tutorials (https://fsharpforfunandprofit.com/posts/match-expression/) but it's always simple scenarios and does not help me much. Or I just don't understand them clearly.

Thanks in advance.

like image 280
Cedric Royer-Bertrand Avatar asked Dec 23 '22 15:12

Cedric Royer-Bertrand


2 Answers

Using active patterns you can get very readable code like this:

type Vector= {X:int; Y:int}

let (|West|_|) v = if v.X < 0 then Some () else None
let (|East|_|) v = if v.X > 0 then Some () else None
let (|North|_|) v = if v.Y > 0 then Some () else None
let (|South|_|) v = if v.Y < 0 then Some () else None

let calculateDirection = function
    | North & East -> Some "NE"
    | North & West -> Some "NW"
    | North -> Some "N"
    | South & East -> Some "SE"
    | South & West -> Some "SW"
    | South -> Some "S"
    | East -> Some "E"
    | West -> Some "W"
    | _ -> None
like image 61
Matiasd Avatar answered Dec 26 '22 03:12

Matiasd


To match with records, you can use record match syntax, which is just like record construction syntax:

match vector with
| { X = x; Y = y } -> sprintf "Vector (%d, %d)" x y

You can combine that with guards, too:

match vector with
| { X = 0; Y = y } when y > 0 -> "N"
| { X = x; Y = y } when x > 0 && y > 0 -> "NE"
| { X = x; Y = 0 } when x > 0 -> "E"
...

But that looks a bit ugly. To help with the ugliness, you could also construct your own matchers (aka "active patterns") - which are just like regular functions, but can be used for matching. They have kind of a funny syntax:

let (|Positive|_|) x = if x > 0 then Some() else None
let (|Negative|_|) x = if x < 0 then Some() else None

match vector with
| { X = 0; Y = Positive } -> "N"
| { X = Positive; Y = Positive } -> "NE"
| { X = Positive; Y = 0 } -> "E"
| { X = Positive; Y = Negative } -> "SE"
...
like image 25
Fyodor Soikin Avatar answered Dec 26 '22 03:12

Fyodor Soikin