packages feed

inch-0.1.0: examples/Wires.hs

{-# OPTIONS_GHC -F -pgmF inch #-}
{-# LANGUAGE RankNTypes, GADTs, KindSignatures, ScopedTypeVariables,
             NPlusKPatterns #-}

module Wires where

import Vectors


-- A value of type Wire m a n b represents a process that consumes m
-- inputs of type a and delivers n outputs of type b.

data Wire :: Nat -> * -> Nat -> * -> * where
  Stop  :: Wire 0 a 0 b
  Say   :: forall (m n :: Nat) a b .
               b -> Wire m a n b -> Wire m a (n+1) b
  Ask   :: forall (m n :: Nat) a b .
               (a -> Wire m a n b) -> Wire (m+1) a n b


-- Given a vector of inputs, we can run it to produce a vector of outputs

run :: forall (m n :: Nat) a b . Wire m a n b -> Vec a m -> Vec b n
run Stop       VNil          = VNil
run (Say a p)  xs            = VCons a (run p xs)
run (Ask f)    (VCons x xs)  = run (f x) xs


-- "Horizontal" composition of wires

sequ :: forall (m n i j :: Nat) a b .
            Wire m a i b -> Wire n a j b -> Wire (m + n) a (i + j) b
sequ Stop       q = q
sequ (Say b p)  q = Say b (sequ p q)
sequ (Ask f)    q = Ask (\ x -> sequ (f x) q)


-- "Vertical" composition of wires

pipe :: forall (m n i :: Nat) a b c .
            Wire m a n b -> Wire n b i c -> Wire m a i c
pipe Stop       Stop       = Stop
pipe (Ask f)    Stop       = Ask (\ x -> pipe (f x) Stop)
pipe p          (Say b q)  = Say b (pipe p q)
pipe (Say x p)  (Ask g)    = pipe p (g x)
pipe (Ask f)    (Ask g)    = Ask (\ x -> pipe (f x) (Ask g))


-- Some basic combinators and logic gates

always p = Ask (\ zzz -> p)

askB t f = Ask (bool t f)
  where
    bool t f True   = t
    bool t f False  = f

wire     = Ask (\ a -> Say a Stop)
notGate  = Ask (\ b -> Say (not b) Stop)
andGate  = askB wire (always (Say False Stop))
split    = Ask (\ a -> Say a (Say a Stop))
cross    = Ask (\ a -> Ask (\ b -> Say b (Say a Stop)))

mkGate tt tf ft ff = askB (askB (Say tt Stop) (Say tf Stop))
                          (askB (Say ft Stop) (Say ff Stop))

orGate    = mkGate True True True False
nandGate  = pipe andGate notGate
norGate   = pipe orGate notGate
xorGate   = askB notGate wire

wires :: forall a. pi (n :: Nat) . Wire n a n a
wires {0}   = Stop
wires {n+1} = sequ wire (wires {n})

manyWires = wires {1000}
sillyWires {n} = wires {1000000*n}

bind :: forall (m n j :: Nat) a . (0 < n, 0 < j) =>
            Wire m a 1 a -> (a -> Wire n a j a) -> Wire (m + n) a j a 
bind (Say a p) g = sequ p (g a)
bind (Ask f)   g = Ask (\ x -> bind (f x) g)


-- Half and full adders

hadd :: Wire 2 Bool 2 Bool
hadd = pipe (sequ split split)
      (pipe (sequ wire (sequ cross wire))
            (sequ andGate xorGate))

fadd :: Wire 3 Bool 2 Bool
fadd = pipe (sequ hadd wire)
      (pipe (sequ wire hadd)
            (sequ orGate wire))


-- Converting from multiple wires to vectors and vice versa

askVec :: forall a . pi (m :: Nat) . Wire m a 1 (Vec a m)
askVec = help VNil
  where
    help :: forall a (k :: Nat) . Vec a k -> (pi (m :: Nat) . Wire m a 1 (Vec a (m+k)))
    help xs {0}   = Say xs Stop
    help xs {m+1} = Ask (\ x -> help (VCons x xs) {m})

sayVec :: forall a b (k :: Nat) . Vec b k -> Wire 0 a k b
sayVec VNil          = Stop
sayVec (VCons x xs)  = Say x (sayVec xs)

bundle :: forall a. pi (m :: Nat) . Wire (2*m) a 2 (Vec a m)
bundle {m} = sequ (askVec {m}) (askVec {m})

unbundle :: forall a . pi (m :: Nat) . Wire 2 (Vec a m) (2*m) a
unbundle {m} = Ask (\ xs -> Ask (\ ys ->
                   sequ (sayVec (vreverse xs)) (sayVec (vreverse ys))))


-- Various bits and pieces to build a ripple-carry adder recursively

crosses :: forall a . pi (k :: Nat) . Wire (4 * k) a (4 * k) a
crosses {k} = pipe (sequ (bundle {k}) (bundle {k}))
                  (pipe (sequ wire (sequ cross wire))
                        (sequ (unbundle {k}) (unbundle {k})))

ripple :: forall a . pi (m :: Nat) .
              Wire (2 * 2 ^ m + 1) a (1 + 2 ^ m) a ->
                  Wire (2 * 2 ^ (m+1) + 1) a (1 + 2 ^ (m+1)) a
ripple {m} add | {0 <= 2 ^ m} = pipe (sequ (crosses {2 ^ m}) wire)
                                   (pipe (sequ (wires {2 ^ (m+1)}) add)
                                         (sequ add (wires {2 ^ m})))

adder :: pi (m :: Nat) . Wire (2 * 2 ^ m + 1) Bool (1 + 2 ^ m) Bool
adder {0}    = fadd
adder {m+1}  = ripple {m} (adder {m})


-- We don't have type-level div/mod (yet?) but can fake it thus

divvy :: forall a. pi (n d :: Nat) . 1 <= d =>
             (pi (m r :: Nat) . (n ~ d * m + r, r < d) => a) -> a
divvy {n}    {d} f | {n < d} = f {0} {n}
divvy {n}    {d} f | {n >= d} =
                     let g :: pi (m r :: Nat) . (n - d ~ d * m + r, r < d) => a
                         g {m} {r} = f {m+1} {r}
                     in divvy {n-d} {d} g

half :: forall a. pi (n :: Nat) . (pi (m r :: Nat) . (n ~ 2 * m + r, r <= 1) => a) -> a
half {n} = divvy {n} {2}


-- integerToBin {m} {n} is the m-bit unsigned binary representation of
-- the number n; the type guarantees that n is in the range [0..2^m-1]

integerToBin :: pi (m n :: Nat) . n < 2^m => Vec Bool m
integerToBin {m} {n} = vreverse (toBin {m} {n})
  where
    toBin :: pi (m n :: Nat) . n < 2^m => Vec Bool m
    toBin {0}   {n} = VNil
    toBin {m+1} {n} = half {n} (\ {k} {r} -> VCons (odd r) (toBin {m} {k}))


-- binToInteger converts an n-bit unsigned binary number (represented as a
-- vector of booleans) to the corresponding integer

binToInteger :: forall (n :: Nat) . Vec Bool n -> Integer
binToInteger xs = fromBin (vreverse xs)
  where
    fromBin :: forall (n :: Nat) . Vec Bool n -> Integer
    fromBin VNil = 0
    fromBin (VCons True xs) = 1 + (2 * (fromBin xs))
    fromBin (VCons False xs) = 2 * (fromBin xs)


-- binToString converts a vector of booleans to a string
-- representation of the corresponding binary number

binToString xs = map btoc (vec2list xs)
  where
    btoc True   = '1'
    btoc False  = '0'

-- test :: forall (n :: Nat) . pi (m :: Nat) . Wire m Bool n Bool ->
--             (pi (k :: Nat) . k < 2 ^ m => [Char])
test  {m} p {k} = binToString  (run p (integerToBin {m} {k}))
test' {m} p {k} = binToInteger (run p (integerToBin {m} {k}))


-- Calculate 01 + 11 + 0 = 100 (note that 01110bin = 13dec)
calc1 = test {5} (adder {1}) {13}

-- Calculate 0101 + 1100 + 1 = 10010 (note that 010111001bin = 185dec)
calc2 = test {9} (adder {2}) {185}



-- "Horizontal" k-fold repetition of wires requires multiplication. At
-- the moment we have to supply a lemma (operationally the identity
-- function) that proves that a product of positive numbers is
-- positive. A proxy is used as a substitute for type application.

data Proxy :: Integer -> * where
  Proxy :: forall (n :: Integer) . Proxy n

nsequ :: forall (m n :: Nat) a b .
           (forall (x y :: Nat) t . Proxy x -> Proxy y ->
                                        (0 <= x * y => t) -> t) ->
             (pi (k :: Nat) . Wire m a n b -> Wire (m * k) a (n * k) b)
nsequ lem {0}    p = Stop
nsequ lem {k+1}  p = lem (Proxy :: Proxy m) (Proxy :: Proxy k)
                    (lem (Proxy :: Proxy n) (Proxy :: Proxy k)
                        (sequ p (nsequ lem {k} p)))