Qiita初投稿です。
Haskellの練習として、整数の剰余環 $\mathbb{Z}/m\mathbb{Z}$ の実装に挑戦してみます。
特に次の2つを目標にします。
- 整数の剰余環を
Num
クラスのインスタンスとして実装する。 -
a
とb
で法が異なる場合、a + b
などはコンパイルが通らないようにする。
実装
整数の剰余環を実装するにあたって、以下の記事を参考に致しました。
型レベルの自然数についてはまだ十分に理解できていませんが、とりあえず整数の剰余環は実装できたのではないかと思います。
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE KindSignatures #-}
import Data.Proxy
import GHC.TypeLits
newtype IntMod (m :: Nat) = IntMod Integer
instance KnownNat m => Show (IntMod m) where
show t@(IntMod x) = show x ++ " (mod " ++ show (natVal t) ++ ")"
instance KnownNat m => Num (IntMod m) where
fromInteger x = IntMod (mod x (natVal (Proxy :: Proxy m)))
(IntMod x) + (IntMod y) = fromInteger (x + y)
(IntMod x) * (IntMod y) = fromInteger (x * y)
negate (IntMod x) = fromInteger (-x)
abs = id
signum _ = IntMod 1
main = do
let a = fromInteger 12 :: IntMod 101
let b = fromInteger 34 :: IntMod 101
print $ a + b
print $ a - b
print $ a * b
print $ a ^ 100
46 (mod 101)
79 (mod 101)
4 (mod 101)
1 (mod 101)
計算結果が正しいか確認します。
main = do
let a = 12
let b = 34
print $ mod (a + b) 101
print $ mod (a - b) 101
print $ mod (a * b) 101
print $ mod (a ^ 100) 101
46
79
4
1
正しく計算できていそうですね。
ちなみに、4番目に計算しているa ^ 100
ですが、フェルマーの小定理から計算するまでもなく1
であるとわかります。
法が異なる場合も確認してみましょう。
main = do
let a = fromInteger 12 :: IntMod 101
let b = fromInteger 34 :: IntMod 103 -- 法を101から103に変更
print $ a + b
print $ a - b
print $ a * b
print $ a ^ 100
Main.hs:24:15: error:
? Couldn't match type ‘103’ with ‘101’
Expected type: IntMod 101
Actual type: IntMod 103
? In the second argument of ‘(+)’, namely ‘b’
In the second argument of ‘($)’, namely ‘a + b’
In a stmt of a 'do' block: print $ a + b
|
24 | print $ a + b
| ^
ちゃんとコンパイルが通らずにエラーが出ました。
感想など
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE KindSignatures #-}
言語拡張が3つありますが、今の所あまり理解できていません。
今回の場合では型レベルの自然数を扱うために用いているのだと思うのですが、それ以外の例も知りたいところです。
import Data.Proxy
import GHC.TypeLits
こちらもその他の例を知りたいですね。
abs = id
signum _ = IntMod 1
HaskellでNum
クラスのインスタンス宣言をする際、ここがどうしても気になるところですね。
特に環をHaskellで実装しようとすると、(+)
や(*)
は欲しいけどこの2つはいらないことが多い気がします。
とりあえず、上のように定義しておきました。
今回は型レベルの自然数を用いて整数の剰余環を実装しましたが、より一般の剰余環も同様に実装出来るのかが気になりました(例えば多項式環の剰余環など)。
また、型レベルのプログラミングには興味があるので、今後より詳しく勉強してみたいと思います。