Hello,
Some comments:
- The Applicative methods should be fused, meaning that we allocate every
Cons in the output once.
<*>, <* and *> should be all defined explicitly because otherwise the default implementations would create closures.
- In
Monad, since the binding needs to be inline, all arguments should be abstracted with lambdas on the right of = and the recursive helper function should not take the function as argument, to reduce the chance of the function becoming a closure.
So it looks like the following:
instance Applicative List where
{-# inline pure #-}
pure = Single
(<*>) = go where
go Nil as = Nil
go (Cons f fs) as = go' f fs as as
go' f fs Nil as' = go fs as'
go' f fs (Cons a as) as' = Cons (f a) (go' f fs as as')
(*>) = go where
go Nil bs = Nil
go (Cons _ as) bs = go' as bs bs
go' as Nil bs' = go as bs'
go' as (Cons a bs) bs' = Cons a (go' as bs bs')
(<*) = go where
go Nil bs = Nil
go (Cons a as) bs = go' a as bs bs
go' a as Nil bs' = go as bs'
go' a as (Cons _ bs) bs' = Cons a (go' a as bs bs')
instance Monad List where
return = pure
{-# inline (>>=) #-}
(>>=) = \as f ->
let go Nil = Nil
go (Cons a as) = f a <> go as
in go as
{-# inline (>>) #-}
(>>) = (*>)
However: I think that these functions on strict lists are a performance anti-pattern. What we would like instead is some reasonably reliable fusion API, where most of the higher-order list functions should go. So I prefer to not have those functions act directly on lists.