На самом деле это возможно, есть способ сделать это:
type [<Measure>] seconds
type [<Measure>] minutes
type [<Measure>] hours
let seconds_per_minute = 60<seconds>/1<minutes>
let minutes_per_hour = 60<minutes>/1<hours>
let minutes_to_seconds minutes seconds = minutes * seconds_per_minute + seconds
let hours_to_minutes hours minutes = hours * minutes_per_hour + minutes
type D1 = D1
type D2 = D2
type Sum = Sum with
static member inline ($) (Sum, _:^t when ^t: null and ^t: struct) = id
static member inline ($) (Sum, b) = fun _ _ a -> a + b
static member ($) (Sum, b:int<minutes>) = fun D1 _ a -> hours_to_minutes a b
static member ($) (Sum, b:int<seconds>) = fun D1 D2 a -> minutes_to_seconds a b
let inline (+) a b :'t = (Sum $ b) D1 D2 a
let duration = 1<hours> + 2<minutes> + 3<seconds>
Но это действительно Hacky, я бы не рекомендовал его.
UPDATE
Основываясь на комментариях здесь некоторые ответы:
Этот метод использует перегрузкам, разрешаемые при компиляции, так что не снижает производительность во время выполнения. Он основан на том, что я написал некоторое время назад в my blog.
Чтобы добавить больше перегрузкам вам придется добавить еще фиктивные параметры (D3
, D4
, ...) и в конце концов, если вы решили добавить некоторые перегрузки, противоречащим с существующей у вас, возможно, придется использовать тройной оператор (?<-)
или вызов функции с явными ограничениями статического члена. Here's a sample code.
Я думаю, что не буду использовать его, так как он требует много хаков (перегрузка Dummy и 2 фиктивных типа), и код становится менее читаемым. В конце концов, если F # добавит больше поддержки встроенных функций, основанных на перегрузках, я бы определенно подумал об этом.
Phil Trelford's technique (упомянутый в ответе Рида) работает во время выполнения, третьим вариантом будет использование фантомных типов, для этого может потребоваться меньше хаков.
Заключение
Если бы мне пришлось выбирать между всеми альтернативами я хотел бы использовать эту технику, но, будучи более явным на месте вызова, я имею в виду, я бы определил функцию преобразования как minutes
, seconds
и таким образом на месте вызова я хотел бы написать:
let duration = seconds 1<hours> + seconds 2<minutes> + 3<seconds>
а затем определить те функции преобразования я бы использовать перегруженные, но было бы меньше, чем Hacky заново определяя Exis бинарный оператор.
Вау, это здорово! У меня есть несколько вопросов: 1. Почему вы не рекомендуете его? 2. Не освобождайте ли я тип безопасности? 3. Если вам нужно было выбрать, вы бы предпочли пойти с решением времени работы Фила Трерфорта или этим? 4. Что на самом деле делает $? Спасибо! – user3323923
Btw, я бы хотел принять два ответа. – user3323923
@ user3323923, Gustavo определяет оператор '$' с помощью круглых скобок '($)'. @Gustavo, я второй вопрос, почему бы вам не использовать его? Это похоже на законное решение. –