Decostruisci in linea con Type Testing in Pattern Matching F#
C'è un modo per utilizzare Type Test Pattern e Record Pattern inline?
Posso creare Record Pattern senza problemi come questo:
let getName3 (a:A) =
match a with
| { name = name } -> name
E questo è un codice perfettamente valido:
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"
Questo è ciò di cui sto parlando:
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)
| _ -> ""
Aggiornare
Il mio esempio precedente è troppo semplice, usa invece il seguente:
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
Voglio anche chiarire, la mia domanda riguarda la sintassi F #, non gli scenari ad hoc o il boxing attorno allo specifico type A, poiché possiamo creare modelli inline annidati, c'è un modo per creare modelli dopo un modello di test di tipo?
Risposte
Puoi usare il modello attivo. L'idea è di convertire il Type Test Pattern in un altro pattern in cui il valore cast è partecipe del pattern, in modo da poter annidare il Record Pattern in esso.
Il codice sarebbe:
let (|IsA|_|) (a: IA) =
match a with
| :? A as a -> Some a
| _ -> None
let getName2 (a: IA) =
match a with
| IsA {name = name} -> name
| _ -> ""
Penso che Nghia Bui abbia trovato una soluzione piuttosto buona. Volevo solo aggiungere che potresti voler rendere generico il modello attivo, per gestire più casi. Ho rinominato il caso del modello attivo in Unbox, che potrebbe non essere del tutto accurato. Comunque, guarda questo esempio:
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]"}
Questa è attualmente una proposta in fslang-suggerimenti:
https://github.com/fsharp/fslang-suggestions/issues/830
Questa sintassi non esiste ancora