packages feed

circuits-0.2.0.0: src/Circuit/Free.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeAbstractions #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- | The free category over a base arrow.
--
-- The two constructors are 'Lift', which embeds a base arrow, and
-- 'Compose', which sequences two free morphisms.  The universal fold out
-- of 'Free' is 'run'.
module Circuit.Free
  ( Free (..),
    freeze,
  )
where

import Circuit.Category (Category (..), Discrete (..))
import Circuit.Channel (Channel (..), Strength (..), Traced (..))
import Circuit.Layer (Layer (..), (:~>))
import Prelude hiding (id, (.))

-- $setup
-- >> import Circuit.Free
-- >> import Circuit.Layer (run)
-- >> import Prelude hiding (id, (.))

-- | The free category over a base arrow @arr@.
--
-- Two constructors:
--
--   * 'Lift' — embed a base arrow.
--   * 'Compose' — sequential composition.
--
-- .> run (Lift (+1) :: Free (->) Int Int) 5
-- 6
-- .> run (Compose (Lift (+1)) (Lift (*2)) :: Free (->) Int Int) 5
-- 11
data Free arr a b where
  -- | Embed a base arrow.
  Lift :: arr a b -> Free arr a b
  -- | Sequential composition.
  --
  -- The 'Ob' constraint on the intermediate object @b@ is carried in the
  -- constructor so folding does not need a 'Discrete' base.
  Compose :: (Ob arr b) => Free arr b c -> Free arr a b -> Free arr a c

instance (Category arr) => Category (Free arr) where
  type Ob (Free arr) a = Ob arr a
  id = Lift id
  (.) = Compose

-- | A discrete base yields a discrete free category.
instance (Discrete arr) => Discrete (Free arr) where
  withOb @a x = withOb @arr @a x

-- | Layer instance for the free category.
--
-- 'Compose' carries the intermediate 'Ob' evidence of the /source/
-- category, but folding into a target category @arr'@ still needs to
-- manufacture the corresponding 'Ob arr' b' evidence.  That is exactly
-- what 'Discrete arr'' provides, so 'Law' is 'Discrete'.
instance Layer Free where
  type Law Free arr' = Discrete arr'
  type Run Free arr = Category arr
  type Bind Free arr = ()
  unit = Lift
  run (Lift f) = f
  run (Compose g f) = run g . run f
  bind :: forall arr' arr a b. (Law Free arr', Ob arr a, Ob arr b, Ob arr' a, Ob arr' b) => (arr :~> arr') -> Free arr a b -> arr' a b
  bind h (Lift f) = h f
  bind h (Compose @_ @b1 g f) = withOb @arr' @b1 (bind h g . bind h f)

-- | Freeze a 'Free' category into its base arrow.
--
-- This is a synonym for 'run' @Free@.
--
-- .> freeze (Lift (+1) :: Free (->) Int Int) 5
-- 6
freeze :: (Category arr, Ob arr a, Ob arr b) => Free arr a b -> arr a b
freeze (Lift f) = f
freeze (Compose g f) = freeze g . freeze f

-- | Lift the 'Channel' structure through 'Free'.
instance (Channel t arr) => Channel t (Free arr) where
  assoc = Lift assoc
  assoc' = Lift assoc'
  slide = Lift slide

-- | Lift the 'Strength' class through 'Free'.
--
-- A morphism is frozen before tensoring with the feedback channel.
instance (Strength t arr) => Strength t (Free arr) where
  strength = Lift . strength . freeze

-- | Lift the 'Traced' class through 'Free'.
--
-- A loop body in @Free arr@ is frozen before calling the base 'trace'.
instance (Traced t arr) => Traced t (Free arr) where
  trace = Lift . trace . freeze