Comment detail

法演算 (Nested Flatten)
依存型風に書いてみました.法は型で与えます.
法を 3 とするなら,Modulo (S(S(S Z))) Int を
法を10 とするなら,Modulo (S(S(S(S(S(S(S(S(S(S Z)))))))))) Int を
ここでは簡単のために法 0 〜 10 に対応する型エイリアスを宣言してあります.

*Modulo> 1+2 :: Modulo10
3
*Modulo> 7+3 :: Modulo10
0
*Modulo> 11+12 :: Modulo10
3
*Modulo> 3-2 :: Modulo10
1
*Modulo> 2-3 :: Modulo10
9
*Modulo> 2*3 :: Modulo10
6
*Modulo> 11*12 :: Modulo10
2
*Modulo> 18*39 :: Modulo10
2
*Modulo> (1-2::Modulo10) == (1+8::Modulo10)
True
*Modulo> 1+2 :: Modulo7
3
*Modulo> 7+3 :: Modulo7
3
*Modulo> 11+12 :: Modulo7
2
*Modulo> 3-2 :: Modulo7
1
*Modulo> 2-3 :: Modulo7
6
*Modulo> 2*3 :: Modulo7
6
*Modulo> 11*12 :: Modulo7
6
*Modulo> 18*39 :: Modulo7
2
*Modulo> (1-2::Modulo7) == (1+8::Modulo7)
False
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{-# LANGUAGE EmptyDataDecls #-}

module Modulo where

data Z
zero :: Z
zero = undefined
data S a

predecessor :: S a -> a
predecessor = undefined

class Nat a where
  toInteger :: a -> Integer

instance Nat Z where
  toInteger _ = 0

instance Nat a => Nat (S a) where
  toInteger = (1 +) . Modulo.toInteger . predecessor

newtype Modulo a b = Modulo (a,b)

instance Eq b => Eq (Modulo a b) where
  Modulo (_,x) == Modulo (_,y) = x == y

instance Show b => Show (Modulo a b) where
  show (Modulo (_,x)) = show x

instance (Nat a, Integral b) => Num (Modulo a b) where
  Modulo (m,x) + Modulo (_,y) = Modulo (m,z)
    where z = (x+y) `mod` fromInteger (Modulo.toInteger m)
  Modulo (m,x) - Modulo (_,y) = Modulo (m,z)
    where z = (x-y) `mod` fromInteger (Modulo.toInteger m)
  Modulo (m,x) * Modulo (_,y) = Modulo (m,z)
    where z = (x*y) `mod` fromInteger (Modulo.toInteger m)
  abs = id
  signum (Modulo (m,x)) = Modulo $ (m,signum x)
  fromInteger n = Modulo (m,z)
    where z = fromInteger (n `mod` Modulo.toInteger m)
          m = undefined :: Nat a => a

type Zero  = Z
type One   = S Zero
type Two   = S One      ; type Modulo2 = Modulo Two Int
type Three = S Two      ; type Modulo3 = Modulo Three Int
type Four  = S Three    ; type Modulo4 = Modulo Four Int
type Five  = S Four     ; type Modulo5 = Modulo Five Int
type Six   = S Five     ; type Modulo6 = Modulo Six Int
type Seven = S Six      ; type Modulo7 = Modulo Seven Int
type Eight = S Seven    ; type Modulo8 = Modulo Eight Int
type Nine  = S Eight    ; type Modulo9 = Modulo Nine Int
type Ten   = S Nine     ; type Modulo10= Modulo Ten Int

これはおもしろい。適切なinstance定義が与えてあれば、型推論によって1などのリテラルさえも後つけのModulo型と解釈させることができちゃうわけですね。duck typingみたいだなあ。

Haskell での 0 とか 1 などの整数表記リテラルの型を調べると、

% ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> :type 0
0 :: (Num t) => t
Prelude> :type 1
1 :: (Num t) => t

のように Numクラスのインスタンスである型になります。
ということは、Numクラスのインスタンスであると宣言された型が
出現するべきだと型推論されたところで表れた整数表記リテラルは
当該の型であること認識できるというわけです。この場合
Modulo a b 型は、aがNatクラスのインスタンス、bがIntegralクラス
のインスタンスである文脈でNumクラスのインスタンスであると宣言
されていますので、Modulo Ten Int やModulo Seven Int が来るべき
場所で整数表記リテラルが出現すれば、それぞれの型として解釈されます。
以前に有限体GF(p)を扱うのに同様の方法を使ってみたことがあるので、参考までに転載します。 nobsunのコードとほぼ同じですが、法が素数の場合には除算もできるようになっています。
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
{-# OPTIONS -fglasgow-exts -fallow-undecidable-instances #-}
module GF
    ( Z
    , S
    , Nat
    , Prime
    , GF
    , GF2
    , GF3
    , GF5
    --, gf
    ) where

import Data.Ratio (denominator, numerator)
--import Language.Haskell.TH

-----------------------------------------------------------------------------

data Z
data S n

newtype NatInt n = NatInt Int

class Nat n where
    natInt :: NatInt n

instance Nat Z where
    natInt = NatInt 0

instance Nat n => Nat (S n) where
    natInt = case (natInt :: NatInt n) of
             NatInt n -> NatInt (n+1)

-----------------------------------------------------------------------------

class Nat n => Prime n
instance (Nat n, PrimeTest (S (S n)) (S (S n))) => Prime (S (S n))

-- p は {x | 1<x<a} では割り切れない
-- 実装は手抜き
class PrimeTest p a
instance PrimeTest p (S (S Z))
instance (PrimeTest p (S (S a)), NotDivide (S (S a)) p)
    => PrimeTest p (S (S (S a)))

-- aはbを割り切らない
class NotDivide a b
instance NotDivide' a a b => NotDivide a b
class NotDivide' a x y
instance NotDivide' (S a) (S y) Z 
instance NotDivide' (S a) a y => NotDivide' (S a) Z (S y)
instance NotDivide' (S a) x y => NotDivide' (S a) (S x) (S y)

-----------------------------------------------------------------------------

newtype GF p = GF Int deriving Eq

fromInt :: forall p. Nat p => Int -> GF p
fromInt a =
    case (natInt :: NatInt p) of
    NatInt p -> GF (a `mod` p)

toInt :: GF p -> Int
toInt (GF x) = x

instance Nat p => Show (GF p) where
    showsPrec n (GF x) = showsPrec n x

instance Nat p => Read (GF p) where
    readsPrec n s = [(fromInt a, s') | (a,s') <- readsPrec n s]

instance Nat p => Num (GF p) where
    GF a + GF b   = fromInt $ a+b
    GF a * GF b   = fromInt $ a*b
    GF a - GF b   = fromInt $ a-b
    negate (GF a) = fromInt $ negate a
    abs a         = a
    signum _      = 1
    fromInteger   = fromInt . fromIntegral

instance Nat p => Ord (GF p) where
    GF a `compare` GF b = a `compare` b
    GF a `max` GF b = GF (a `max` b)
    GF a `min` GF b = GF (a `min` b)

instance Nat p => Bounded (GF p) where
    minBound = 0
    maxBound =
        case (natInt :: NatInt p) of
        NatInt p -> GF (p - 1)

instance Nat p => Real (GF p) where
    toRational (GF x) = toRational x

instance Prime p => Fractional (GF p) where
    fromRational r =
        fromInteger (numerator r) / fromInteger (denominator r)
    _ / 0 = error "divide by zero"
    (GF x) / (GF y) =
        case (natInt :: NatInt p) of
        NatInt p ->
            case exGCD y p of
            (a,_,_) -> fromInt (a*x)

-- 拡張ユークリッド互除法
exGCD :: Int -> Int -> (Int,Int,Int)
exGCD x y = loop x y 1 0 0 1
    where loop x y a0 a1 b0 b1
              | y == 0 = (a0,b0,x)
              | otherwise = loop y r a1 (a0 - q*a1) b1 (b0 - q*b1)
                  where (q,r) = x `divMod` y

instance Nat p => Enum (GF p) where
    toEnum x
        | toInt (minBound :: GF p) <= x
          && x <= toInt (maxBound :: GF p) = fromInt x
        | otherwise = error "GF.toEnum: bad argument"
    fromEnum (GF x) = x

-----------------------------------------------------------------------------

type GF2 = GF (S (S Z))
type GF3 = GF (S (S (S Z)))
type GF5 = GF (S (S (S (S (S Z)))))

-----------------------------------------------------------------------------

{-
gf :: Int -> Type
gf x = AppT (ConT (mkName "GF.GF")) (f x)
    where f 0     = ConT (mkName "GF.Z")
          f (x+1) = AppT (ConT (mkName "GF.S")) (f x)
-}

-----------------------------------------------------------------------------

Index

Feed

Other

Link

Pathtraq

loading...