{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE LinearTypes #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# OPTIONS_HADDOCK hide #-}

module Data.Unrestricted.Linear.Internal.Movable
  ( -- * Movable
    Movable (..),
    GMovable,
    genericMove,
  )
where

import qualified Data.Functor.Linear.Internal.Applicative as Data
import qualified Data.Functor.Linear.Internal.Functor as Data
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.Semigroup as Semigroup
import Data.Unrestricted.Linear.Internal.Dupable
import Data.Unrestricted.Linear.Internal.Ur
import GHC.Tuple (Solo)
import GHC.Types (Multiplicity (..))
import Generics.Linear
import Prelude.Linear.Generically
import Prelude.Linear.Internal
import qualified Unsafe.Linear as Unsafe
import Prelude (Bool (..), Char, Double, Float, Int, Ordering (..), Word)
import qualified Prelude as Prelude

-- | Use @'Movable' a@ to represent a type which can be used many times even
-- when given linearly. Simple data types such as 'Bool' or @[]@ are 'Movable'.
-- Though, bear in mind that this typically induces a deep copy of the value.
--
-- Formally, @'Movable' a@ is the class of
-- [coalgebras](https://ncatlab.org/nlab/show/coalgebra+over+a+comonad) of the
-- 'Ur' comonad. That is
--
-- * @unur (move x) = x@
-- * @move \@(Ur a) (move \@a x) = fmap (move \@a) $ move \@a x@
--
-- Additionally, a 'Movable' instance must be compatible with its 'Dupable' parent instance. That is:
--
-- * @case move x of {Ur _ -> ()} = consume x@
-- * @case move x of {Ur x -> (x, x)} = dup2 x@
class (Dupable a) => Movable a where
  move :: a %1 -> Ur a

-- -------------
-- Instances

deriving via
  Generically Bool
  instance
    Movable Bool

deriving via
  Generically Char
  instance
    Movable Char

deriving via
  Generically Double
  instance
    Movable Double

deriving via
  Generically Float
  instance
    Movable Float

deriving via
  Generically Int
  instance
    Movable Int

deriving via
  Generically Word
  instance
    Movable Word

deriving via
  Generically Prelude.Ordering
  instance
    Movable Prelude.Ordering

instance Movable () where
  move :: () %1 -> Ur ()
move () = forall a. a -> Ur a
Ur ()

deriving via
  Generically (Solo a)
  instance
    (Movable a) => Movable (Solo a)

deriving via
  Generically (a, b)
  instance
    (Movable a, Movable b) => Movable (a, b)

deriving via
  Generically (a, b, c)
  instance
    (Movable a, Movable b, Movable c) => Movable (a, b, c)

deriving via
  Generically (a, b, c, d)
  instance
    (Movable a, Movable b, Movable c, Movable d) => Movable (a, b, c, d)

deriving via
  Generically (a, b, c, d, e)
  instance
    (Movable a, Movable b, Movable c, Movable d, Movable e) => Movable (a, b, c, d, e)

instance (Movable a) => Movable (Prelude.Maybe a) where
  move :: Maybe a %1 -> Ur (Maybe a)
move (Maybe a
Prelude.Nothing) = forall a. a -> Ur a
Ur forall a. Maybe a
Prelude.Nothing
  move (Prelude.Just a
x) = forall (f :: * -> *) a b. Functor f => (a %1 -> b) -> f a %1 -> f b
Data.fmap forall a. a -> Maybe a
Prelude.Just (forall a. Movable a => a %1 -> Ur a
move a
x)

instance (Movable a, Movable b) => Movable (Prelude.Either a b) where
  move :: Either a b %1 -> Ur (Either a b)
move (Prelude.Left a
a) = forall (f :: * -> *) a b. Functor f => (a %1 -> b) -> f a %1 -> f b
Data.fmap forall a b. a -> Either a b
Prelude.Left (forall a. Movable a => a %1 -> Ur a
move a
a)
  move (Prelude.Right b
b) = forall (f :: * -> *) a b. Functor f => (a %1 -> b) -> f a %1 -> f b
Data.fmap forall a b. b -> Either a b
Prelude.Right (forall a. Movable a => a %1 -> Ur a
move b
b)

instance (Movable a) => Movable [a] where
  -- The explicit go function lets this specialize.
  move :: [a] %1 -> Ur [a]
move = [a] %1 -> Ur [a]
go
    where
      go :: [a] %1 -> Ur [a]
      go :: [a] %1 -> Ur [a]
go [] = forall a. a -> Ur a
Ur []
      go (a
a : [a]
l) = (:) forall (f :: * -> *) a b. Functor f => (a %1 -> b) -> f a %1 -> f b
Data.<$> forall a. Movable a => a %1 -> Ur a
move a
a forall (f :: * -> *) a b.
Applicative f =>
f (a %1 -> b) %1 -> f a %1 -> f b
Data.<*> [a] %1 -> Ur [a]
go [a]
l

instance (Movable a) => Movable (NonEmpty a) where
  move :: NonEmpty a %1 -> Ur (NonEmpty a)
move (a
x :| [a]
xs) = forall a. a -> [a] -> NonEmpty a
(:|) forall (f :: * -> *) a b. Functor f => (a %1 -> b) -> f a %1 -> f b
Data.<$> forall a. Movable a => a %1 -> Ur a
move a
x forall (f :: * -> *) a b.
Applicative f =>
f (a %1 -> b) %1 -> f a %1 -> f b
Data.<*> forall a. Movable a => a %1 -> Ur a
move [a]
xs

instance Movable (Ur a) where
  move :: Ur a %1 -> Ur (Ur a)
move (Ur a
a) = forall a. a -> Ur a
Ur (forall a. a -> Ur a
Ur a
a)

-- Some stock instances
deriving newtype instance (Movable a) => Movable (Semigroup.Sum a)

deriving newtype instance (Movable a) => Movable (Semigroup.Product a)

deriving newtype instance Movable Semigroup.All

deriving newtype instance Movable Semigroup.Any

-- -------------
-- Generic deriving

instance (Generic a, GMovable (Rep a)) => Movable (Generically a) where
  move :: Generically a %1 -> Ur (Generically a)
move = forall (f :: * -> *) a b. Functor f => (a %1 -> b) -> f a %1 -> f b
Data.fmap (forall a. a -> Generically a
Generically forall b c a (q :: Multiplicity) (m :: Multiplicity)
       (n :: Multiplicity).
(b %1 -> c) %q -> (a %1 -> b) %m -> a %n -> c
. forall a p (m :: Multiplicity). Generic a => Rep a p %m -> a
to) forall b c a (q :: Multiplicity) (m :: Multiplicity)
       (n :: Multiplicity).
(b %1 -> c) %q -> (a %1 -> b) %m -> a %n -> c
. forall (f :: * -> *) p. GMovable f => f p %1 -> Ur (f p)
gmove forall b c a (q :: Multiplicity) (m :: Multiplicity)
       (n :: Multiplicity).
(b %1 -> c) %q -> (a %1 -> b) %m -> a %n -> c
. forall a p (m :: Multiplicity). Generic a => a %m -> Rep a p
from forall b c a (q :: Multiplicity) (m :: Multiplicity)
       (n :: Multiplicity).
(b %1 -> c) %q -> (a %1 -> b) %m -> a %n -> c
. forall a. Generically a %1 -> a
unGenerically

genericMove :: (Generic a, GMovable (Rep a)) => a %1 -> Ur a
genericMove :: forall a. (Generic a, GMovable (Rep a)) => a %1 -> Ur a
genericMove = forall (f :: * -> *) a b. Functor f => (a %1 -> b) -> f a %1 -> f b
Data.fmap forall a p (m :: Multiplicity). Generic a => Rep a p %m -> a
to forall b c a (q :: Multiplicity) (m :: Multiplicity)
       (n :: Multiplicity).
(b %1 -> c) %q -> (a %1 -> b) %m -> a %n -> c
. forall (f :: * -> *) p. GMovable f => f p %1 -> Ur (f p)
gmove forall b c a (q :: Multiplicity) (m :: Multiplicity)
       (n :: Multiplicity).
(b %1 -> c) %q -> (a %1 -> b) %m -> a %n -> c
. forall a p (m :: Multiplicity). Generic a => a %m -> Rep a p
from

class (GDupable f) => GMovable f where
  gmove :: f p %1 -> Ur (f p)

instance GMovable V1 where
  gmove :: forall p. V1 p %1 -> Ur (V1 p)
gmove = \case {}

instance GMovable U1 where
  gmove :: forall p. U1 p %1 -> Ur (U1 p)
gmove U1 p
U1 = forall a. a -> Ur a
Ur forall k (p :: k). U1 p
U1

instance (GMovable f, GMovable g) => GMovable (f :+: g) where
  gmove :: forall p. (:+:) f g p %1 -> Ur ((:+:) f g p)
gmove (L1 f p
a) = forall (f :: * -> *) p. GMovable f => f p %1 -> Ur (f p)
gmove f p
a forall a b (p :: Multiplicity) (q :: Multiplicity).
a %p -> (a %p -> b) %q -> b
& \case (Ur f p
x) -> forall a. a -> Ur a
Ur (forall k (f :: k -> *) (g :: k -> *) (p :: k). f p -> (:+:) f g p
L1 f p
x)
  gmove (R1 g p
a) = forall (f :: * -> *) p. GMovable f => f p %1 -> Ur (f p)
gmove g p
a forall a b (p :: Multiplicity) (q :: Multiplicity).
a %p -> (a %p -> b) %q -> b
& \case (Ur g p
x) -> forall a. a -> Ur a
Ur (forall k (f :: k -> *) (g :: k -> *) (p :: k). g p -> (:+:) f g p
R1 g p
x)

instance (GMovable f, GMovable g) => GMovable (f :*: g) where
  gmove :: forall p. (:*:) f g p %1 -> Ur ((:*:) f g p)
gmove (f p
a :*: g p
b) =
    forall (f :: * -> *) p. GMovable f => f p %1 -> Ur (f p)
gmove f p
a forall a b (p :: Multiplicity) (q :: Multiplicity).
a %p -> (a %p -> b) %q -> b
& \case
      (Ur f p
x) ->
        forall (f :: * -> *) p. GMovable f => f p %1 -> Ur (f p)
gmove g p
b forall a b (p :: Multiplicity) (q :: Multiplicity).
a %p -> (a %p -> b) %q -> b
& \case
          (Ur g p
y) -> forall a. a -> Ur a
Ur (f p
x forall k (f :: k -> *) (g :: k -> *) (p :: k).
f p -> g p -> (:*:) f g p
:*: g p
y)

instance (Movable c) => GMovable (K1 i c) where
  gmove :: forall p. K1 i c p %1 -> Ur (K1 i c p)
gmove (K1 c
c) = forall a b. Coercible a b => a %1 -> b
lcoerce (forall a. Movable a => a %1 -> Ur a
move c
c)

instance (GMovable f) => GMovable (M1 i t f) where
  gmove :: forall p. M1 i t f p %1 -> Ur (M1 i t f p)
gmove (M1 f p
a) = forall a b. Coercible a b => a %1 -> b
lcoerce (forall (f :: * -> *) p. GMovable f => f p %1 -> Ur (f p)
gmove f p
a)

instance GMovable (MP1 'Many f) where
  gmove :: forall p. MP1 'Many f p %1 -> Ur (MP1 'Many f p)
gmove (MP1 f p
x) = forall a. a -> Ur a
Ur (forall {k} (b :: k -> *) (c :: k) (a :: Multiplicity).
b c %a -> MP1 a b c
MP1 f p
x)

instance (GMovable f) => GMovable (MP1 'One f) where
  gmove :: forall p. MP1 'One f p %1 -> Ur (MP1 'One f p)
gmove (MP1 f p
a) = forall (f :: * -> *) p. GMovable f => f p %1 -> Ur (f p)
gmove f p
a forall a b (p :: Multiplicity) (q :: Multiplicity).
a %p -> (a %p -> b) %q -> b
& \case Ur f p
x -> forall a. a -> Ur a
Ur (forall {k} (b :: k -> *) (c :: k) (a :: Multiplicity).
b c %a -> MP1 a b c
MP1 f p
x)

instance GMovable UChar where
  gmove :: forall p. UChar p %1 -> Ur (UChar p)
gmove (UChar Char#
c) = forall a b (p :: Multiplicity) (x :: Multiplicity).
(a %p -> b) %1 -> a %x -> b
Unsafe.toLinear (\Char#
x -> forall a. a -> Ur a
Ur (forall k (p :: k). Char# -> URec Char p
UChar Char#
x)) Char#
c

instance GMovable UDouble where
  gmove :: forall p. UDouble p %1 -> Ur (UDouble p)
gmove (UDouble Double#
c) = forall a b (p :: Multiplicity) (x :: Multiplicity).
(a %p -> b) %1 -> a %x -> b
Unsafe.toLinear (\Double#
x -> forall a. a -> Ur a
Ur (forall k (p :: k). Double# -> URec Double p
UDouble Double#
x)) Double#
c

instance GMovable UFloat where
  gmove :: forall p. UFloat p %1 -> Ur (UFloat p)
gmove (UFloat Float#
c) = forall a b (p :: Multiplicity) (x :: Multiplicity).
(a %p -> b) %1 -> a %x -> b
Unsafe.toLinear (\Float#
x -> forall a. a -> Ur a
Ur (forall k (p :: k). Float# -> URec Float p
UFloat Float#
x)) Float#
c

instance GMovable UInt where
  gmove :: forall p. UInt p %1 -> Ur (UInt p)
gmove (UInt Int#
c) = forall a b (p :: Multiplicity) (x :: Multiplicity).
(a %p -> b) %1 -> a %x -> b
Unsafe.toLinear (\Int#
x -> forall a. a -> Ur a
Ur (forall k (p :: k). Int# -> URec Int p
UInt Int#
x)) Int#
c

instance GMovable UWord where
  gmove :: forall p. UWord p %1 -> Ur (UWord p)
gmove (UWord Word#
c) = forall a b (p :: Multiplicity) (x :: Multiplicity).
(a %p -> b) %1 -> a %x -> b
Unsafe.toLinear (\Word#
x -> forall a. a -> Ur a
Ur (forall k (p :: k). Word# -> URec Word p
UWord Word#
x)) Word#
c