packages feed

fir-0.1.0.0: src/FIR/AST/Optics.hs

{-# OPTIONS_GHC -fno-warn-missing-pattern-synonym-signatures -Wno-missing-signatures #-}

{-# LANGUAGE BlockArguments         #-}
{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE GADTs                  #-}
{-# LANGUAGE LambdaCase             #-}
{-# LANGUAGE PatternSynonyms        #-}
{-# LANGUAGE PolyKinds              #-}
{-# LANGUAGE RankNTypes             #-}
{-# LANGUAGE ScopedTypeVariables    #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE UndecidableInstances   #-}

{-|
Module: FIR.AST.Optics

AST operations involving optics (view, set, use, assign).
-}

module FIR.AST.Optics where

-- base
import Data.Kind
  ( Type )
import qualified GHC.Stack
  ( HasCallStack )

-- variant
import Data.Variant.EGADT
  ( pattern VF )

-- fir
import {-# SOURCE #-} Control.Type.Optic
  ( Optic
  , Gettable, ReifiedGetter
  , Settable, ReifiedSetter
  , Indices, Part
  )
import Data.Type.List
  ( SLength, Snoc )
import FIR.AST.Display
  ( Display(toTreeArgs), named )
import FIR.AST.Type
  ( AugType(Val, (:-->)), Eff )
import FIR.Prim.Atomic
  ( SAtomicOp, atomicOp
  , AtomicShape(..), AtomicOpShape, AtomicOpTy
  )
import FIR.ProgramState
  ( ProgramState )
import {-# SOURCE #-} FIR.Syntax.Optics
  ( KnownOptic, SOptic, showSOptic )

------------------------------------------------------------

pattern Use    lg optic = VF ( UseF    lg optic )
pattern Assign lg optic = VF ( AssignF lg optic )
pattern View   lg optic = VF ( ViewF   lg optic )
pattern Set    lg optic = VF ( SetF    lg optic )
pattern Atomic op lg optic = VF ( AtomicF op lg optic )


data OpticF ( ast :: AugType -> Type ) ( t :: AugType ) where
  -- | /Use/ an optic, returning a monadic value read from the (indexed) state.
  --
  -- Like @use@ from the lens library.
  UseF
    :: forall optic ast
    .  ( GHC.Stack.HasCallStack
       , KnownOptic optic, Gettable optic
       )
    => SLength ( Indices optic ) -- ^ Singleton for the number of run-time indices.
    -> SOptic optic              -- ^ Singleton for the optic.
    -> OpticF ast ( AugUser optic )

  -- | /Assign/ a new value with an optic.
  --
  -- Like @assign@ from the lens library.
  AssignF
    :: forall optic ast
    .  ( GHC.Stack.HasCallStack
       , KnownOptic optic, Settable optic
       )
    => SLength ( Indices optic ) -- ^ Singleton for the number of run-time indices.
    -> SOptic optic              -- ^ Singleton for the optic.
    -> OpticF ast ( AugAssigner optic )

  -- | /View/: access a value using an optic.
  --
  -- Like @view@ from the lens library.
  ViewF
    :: forall optic ast
    .  ( GHC.Stack.HasCallStack
       , KnownOptic optic, ReifiedGetter optic
       )
    => SLength ( Indices optic ) -- ^ Singleton for the number of run-time indices.
    -> SOptic optic              -- ^ Singleton for the optic.
    -> OpticF ast ( AugViewer optic )

  -- | /Set/: set a value using an optic.
  --
  -- Like @set@ from the lens library.
  SetF
    :: forall optic ast
    .  ( GHC.Stack.HasCallStack
       , KnownOptic optic, ReifiedSetter optic
       )
    => SLength ( Indices optic ) -- ^ Singleton for the number of run-time indices.
    -> SOptic optic              -- ^ Singleton for the optic.
    -> OpticF ast ( AugSetter optic )

  -- | An atomic operation on the scalar an optic focuses on.
  AtomicF
    :: forall op optic ast
    .  ( GHC.Stack.HasCallStack
       , KnownOptic optic, Gettable optic, Settable optic
       , AtomicOpTy op ( Part optic )
       )
    => SAtomicOp op
    -> SLength ( Indices optic ) -- ^ Singleton for the number of run-time indices.
    -> SOptic optic              -- ^ Singleton for the optic.
    -> OpticF ast ( AugAtomic ( AtomicOpShape op ) optic )

------------------------------------------------------------
-- type synonyms for augmented types of optical operations

type AugUser     (g :: Optic as i b) = AugListVariadicIx i as b
type AugViewer   (g :: Optic is s a) = AugListVariadic   (is `Snoc` s) a
type AugAssigner (g :: Optic as i b) = AugListVariadicIx i (as `Snoc` b) ()
type AugSetter   (g :: Optic is s a) = AugListVariadic   (is `Snoc` a `Snoc` s) s
type AugExchanger (g :: Optic as i b)
  = AugListVariadicIx i (as `Snoc` b) b
type AugCompareExchanger (g :: Optic as i b)
  = AugListVariadicIx i (as `Snoc` b `Snoc` b) b

-- | The augmented type of an atomic operation of the given shape.
type family AugAtomic (sh :: AtomicShape) (g :: Optic as i b) :: AugType where
  AugAtomic 'Reads    g = AugUser g
  AugAtomic 'Writes   g = AugAssigner g
  AugAtomic 'Modifies g = AugExchanger g
  AugAtomic 'Compares g = AugCompareExchanger g

type family AugListVariadic
              ( as :: [Type] )
              ( b  :: Type   )
            = ( r :: AugType )
            | r -> as b where
  AugListVariadic '[]       b = Val b
  AugListVariadic (a ': as) b = Val a :--> AugListVariadic as b

type family AugListVariadicIx
              ( i  :: ProgramState )
              ( as :: [Type]  )
              ( b  :: Type    )
            = ( r  :: AugType )
            | r -> as i b  where
  AugListVariadicIx i '[]       b = Eff i i b
  AugListVariadicIx i (a ': as) b = Val a :--> AugListVariadicIx i as b

------------------------------------------------------------
-- displaying

instance Display (OpticF ast) where
  toTreeArgs = named \ case
    UseF    _ o ->    "Use @(" ++ showSOptic o ++ ")"
    AssignF _ o -> "Assign @(" ++ showSOptic o ++ ")"
    ViewF   _ o ->   "View @(" ++ showSOptic o ++ ")"
    SetF    _ o ->    "Set @(" ++ showSOptic o ++ ")"
    AtomicF op _ o -> show ( atomicOp op ) ++ " @(" ++ showSOptic o ++ ")"