Глядя на FSharpPlus, я думал о том, как создать универсальную функцию, которая будет использоваться в
let qr0 = divRem 7 3
let qr1 = divRem 7I 3I
let qr2 = divRem 7. 3.
и вышло с возможным (рабочим) решением
let inline divRem (D:^T) (d:^T): ^T * ^T = let q = D / d in q, D - q * d
затем я посмотрел, как FSharpPlus внедрил его, и я обнаружил:
open System.Runtime.InteropServices
type Default6 = class end
type Default5 = class inherit Default6 end
type Default4 = class inherit Default5 end
type Default3 = class inherit Default4 end
type Default2 = class inherit Default3 end
type Default1 = class inherit Default2 end
type DivRem =
inherit Default1
static member inline DivRem (x:^t when ^t: null and ^t: struct, y:^t, _thisClass:DivRem) = (x, y)
static member inline DivRem (D:'T, d:'T, [<Optional>]_impl:Default1) = let q = D / d in q, D - q * d
static member inline DivRem (D:'T, d:'T, [<Optional>]_impl:DivRem ) =
let mutable r = Unchecked.defaultof<'T>
(^T: (static member DivRem: _ * _ -> _ -> _) (D, d, &r)), r
static member inline Invoke (D:'T) (d:'T) :'T*'T =
let inline call_3 (a:^a, b:^b, c:^c) = ((^a or ^b or ^c) : (static member DivRem: _*_*_ -> _) b, c, a)
let inline call (a:'a, b:'b, c:'c) = call_3 (a, b, c)
call (Unchecked.defaultof<DivRem>, D, d)
let inline divRem (D:'T) (d:'T) :'T*'T = DivRem.Invoke D d
Я уверен, что есть веские причины сделать это как таковое; однако меня не интересует, почему так было сделано, но:
Как это работает?
Есть ли какая-либо документация, помогающая понять, как работает этот синтаксис, особенно три перегруженных статических метода DivRem?
РЕДАКТИРОВАТЬ
Таким образом, реализация FSharp+ имеет то преимущество, что если числовой тип, используемый в вызове divRem, реализует статический член DivRem (например, BigInteger), он будет использоваться вместо возможных существующих арифметических операторов. Это, если предположить, что DivRem более эффективен, чем вызов операторов по умолчанию, сделает divRem оптимальным по эффективности. Однако остается вопрос:
зачем нам вводить "двусмысленность" (o1)?
Позвольте назвать три перегрузки o1, o2, o3
Если мы прокомментируем o1 и вызываем divRem с числовым параметром, тип которого не реализует DivRem (например, int или float), то o3 не может использоваться из-за ограничения члена. Компилятор мог выбрать o2, но это не так, как сказано: "У вас есть идеальная сигнатура, перегружающая o3 (поэтому я буду игнорировать менее совершенную подпись в o2), но ограничение члена не выполняется". Поэтому, если я раскомментирую o1, я бы ожидал, что он скажет: "у вас есть две совершенные сигнатурные перегрузки (поэтому я проигнорирую менее совершенную подпись в o2), но обе они имеют невыполненные ограничения". Вместо этого, похоже, "у вас есть две совершенные сигнатурные перегрузки, но у обоих из них есть невыполненные ограничения, поэтому я возьму o2, который даже с менее совершенной подписью сможет выполнить эту работу". Не было бы правильнее избегать трюка o1 и позволить компилятору сказать: "Ваша совершенная перегрузка подписи o3 имеет невыполненное ограничение члена, поэтому я беру o2, который менее совершенен подписи, но может выполнять работу" даже в первом пример?