これを行う1つの方法は、異なる温度単位に対して3つの別々のタイプを使用し、温度としてそれらを「統合」するためにタイプクラスを使用することです。
今例えば、あなただけの
toKelvin
/
fromKelvin
を使用することができますし、適切な実装が(推論)戻り値の型に基づいて選択されます
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
(newtype
なくdata
の使用に注意してください、これはdata
と同じであるが、余分なコンストラクタのランタイムコストなしで)
absoluteZeroInF :: Fahrenheit
absoluteZeroInF = fromKelvin (Kelvin 0)
この方法は、自動的に、任意の変換関数convert :: (TempUnit a, TempUnit b) => a -> b
提供する:convert = fromKelvin . toKelvin
を。その点では、TempUnit
ではなく、TempUnit a => ... a
という制約で任意の温度を処理する関数の型シグネチャを記述する必要があります。
「センチネル」値は、それ以外の場合は無視されます。
fromKelvin :: TempUnit -> TempUnit -> TempUnit
fromKelvin (Kelvin _) (Kelvin k) = Kelvin k
fromKelvin (Celcius _) (Kelvin k) = Celcius (k - 273.15)
fromKelvin (Fahrenheit _) (Kelvin k) = Fahrenheit (...)
(これはおそらくより良い示唆@seliopou方法で行われます。別のUnit
タイプを壊す)
これがそうのように使用することができます。この方法という
-- aliases for convenience
toC = Celcius 0
toK = Kelvin 0
toF = Fahrenheit 0
fromKelvin toC (Kelvin 10)
fromKelvin toF (Kelvin 10000)
注意ではないタイプセーフ:をfromKelvin
に変換しようとするとどうなりますか? (すなわち、fromKelvin toF (Celcius 100)
の値は何ですか?)
このすべては言った、それは内部的に一つのユニットを標準とだけ入力と出力に他の人に変換し、読みつまりのみの機能や温度が必要と書くことが最善だろうコンバージョンを心配するには、他のすべてがちょうど(例えば)Kelvin
で動作します。
@SvenKこれはあなたが得る可能性のあるソリューションと同じくらい良いです。このパターンと一致するように独自のコードをリファクタリングできる場合は、そうする必要があります。 – seliopou
私は原則として同意しますが、私はケルビンスケールの特権を失うことに強く反対しなければなりません。あなたは、[Rankine scale](http://en.wikipedia.org/wiki/Rankine_scale)という賢明な尺度に基づいて変換を行う必要があります。 –
偉大な答えをお寄せいただきありがとうございます。 @dbauppこれはプロダクションコードではありません。私は大学での自分の研究の機能プログラミングに関するいくつかの練習問題に取り組んでいます。明けましておめでとうございます。 – SvenK