{-# LANGUAGE StrictData #-}

-- | Strict data types for use as internal
-- accumulators to achieve constant space usage.
module Strict
  (
    {- * Maybe -} Maybe (..), lazy, strict,
    {- * Either -} Either (..), hush,
    {- * Tuples -} Tuple2 (..), Tuple3 (..),
    {- * Shortcut -} Vitality (..), Will (..),
        unlessDead, vitality2, willSave,
  )
  where

import Data.Functor (Functor (..))
import Data.Monoid (Monoid, mempty)
import Data.Semigroup (Semigroup, (<>))

import qualified Data.Maybe as Lazy

data Maybe a = Just a | Nothing

instance Semigroup a => Semigroup (Maybe a) where
    Maybe a
Nothing <> :: Maybe a -> Maybe a -> Maybe a
<> Maybe a
x = Maybe a
x
    Maybe a
x <> Maybe a
Nothing = Maybe a
x
    Just a
x <> Just a
y = forall a. a -> Maybe a
Just (a
x forall a. Semigroup a => a -> a -> a
<> a
y)

instance Semigroup a => Monoid (Maybe a) where
    mempty :: Maybe a
mempty = forall a. Maybe a
Nothing

lazy :: Maybe a -> Lazy.Maybe a
lazy :: forall a. Maybe a -> Maybe a
lazy Maybe a
Nothing = forall a. Maybe a
Lazy.Nothing
lazy (Just a
a) = forall a. a -> Maybe a
Lazy.Just a
a

strict :: Lazy.Maybe a -> Maybe a
strict :: forall a. Maybe a -> Maybe a
strict Maybe a
Lazy.Nothing = forall a. Maybe a
Nothing
strict (Lazy.Just a
a) = forall a. a -> Maybe a
Just a
a

data Either a b = Left a | Right b

hush :: Either a b -> Lazy.Maybe b
hush :: forall a b. Either a b -> Maybe b
hush (Left a
_) = forall a. Maybe a
Lazy.Nothing
hush (Right b
b) = forall a. a -> Maybe a
Lazy.Just b
b

data Tuple2 a b = Tuple2 a b

data Tuple3 a b c = Tuple3 a b c

data Will = Ambivalent | Tenacious

instance Semigroup Will where
    Will
Tenacious <> :: Will -> Will -> Will
<> Will
_ = Will
Tenacious
    Will
_ <> Will
Tenacious = Will
Tenacious
    Will
_ <> Will
_ = Will
Ambivalent

instance Monoid Will where
    mempty :: Will
mempty = Will
Ambivalent

data Vitality a b = Dead a | Alive Will b
    deriving forall a b. a -> Vitality a b -> Vitality a a
forall a b. (a -> b) -> Vitality a a -> Vitality a b
forall a a b. a -> Vitality a b -> Vitality a a
forall a a b. (a -> b) -> Vitality a a -> Vitality a b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Vitality a b -> Vitality a a
$c<$ :: forall a a b. a -> Vitality a b -> Vitality a a
fmap :: forall a b. (a -> b) -> Vitality a a -> Vitality a b
$cfmap :: forall a a b. (a -> b) -> Vitality a a -> Vitality a b
Functor

unlessDead :: (b -> Vitality a b) -> Vitality a b -> Vitality a b
unlessDead :: forall b a. (b -> Vitality a b) -> Vitality a b -> Vitality a b
unlessDead b -> Vitality a b
f Vitality a b
s = case Vitality a b
s of { Alive Will
_ b
x -> b -> Vitality a b
f b
x; Vitality a b
_ -> Vitality a b
s }

willSave :: Vitality a b -> Vitality (Either a b) b
willSave :: forall a b. Vitality a b -> Vitality (Either a b) b
willSave Vitality a b
v = case Vitality a b
v of
    Dead a
x -> forall a b. a -> Vitality a b
Dead (forall a b. a -> Either a b
Left a
x)
    Alive Will
Ambivalent b
x -> forall a b. a -> Vitality a b
Dead (forall a b. b -> Either a b
Right b
x)
    Alive Will
Tenacious b
x -> forall a b. Will -> b -> Vitality a b
Alive Will
Tenacious b
x

type Vitality' a = Vitality a a

vitality2 :: Vitality a1 b1 -> Vitality a2 b2
    -> Vitality' (Strict.Tuple2 (Vitality a1 b1) (Vitality a2 b2))
vitality2 :: forall a1 b1 a2 b2.
Vitality a1 b1
-> Vitality a2 b2
-> Vitality' (Tuple2 (Vitality a1 b1) (Vitality a2 b2))
vitality2 a :: Vitality a1 b1
a@(Alive Will
v1 b1
_) b :: Vitality a2 b2
b@(Alive Will
v2 b2
_) = forall a b. Will -> b -> Vitality a b
Alive (Will
v1 forall a. Semigroup a => a -> a -> a
<> Will
v2) (forall a b. a -> b -> Tuple2 a b
Strict.Tuple2 Vitality a1 b1
a Vitality a2 b2
b)
vitality2 a :: Vitality a1 b1
a@(Dead a1
_)     b :: Vitality a2 b2
b@(Alive Will
v b2
_)  = forall a b. Will -> b -> Vitality a b
Alive Will
v          (forall a b. a -> b -> Tuple2 a b
Strict.Tuple2 Vitality a1 b1
a Vitality a2 b2
b)
vitality2 a :: Vitality a1 b1
a@(Alive Will
v b1
_)  b :: Vitality a2 b2
b@(Dead a2
_)     = forall a b. Will -> b -> Vitality a b
Alive Will
v          (forall a b. a -> b -> Tuple2 a b
Strict.Tuple2 Vitality a1 b1
a Vitality a2 b2
b)
vitality2 a :: Vitality a1 b1
a@(Dead a1
_)     b :: Vitality a2 b2
b@(Dead a2
_)     = forall a b. a -> Vitality a b
Dead             (forall a b. a -> b -> Tuple2 a b
Strict.Tuple2 Vitality a1 b1
a Vitality a2 b2
b)