ATUALIZAR
Encontrei uma versão mais simples usando um operador em ($)vez de um membro. Inspirado em 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
O restante da explicação ainda se aplica e é útil ...
Eu encontrei uma maneira de tornar isso possível:
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
Executando seu exemplo:
let list v = List.replicate 6 v
1
|> list |> list |> list |> list |> list
|> list |> list |> list |> list |> list
|> getSum |> printfn "%d" // = 60466176
Isso se baseia no uso de SRTPs com restrições de membro:, static member Suma restrição requer que o tipo tenha um membro chamado Sum
que retorne um int. Ao usar SRTPs, funções genéricas precisam ser inline.
Essa não é a parte difícil. A parte difícil é "adicionar" Summembro a um tipo existente como inte Listque não é permitido. Porém, podemos adicioná-lo a um novo tipo SumOperationse incluir na restrição (^t or ^a)
onde ^tsempre estará SumOperations.
getSum0declara a Sumrestrição de membro e a invoca.
getSum passa SumOperationscomo o primeiro parâmetro de tipo paragetSum0
A linha static member inline Sum(x : float ) = int xfoi adicionada para convencer o compilador a usar uma chamada de função dinâmica genérica e não apenas o padrão ao static member inline Sum(x : int )chamarList.sumBy
Como você pode ver, é um pouco complicado, a sintaxe é complexa e foi necessário contornar algumas peculiaridades no compilador, mas no final foi possível.
Esse método pode ser estendido para trabalhar com matrizes, tuplas, opções etc. ou qualquer combinação delas, adicionando mais definições 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)))) nestedListonde o número dedictListcorresponde ao número de[]no tipo denestedList.