packages feed

arrow-utils-0.1.0.0: src/Control/Arrow/Utils.hs

{-# LANGUAGE Arrows #-}

module Control.Arrow.Utils (
    SameInputArrow(..)
  , traverseArr
  , traverseArr_
  , sequenceArr_
  , sequenceArr
  , zipSequenceArrVec
  , zipSequenceArrList
  , whenArr
  , unlessArr
  , constantly
) where

import Control.Arrow
    ( returnA, (>>>), Arrow((***), arr), ArrowChoice )
import Data.Foldable (traverse_)
import Data.Maybe ( fromJust )
import Data.Vector.Sized ( fromList, toList, Vector )
import qualified Data.Vector.Sized as Vec
import GHC.TypeLits ( KnownNat )

-- | Wrap the Arrow in a newtype in order to create new class instances.
--   This is a generalisation of 'ArrowMonad',
--   which is isomorphic to @'SameInputArrow' a () c@.
newtype SameInputArrow a b c = SameInputArrow { unSameInputArrow :: a b c }

-- | @'fmap' f@ postcomposes with @f@
instance (Arrow a) => Functor (SameInputArrow a b) where
  fmap f a = SameInputArrow (unSameInputArrow a >>> arr f)

-- | @'<*>'@ runs the arrows in parallel
instance (Arrow a) => Applicative (SameInputArrow a b) where
  pure c = SameInputArrow $ constantly c
  f <*> a = SameInputArrow $ proc input -> do
    fres <- unSameInputArrow f -< input
    ares <- unSameInputArrow a -< input
    returnA -< fres ares

-- | Creates arrows using f, then runs all arrows in the given 'Foldable',
-- discarding the results.
traverseArr_ :: (Foldable t, Arrow a) => (x -> a b c) -> t x -> a b ()
traverseArr_ f xs = unSameInputArrow $ traverse_ (SameInputArrow . f) xs

-- | Creates arrows using f, then runs all arrows in the given 'Traversable',
-- collecting the results.
--
--   @traverseArr (+) [1,10] 1 == [2,11]@
traverseArr :: (Traversable t, Arrow a) => (x -> a b c) -> t x -> a b (t c)
traverseArr f xs = unSameInputArrow $ traverse (SameInputArrow . f) xs

-- | Like 'sequenceArr', but discard the results.
sequenceArr_ :: (Foldable t, Arrow a) => t (a b any) -> a b ()
sequenceArr_ = traverseArr_ id

-- | Run all arrows in the given 'Traversable', collecting the results.
--
--   @sequenceArr [(+1), (+10)] 1 == [2,11]@
sequenceArr :: (Traversable t, Arrow a) => t (a b c) -> a b (t c)
sequenceArr = traverseArr id

-- | Fans each input from @Vector n b@ to a separate arrow from the given vector.
--
--   @sequenceArrVec (Vec.generate ((+).fromIntegral) :: Vector 5 (Int -> Int)) (Vec.replicate 1 :: Vector 5 Int) == Vector [1,2,3,4,5]@
zipSequenceArrVec :: (Arrow a, KnownNat n) => Vector n (a b c) -> a (Vector n b) (Vector n c)
zipSequenceArrVec cells = arr toList >>> zipSequenceArrListUnsafe (toList cells) >>> arr (fromJust . fromList)

-- Not safe, doesn't check size of the lists.
-- When used in sequenceArrVec it is safe as the size of the
-- vectors are all the same. Not to export.
zipSequenceArrListUnsafe :: Arrow a => [a b c] -> a [b] [c]
zipSequenceArrListUnsafe [] = constantly []
zipSequenceArrListUnsafe (x:xs) = proc (y:ys) -> do
  xres <- x -< y
  xsres <- zipSequenceArrListUnsafe xs -< ys
  returnA -< (xres:xsres)

-- | Fans each input from @[b]@ to a separate arrow from the given list.
--   The output list has length of the minimum of the input list length and the arrow list length.
--
--  @
--  sequenceArrList [(+1), (+10)] [1,2] == [2,12]
--  sequenceArrList [(+1), (+10)] [1]   == [2]
--  sequenceArrList [(+1)] [1,2,3,4]    == [2]@
zipSequenceArrList :: (Arrow a, ArrowChoice a) => [a b c] -> a [b] [c]
zipSequenceArrList [] = constantly []
zipSequenceArrList (a : as) = proc bs' -> case bs' of
  [] -> returnA -< []
  b : bs -> do
    c <- a -< b
    cs <- zipSequenceArrList as -< bs
    returnA -< c : cs

-- | Similar to @'when'@ for @'Applicative'@. Relevant for
--   arrows which embeded a Monad.
whenArr :: ArrowChoice a => a b () -> a (Bool, b) ()
whenArr cell = proc (b, input) -> do
  if b
    then cell -< input
    else constantly () -< input

-- | Similar to @'unless'@ for @'Applicative'@. Relevant for
--   arrows which embeded a Monad.
unlessArr :: ArrowChoice a => a b () -> a (Bool, b) ()
unlessArr cell = arr not *** arr id >>> whenArr cell

-- | Always output the given value.
constantly :: Arrow a => b -> a any b
constantly = arr . const