packages feed

bot-0.0: src/Data/Bot/Chatter.hs

{-# OPTIONS -Wall -fno-warn-orphans #-}
{-# LANGUAGE TypeOperators, GeneralizedNewtypeDeriving #-}

----------------------------------------------------------------------
-- |
-- Module      :  Data.Bot.Chatter
-- Copyright   :  (c) Conal Elliott 2008
-- License     :  BSD3
-- 
-- Maintainer  :  conal@conal.net
-- Stability   :  experimental
-- 
-- Bots who can produce any number of reactions to each input.  See
-- <http://conal.net/blog/tag/bot/> for explanation and @Examples.Chatter@
-- for examples.  Builds on "Data.Bot.Mutant".

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

module Data.Bot.Chatter
  ( ChatterBot(..), (:->)
  , justC, filterC, scanlC, accumC
  ) where

import Control.Arrow hiding (pure)
import Control.Applicative
import Data.Monoid
import Data.Maybe

import Data.Bot.Mutant


-- | Chatter-bots.  Can have any number of outputs per input.  'mappend'
-- appends outputs.
newtype ChatterBot i o =
  Chatter { unChatter :: MutantBot i [o] } deriving Monoid

instance Arrow ChatterBot where
  arr h = Chatter (arr (pure . h))
  Chatter ab >>> Chatter bc = Chatter (ab >>> concatMB bc)
  first (Chatter f) = Chatter $
    first f >>> arr (\ (bs,c) -> [(b,c) | b <- bs])
    -- first f >>> arr (\ (bs,c) -> fmap (flip (,) c) bs)

-- Boilerplate Functor & Applicative for Arrow instance

instance Functor (ChatterBot i) where fmap = (^<<)

instance Applicative (ChatterBot i) where
  pure x        = arr (const x)
  fbot <*> xbot = fbot &&& xbot >>> arr (uncurry ($))

-- TODO: generalize from lists.

-- | Friendly synonym for 'ChatterBot'
type (:->) = ChatterBot


---- Operations

-- | Each 'Nothing' gets dropped, and the 'Just' constructors are stripped
-- from what's left.
justC :: Maybe a :-> a
justC = Chatter (arr maybeToList)

-- | Pass through whatever satisfies a given predicate 
filterC :: (a -> Bool) -> a :-> a
filterC test = f ^>> justC
 where
   f a | test a    = Just a
       | otherwise = Nothing


-- | Chatter-bot analog to list 'scanl', but without initial @b@
scanlC :: (b -> a -> b) -> b -> (a :-> b)
scanlC = (fmap.fmap) (Chatter . fmap pure) scanlM

-- scanlC f b = Chatter $ pure <$> scanlM f b

-- | Cumulative function applications.  @accumC == scanlC (flip ($))@
accumC :: a -> ((a->a) :-> a)
accumC = scanlC (flip ($))