2

私は Haskell に非常に慣れていないので、いくつかのモジュラスで Integral の任意のインスタンスを表す型を作成しようとしています。私はいくつかのサンプルコードをオンラインで見つけてそれを使って作業しているので、私の型定義は次のようになります:

data Zn n a = Zn !a !a

ほとんどのことは思い通りに機能しています。表示、加算、減算などを行うことができます。

instance (Integral a, Show a) => Show (Zn n a) where
   show (Zn n x) = printf "(%s mod %s)" (show (mod x n)) (show n)

instance (Integral a, Reifies n a) => Num (Zn n a) where
  Zn n x + Zn _ y = Zn n (mod (x + y) n)
  Zn n x - Zn _ y = Zn n (mod (x - y) n)
  Zn n x * Zn _ y = Zn n (mod (x * y) n)
  negate (Zn n x) = Zn n (n - x)
  abs = id
  signum x@(Zn _ 0) = x
  signum (Zn n _) = Zn n 1
  fromInteger x = Zn n (mod (fromInteger x) n)
    where n = reflect (Proxy :: Proxy n)


znToIntegral :: Integral a => Zn n a -> a
znToIntegral (Zn n x) = fromIntegral x

ただし、これらの型の算術演算の結果を表示することはできません。たとえば、GHCi では次のようになります。

*Main> let x = Zn 5 3
*Main> x
(3 mod 5)
*Main> let y = Zn 5 7
(2 mod 5)
*Main> let z = x + y
*Main> z
<interactive>:6:1:
    No instance for (Integral a0) arising from a use of ‘print’
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
      instance Integral GHC.Int.Int16 -- Defined in ‘GHC.Int’
      instance Integral GHC.Int.Int32 -- Defined in ‘GHC.Int’
      instance Integral GHC.Int.Int64 -- Defined in ‘GHC.Int’
      ...plus 9 others
    In a stmt of an interactive GHCi command: print it

この問題は、これらの数値を実装しようとした他の多くの方法で発生し、Data.Reflection パッケージがどのように機能するかを理解することで問題が発生することがわかりました。また、他の人にとってより自然に見える他の実装にも興味があります。私はもともと次のようなことをしようとしていました

newtype Zn n a = Zn a

と切り替えました。乾杯!

4

2 に答える 2

2

比較のために、モジュラスを保存せず、常に を使用する実装を次に示しますreflect

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}

import Data.Reflection
import Data.Proxy
import Text.Printf

newtype Zn a s = Zn { getZ :: a }

instance (Integral a, Show a, Reifies s a) => Show (Zn a s) where
       show x = 
           let 
               p = reflect (Proxy::Proxy s)           
           in
               printf "(%s mod %s)" (show (mod (getZ x) p)) (show p)

instance (Integral a, Reifies s a) => Num (Zn a s) where
      Zn x + Zn y = Zn (mod (x + y) (reflect (Proxy::Proxy s)))
      Zn x - Zn y = Zn (mod (x - y) (reflect (Proxy::Proxy s)))
      Zn x * Zn y = Zn (mod (x * y) (reflect (Proxy::Proxy s)))
      negate (Zn x) = Zn ((reflect (Proxy::Proxy s)) - x)
      abs = id
      signum x@(Zn 0) = x
      signum _ = Zn 1
      fromInteger x = Zn (mod (fromInteger x) p)
            where p = reflect (Proxy :: Proxy s)

いくつかの補助機能:

znToIntegral :: Integral a => Zn a s -> a
znToIntegral (Zn x) = fromIntegral x

-- Convince the compiler that the phantom type in the proxy
-- is the same as the one in the Zn
likeProxy :: Proxy s -> Zn a s -> Zn a s
likeProxy _ = id

withZn :: Integral a => a -> (forall s. Reifies s a => Zn a s) -> a
withZn p z = reify p $ \proxy -> znToIntegral . likeProxy proxy $ z 

使用例:

main :: IO ()
main = print $ withZn (7::Int) (Zn 3 + Zn 5)

withZnghciからも動作します。

于 2015-07-15T22:29:25.567 に答える
2

この特定の例でZnは、 は内部実装の詳細であり、数値の構築には使用しないでください。理想的にZnは、ライブラリからエクスポートすることさえすべきではありません。fromInteger代わりに、数値リテラルを使用して構築することになっています。

モジュラスはリフレクションによって に導入され、パフォーマンス上の理由から、 のさらなる使用を避けるためfromIntegerに にのみ格納します (ただし、デフォルトではあまりオーバーヘッドがないと思われるため、このスキームではあまり得られない可能性があります)。を使用して新しい -s を作成するだけの場合、モジュラスは制約のある計算全体で均一になります。一方、モジュラスを手動で に差し込むと、 のその部分は常にそのモジュラスを持ち、(暗黙的な構成を提供する方法は) まったく影響しません。言い換えれば、それは想定された不変条件を台無しにします。ZnreflectreflectfromIntegerZnReifiesZnZnreify

使用例:

foo :: (Integral a, Reifies s a) => Zn s a -> Zn s a -> Zn s a
foo a b = e where
  c = 123
  d = a * b - 3
  e = negate (d + c)

showAFooResult = reify 12 (\(Proxy :: Proxy s) -> show (foo 3 4 :: Zn s Int))
-- fooResult == "(0 mod 12)"

を使用してプラグインした後、fooすべての本体内にモジュラスがあります。12reify

少し目を細めると、内部でモジュラスとして使用fooする追加の引数を期待する関数にすぎません。のインデックスを使用して、引数を指定するためにIntegral使用できます。reifysProxy

穴を塞ぐための値を指定しないとReifies、値を引き出すことができません。もちろん、関数を印刷できないのと同じように、それを印刷することはできません。

Zn 5 5typeがあるので印刷できるのでNum a => Zn n a、埋める穴がありません。一方、Zn 5 5 + Zn 5 5has typeは、インスタンスで制約(Integral a, Reifies n a) => Zn n aを指定したためです。もちろん、 を使用するとインスタンスが必要になります。ここでは、(またはそれを呼び出す他のヘルパー関数) を使用して結果を取得する必要があります。Reifies n aNum+Numreify

于 2015-07-15T21:02:57.927 に答える