Ответ 1
Да.
Вы можете сделать это с помощью следующего простого трюка:
{-# LANGUAGE FlexibleInstances #-}
instance Num (Integer -> Integer) where
fromInteger n = \scale -> n * scale -- return a function that takes
-- a number and returns a number
Затем вы можете написать:
seconds, minutes, hours, days :: Integer
seconds = 1000000 -- base unit, e.g. microseconds
minutes = 60 seconds
hours = 60 minutes
days = 24 hours
soon :: Integer
soon = 2 hours + 4 seconds
Как это работает?
Выше мы представили экземпляр Num
для Integer -> Integer
, то есть для функции, которая принимает целое число и возвращает целое число.
Каждый тип, реализующий Num
и имеющий свою функцию fromInteger
, разрешен для представления числовым литералом, например. 3
.
Это означает, что мы можем написать 3 :: Integer -> Integer
- здесь 3
- это функция, которая берет целое число и возвращает целое число!
Поэтому мы можем применить к нему целое число, например seconds
; мы можем написать 3 seconds
, и выражение будет иметь тип Integer
.
Более безопасная версия
Фактически, мы могли бы даже написать 3 (3 :: Integer)
сейчас - это, вероятно, не имеет особого смысла. Мы можем ограничить это, сделав его более безопасным для типов:
newtype TimeUnit = TimeUnit Integer
deriving (Eq, Show, Num)
instance Num (TimeUnit -> TimeUnit) where
fromInteger n = \(TimeUnit scale) -> TimeUnit (n * scale)
seconds, minutes, hours, days :: TimeUnit
seconds = TimeUnit 1000000
minutes = 60 seconds
hours = 60 minutes
days = 24 hours
Теперь мы можем применять только типы типа TimeUnit
к числу литералов.
Вы можете сделать это для всех видов других единиц, таких как веса или расстояния или люди.