Decostruisci in linea con Type Testing in Pattern Matching F#

Aug 26 2020

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

3 NghiaBui Aug 26 2020 at 14:27

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
  | _ -> ""
torbonde Aug 26 2020 at 16:24

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]"}
CodingEdgar Aug 26 2020 at 23:15

Questa è attualmente una proposta in fslang-suggerimenti:

https://github.com/fsharp/fslang-suggestions/issues/830

Questa sintassi non esiste ancora