ACTUALIZAR
Encontré una versión más simple usando un operador en ($)
lugar de un miembro. Inspirado en 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
El resto de la explicación aún se aplica y es útil ...
Encontré una manera de hacerlo posible:
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
Ejecutando su ejemplo:
let list v = List.replicate 6 v
1
|> list |> list |> list |> list |> list
|> list |> list |> list |> list |> list
|> getSum |> printfn "%d" // = 60466176
Esto se basa en el uso de SRTP con restricciones de miembros: static member Sum
la restricción requiere que el tipo tenga un miembro llamado Sum
que devuelva un int
. Cuando se usan SRTP, las funciones genéricas deben ser inline
.
Esa no es la parte difícil. La parte difícil es "agregar" Sum
miembros a un tipo existente como int
y List
que no está permitido. Pero, podemos agregarlo a un nuevo tipo SumOperations
e incluir en la restricción (^t or ^a)
dónde ^t
siempre va a estar SumOperations
.
getSum0
declara la Sum
restricción de miembro y la invoca.
getSum
pasa SumOperations
como el primer parámetro de tipo agetSum0
La línea static member inline Sum(x : float ) = int x
se agregó para convencer al compilador de que use una llamada de función dinámica genérica y no solo por defecto static member inline Sum(x : int )
al llamarList.sumBy
Como puede ver es un poco complicado, la sintaxis es compleja y fue necesario solucionar algunas peculiaridades en el compilador, pero al final fue posible.
Este método se puede extender para trabajar con matrices, tuplas, opciones, etc. o cualquier combinación de ellas agregando más definiciones 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
el número dedictList
coincidencias con el número del[]
tipo denestedList
.