0.213123
たとえば、値or 0.0
、または、を表すことができる数値タイプを探していますが、および1.0
などの範囲外の値を拒否しています。その目的に合う特定のタイプがありますか、そして特定の範囲に数を制限するための最適な一般的なアプローチは何ですか?-0.2123
1.2312
もちろん、最初に頭に浮かぶ答えは次のとおりです。使用するだけですDouble
が、Haskellの型システムに甘やかされて、型レベルでプログラムを最大限に保護することに慣れてきました。
0.213123
たとえば、値or 0.0
、または、を表すことができる数値タイプを探していますが、および1.0
などの範囲外の値を拒否しています。その目的に合う特定のタイプがありますか、そして特定の範囲に数を制限するための最適な一般的なアプローチは何ですか?-0.2123
1.2312
もちろん、最初に頭に浮かぶ答えは次のとおりです。使用するだけですDouble
が、Haskellの型システムに甘やかされて、型レベルでプログラムを最大限に保護することに慣れてきました。
深刻な提案
適切なビットサイズの単語の周りにニュータイプラッパー(およびスマートコンストラクター)を使用できます。
newtype SmallFrac = SF Word64
-- Example conversion (You'd actually want to make
-- instances of common classes, I assume)
sfToDouble :: SmallFrac -> Double
sfToDouble (SF x) = fromIntegral x / fromIntegral (maxBound `asTypeOf` x)
instance Show SmallFrac where
show = show . sfToDouble
乗算と除算の実装は、必要以上にコストがかかる可能性がありますが、少なくとも加算は簡単で(モジュロでオーバーフロー/アンダーフローから保護)、操作は必要ないと主張します。
あまり役に立たない提案
必要なのが1と0の間に存在する値を表すシンボルだけである場合は、dave4420の提案を採用し、ユニットタイプを指定してください。
newtype SmallFrac = SF ()
このタイプの操作はなく、などの他のタイプのインタレストとの間の変換もありませんDouble
が、これは前述の要求を満たします。
標準ではありません。1つ作成する必要があります-スマートコンストラクターをお勧めします。ただし、このような型がサポートする数値演算はごくわずかであることに注意してください。これらを追加してセットに保持したり、否定したりすることはできないため、Num
インスタンスに対してアドバイスします。Monoid
乗算のAは合理的です。
newtype Rep1 = Rep1 Double
checkRange :: Double -> Maybe Double
checkRange x
| 0 < x && x < 1 = Just x
| otherwise = Nothing
toRep1 :: Double -> Maybe Rep1
toRep1 x = Rep1 . (\x -> tan $ (x-0.5) * pi) <$> checkRange x
fromRep1 :: Rep1 -> Double
fromRep1 (Rep1 x) = atan x / pi + 0.5
data Rep2 = Rep2 Integer Integer
fromRep2 :: Rep2 -> Double
fromRep2 (Rep2 a b) = fromIntegral (abs a) / fromIntegral (abs a + abs b + 1)
toRep2 :: Double -> Maybe Rep2
toRep2 = error "left to the reader"
スマートコンストラクターパターンのバリエーション。
これはやり過ぎかもしれません。
{-# LANGUAGE TemplateHaskell #-}
module Foo (Foo(), doubleFromFoo,
maybeFooFromDouble, unsafeFooFromDouble, thFooFromDouble)
where
import Language.Haskell.TH
とにかく、標準newtype
..。
newtype Foo = Foo Double
外出Double
は簡単です...
doubleFromFoo :: Foo -> Double
doubleFromFoo (Foo x) = x
実行Double
時に入力すると、実行時チェックが発生しますが、それを回避することはできません...
maybeFooFromDouble :: Double -> Maybe Foo
maybeFooFromDouble x
| 0 <= x && x <= 1 = Just (Foo x)
| otherwise = Nothing
...あなたが安全でないことに満足していない限り(そして、すべての使用unsafeFooFromDouble
が実際に安全であることを強制するいくつかの社会的手段を持っていない限り)...
unsafeFooFromDouble :: Double -> Foo
unsafeFooFromDouble = Foo
ただし、コンパイル時定数の場合は、実行時のオーバーヘッドなしで、コンパイル時にチェックを実行できます。
thFooFromDouble :: (Real a, Show a) => a -> Q Exp
thFooFromDouble x
| 0 <= x && x <= 1 = return $ AppE (VarE 'unsafeFooFromDouble)
(LitE (RationalL (toRational x)))
| otherwise = fail $ show x ++ " is not between 0 and 1"
そして、これが最後の関数の使い方です。
$(thFooFromDouble 0.3)
$
と(
!の間にスペースを入れないでください。