ОНОВЛЕННЯ
Я знайшов більш просту версію за допомогою оператора ($)замість члена. Натхненний 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
Решта пояснення все ж стосується, і це корисно ...
Я знайшов спосіб зробити це можливим:
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
Запуск вашого прикладу:
let list v = List.replicate 6 v
1
|> list |> list |> list |> list |> list
|> list |> list |> list |> list |> list
|> getSum |> printfn "%d" // = 60466176
Це засновано на використанні SRTP з обмеженнями членів:, static member Sumдля обмеження потрібен тип, щоб мати член, який називається, Sum
який повертає int. При використанні SRTP повинні бути загальні функції inline.
Це не важка частина. Тверда частина «додавання» Sumелемента до існуючого типу , як intі List, не допускається. Але ми можемо додати його до нового типу SumOperationsі включити в обмеження, (^t or ^a)
де ^tзавжди буде SumOperations.
getSum0оголошує Sumобмеження члена і викликає його.
getSum передається SumOperationsяк параметр першого типу доgetSum0
Додано рядок, static member inline Sum(x : float ) = int xщоб переконати компілятора використовувати загальний динамічний виклик функції, а не просто за замовчуванням під static member inline Sum(x : int )час викликуList.sumBy
Як ви бачите, це трохи заплутано, синтаксис складний, і потрібно було обійти деякі примхи щодо компілятора, але врешті-решт це було можливо.
Цей метод може бути розширений для роботи з масивами, кортежами, параметрами тощо тощо або будь-якою їх комбінацією, додавши додаткові визначення до 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де кількістьdictListзбігів відповідає кількості[]в типіnestedList.