2016-08-30 4 views
6

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)を取得する唯一の方法は、uNoUnitに設定することです。残りのaは、デフォルトのルールによって処理される必要があります。

残念ながら、ハスケルのオープンな仮定のために、ユニットを持つ数字でさえ有効なNumインスタンスでなければならない可能性があります。ユニットとの数値の乗算は(*) :: a -> a -> aに違反します。その型シグネチャを適合させます。

今、オープンワールドの仮定は、特に孤児のインスタンスがHaskellレポートによって禁止されていないため、不合理なものではありません。しかし、この具体的なケースで私は実際にNumインスタンスの唯一の有効なファントムユニットタイプがUnitであることをGHCに伝えたいと思います。NoUnitです。

明示的にこれを述べる方法はありますか?また、孤児のインスタンスを許可しないと、GHCはオープンワールドの想定をまったく気にしなくなります。

この種のことは、部分的な依存型の入力でプログラムの型をより安全にしようとしたときに、何度か出てきました。ベースケースにNumまたはIsStringまたはIsListインスタンスを指定してから、カスタム値または関数を使用して、他のすべての可能なケースを取得する必要があるときはいつでも

+0

これは関係ありませんが、 '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

+0

@dfeuer助けてくれてありがとうございますが、私の実際のコードはこのようなものではありませんでしたが、これは最小限の失敗例でした。私の実際のコードは非常に異なり、 'DataKinds'を使います。 7つの基本SI単位のそれぞれの数が各値にどのように組み合わされているかを追跡するだけで、すべてのSI単位に対して正しく機能するようです。 – semicolon

答えて

14

オープンワールドの仮定を無効にすることはできませんが、この時間も含めて制限する方法はあります。 GHCはそれがNum (Unit u) aを必要としていることを見たとき、それはそれと結論し、あなたが本当に欲しいもの

instance Num a => Num (Unit NoUnit a) 

instance (Num a, u ~ NoUnit) => Num (Unit u a) 

この方法:問題は、あなたのケースでは、あなたがNumインスタンスを書いた方法ですNum au ~ NoUnitの両方が必要です。あなたが書いたやり方で、あなたはどこか別のインスタンスの可能性を残しました。

=>の右側にあるタイプコンストラクタを回すというトリックは、左辺の等式制約によく役立ちます。

関連する問題