MISE À JOUR
J'ai trouvé une version plus simple utilisant un opérateur ($)
au lieu d'un membre. Inspiré par https://stackoverflow.com/a/7224269/4550898 :
type SumOperations = SumOperations
let inline getSum b = SumOperations $ b // <-- puting this here avoids defaulting to int
type SumOperations with
static member inline ($) (SumOperations, x : int ) = x
static member inline ($) (SumOperations, xl : _ list) = xl |> List.sumBy getSum
Le reste de l'explication s'applique toujours et c'est utile ...
J'ai trouvé un moyen de le rendre possible:
let inline getSum0< ^t, ^a when (^t or ^a) : (static member Sum : ^a -> int)> a : int =
((^t or ^a) : (static member Sum : ^a -> int) a)
type SumOperations =
static member inline Sum( x : float ) = int x
static member inline Sum( x : int ) = x
static member inline Sum(lx : _ list) = lx |> List.sumBy getSum0<SumOperations, _>
let inline getSum x = getSum0<SumOperations, _> x
2 |> getSum |> printfn "%d" // = 2
[ 2 ; 1 ] |> getSum |> printfn "%d" // = 3
[[2; 3] ; [4; 5] ] |> getSum |> printfn "%d" // = 14
Exécuter votre exemple:
let list v = List.replicate 6 v
1
|> list |> list |> list |> list |> list
|> list |> list |> list |> list |> list
|> getSum |> printfn "%d" // = 60466176
Ceci est basé sur l'utilisation de SRTP avec des contraintes de membre:, static member Sum
la contrainte requiert que le type ait un membre appelé Sum
qui renvoie un int
. Lors de l'utilisation de SRTP, les fonctions génériques doivent l'être inline
.
Ce n'est pas la partie difficile. La partie difficile est « ajoutant » Sum
membre à un type existant comme int
et ce List
qui est interdit. Mais, nous pouvons l'ajouter à un nouveau type SumOperations
et l'inclure dans la contrainte (^t or ^a)
où ^t
va toujours être SumOperations
.
getSum0
déclare la Sum
contrainte de membre et l'invoque.
getSum
passe SumOperations
comme premier paramètre de type àgetSum0
La ligne a static member inline Sum(x : float ) = int x
été ajoutée pour convaincre le compilateur d'utiliser un appel de fonction dynamique générique et pas seulement par défaut static member inline Sum(x : int )
lors de l'appelList.sumBy
Comme vous pouvez le voir, c'est un peu compliqué, la syntaxe est complexe et il était nécessaire de contourner certaines bizarreries sur le compilateur mais à la fin c'était possible.
Cette méthode peut être étendue pour fonctionner avec des tableaux, des tuples, des options, etc. ou toute combinaison de ceux-ci en ajoutant plus de définitions à SumOperations
:
type SumOperations with
static member inline ($) (SumOperations, lx : _ [] ) = lx |> Array.sumBy getSum
static member inline ($) (SumOperations, a : ^a * ^b ) = match a with a, b -> getSum a + getSum b
static member inline ($) (SumOperations, ox : _ option) = ox |> Option.map getSum |> Option.defaultValue 0
(Some 3, [| 2 ; 1 |]) |> getSum |> printfn "%d" // = 6
https://dotnetfiddle.net/03rVWT
getSum (dictList (dictList (..... (dictList dictInt)))) nestedList
où le nombre dedictList
correspond au nombre de[]
dans le type denestedList
.