The ICFP'22 playlist has been published to YouTube

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
3 Likes