-- |
-- Module      : Data.Functor.Combinator.Unsafe
-- Copyright   : (c) Justin Le 2019
-- License     : BSD3
--
-- Maintainer  : justin@jle.im
-- Stability   : experimental
-- Portability : non-portable
--
-- Working with non-standard typeclasses like 'Plus', 'Apply', 'Bind', and
-- 'Pointed' will sometimes cause problems when using with libraries that
-- do not provide instances, even though their types already are instances
-- of 'Alternative' or 'Applicative' or 'Monad'.
--
-- This module provides unsafe methods to "promote" 'Applicative' instances
-- to 'Apply', 'Alternative' to 'Plus', etc.
--
-- They are unsafe in the sense that if those types /already/ have those
-- instances, this will cause overlapping instances errors or problems with
-- coherence.  Because of this, you should always use these with /specific/
-- @f@s, and never in a polymorphic way over @f@.
module Data.Functor.Combinator.Unsafe (
    unsafePlus
  , unsafeApply
  , unsafeBind
  , unsafePointed
  , unsafeConclude
  , unsafeDivise
  , unsafeInvariantCo
  , unsafeInvariantContra
  , unsafeInplyCo
  , unsafeInplyContra
  , unsafeInplicativeCo
  , unsafeInplicativeContra
  ) where

import           Control.Applicative
import           Data.Constraint
import           Data.Constraint.Unsafe
import           Data.Functor.Bind
import           Data.Functor.Contravariant
import           Data.Functor.Contravariant.Conclude
import           Data.Functor.Contravariant.Divise
import           Data.Functor.Contravariant.Divisible
import           Data.Functor.Invariant
import           Data.Functor.Invariant.Inplicative
import           Data.Functor.Plus
import           Data.Pointed

-- | For any @'Alternative' f@, produce a value that would require @'Plus'
-- f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has a 'Plus' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
unsafePlus :: forall f proxy r. Alternative f => proxy f -> (Plus f => r) -> r
unsafePlus :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
Alternative f =>
proxy f -> (Plus f => r) -> r
unsafePlus proxy f
_ Plus f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Plus (WrappedApplicative f)) @(Plus f) of
    Sub Dict (Plus f)
Plus (WrappedApplicative f) => Dict (Plus f)
Dict -> Plus f => r
x

-- | For any @'Applicative' f@, produce a value that would require @'Apply'
-- f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has a 'Apply' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
unsafeApply :: forall f proxy r. Applicative f => proxy f -> (Apply f => r) -> r
unsafeApply :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
Applicative f =>
proxy f -> (Apply f => r) -> r
unsafeApply proxy f
_ Apply f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Apply (WrappedApplicative f)) @(Apply f) of
    Sub Dict (Apply f)
Apply (WrappedApplicative f) => Dict (Apply f)
Dict -> Apply f => r
x

-- | For any @'Monad' f@, produce a value that would require @'Bind'
-- f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has a 'Bind' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
unsafeBind :: forall f proxy r. Monad f => proxy f -> (Bind f => r) -> r
unsafeBind :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
Monad f =>
proxy f -> (Bind f => r) -> r
unsafeBind proxy f
_ Bind f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Bind (WrappedMonad f)) @(Bind f) of
    Sub Dict (Bind f)
Bind (WrappedMonad f) => Dict (Bind f)
Dict -> Bind f => r
x

newtype PointMe f a = PointMe (f a)

instance Applicative f => Pointed (PointMe f) where
    point :: forall a. a -> PointMe f a
point = forall {k} (f :: k -> *) (a :: k). f a -> PointMe f a
PointMe forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Applicative f => a -> f a
pure

-- | For any @'Applicative' f@, produce a value that would require
-- @'Pointed' f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has a 'Pointed' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
unsafePointed :: forall f proxy r. Applicative f => proxy f -> (Pointed f => r) -> r
unsafePointed :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
Applicative f =>
proxy f -> (Pointed f => r) -> r
unsafePointed proxy f
_ Pointed f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Pointed (PointMe f)) @(Pointed f) of
    Sub Dict (Pointed f)
Pointed (PointMe f) => Dict (Pointed f)
Dict -> Pointed f => r
x

-- | For any @'Decidable' f@, produce a value that would require @'Conclude'
-- f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has a 'Conclude' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
--
-- @since 0.3.0.0
unsafeConclude :: forall f proxy r. Decidable f => proxy f -> (Conclude f => r) -> r
unsafeConclude :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
Decidable f =>
proxy f -> (Conclude f => r) -> r
unsafeConclude proxy f
_ Conclude f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Conclude (WrappedDivisible f)) @(Conclude f) of
    Sub Dict (Conclude f)
Conclude (WrappedDivisible f) => Dict (Conclude f)
Dict -> Conclude f => r
x

-- | For any @'Divisible' f@, produce a value that would require @'Divise'
-- f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has a 'Divise' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
--
-- @since 0.3.0.0
unsafeDivise :: forall f proxy r. Divisible f => proxy f -> (Divise f => r) -> r
unsafeDivise :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
Divisible f =>
proxy f -> (Divise f => r) -> r
unsafeDivise proxy f
_ Divise f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Divise (WrappedDivisible f)) @(Divise f) of
    Sub Dict (Divise f)
Divise (WrappedDivisible f) => Dict (Divise f)
Dict -> Divise f => r
x

-- | For any @'Functor' f@, produce a value that would require @'Invariant'
-- f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has an 'Invariant' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
--
-- @since 0.4.1.0
unsafeInvariantCo :: forall f proxy r. Functor f => proxy f -> (Invariant f => r) -> r
unsafeInvariantCo :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
Functor f =>
proxy f -> (Invariant f => r) -> r
unsafeInvariantCo proxy f
_ Invariant f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Invariant (WrappedFunctor f)) @(Invariant f) of
    Sub Dict (Invariant f)
Invariant (WrappedFunctor f) => Dict (Invariant f)
Dict -> Invariant f => r
x

-- | For any @'Contravariant' f@, produce a value that would require @'Invariant'
-- f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has an 'Invariant' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
--
-- @since 0.4.1.0
unsafeInvariantContra :: forall f proxy r. Contravariant f => proxy f -> (Invariant f => r) -> r
unsafeInvariantContra :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
Contravariant f =>
proxy f -> (Invariant f => r) -> r
unsafeInvariantContra proxy f
_ Invariant f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Invariant (WrappedContravariant f)) @(Invariant f) of
    Sub Dict (Invariant f)
Invariant (WrappedContravariant f) => Dict (Invariant f)
Dict -> Invariant f => r
x

-- | For any @'Apply' f@, produce a value that would require @'Inply'
-- f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has an 'Inply' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
--
-- @since 0.4.1.0
unsafeInplyCo :: forall f proxy r. Apply f => proxy f -> (Inply f => r) -> r
unsafeInplyCo :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
Apply f =>
proxy f -> (Inply f => r) -> r
unsafeInplyCo proxy f
_ Inply f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Inply (WrappedFunctor f)) @(Inply f) of
    Sub Dict (Inply f)
Inply (WrappedFunctor f) => Dict (Inply f)
Dict -> Inply f => r
x

-- | For any @'Divise' f@, produce a value that would require @'Inply'
-- f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has an 'Inply' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
--
-- @since 0.4.1.0
unsafeInplyContra :: forall f proxy r. Divise f => proxy f -> (Inply f => r) -> r
unsafeInplyContra :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
Divise f =>
proxy f -> (Inply f => r) -> r
unsafeInplyContra proxy f
_ Inply f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Inply (WrappedContravariant f)) @(Inply f) of
    Sub Dict (Inply f)
Inply (WrappedContravariant f) => Dict (Inply f)
Dict -> Inply f => r
x

-- | For any @'Applicative' f@, produce a value that would require
-- @'Inplicative' f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has an 'Inplicative' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
--
-- @since 0.4.1.0
unsafeInplicativeCo :: forall f proxy r. (Applicative f, Invariant f) => proxy f -> (Inplicative f => r) -> r
unsafeInplicativeCo :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
(Applicative f, Invariant f) =>
proxy f -> (Inplicative f => r) -> r
unsafeInplicativeCo proxy f
_ Inplicative f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Inply (WrappedApplicativeOnly f)) @(Inplicative f) of
    Sub Dict (Inplicative f)
Inply (WrappedApplicativeOnly f) => Dict (Inplicative f)
Dict -> Inplicative f => r
x

-- | For any @'Divisibl3' f@, produce a value that would require
-- @'Inplicative' f@.
--
-- Always use with concrete and specific @f@ only, and never use with any
-- @f@ that already has an 'Inplicative' instance.
--
-- The 'Data.Proxy.Proxy' argument allows you to specify which specific @f@
-- you want to enhance.  You can pass in something like @'Data.Proxy.Proxy'
-- \@MyFunctor@.
--
-- @since 0.4.1.0
unsafeInplicativeContra :: forall f proxy r. (Divisible f, Invariant f) => proxy f -> (Inplicative f => r) -> r
unsafeInplicativeContra :: forall (f :: * -> *) (proxy :: (* -> *) -> *) r.
(Divisible f, Invariant f) =>
proxy f -> (Inplicative f => r) -> r
unsafeInplicativeContra proxy f
_ Inplicative f => r
x = case forall (a :: Constraint) (b :: Constraint). a :- b
unsafeCoerceConstraint @(Inply (WrappedDivisibleOnly f)) @(Inplicative f) of
    Sub Dict (Inplicative f)
Inply (WrappedDivisibleOnly f) => Dict (Inplicative f)
Dict -> Inplicative f => r
x