packages feed

typed-range-0.1.0.0: Data/Range/Typed/Data.hs

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}

-- | The Data module for common data types within the code.
module Data.Range.Typed.Data where

import Data.Kind
import Optics.Getter (view)
import Optics.Lens (Lens', lens)
import Optics.Setter (set)

data OverlapType = Separate | Overlap | Adjoin
  deriving (Eq, Show)

-- | Represents a bound, with exclusiveness.
data Bound a
  = -- | The value should be included in the bound.
    InclusiveBound a
  | -- | The value should be excluded in the bound.
    ExclusiveBound a
  deriving stock (Eq, Show, Functor)

-- | All kinds of ranges.
data Range (hasLowerBound :: Bool) (hasUpperBound :: Bool) (a :: Type) where
  -- | A single element. It is equivalent to @SpanRange (InclusiveBound a) (InclusiveBound a)@.
  SingletonRange :: a -> Range 'True 'True a
  -- | A span of elements. Make sure lower bound <= upper bound.
  SpanRange :: Bound a -> Bound a -> Range 'True 'True a
  -- | A range with a finite lower bound and an infinite upper bound.
  LowerBoundRange :: Bound a -> Range 'True 'False a
  -- | A range with an infinite lower bound and a finite upper bound.
  UpperBoundRange :: Bound a -> Range 'False 'True a
  -- | An infinite range.
  InfiniteRange :: Range 'False 'False a
  -- | An empty range.
  EmptyRange :: Range 'False 'False a

deriving stock instance (Eq a) => Eq (Range l r a)

deriving stock instance Functor (Range l r)

instance (Show a) => Show (Range r l a) where
  showsPrec i =
    \case
      SingletonRange a -> (<>) "singleton " . showsPrec i a
      SpanRange lBound rBound ->
        let s l symbol r = showsPrec i l . (<>) symbol . showsPrec i r
         in case (lBound, rBound) of
              (InclusiveBound l, InclusiveBound r) -> s l " +=+ " r
              (InclusiveBound l, ExclusiveBound r) -> s l " +=* " r
              (ExclusiveBound l, InclusiveBound r) -> s l " *=+ " r
              (ExclusiveBound l, ExclusiveBound r) -> s l " *=* " r
      (LowerBoundRange (InclusiveBound a)) -> (<>) "lbi " . showsPrec i a
      (LowerBoundRange (ExclusiveBound a)) -> (<>) "lbe " . showsPrec i a
      (UpperBoundRange (InclusiveBound a)) -> (<>) "ubi " . showsPrec i a
      (UpperBoundRange (ExclusiveBound a)) -> (<>) "ube " . showsPrec i a
      InfiniteRange -> (<>) "inf"
      EmptyRange -> (<>) "empty"

type AnyRange = AnyRangeFor AnyRangeConstraint

class AnyRangeConstraint (range :: Type -> Type)

instance AnyRangeConstraint (Range l r)

data AnyRangeFor (c :: (Type -> Type) -> Constraint) a
  = forall hasLowerBound hasUpperBound.
    (c (Range hasLowerBound hasUpperBound)) =>
    AnyRangeFor (Range hasLowerBound hasUpperBound a)

instance (Show a) => Show (AnyRangeFor c a) where
  showsPrec i (AnyRangeFor range) =
    showsPrec i range

instance (Eq a) => Eq (AnyRangeFor c a) where
  AnyRangeFor lower == AnyRangeFor upper =
    case (lower, upper) of
      (SingletonRange l, SingletonRange r) -> r == l
      (SpanRange ll lr, SpanRange rl rr) -> rl == ll && lr == rr
      (LowerBoundRange l, LowerBoundRange r) -> r == l
      (UpperBoundRange l, UpperBoundRange r) -> r == l
      (InfiniteRange, InfiniteRange) -> True
      (EmptyRange, EmptyRange) -> True
      _ -> False

instance Functor (AnyRangeFor c) where
  fmap i (AnyRangeFor range) =
    AnyRangeFor $ fmap i range

-- | `Range` has a lower bound
class WithLowerBound range where
  -- | Changing `Range`'s lower bound (preserving the constructor)
  lowerBound :: Lens' (range a) (Bound a)

instance WithLowerBound (Range 'True hasUpperBound) where
  lowerBound = lens g s
    where
      g :: Range 'True hasUpperBound a -> Bound a
      g =
        \case
          SingletonRange a -> InclusiveBound a
          SpanRange x _ -> x
          LowerBoundRange x -> x
      s :: Range 'True hasUpperBound a -> Bound a -> Range 'True hasUpperBound a
      s range y =
        case range of
          SingletonRange _ ->
            SingletonRange $
              case y of
                InclusiveBound y' -> y'
                ExclusiveBound y' -> y'
          SpanRange _ x -> SpanRange y x
          LowerBoundRange _ -> LowerBoundRange y

instance WithLowerBound (AnyRangeFor WithLowerBound) where
  lowerBound =
    lens
      (\(AnyRangeFor range) -> view lowerBound range)
      (\(AnyRangeFor range) bound -> AnyRangeFor $ set lowerBound bound range)

instance WithLowerBound (AnyRangeFor WithAllBounds) where
  lowerBound =
    lens
      (\(AnyRangeFor range) -> view lowerBound range)
      (\(AnyRangeFor range) bound -> AnyRangeFor $ set lowerBound bound range)

-- | `Range` has a upper bound
class WithUpperBound range where
  -- | Changing `Range`'s upper bound (preserving the constructor)
  upperBound :: Lens' (range a) (Bound a)

instance WithUpperBound (Range hasLowerBound 'True) where
  upperBound = lens g s
    where
      g :: Range hasLowerBound 'True a -> Bound a
      g =
        \case
          SingletonRange a -> InclusiveBound a
          SpanRange x _ -> x
          UpperBoundRange x -> x
      s :: Range hasLowerBound 'True a -> Bound a -> Range hasLowerBound 'True a
      s range y =
        case range of
          SingletonRange _ ->
            SingletonRange $
              case y of
                InclusiveBound y' -> y'
                ExclusiveBound y' -> y'
          SpanRange x _ -> SpanRange x y
          UpperBoundRange _ -> UpperBoundRange y

instance WithUpperBound (AnyRangeFor WithUpperBound) where
  upperBound =
    lens
      (\(AnyRangeFor range) -> view upperBound range)
      (\(AnyRangeFor range) bound -> AnyRangeFor $ set upperBound bound range)

instance WithUpperBound (AnyRangeFor WithAllBounds) where
  upperBound =
    lens
      (\(AnyRangeFor range) -> view upperBound range)
      (\(AnyRangeFor range) bound -> AnyRangeFor $ set upperBound bound range)

class (WithLowerBound a, WithUpperBound a) => WithAllBounds (a :: Type -> Type)

instance (WithLowerBound a, WithUpperBound a) => WithAllBounds a