I’ve been thinking more about it; you can actually already get very close with just a custom transformer (actually it is just ExceptT
with different names):
findM :: Monad m => (a -> m Bool) -> [a] -> m (Maybe a)
findM p xs = runEarlyT do
for_ xs \x -> do
b <- lift (p x)
when b (earlyReturn (Just x))
pure Nothing
So really the only difference with the new Lean syntax is that it requires two extra functions: runEarlyT
and lift
.
Click to expand the full implementation:
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE BlockArguments #-}
import Control.Monad ( ap, when )
import Control.Monad.Trans.Class ( MonadTrans(..) )
import Data.Foldable ( for_ )
newtype EarlyT r m a = EarlyT (m (Either r a)) deriving Functor
instance Monad m => Applicative (EarlyT r m) where
pure x = EarlyT (pure (Right x))
(<*>) = ap
instance Monad m => Monad (EarlyT r m) where
EarlyT m >>= k = EarlyT $
m >>= \case
Left x -> pure (Left x)
Right x | EarlyT y <- k x -> y
instance MonadTrans (EarlyT r) where
lift :: Monad m => m a -> EarlyT r m a
lift x = EarlyT (fmap Right x)
earlyReturn :: Applicative m => r -> EarlyT r m a
earlyReturn = EarlyT . pure . Left
runEarlyT :: Monad m => EarlyT a m a -> m a
runEarlyT (EarlyT m) = m >>= \case
Left y -> pure y
Right y -> pure y
findM :: Monad m => (a -> m Bool) -> [a] -> m (Maybe a)
findM p xs = runEarlyT do
for_ xs \x -> do
b <- lift (p x)
when b (earlyReturn (Just x))
pure Nothing