type-unary-0.1.2: src/TypeUnary/Vec.hs
{-# LANGUAGE TypeFamilies, EmptyDataDecls, TypeOperators
, GADTs, KindSignatures
, FlexibleInstances, FlexibleContexts
, UndecidableInstances
, ScopedTypeVariables, CPP
, RankNTypes
, MultiParamTypeClasses, FunctionalDependencies
#-}
{-# OPTIONS_GHC -Wall -fno-warn-incomplete-patterns #-}
----------------------------------------------------------------------
-- |
-- Module : TypeUnary.Vec
-- Copyright : (c) Conal Elliott 2009
-- License : BSD3
--
-- Maintainer : conal@conal.net
-- Stability : experimental
--
-- Experiment in length-typed vectors
----------------------------------------------------------------------
module TypeUnary.Vec
(
-- * Type-level numbers
Z, S, (:+:)
, N0,N1,N2,N3,N4,N5,N6,N7,N8,N9,N10,N11,N12,N13,N14,N15,N16
-- * Typed natural numbers
, Nat(..), zero, one, two, three, four
, withIsNat, natSucc, natIsNat
, natToZ, natEq, natAdd, (:<:)
, Index(..), succI, index0, index1, index2, index3
-- * Vectors
, Vec(..), IsNat(..), (<+>), indices
, Vec0,Vec1,Vec2,Vec3,Vec4,Vec5,Vec6,Vec7,Vec8,Vec9
, Vec10,Vec11,Vec12,Vec13,Vec14,Vec15,Vec16
, vec1, vec2, vec3, vec4
, un1, un2, un3, un4
, get0, get1, get2, get3
, get, swizzle, split
, ToVec(..)
) where
import Prelude hiding (foldr,sum)
-- #include "Typeable.h"
import Control.Applicative (Applicative(..),liftA2,(<$>))
import Data.Foldable (Foldable(..),toList,sum)
import Data.Traversable (Traversable(..))
import Data.Maybe (isJust)
-- import Data.Typeable
import Foreign.Storable
import Foreign.Ptr (Ptr,plusPtr,castPtr)
import Data.VectorSpace
import Data.Proof.EQ
{--------------------------------------------------------------------
Type-level numbers
--------------------------------------------------------------------}
-- | Type-level representation of zero
data Z
-- | Type-level representation of successor
data S n
-- INSTANCE_TYPEABLE0(Z,zTC ,"Z")
-- INSTANCE_TYPEABLE1(S,sTC ,"S")
infixl 6 :+:
-- | Sum of type-level numbers
type family a :+: b
type instance Z :+: b = b
type instance S a :+: b = S (a :+: b)
type N0 = Z
type N1 = S N0
type N2 = S N1
type N3 = S N2
type N4 = S N3
type N5 = S N4
type N6 = S N5
type N7 = S N6
type N8 = S N7
type N9 = S N8
type N10 = S N9
type N11 = S N10
type N12 = S N11
type N13 = S N12
type N14 = S N13
type N15 = S N14
type N16 = S N15
-- putStrLn $ unlines ["type N" ++ show (n+1) ++ " = S N" ++ show n | n <- [0..15]]
{--------------------------------------------------------------------
Typed natural numbers
--------------------------------------------------------------------}
-- Natural numbers
data Nat :: * -> * where
Zero :: Nat Z
Succ :: IsNat n => Nat n -> Nat (S n)
instance Show (Nat n) where show = show . natToZ
withIsNat :: (IsNat n => Nat n -> a) -> (Nat n -> a)
withIsNat p Zero = p Zero
withIsNat p (Succ n) = p (Succ n)
-- Helper for when we don't have a convenient proof of IsNat n.
natSucc :: Nat n -> Nat (S n)
natSucc = withIsNat Succ
natIsNat :: Nat n -> (IsNat n => Nat n)
natIsNat Zero = Zero
natIsNat (Succ n) = Succ n
{-
-- Another approach (also works):
data NatIsNat :: * -> * where
NatIsNat :: IsNat n' => Nat n' -> (n :=: n') -> NatIsNat n
natIsNat' :: Nat n -> NatIsNat n
natIsNat' Zero = NatIsNat Zero Refl
natIsNat' (Succ n) = NatIsNat (Succ n) Refl
withIsNat' :: (IsNat n => Nat n -> a) -> (Nat n -> a)
withIsNat' p n = case natIsNat' n of
NatIsNat n' Refl -> p n'
-}
-- | Interpret a 'Nat' as an 'Integer'
natToZ :: Nat n -> Integer
natToZ Zero = 0
natToZ (Succ n) = (succ . natToZ) n
-- | Equality test
natEq :: Nat m -> Nat n -> Maybe (m :=: n)
Zero `natEq` Zero = Just Refl
Succ m `natEq` Succ n = liftEq <$> (m `natEq` n)
_ `natEq` _ = Nothing
-- | Sum of naturals
natAdd :: Nat m -> Nat n -> Nat (m :+: n)
Zero `natAdd` n = n
Succ m `natAdd` n = natSucc (m `natAdd` n)
zero :: Nat N0
zero = Zero
one :: Nat N1
one = Succ zero
two :: Nat N2
two = Succ one
three :: Nat N3
three = Succ two
four :: Nat N4
four = Succ three
infix 4 :<:
-- | Proof that @m < n@
data m :<: n where
ZLess :: Z :<: S n
SLess :: m :<: n -> S m :<: S n
-- data Index :: * -> * where
-- Index :: (n :<: lim) -> Nat n -> Index lim
-- or
-- | A number under the given limit, with proof
data Index lim = forall n. IsNat n => Index (n :<: lim) (Nat n)
instance Eq (Index lim) where
Index _ n == Index _ n' = isJust (n `natEq` n')
succI :: Index m -> Index (S m)
succI (Index p n) = Index (SLess p) (Succ n)
index0 :: Index (S m)
index0 = Index ZLess Zero
index1 :: Index (S (S m))
index1 = succI index0
index2 :: Index (S (S (S m)))
index2 = succI index1
index3 :: Index (S (S (S (S m))))
index3 = succI index2
{--------------------------------------------------------------------
Vectors
--------------------------------------------------------------------}
infixr 5 :<
-- | Vectors with type-determined length, having empty vector ('ZVec') and
-- vector cons ('(:<)').
data Vec :: * -> * -> * where
ZVec :: Vec Z a -- -- ^ zero vector
(:<) :: a -> Vec n a -> Vec (S n) a -- -- ^ vector cons
-- TODO: when haddock is fixed, reinstate per-ctor haddock comments and
-- remove the constructor comments in the data doc.
-- INSTANCE_TYPEABLE2(Vec,vecTC ,"Vec")
-- instance Show a => Show (Vec n a) where
-- show ZVec = "ZVec"
-- show (a :< v) = show a ++ " :< " ++ show v
-- | Enumerate the elements of a vector. See also 'elemsV'
-- vElems :: Vec n a -> [a]
-- vElems ZVec = []
-- vElems (a :< as) = a : vElems as
-- TODO: Add strictness annotations ("!") to (:<) arguments & compare
{-
{-# DEPRECATED vElems "Use Data.Foldable.toList instead" #-}
vElems :: Vec n a -> [a]
vElems = foldr (:) []
-}
{--------------------------------------------------------------------
Instances for standard classes
--------------------------------------------------------------------}
instance Eq a => Eq (Vec n a) where
ZVec == ZVec = True
a :< as == b :< bs = a==b && as==bs
instance Ord a => Ord (Vec n a) where
ZVec `compare` ZVec = EQ
(a :< as) `compare` (b :< bs) =
case a `compare` b of
LT -> LT
GT -> GT
EQ -> as `compare` bs
instance Show a => Show (Vec n a) where
show v = "elemsV " ++ show (toList v)
instance Functor (Vec n) where
fmap _ ZVec = ZVec
fmap f (a :< u) = f a :< fmap f u
instance IsNat n => Applicative (Vec n) where
pure = pureV
(<*>) = applyV
applyV :: Vec n (a -> b) -> Vec n a -> Vec n b
ZVec `applyV` ZVec = ZVec
(f :< fs) `applyV` (x :< xs) = f x :< (fs `applyV` xs)
-- Without -fno-warn-incomplete-patterns above,
-- the previous two instances lead to warnings about non-exhaustive
-- pattern matches, although the other possibilities
-- are type-incorrect. According to SLPJ:
--
-- The overlap warning checker simply doesn't take account of GADTs.
-- There's a long-standing project suggestion to fix this:
-- http://hackage.haskell.org/trac/ghc/wiki/ProjectSuggestions .
-- Perhaps a good GSoc project.
instance Foldable (Vec n) where
foldr _ b ZVec = b
foldr h b (a :< as) = a `h` foldr h b as
instance Traversable (Vec n) where
traverse _ ZVec = pure ZVec
traverse f (a :< as) = liftA2 (:<) (f a) (traverse f as)
instance (IsNat n, Num a) => AdditiveGroup (Vec n a) where
{ zeroV = pure 0; (^+^) = liftA2 (+) ; negateV = fmap negate }
instance (IsNat n, Num a) => VectorSpace (Vec n a) where
type Scalar (Vec n a) = Vec1 a
(*^) (s :< ZVec) = fmap (s *)
instance (IsNat n, Num a) => InnerSpace (Vec n a) where
-- u <.> v = vec1 (sum (liftA2 (*) u v))
(<.>) = (result.result) (vec1 . sum) (liftA2 (*))
instance (IsNat n, Storable a) => Storable (Vec n a) where
sizeOf = const (fromIntegral (natToZ (nat :: Nat n))
* sizeOf (undefined :: a))
alignment = const (alignment (undefined :: a))
peek = peekV . castPtr
poke = pokeV . castPtr
{--------------------------------------------------------------------
IsNat
--------------------------------------------------------------------}
instance IsNat Z where
nat = Zero
pureV _ = ZVec
elemsV [] = ZVec
elemsV (_:_) = error "elemsV: too many elements"
peekV = const (return ZVec)
pokeV = const (const (return ()))
instance IsNat n => IsNat (S n) where
nat = Succ nat
pureV a = a :< pureV a
elemsV [] = error "elemsV: too few elements"
elemsV (a : as) = a :< elemsV as
peekV p = do a <- peek p
as <- peekV (p `plusPtr` sizeOf a)
return (a :< as)
-- liftA2 (:<) (peek p) (peekV (succPtr p))
-- peekV = (liftA2.liftA2) (:<) peek (peekV . succPtr)
-- TODO: Try these niftier peekV definitions
pokeV p (a :< as) = do poke p a
pokeV (p `plusPtr` sizeOf a) as
-- -- Experiment toward simplifying away the plusPtr calls.
-- succPtr :: forall a. Storable a => Ptr a -> Ptr a
-- succPtr p = p `plusPtr` sizeOf (undefined :: a)
-- TODO: Optimize peekV, pokeV. For instance, unroll the loop in the
-- dictionary, remove the sizeOf dependence on @a@.
infixl 1 <+>
-- | Concatenation of vectors
(<+>) :: Vec m a -> Vec n a -> Vec (m :+: n) a
ZVec <+> v = v
(a :< u) <+> v = a :< (u <+> v)
-- | Indices under @n@: 'index0' :< 'index1' :< ...
indices :: Nat n -> Vec n (Index n)
indices Zero = ZVec
indices (Succ n) = index0 :< fmap succI (indices n)
-- TODO: Try reimplementing many Vec functions via foldr. Warning: some
-- (most?) will fail because they rely on a polymorphic combining function.
-- | @n@ a vector length.
class {- Typeable n => -} IsNat n where
nat :: Nat n
pureV :: a -> Vec n a
elemsV :: [a] -> Vec n a
peekV :: Storable a => Ptr a -> IO (Vec n a)
pokeV :: Storable a => Ptr a -> Vec n a -> IO ()
-- Convert from vector to list via Data.Foldable.toList
{-
-- TODO: remove all but nat from the class. Define the rest outside of the
-- class by using nat. Then break this module into Nat and Vec. For instance,
pureV :: IsNat n => a -> Vec n a
pureV = pureN nat
pureN :: Nat n -> a -> Vec n a
pureN Zero _ = ZVec
pureN (Succ n) a = a :< pureN n a
-}
-- Convenient nicknames
type Vec0 = Vec N0
type Vec1 = Vec N1
type Vec2 = Vec N2
type Vec3 = Vec N3
type Vec4 = Vec N4
type Vec5 = Vec N5
type Vec6 = Vec N6
type Vec7 = Vec N7
type Vec8 = Vec N8
type Vec9 = Vec N9
type Vec10 = Vec N10
type Vec11 = Vec N11
type Vec12 = Vec N12
type Vec13 = Vec N13
type Vec14 = Vec N14
type Vec15 = Vec N15
type Vec16 = Vec N16
-- putStr $ unlines ["type Vec" ++ show n ++ " = Vec N" ++ show n | n <- [0..16]]
vec1 :: a -> Vec1 a
vec1 a = a :< ZVec
vec2 :: a -> a -> Vec2 a
vec2 a b = a :< vec1 b
vec3 :: a -> a -> a -> Vec3 a
vec3 a b c = a :< vec2 b c
vec4 :: a -> a -> a -> a -> Vec4 a
vec4 a b c d = a :< vec3 b c d
-- | Extract element
un1 :: Vec1 a -> a
un1 (a :< ZVec) = a
-- | Extract elements
un2 :: Vec2 a -> (a,a)
un2 (a :< b :< ZVec) = (a,b)
-- | Extract elements
un3 :: Vec3 a -> (a,a,a)
un3 (a :< b :< c :< ZVec) = (a,b,c)
-- | Extract elements
un4 :: Vec4 a -> (a,a,a,a)
un4 (a :< b :< c :< d :< ZVec) = (a,b,c,d)
{--------------------------------------------------------------------
Extract elements
--------------------------------------------------------------------}
-- | General indexing, taking a proof that the index is within bounds.
get :: Index n -> Vec n a -> Vec1 a
get (Index ZLess Zero ) (a :< _) = vec1 a
get (Index (SLess p) (Succ m)) (_ :< as) = get (Index p m) as
get0 :: Vec (S n) a -> Vec1 a
get1 :: Vec (S (S n)) a -> Vec1 a
get2 :: Vec (S (S (S n))) a -> Vec1 a
get3 :: Vec (S (S (S (S n)))) a -> Vec1 a
get0 = get index0
get1 = get index1
get2 = get index2
get3 = get index3
-- | Swizzling. Extract multiple elements simultaneously.
swizzle :: Vec n (Index m) -> Vec m a -> Vec n a
swizzle ZVec _ = ZVec
swizzle (ix :< ixs) v = un1 (get ix v) :< swizzle ixs v
{-
-- 'a' :< 'b' :< 'c' :< ZVec
t1 :: Three Char
t1 = elemsV "abc"
-- 'a' :< 'b' :< 'c' :< ZVec
t2 :: Four (Index N3)
t2 = elemsV [index2, index0 ,index1, index2]
-- 'c' :< 'a' :< 'b' :< 'c' :< ZVec
t3 :: Four Char
t3 = swizzle t2 t1
-}
-- | Split a vector
split :: IsNat n => Vec (n :+: m) a -> (Vec n a, Vec m a)
split = split' nat
split' :: Nat n -> Vec (n :+: m) a -> (Vec n a, Vec m a)
split' Zero v = (ZVec, v)
split' (Succ n) (a :< as) = (a :< bs, cs)
where
(bs,cs) = split' n as
-- For instance,
--
-- *TypeUnary.Vec> split (pure 3) :: (Vec7 Int, Vec4 Int)
-- (elemsV [3,3,3,3,3,3,3],elemsV [3,3,3,3])
--
-- Note that 'pure 3' was inferred to have type 'Vec11 Int'.
-- I'd like to define take & drop similarly, e.g.,
--
-- take :: IsNat n => Vec (n :+: m) a -> Vec n a
-- take = fst . split
--
-- However,
--
-- Could not deduce ((n :+: m0) ~ (n :+: m))
-- from the context (IsNat n)
-- bound by the type signature for
-- TypeUnary.Vec.take :: IsNat n => Vec (n :+: m) a -> Vec n a
-- at /Users/conal/Haskell/type-unary/src/TypeUnary/Vec.hs:488:1-18
-- NB: `:+:' is a type function, and may not be injective
{--------------------------------------------------------------------
Conversion to vectors
--------------------------------------------------------------------}
class ToVec c n a | c -> n a where
toVec :: c -> Vec n a
instance ToVec (Vec n a) n a where toVec = id
{--------------------------------------------------------------------
Misc
--------------------------------------------------------------------}
result :: (b -> b') -> ((a -> b) -> (a -> b'))
result = (.)