packages feed

smerdyakov-0.0.0.0: src/Smerdyakov/Internal/Class.hs

{-# LANGUAGE IncoherentInstances #-}
module Smerdyakov.Internal.Class where

import Data.Proxy (Proxy(Proxy))
import Data.Coerce (coerce)

import Smerdyakov.Internal.FreeIO

-- | An instance of the @Gives@ class says how to generate that target.
--
-- As a general rule, you will only write instances for @Gives@, and never
-- write a function with a @Gives@ constraint. If you need something, say that
-- you 'Needs' it.
--
-- >  make                 | smerdyakov
-- > ----------------------+---------------------------------------------------
-- > target: requirement   | instance (Needs Requirement) => Gives Target where
-- >    action             |    give = action
class Gives a where
    give :: Action a

-- | @make@ gathers, recursively, all the actions needed to fulfill a
-- requirement or target. The 'Action' can then be run with 'interpretIO', or
-- inspected.
make :: forall a proxy r. Gives a => proxy a -> (Needs a => Action r) -> Action r
make _ f = unmake (Proxy :: Proxy a) f

-- We can't have @Gives@ be a superclass of @Needs'@ and still have
-- the representational type role, so we defined the @Needs@ type
-- synonym instead.
class Needs' a

-- | Use @Needs@ to describe a requirement or assumption.
type Needs a = (Needs' a, Gives a)

newtype Wrap b r = Wrap { unWrap :: Needs' b => r }

newtype Make e = Make e
instance Needs' (Make e)

type role Needs' representational

coerceWrap :: Wrap e a -> Wrap (Make e) a
coerceWrap = coerce

unmake :: proxy e -> (Needs' e => a) -> a
unmake _ = unWrap . coerceWrap . Wrap