SciFlow-0.7.0: src/Control/Arrow/Free.hs
-- {-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE Arrows #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
-- | Various varieties of free arrow constructions.
--
-- For all of these constructions, there are only two important functions:
-- - 'eval' evaluates the free arrow in the context of another arrow.
-- - 'effect' lifts the underlying effect into the arrow.
--
-- The class 'FreeArrowLike', which is not exported from this module, exists
-- to allow these to be defined generally.
--
-- This module also defines some arrow combinators which are not exposed by
-- the standard arrow library.
module Control.Arrow.Free
( Free
, Choice
, effect
, eval
-- * Arrow functions
, mapA
, mapSeqA
, filterA
, type (~>)
) where
import Control.Arrow
import Control.Category
import Data.Bool (Bool)
import Data.Constraint (Constraint, Dict (..), mapDict, weaken1,
weaken2)
import Data.Either (Either (..))
import Data.Function (const, flip, ($))
import Data.List (uncons)
import Data.Maybe (maybe)
import Data.Monoid (Monoid)
import Data.Tuple (uncurry)
-- | A natural transformation on type constructors of two arguments.
type x ~> y = forall a b. x a b -> y a b
--------------------------------------------------------------------------------
-- FreeArrowLike
--------------------------------------------------------------------------------
-- | Small class letting us define `eval` and `effect` generally over
-- multiple free structures
class FreeArrowLike fal where
type Ctx fal :: (k -> k -> *) -> Constraint
effect :: eff a b -> fal eff a b
eval :: forall eff arr a b. ((Ctx fal) arr)
=> (eff ~> arr)
-> fal eff a b
-> arr a b
-- | Annoying hackery to let us tuple constraints and still use 'effect'
-- and 'eval'
class Join ( a :: k -> Constraint) (b :: k -> Constraint) (x :: k) where
ctx :: Dict (a x, b x)
instance (a x, b x) => Join a b x where
ctx = Dict
--------------------------------------------------------------------------------
-- Arrow
--------------------------------------------------------------------------------
-- | Freely generated arrows over an effect.
data Free eff a b where
Pure :: (a -> b) -> Free eff a b
Effect :: eff a b -> Free eff a b
Seq :: Free eff a b -> Free eff b c -> Free eff a c
Par :: Free eff a1 b1 -> Free eff a2 b2 -> Free eff (a1, a2) (b1, b2)
instance Category (Free eff) where
id = Pure id
(.) = flip Seq
instance Arrow (Free eff) where
arr = Pure
first f = Par f id
second f = Par id f
(***) = Par
instance FreeArrowLike Free where
type Ctx Free = Arrow
-- | Lift an effect into an arrow.
effect :: eff a b -> Free eff a b
effect = Effect
-- | Evaluate given an implicit arrow
eval :: forall eff arr a0 b0. (Arrow arr)
=> (eff ~> arr)
-> Free eff a0 b0
-> arr a0 b0
eval exec = go
where
go :: forall a b. Free eff a b -> arr a b
go freeA = case freeA of
Pure f -> arr f
Seq f1 f2 -> go f2 . go f1
Par f1 f2 -> go f1 *** go f2
Effect eff -> exec eff
--------------------------------------------------------------------------------
-- ArrowChoice
--------------------------------------------------------------------------------
-- | Freely generated `ArrowChoice` over an effect.
newtype Choice eff a b = Choice {
runChoice :: forall ac. ArrowChoice ac => (eff ~> ac) -> ac a b
}
instance Category (Choice eff) where
id = Choice $ const id
Choice f . Choice g = Choice $ \x -> f x . g x
instance Arrow (Choice eff) where
arr a = Choice $ const $ arr a
first (Choice a) = Choice $ \f -> first (a f)
second (Choice a) = Choice $ \f -> second (a f)
(Choice a) *** (Choice b) = Choice $ \f -> a f *** b f
instance ArrowChoice (Choice eff) where
left (Choice a) = Choice $ \f -> left (a f)
right (Choice a) = Choice $ \f -> right (a f)
(Choice a) ||| (Choice b) = Choice $ \f -> a f ||| b f
instance FreeArrowLike Choice where
type Ctx Choice = ArrowChoice
effect :: eff a b -> Choice eff a b
effect a = Choice $ \f -> f a
eval :: forall eff arr a0 b0. (ArrowChoice arr)
=> (eff ~> arr)
-> Choice eff a0 b0
-> arr a0 b0
eval f a = runChoice a f
--------------------------------------------------------------------------------
-- Functions
--------------------------------------------------------------------------------
-- | Map an arrow over a list.
mapA :: ArrowChoice a => a b c -> a [b] [c]
mapA f = arr (maybe (Left ()) Right . uncons)
>>> (arr (const []) ||| ((f *** mapA f) >>> arr (uncurry (:))))
-- | Map an arrow over a list, forcing sequencing between each element.
mapSeqA :: ArrowChoice a => a b c -> a [b] [c]
mapSeqA f = arr (maybe (Left ()) Right . uncons)
>>> (arr (const []) ||| ((first f >>> second (mapSeqA f)) >>> arr (uncurry (:))))
-- | Filter a list given an arrow filter
filterA :: ArrowChoice a => a b Bool -> a [b] [b]
filterA f = proc xs ->
case xs of
[] -> returnA -< []
(y:ys) -> do
b <- f -< y
if b then
(second (filterA f) >>> arr (uncurry (:))) -< (y,ys)
else
filterA f -< ys