Ja.
Sie können dies mit dem folgenden einfachen Trick:
{-# LANGUAGE FlexibleInstances #-}
instance Num (Integer -> Integer) where
fromInteger n = \scale -> n * scale -- return a function that takes
-- a number and returns a number
Dann können Sie schreiben:
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
Wie funktionierts?
Oben haben wir ein Num
Beispiel für Integer -> Integer
gegeben, das heißt für eine -Funktion, die eine ganze Zahl nimmt und gibt eine ganze Zahl.
Jeder Typ, der Num
implementiert und dessen Funktion fromInteger
definiert ist, darf durch ein numerisches Literal, z. 3
.
Dies bedeutet, dass wir schreiben können 3 :: Integer -> Integer
- hier 3
ist eine Funktion, die eine ganze Zahl nimmt und eine ganze Zahl zurückgibt!
Daher können wir eine ganze Zahl darauf anwenden, zum Beispiel seconds
; wir können 3 seconds
schreiben und der Ausdruck wird vom Typ Integer
sein.
Eine typsichere Version
In der Tat, wir haben sogar 3 (3 :: Integer)
jetzt schreiben könnte - dies wahrscheinlich viel Sinn macht allerdings nicht machen. Wir können dies einschränken, indem sie mehr typsicher:
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
Jetzt können wir nur Dinge vom Typ Literale TimeUnit
Nummer anzuwenden.
Sie könnten das für alle Arten von anderen Einheiten, wie Gewichte oder Entfernungen oder Menschen tun.
Vielleicht sollten Sie einen Blick auf http://hackage.haskell.org/package/dimensional. Sie benutzen etwas wie "timeout = 3 * ~ seconds" ... aber Sie erhalten auch alle anderen si-Einheiten und Präfixe. – fho