Сегодня я встретил разочарование в Haskell.
Вот что произошло:
- Я написал функцию в ghci и дал ей подпись типа
- ghci жаловался на тип
- Я удалил подпись типа
- ghci принял функцию
- Я проверил выведенный тип
- предполагаемый тип был точно таким же, как и тип, который я пытался ему дать
- Я очень огорчился.
- Я обнаружил, что могу воспроизвести проблему в любом let-expression
- Скрежет зубов; решил проконсультироваться с экспертами в SO
Попытка определить функцию с сигнатурой типа:
Prelude Control.Monad> let myFilterM f m = do {x <- m; guard (f x); return x} :: (MonadPlus m) => (b -> Bool) -> m b -> m b
<interactive>:1:20:
Inferred type is less polymorphic than expected
Quantified type variable `b' is mentioned in the environment:
m :: (b -> Bool) -> m b -> m b (bound at <interactive>:1:16)
f :: (m b -> m b) -> Bool (bound at <interactive>:1:14)
Quantified type variable `m' is mentioned in the environment:
m :: (b -> Bool) -> m b -> m b (bound at <interactive>:1:16)
f :: (m b -> m b) -> Bool (bound at <interactive>:1:14)
In the expression:
do { x <- m;
guard (f x);
return x } ::
(MonadPlus m) => (b -> Bool) -> m b -> m b
In the definition of `myFilterM':
myFilterM f m
= do { x <- m;
guard (f x);
return x } ::
(MonadPlus m) => (b -> Bool) -> m b -> m b
Определили функцию без сигнатуры типа, проверили выведенный тип:
Prelude Control.Monad> let myFilterM f m = do {x <- m; guard (f x); return x}
Prelude Control.Monad> :t myFilterM
myFilterM :: (MonadPlus m) => (b -> Bool) -> m b -> m b
Использовал функцию для отличного результата - она работала правильно:
Prelude Control.Monad> myFilterM (>3) (Just 4)
Just 4
Prelude Control.Monad> myFilterM (>3) (Just 3)
Nothing
Мое лучшее предположение о том, что происходит:
аннотации типа каким-то образом не работают с let-выражениями, когда есть блок do-block.
За бонусные баллы:
есть ли функция в стандартном распределении Haskell, которая делает это? Я был удивлен, что filterM
делает что-то совсем другое.