{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} module SizedGrid.Coord.Class where import SizedGrid.Ordinal import Control.Lens import Data.Maybe (fromJust) import Data.Proxy import GHC.TypeLits -- | Everything that can be uses as a Coordinate. The only required function is `asOrdinal` and the type instance of `CoordSized`: the rest can be derived automatically. class IsCoord (c :: Nat -> *) where -- | As each coord represents a finite number of states, it must be isomorphic to an Ordinal asOrdinal :: Iso' (c n) (Ordinal n) -- | The origin. If c is an instance of `Monoid`, this should be mempty zeroPosition :: (1 <= n, KnownNat n) => c n default zeroPosition :: Monoid (c n) => c n zeroPosition = mempty -- | Retrive a `Proxy` of the size sCoordSized :: Proxy (c n) -> Proxy n sCoordSized _ = Proxy -- | The largest possible number expressable maxCoordSize :: KnownNat n => Proxy (c n) -> Integer maxCoordSize p = natVal (sCoordSized p) - 1 -- | The maximum value of a coord maxCoord :: KnownNat n => Proxy n -> c n maxCoord _ = view (re asOrdinal) (maxCoord (Proxy :: Proxy n)) asSizeProxy :: c n -> (forall m. (KnownNat m, m + 1 <= n) => Proxy m -> x) -> x asSizeProxy c = asSizeProxy (view asOrdinal c) weakenIsCoord :: KnownNat m => c n -> Maybe (c m) weakenIsCoord = fmap (review asOrdinal) . weakenOrdinal . view asOrdinal strengthenIsCoord :: (KnownNat m, (n <= m)) => c n -> c m strengthenIsCoord = review asOrdinal . strengthenOrdinal . view asOrdinal -- | Sometimes it useful to work with Coords of type *, not Nat -> *. This is away of doing so. -- | -- | It should be autogenerated for all valid instances of `IsCoord` class ( x ~ ((CoordContainer x) (CoordNat x)) , 1 <= CoordNat x , IsCoord (CoordContainer x) , KnownNat (CoordNat x) ) => IsCoordLifted x where type CoordContainer x :: Nat -> * type CoordNat x :: Nat instance (KnownNat n, 1 <= n, IsCoord c) => IsCoordLifted (c n) where type CoordContainer (c n) = c type CoordNat (c n) = n instance IsCoord Ordinal where asOrdinal = id zeroPosition = Ordinal (Proxy @0) asSizeProxy (Ordinal p) func = func p maxCoord :: forall n proxy . KnownNat n => proxy n -> Ordinal n maxCoord _ = fromJust $ numToOrdinal (maxCoordSize (Proxy :: Proxy (Ordinal n))) -- | Enumerate all possible values of a coord, in order allCoordLike :: (1 <= n, IsCoord c, KnownNat n) => [c n] allCoordLike = toListOf (traverse . re asOrdinal) [minBound .. maxBound]