2017-02-04 1 views
5

Mon but est d'écrire la liste des paires nom/valeur dans un DSL et rendre cela lisible. Les valeurs ici peuvent être int, float, string ou une liste de l'un de ces types. J'utilise string * obj paires et en leur passant à une fonction qui prend un (string * obj) list.Comment écrire une liste de (string * obj) sans utiliser upcast

Y at-il d'écrire la liste sans mettre à jour le paramètre obj ?.

let myfun (values:(string*obj) list) = 
    // Do something... 

// This is pretty ugly 
myfun ["Name", upcast "Freddie"; "Age", upcast 50] 

// This would be the ideal 
myfun ["Name", "Freddie"; "Age", 50] 

Répondre

8

Programmation 101: si vous vous trouvez à répéter la même chose et plus, emballez pour la réutilisation, faites une fonction. Dans votre cas, la fonction serait générique (par exemple prendre paramètre de tout type) et faire le upcast sur le paramètre:

let pair name value = name, value :> obj 
myfun [pair "Name" "Freddie"; pair "Age" 50] 

Hmm ... Pas beaucoup plus agréable, est-il? Mais attendez, nous n'avons pas encore fini! Maintenant que vous avez cette fonction, vous pouvez lui donner un nom plus joli, ce qui rendrait l'appel plus agréable. Dites, ==>:

let (==>) name value = name, value :> obj 
myfun ["Name" ==> "Freddie"; "Age" ==> 50] 

Si votre ensemble de types possibles est connu à l'avance et relativement faible (comme votre question semble indiquer), vous pouvez aller plus loin et avoir le contrôle du compilateur que seuls types autorisés sont utilisés . Pour ce faire, vous aurez besoin d'utiliser la méthode, les surcharges des contraintes de type statique résolus et une supercherie syntaxique:

type Casters() = 
    static member cast (v: string) = v :> obj 
    static member cast (v: float) = v :> obj 
    static member cast (v: int) = v :> obj 
    static member cast (v: string list) = v :> obj 
    static member cast (v: float list) = v :> obj 
    static member cast (v: int list) = v :> obj 

let inline cast (casters: ^c) (value: ^t) = 
    ((^c or ^t) : (static member cast : ^t -> obj) value) 

let inline (==>) name value = name, (cast (Casters()) value) 

["Name" ==> "Freddie"; "Age" ==> 50] // OK 
["What?" ==> true] // Error: "bool" is not an allowed type 
+0

Merci, mais votre fonction _pair_ me donne _La la contrainte statique de type » a à « b implique un type indéterminé sur la base d'informations avant cette point_ du programme. Ne voyez-vous pas la même erreur? [tryfsharp] (http://www.tryfsharp.org/create/seankearon/file3.fsx) –

+2

Une erreur. J'ai copié votre 'upcast' au lieu de l'opérateur de mise à jour approprié avec le type réel. Corrigé maintenant. –

+0

C'est adorable, merci! –

2

Votre dire que vos valeurs ne peuvent avoir certains types énumérés. Je me demande si vous avez une raison particulière d'utiliser obj au lieu d'une union discriminée, ce qui est parfaitement adapté à cette tâche?

J'ai modifié la réponse de Fiodor utiliser un type DU au lieu de obj:

type Value = 
    | Int  of int  | Float  of float  | String  of string 
    | IntList of int list | FloatList of float list | StringList of string list 

type Casters() = 
    static member cast v = Int v 
    static member cast v = Float v 
    static member cast v = String v 
    static member cast v = IntList v 
    static member cast v = FloatList v 
    static member cast v = StringList v 

let inline cast (casters: ^c) (value: ^t) = 
    ((^c or ^t) : (static member cast : ^t -> Value) value) 

let inline (==>) name value = name, (cast (Casters()) value) 

["Name" ==> "Freddie"; "Age" ==> 50] // OK 
["What?" ==> true] // Error: "bool" is not an allowed type 

L'avantage de cette approche est que vous avez maintenant correspondant de motif à carreaux type lorsque vous accédez aux valeurs, et vous n « t doivent faire downcasting dangereux du obj:

let myfun (values:(string*Value) list) = 
    values 
    |> List.map (fun (k, v) -> 
     match v with 
     | Int v -> k + ":" + string v 
     | String v -> k + ":" + v.Trim()) 
     // etc. 
    |> String.concat "\n" 

myfun ["Name" ==> "Freddie"; "Age" ==> 50] |> printfn "%s" 
//Name:Freddie 
//Age:50 
+0

Oui, c'est un très bon point. Je le fais ailleurs dans le programme, la question liée au code qui comprenait le DSL. –