{-# LANGUAGE FlexibleContexts #-}

module TypeMachine.TM.Syntax ((<:>), (<::>), (<.>)) where

import Language.Haskell.TH (Name)
import TypeMachine.TM
import TypeMachine.TM.Liftable
import TypeMachine.Type

-- | Apply a `TM a` value to a 'TM' computation that expects an `a`
--
-- Example:
--
-- @
--  'omit' ["id"] '<:>' 'toType' ''User
-- @
(<:>) :: (LiftableTMFunction (a -> b)) => (a -> b) -> TM a -> b
<:> :: forall a b. LiftableTMFunction (a -> b) => (a -> b) -> TM a -> b
(<:>) = (a -> b) -> TM a -> b
forall f a b.
(LiftableTMFunction f, f ~ (a -> b)) =>
(a -> b) -> TM a -> b
forall a b. ((a -> b) ~ (a -> b)) => (a -> b) -> TM a -> b
applyTM

-- | Allows passing a name to a 'TM' function that takes a 'Type'
--
-- @
--  f '<::>' ''User == f '<:>' 'toType' ''User
-- @
(<::>) :: (LiftableTMFunction (Type -> a)) => (Type -> a) -> Name -> a
Type -> a
f <::> :: forall a.
LiftableTMFunction (Type -> a) =>
(Type -> a) -> Name -> a
<::> Name
n = Type -> a
f (Type -> a) -> TM Type -> a
forall a b. LiftableTMFunction (a -> b) => (a -> b) -> TM a -> b
<:> Name -> TM Type
toType Name
n

-- | Apply a `a` value to a 'TM' computation that expects an `a`.
--
-- It is just an application operator. It exists so that applications of 'TM' functions is visually homogeneous
-- Not using it when `a` is the first parameter can enhance readability.
--
-- Examples:
--
-- @
--  'omit' '<.>' ["id"] '<:>' 'toType' ''User
--  -- Is equivalent to
--  'omit' ["id"] '<:>' 'toType' ''User
-- @
--
-- If the parameters to this function were flipped, using '<.>' can be handy:
--
-- @
--  'flip' 'omit' '<:>' 'toType' ''User '<.>' ["id"]
--  -- Instead of having to use parenthesis
--  ('flip' 'omit' '<:>' 'toType' ''User) ["id"]
--  -- Or the application operator:
--  'flip' 'omit' '<:>' 'toType' ''User $ ["id"]
-- @
(<.>) :: (a -> b) -> a -> b
a -> b
f <.> :: forall a b. (a -> b) -> a -> b
<.> a
v = a -> b
f a
v