{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
module Linear.Covector
  ( Covector(..)
  , ($*)
  ) where

import Control.Applicative
import Control.Monad
import Data.Functor.Plus hiding (zero)
import qualified Data.Functor.Plus as Plus
import Data.Functor.Bind
import Data.Functor.Rep as Rep
import Linear.Algebra

-- | Linear functionals from elements of an (infinite) free module to a scalar

newtype Covector r a = Covector { runCovector :: (a -> r) -> r }

infixr 0 $*

($*) :: Representable f => Covector r (Rep f) -> f r -> r
Covector f $* m = f (Rep.index m)

instance Functor (Covector r) where
  fmap f (Covector m) = Covector $ \k -> m (k . f)

instance Apply (Covector r) where
  Covector mf <.> Covector ma = Covector $ \k -> mf $ \f -> ma (k . f)

instance Applicative (Covector r) where
  pure a = Covector $ \k -> k a
  Covector mf <*> Covector ma = Covector $ \k -> mf $ \f -> ma $ k . f

instance Bind (Covector r) where
  Covector m >>- f = Covector $ \k -> m $ \a -> runCovector (f a) k

instance Monad (Covector r) where
  return a = Covector $ \k -> k a
  Covector m >>= f = Covector $ \k -> m $ \a -> runCovector (f a) k

instance Num r => Alt (Covector r) where
  Covector m <!> Covector n = Covector $ \k -> m k + n k

instance Num r => Plus (Covector r) where
  zero = Covector (const 0)

instance Num r => Alternative (Covector r) where
  Covector m <|> Covector n = Covector $ \k -> m k + n k
  empty = Covector (const 0)

instance Num r => MonadPlus (Covector r) where
  Covector m `mplus` Covector n = Covector $ \k -> m k + n k
  mzero = Covector (const 0)

instance Coalgebra r m => Num (Covector r m) where
  Covector f + Covector g = Covector $ \k -> f k + g k
  Covector f - Covector g = Covector $ \k -> f k - g k
  Covector f * Covector g = Covector $ \k -> f $ \m -> g $ comult k m
  negate (Covector f) = Covector $ \k -> negate (f k)
  abs _    = error "Covector.abs: undefined"
  signum _ = error "Covector.signum: undefined"
  fromInteger n = Covector $ \ k -> fromInteger n * counital k