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 Sum
a 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" Sum
membro a um tipo existente como int
e List
que não é permitido. Porém, podemos adicioná-lo a um novo tipo SumOperations
e incluir na restrição (^t or ^a)
onde ^t
sempre estará SumOperations
.
getSum0
declara a Sum
restrição de membro e a invoca.
getSum
passa SumOperations
como o primeiro parâmetro de tipo paragetSum0
A linha static member inline Sum(x : float ) = int x
foi 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)))) nestedList
onde o número dedictList
corresponde ao número de[]
no tipo denestedList
.