GHC拡張に関する私の知識を向上させるために、ユニットで数値を実装しようと決心しました。しかし、それは、ハスケルが行うオープンな世界の仮定のためにあまり実用的ではないことが判明しました。私が試してみて、私は結果の値があいまいでないことを期待しmultiply 1 1
ような何かをすれば今ハスケルでのオープンワールドの仮定を制限する方法
data Unit u a = Unit a
data NoUnit
instance Num a => Num (Unit NoUnit a) where
-- (+) (*) (-) abs signum not important
fromInteger = Unit . fromInteger
type family Multiply a b where
Multiply NoUnit NoUnit = NoUnit
multiply :: Num a => Unit u1 a -> Unit u2 a -> Unit (Multiply u1 u2) a
multiply (Unit a) (Unit b) = Unit $ a * b
:私は仕事を得ることができなかったものの最低限の例は次のようです。タイプNum (Unit u a)
を取得する唯一の方法は、u
をNoUnit
に設定することです。残りのa
は、デフォルトのルールによって処理される必要があります。
残念ながら、ハスケルのオープンな仮定のために、ユニットを持つ数字でさえ有効なNum
インスタンスでなければならない可能性があります。ユニットとの数値の乗算は(*) :: a -> a -> a
に違反します。その型シグネチャを適合させます。
今、オープンワールドの仮定は、特に孤児のインスタンスがHaskellレポートによって禁止されていないため、不合理なものではありません。しかし、この具体的なケースで私は実際にNum
インスタンスの唯一の有効なファントムユニットタイプがUnit
であることをGHCに伝えたいと思います。NoUnit
です。
明示的にこれを述べる方法はありますか?また、孤児のインスタンスを許可しないと、GHCはオープンワールドの想定をまったく気にしなくなります。
この種のことは、部分的な依存型の入力でプログラムの型をより安全にしようとしたときに、何度か出てきました。ベースケースにNum
またはIsString
またはIsList
インスタンスを指定してから、カスタム値または関数を使用して、他のすべての可能なケースを取得する必要があるときはいつでも
これは関係ありませんが、 'Multiply NoUnit NoUnit = NoUnit'の代わりに、' Multiply'型ファミリーに 'Multiply NoUnit y = y'と' Multiply xNoUnit = x'を期待します。また、Adam Gundryは、GHCの組み込み施設は、良い計量作業には十分ではないと結論付けました。この種のものをサポートする型チェッカープラグインである[uom-plugin](https://hackage.haskell.org/package/uom-plugin)をチェックしてください。 – dfeuer
@dfeuer助けてくれてありがとうございますが、私の実際のコードはこのようなものではありませんでしたが、これは最小限の失敗例でした。私の実際のコードは非常に異なり、 'DataKinds'を使います。 7つの基本SI単位のそれぞれの数が各値にどのように組み合わされているかを追跡するだけで、すべてのSI単位に対して正しく機能するようです。 – semicolon