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 Sumla 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" Summiembros a un tipo existente como inty Listque no está permitido. Pero, podemos agregarlo a un nuevo tipo SumOperationse incluir en la restricción (^t or ^a)
dónde ^tsiempre va a estar SumOperations.
getSum0declara la Sumrestricción de miembro y la invoca.
getSum pasa SumOperationscomo el primer parámetro de tipo agetSum0
La línea static member inline Sum(x : float ) = int xse 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)))) nestedListel número dedictListcoincidencias con el número del[]tipo denestedList.