Ваша ошибка типа происходит из-за указания сигнатуры типа, которая является полиморфной (m
), но является реализацией, которая специализируется на конкретном m
(Maybe
) - вы сказали, что средство проверки типов работает, forall m
, но это не так.
Чтобы обобщить это, вам необходимо решить две другие незначительные проблемы: во-первых, вы не можете использовать общее монадическое условие в охране, только чистое условие. Во-вторых, вы не можете использовать оператор :
для x
и результат filterM
, поскольку :
чисто, но вы дали ему монадический аргумент.
Для монадической версии вы можете использовать do
запись:
filterM p [] = pure []
filterM p (x : xs) = do
px <- p x -- Run the condition
if px
then do
xs' <- filterM p xs -- Run the recursive action
pure (x : xs') -- Build the result
else filterM p xs
Я бы использовал нотацию do, но это не лучший вариант, поскольку существует рекурсия.
Совершенно нормально писать рекурсивный монадический код с или без нотации do
, как вы можете видеть здесь - вам просто нужно знать, когда вы выполняете действия против оценки выражения .
Случай otherwise
в вашем исходном коде неявно обрабатывается монадическими привязками; если p x
возвращает Nothing
для m ~ Maybe
, тогда все вычисления вернут Nothing
.
Если вы хотите избежать записи do
, вы можете использовать комбинаторы монад / функторов напрямую:
filterM p [] = pure []
filterM p (x : xs)
= p x >>= \ px -> if px
then (x :) <$> filterM p xs
else filterM p xs
Хотя <$>
(fmap
) здесь лучше, я бы лично предпочел использовать do
вместо >>=
с лямбдой.
Это также можно несколько упростить, если учесть повторение filterM p
и, поскольку p
не изменяется в вызове на filterM
, просто ссылается на него в вспомогательной функции.
filterM p = go
where
go [] = pure []
go (x : xs) = do
px <- p x
-- Or: if px then (x :) <$> go xs else go xs
xs' <- go xs
pure (if px then x : xs' else xs')