packages feed

groot-0.0.1.0: src/Groot/Data/Filter.hs

{-# LANGUAGE TypeFamilies #-}

module Groot.Data.Filter where

import           Control.Monad
import           Data.Conduit
import qualified Data.Conduit.List     as CL
import           Data.Functor.Identity

data FilterOp a =
    Or a a
  | And a a
  | Not a
  deriving (Eq, Show)

class Filter a where
  type FilterItem a :: *

  matches :: a -> FilterItem a -> Bool

  maybeMatchesM :: Monad m => a -> m (FilterItem a) -> m (Maybe (FilterItem a))
  maybeMatchesM p me = do
    e <- me
    return $ if (matches p e) then (Just e) else Nothing

  maybeMatches :: a -> FilterItem a -> Maybe (FilterItem a)
  maybeMatches p e = runIdentity $ maybeMatchesM p (Identity e)

  (|||) :: a -> a -> FilterOp a
  (|||) x y = Or x y

  (&&&) :: a -> a -> FilterOp a
  (&&&) x y = And x y

  notP :: a -> FilterOp a
  notP x = Not x

instance Filter a => Filter (FilterOp a) where
  type FilterItem (FilterOp a) = FilterItem a

  matches (Or x y)  res = (matches x res) || (matches y res)
  matches (And x y) res = (matches x res) && (matches y res)
  matches (Not x)   res = not (matches x res)

filterOnM :: (MonadPlus m, Filter p)
          => (a -> FilterItem p)
          -> p                        -- The actual predicate
          -> m a                      -- The item to which to apply the filter, wrapped in a Monad
          -> m a                      -- The result after applying the predicate, wrapped in the same Monad
filterOnM f p = mfilter (\x -> matches p $ f x)

filterM :: (MonadPlus m, Filter p)
        => p
        -> m (FilterItem p)
        -> m (FilterItem p)
filterM = filterOnM id

filterC :: (Monad m, Filter p)
        => p
        -> Conduit (FilterItem p) m (FilterItem p)
filterC = filterOnC id

filterOnC :: (Monad m, Filter p)
          => (a -> FilterItem p)
          -> p
          -> Conduit a m a
filterOnC f p = CL.filter (\x -> matches p $ f x)