AGGIORNARE
Ho trovato una versione più semplice utilizzando un operatore ($)
anziché un membro. Ispirato da 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
Il resto della spiegazione è ancora valido ed è utile ...
Ho trovato un modo per renderlo possibile:
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
Eseguendo il tuo esempio:
let list v = List.replicate 6 v
1
|> list |> list |> list |> list |> list
|> list |> list |> list |> list |> list
|> getSum |> printfn "%d" // = 60466176
Questo si basa sull'uso di SRTP con vincoli di membro: static member Sum
il vincolo richiede che il tipo abbia un membro chiamato Sum
che restituisce un int
. Quando si utilizzano SRTP, è necessario disporre di funzioni generiche inline
.
Questa non è la parte difficile. La parte difficile è "aggiungere" un Sum
membro a un tipo esistente simile int
e List
che non è consentito. Ma possiamo aggiungerlo a un nuovo tipoSumOperations
e includerlo nel vincolo (^t or ^a)
dove ^t
sarà sempre SumOperations
.
getSum0
dichiara il Sum
vincolo membro e lo invoca.
getSum
passaggi SumOperations
come primo parametro di tipo agetSum0
La linea static member inline Sum(x : float ) = int x
stata aggiunta per convincere il compilatore a utilizzare una chiamata di funzione dinamica generica e non solo a quella predefinita static member inline Sum(x : int )
durante la chiamataList.sumBy
Come puoi vedere è un po 'contorto, la sintassi è complessa ed era necessario aggirare alcune stranezze sul compilatore ma alla fine era possibile.
Questo metodo può essere esteso per funzionare con array, tuple, opzioni, ecc. O qualsiasi combinazione di essi aggiungendo più definizioni a 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
dove il numero didictList
corrisponde al numero di[]
nel tipo dinestedList
.