Jednym ze sposobów jest użycie 3 oddzielnych typów dla różnych jednostek temperatury, a następnie użycie klasy typu, aby "zjednoczyć" je jako temperatury, np.
newtype Kelvin = Kelvin Float
newtype Celcius = Celcius Float
newtype Fahrenheit = Fahrenheit Float
class TempUnit a where
fromKelvin :: Kelvin -> a
toKelvin :: a -> Kelvin
instance TempUnit Kelvin where
fromKelvin = id
toKelvin = id
instance TempUnit Celcius where
fromKelvin (Kelvin k) = Celcius (k - 273.15)
toKelvin (Celcius c) = Kelvin (c + 273.15)
instance TempUnit Fahrenheit where
fromKelvin (Kelvin k) = Fahrenheit ((k-273.15)*1.8 + 32)
toKelvin (Fahrenheit f) = Kelvin ((f - 32)/1.8 + 273.15
Teraz możesz po prostu użyć toKelvin
/fromKelvin
i odpowiednia realizacja zostanie wybrany na podstawie (wywnioskować) Zwraca typ, np
absoluteZeroInF :: Fahrenheit
absoluteZeroInF = fromKelvin (Kelvin 0)
(Uwaga wykorzystania newtype
zamiast data
ta jest taka sama jak data
ale bez kosztów wykonawczego z dodatkowym konstruktora.)
Ta metoda zapewnia dowolną funkcję konwersji convert :: (TempUnit a, TempUnit b) => a -> b
automatycznie: convert = fromKelvin . toKelvin
. W tym przypadku wymaga to pisania podpisów funkcji, które obsługują arbitralne temperatury z ograniczeniami TempUnit a => ... a
, a nie zwykłym TempUnit
.
Można również użyć wartości "wartownika", która jest zignorowana, np.
fromKelvin :: TempUnit -> TempUnit -> TempUnit
fromKelvin (Kelvin _) (Kelvin k) = Kelvin k
fromKelvin (Celcius _) (Kelvin k) = Celcius (k - 273.15)
fromKelvin (Fahrenheit _) (Kelvin k) = Fahrenheit (...)
(to chyba lepiej zrobić metodą @seliopou sugeruje. Wyrwanie się oddzielny Unit
typ)
ten może być stosowany tak:
-- aliases for convenience
toC = Celcius 0
toK = Kelvin 0
toF = Fahrenheit 0
fromKelvin toC (Kelvin 10)
fromKelvin toF (Kelvin 10000)
Zauważ, że ta metoda to jest , a nie typ-safe: co się stanie, gdy spróbujesz przekonwertować Celcius 100
z fromKelvin
? (to znaczy.co jest wartością fromKelvin toF (Celcius 100)
?)
Wszystko to powiedział, że najlepiej byłoby, aby wewnętrznie standaryzację na jednym urządzeniu i konwertować tylko do innych na wejściu i wyjściu, czyli tylko te funkcje, które czytają lub zapisują temperatury potrzebują martwić się o konwersje, wszystko inne działa z (np.) Kelvin
.
@SvenK Jest to tak dobre rozwiązanie, jak się najprawdopodobniej dostaniesz. Jeśli możesz zmienić własny kod, aby pasował do tego wzorca, powinieneś to zrobić. – seliopou
Zgadzam się co do zasady, ale muszę stanowczo sprzeciwić się niezagrożonym przywilejom skali Kelvina. Powinieneś był oprzeć konwersję tylko na rozsądnej skali [Rankine scale] (http://en.wikipedia.org/wiki/Rankine_scale). –
Dziękuję wszystkim za wspaniałe odpowiedzi. @dbaupp To nie jest kod produkcyjny, pracuję nad kilkoma ćwiczeniami na temat programowania funkcjonalnego moich studiów na uniwersytecie, aby nauczyć mnie własnego haskell. Szczęśliwego Nowego Roku. – SvenK