{-# LANGUAGE GADTs #-}
{-# LANGUAGE LinearTypes #-}
{-# LANGUAGE NoImplicitPrelude #-}

-- | This module contains useful functions for working with 'Either's.
module Data.Either.Linear
  ( Either (..),
    either,
    lefts,
    rights,
    fromLeft,
    fromRight,
    partitionEithers,
  )
where

import Data.Unrestricted.Linear
import Prelude (Either (..))

-- XXX Design Notes
-- Functions like isLeft do not make sense in a linear program.
--------------------------------------------------------------------------------

-- | Linearly consume an @Either@ by applying the first linear function on a
-- value constructed with @Left@ and the second linear function on a value
-- constructed with @Right@.
either :: (a %1 -> c) -> (b %1 -> c) -> Either a b %1 -> c
either f _ (Left x) = f x
either _ g (Right y) = g y

-- | Get all the left elements in order, and consume the right ones.
lefts :: (Consumable b) => [Either a b] %1 -> [a]
lefts [] = []
lefts (Left a : xs) = a : lefts xs
lefts (Right b : xs) = lseq b (lefts xs)

-- | Get all the right elements in order, and consume the left ones.
rights :: (Consumable a) => [Either a b] %1 -> [b]
rights [] = []
rights (Left a : xs) = lseq a (rights xs)
rights (Right b : xs) = b : rights xs

-- | Get the left element of a consumable @Either@ with a default
fromLeft :: (Consumable a, Consumable b) => a %1 -> Either a b %1 -> a
fromLeft x (Left a) = lseq x a
fromLeft x (Right b) = lseq b x

-- | Get the right element of a consumable @Either@ with a default
fromRight :: (Consumable a, Consumable b) => b %1 -> Either a b %1 -> b
fromRight x (Left a) = lseq a x
fromRight x (Right b) = lseq x b

-- | Partition and consume a list of @Either@s into two lists with all the
-- lefts in one and the rights in the second, in the order they appeared in the
-- initial list.
partitionEithers :: [Either a b] %1 -> ([a], [b])
partitionEithers [] = ([], [])
partitionEithers (x : xs) = fromRecur x (partitionEithers xs)
  where
    fromRecur :: Either a b %1 -> ([a], [b]) %1 -> ([a], [b])
    fromRecur (Left a) (as, bs) = (a : as, bs)
    fromRecur (Right b) (as, bs) = (as, b : bs)
