circuits
Safe HaskellNone
LanguageGHC2024

Circuit.Hyper

Description

A Hyper is completely determined by its dual. To get a b you must provide a continuation that can itself produce an a.

Hyper is the function-category specialisation:

type Hyper = HyperA (->)

The two named targets are:

  • Hyper — arrows are plain functions and knots tie by Haskell laziness.
  • HyperA (K m) — arrows are Kleisli arrows and knots tie with mfix.

They share the same newtype, but the constructors and eliminators are specialised because the two bases have different notions of observation and fixed point.

doctests

>>> import Circuit.Hyper
>>> import Circuit.Channel (trace)
>>> import Circuit.Category (K (..))
>>> import Data.Functor.Identity (Identity (..))
>>> let body = liftK (K (\(xs, ()) -> Identity (0 : xs, take 3 xs)) :: K Identity ([Int], ()) ([Int], [Int]))
>>> runIdentity (observeK (trace body) ())
[0,0,0]
Synopsis

Parameterised hyperfunctions

newtype HyperA (arr :: Type -> k -> Type) (a :: k) (b :: k) Source #

A hyperfunction from a to b over the base category arr.

Constructors

HyperA 

Fields

  • invoke :: arr (HyperA arr b a) b

    Feed a continuation into the hyperfunction.

Instances

Instances details
Category Hyper Source # 
Instance details

Defined in Circuit.Hyper

Methods

id :: Hyper a a Source #

(.) :: Hyper b c -> Hyper a b -> Hyper a c Source #

Channel (,) Hyper Source # 
Instance details

Defined in Circuit.Hyper

Methods

assoc :: Hyper ((a, b), c) (a, (b, c)) Source #

assoc' :: Hyper (a, (b, c)) ((a, b), c) Source #

slide :: Hyper (a, (b, c)) (b, (a, c)) Source #

Strength (,) Hyper Source # 
Instance details

Defined in Circuit.Hyper

Methods

strength :: Hyper b c -> Hyper (a, b) (a, c) Source #

Traced (,) Hyper Source # 
Instance details

Defined in Circuit.Hyper

Methods

trace :: Hyper (a, b) (a, c) -> Hyper b c Source #

Monad m => Channel (,) (HyperA (K m) :: Type -> Type -> Type) Source # 
Instance details

Defined in Circuit.Hyper

Methods

assoc :: HyperA (K m) ((a, b), c) (a, (b, c)) Source #

assoc' :: HyperA (K m) (a, (b, c)) ((a, b), c) Source #

slide :: HyperA (K m) (a, (b, c)) (b, (a, c)) Source #

Monad m => Strength (,) (HyperA (K m) :: Type -> Type -> Type) Source # 
Instance details

Defined in Circuit.Hyper

Methods

strength :: HyperA (K m) b c -> HyperA (K m) (a, b) (a, c) Source #

MonadFix m => Traced (,) (HyperA (K m) :: Type -> Type -> Type) Source # 
Instance details

Defined in Circuit.Hyper

Methods

trace :: HyperA (K m) (a, b) (a, c) -> HyperA (K m) b c Source #

Monad m => Category (HyperA (K m) :: Type -> Type -> Type) Source # 
Instance details

Defined in Circuit.Hyper

Methods

id :: HyperA (K m) a a Source #

(.) :: HyperA (K m) b c -> HyperA (K m) a b -> HyperA (K m) a c Source #

type Hyper = HyperA (->) Source #

The function-category hyperfunction.

pattern Hyper :: (HyperA (->) b a -> b) -> Hyper a b Source #

Bidirectional pattern for the function-category hyperfunction.

Function-category hyperfunctions

lift :: (a -> b) -> Hyper a b Source #

Embed a plain function into a hyperfunction.

>>> observe (lift (+1)) 5
6

observe :: Hyper a b -> a -> b Source #

Extract a plain function from a hyperfunction.

>>> observe (lift reverse) "hello"
"olleh"

base :: a -> Hyper b a Source #

Ignores the input and returns a constant value.

>>> observe (base 42) undefined
42

push :: (a -> b) -> Hyper a b -> Hyper a b Source #

Push a plain function onto a hyperfunction.

>>> observe (push (+1) (lift (*2))) 5
6

runHyper :: Hyper a a -> a Source #

Close the self-referential loop.

>>> runHyper (Hyper $ \_ -> 42 :: Int)
42

Kleisli hyperfunctions

liftK :: forall (m :: Type -> Type) a b. Monad m => K m a b -> HyperA (K m) a b Source #

Embed a Kleisli arrow into a hyperfunction.

observeK :: Monad m => HyperA (K m) a b -> a -> m b Source #

Extract the underlying Kleisli arrow from a hyperfunction.

baseK :: forall (m :: Type -> Type) b a. Monad m => b -> HyperA (K m) a b Source #

A constant Kleisli hyperfunction.

pushK :: forall (m :: Type -> Type) a b. Monad m => K m a b -> HyperA (K m) a b -> HyperA (K m) a b Source #

Push a Kleisli arrow onto a hyperfunction.

runHyperK :: MonadFix m => HyperA (K m) a a -> m a Source #

Close the self-referential loop using mfix.

Either-loop state machine

encodeEither :: (Either a b -> Either a c) -> Hyper (Either a b -> c) (Either a b -> c) Source #

Encode an Either-loop as a self-referential Hyper.

Whereas encode handles the (,) tensor using Hyper's own Traced instance, this preserves the Either-loop state in the function domain. Left a feeds back; Right c terminates with output.

>>> :{
let step = \case
      Right n | n < 3 -> Left (n + 1)
      Right n         -> Right n
      Left n  | n < 3 -> Left (n + 1)
      Left n          -> Right n
:}
>>> runEither step (0 :: Int)
3

runEither :: (Either a b -> Either a c) -> b -> c Source #

Run an encodeEither-encoded circuit from initial input b.

encodeEither embeds the Either state machine into Hyper, runHyper ties the self-referential knot, and Right b injects the initial state.

>>> :{
let step = \case
      Right n | n < 3 -> Left (n + 1)
      Right n         -> Right n
      Left n  | n < 3 -> Left (n + 1)
      Left n          -> Right n
:}
>>> runEither step (0 :: Int)
3

Bridges from initial syntax

encode :: Trace (,) (->) a b -> Hyper a b Source #

Encode a function-category Trace into a Hyper.

This is the unique traced functor from the initial syntax (Trace) to the final object (Hyper), satisfying the commuting triangle observe . encode = eval.

base constructors embed directly via lift; yank constructors become trace over a hyperfunction.

>>> import qualified Circuit.Trace as Trace
>>> observe (encode (Trace.base (+1) :: Trace.Trace (,) (->) Int Int)) 5
6

encodeK :: forall (m :: Type -> Type) a b. MonadFix m => Trace (,) (K m) a b -> HyperA (K m) a b Source #

Encode a Kleisli Trace into a HyperA (K m).