Reputation: 1465
Is there a way to use Type Test Pattern and Record Pattern inline?
I can make Record Pattern without problem like this:
let getName3 (a:A) =
match a with
| { name = name } -> name
And this is a perfectly valid code:
type IA =
abstract ID: string
type A =
{ id: string
name: string }
interface IA with
member this.ID = this.id
let getName (a: IA) =
match a with
| :? A as a -> a.name
| _ -> ""
getName
{ id = "1234"
name = "andrew" }
// val getName : a:IA -> string
// val it : string = "andrew"
This is what I'm talking about:
let getName2 (a: IA) =
match a with
| :? A ({name = name}) as a -> name // Type Test Pattern with Record Pattern inline, maybe even still use binding (as a)
| _ -> ""
Update
My previous example is too simple, use the following instead:
type IA =
abstract ID: string
type Stage =
| FirstStep
| SecondStep
type A =
{ id: string
name: string option
stage: Stage
}
interface IA with
member this.ID = this.id
// This is a "nested" pattern inline, I match two Option with one match
let tryGetName (a: A option) =
match a with
| Some { name = (Some name) } -> Some name
| _ -> None
// This is a more involved nested pattern inline
let tryGetStageAndName (a: A option) =
match a with
| Some { name = (Some name); stage = stage } -> Some (stage, name)
| _ -> None
// This is the syntax I'm looking for:
let tryGetStageAndName2 (a: IA option) =
match a with
// notice Some (:? A as a) -> is perfectly valid
| Some (:? A ({ name = (Some name); stage = stage }) -> Some (stage, name)
| _ -> None
I also want to clarify, my question is about F# Syntax, not ad-hoc scenarios or boxing around the specific type A
, as we can do nested inline patterns, is there a way to do patterns after a Type Test Pattern?
Upvotes: 1
Views: 293
Reputation: 1465
This is currently a proposal in fslang-suggestions:
https://github.com/fsharp/fslang-suggestions/issues/830
This syntax does not exist yet
Upvotes: 0
Reputation: 2459
I think Nghia Bui came up with a pretty good solution. I just wanted to add that you might want to make the active pattern generic, to handle more cases. I have renamed the active pattern case to Unbox
, which might not be entirely accurate. Anyway, see this example:
type IA =
abstract ID: string
type A =
{ id: string
name: string }
interface IA with
member this.ID = this.id
type B =
{ id: string
name: string
email: string }
interface IA with
member this.ID = this.id
let inline (|Unbox|_|)<'T when 'T :> IA> (a: IA) =
match a with
| :? 'T as a -> Some a
| _ -> None
let getName (a: IA) =
match a with
| Unbox {A.name = name} -> name
| Unbox {B.name = name} -> name
| _ -> ""
getName {id = "123"; name = "A name"}
getName {id = "567"; name = "B name"; email = "[email protected]"}
Upvotes: 0
Reputation: 3784
You can use Active Pattern. The idea is to convert the Type Test Pattern to another pattern in which the casted value is participated in the pattern, so we can nested the Record Pattern into it.
The code would be:
let (|IsA|_|) (a: IA) =
match a with
| :? A as a -> Some a
| _ -> None
let getName2 (a: IA) =
match a with
| IsA {name = name} -> name
| _ -> ""
Upvotes: 3