packages feed

ComonadSheet-0.3.0.0: Control/Comonad/Sheet/Indexed.hs

{- |
Module      :  Control.Comonad.Sheet.Indexed
Description :  Adds absolute position to n-dimensional comonadic spreadsheets.
Copyright   :  Copyright (c) 2014 Kenneth Foner

Maintainer  :  kenneth.foner@gmail.com
Stability   :  experimental
Portability :  non-portable

This module defines the @Indexed@ type, which bolts an absolute coordinate onto a normal nested structure, allowing
you to talk about absolute position as well as relative position.
-}

{-# LANGUAGE ConstraintKinds       #-}
{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE PolyKinds             #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE UndecidableInstances  #-}

module Control.Comonad.Sheet.Indexed where

import Control.Comonad
import Control.Applicative
import Data.Functor.Identity
import Data.Functor.Compose

import Data.Numeric.Witness.Peano
import Data.Stream.Tape
import Control.Comonad.Sheet.Reference
import Data.Functor.Nested
import Data.List.Indexed

-- | An n-dimensional coordinate is a list of length n of absolute references.
type Coordinate n = CountedList n (Ref Absolute)

-- | An indexed sheet is an n-dimensionally nested 'Tape' paired with an n-dimensional coordinate which
--   represents the absolute position of the current focus in the sheet.
data Indexed ts a =
  Indexed { index     :: Coordinate (NestedCount ts)
          , unindexed :: Nested ts a }

instance (Functor (Nested ts)) => Functor (Indexed ts) where
   fmap f (Indexed i t) = Indexed i (fmap f t)

-- | For a sheet to be Indexable, it needs to consist of n-dimensionally nested 'Tape's, such that we can take the
--   cross product of all n tapes to generate a tape of indices.
type Indexable ts = ( Cross (NestedCount ts) Tape , ts ~ NestedNTimes (NestedCount ts) Tape )

instance (ComonadApply (Nested ts), Indexable ts) => Comonad (Indexed ts) where
   extract      = extract . unindexed
   duplicate it = Indexed (index it) $
                     Indexed <$> indices (index it)
                             <@> duplicate (unindexed it)

instance (ComonadApply (Nested ts), Indexable ts) => ComonadApply (Indexed ts) where
   (Indexed i fs) <@> (Indexed _ xs) = Indexed i (fs <@> xs)

-- | Takes an n-coordinate and generates an n-dimensional enumerated space of coordinates.
indices :: (Cross n Tape) => Coordinate n -> Nested (NestedNTimes n Tape) (Coordinate n)
indices = cross . fmap enumerate

-- | The cross product of an n-length counted list of @(t a)@ is an n-nested @t@ of counted lists of @a@.
class Cross n t where
   cross :: CountedList n (t a) -> Nested (NestedNTimes n t) (CountedList n a)

instance (Functor t) => Cross (Succ Zero) t where
   cross (t ::: _) =
      Flat $ (::: CountedNil) <$> t

instance ( Cross (Succ n) t , Functor t
         , Functor (Nested (NestedNTimes (Succ n) t)) )
         => Cross (Succ (Succ n)) t where
   cross (t ::: ts) =
      Nest $ (\xs -> (::: xs) <$> t) <$> cross ts

-- | Counts how deeply a 'Nested' thing is nested.
type family NestedCount x where
   NestedCount (Flat f)   = Succ Zero
   NestedCount (Nest f g) = Succ (NestedCount f)

-- | Computes the type of an n-deep nested structure (similar to replicate for 'Nested').
type family NestedNTimes n f where
   NestedNTimes (Succ Zero) f = Flat f
   NestedNTimes (Succ n)    f = Nest (NestedNTimes n f) f