{-# Language AllowAmbiguousTypes #-}
{-# Language ConstraintKinds #-}
{-# Language DataKinds #-}
{-# Language FlexibleInstances #-}
{-# Language MultiParamTypeClasses #-}
{-# Language KindSignatures #-}
{-# Language ScopedTypeVariables #-}
{-# Language TypeApplications #-}
{-# Language TypeFamilies #-}
{-# Language TypeOperators #-}
{-# Language UndecidableInstances #-}

module Data.Ruin.Deep (
  -- * Sequences of labels
  Labels,
  consLabels,
  mkLabels,
  nilLabels,

  -- * Deep projection
  DeepFieldType,
  DeepHas,
  extricate,
  ) where

import GHC.TypeLits

import Data.Ruin.All
import Data.Ruin.Eval (Eval)
import Data.Ruin.Internal

-----

class DeepHas_ (ss :: [Symbol]) (t :: *) where
  type DeepFieldType_ ss t :: *
  extricate_ :: Labels ss -> t -> Eval (DeepFieldType_ ss t)

instance DeepHas_ '[] t where
  type DeepFieldType_ '[] t = t
  {-# INLINE extricate_ #-}
  extricate_ = \_ -> pure

instance (Has s t,DeepHas ss (FieldType s t)) => DeepHas_ (s ': ss) t where
  type DeepFieldType_ (s ': ss) t = DeepFieldType ss (FieldType s t)
  {-# INLINE extricate_ #-}
  extricate_ = \_ t -> extricate1 (mkLabel @s) t >>= extricate (mkLabels @ss)

-----

-- | This constraint is an implementation detail of 'extricate'. It's
-- just an iteration of 'Has'.
type DeepHas = DeepHas_

-- | This constraint is an implementation detail of 'extricate'. It's
-- just an iteration of 'FieldType'.
type DeepFieldType ss t = DeepFieldType_ ss t

-- | 'extricate' project a field out of nested records by iterating
-- 'extricate1'.
--
-- The first argument is a function type so that the syntax can use
-- @.@ to specify a sequence of labels.
--
--
-- @
--   'extricate' id = return
--
--   'extricate' (\#s . ss) = 'extricate1' \#s Control.Monad.'Control.Monad.>=>' 'extricate' ss
-- @
extricate :: forall ss t. DeepHas ss t => Labels ss -> t -> Eval (DeepFieldType ss t)
{-# INLINE extricate #-}
extricate = extricate_