Сегодня я столкнулся с разочаровывающим чем-то в Haskell.
Вот что произошло:
- Я написал функцию в ghci и дал ей сигнатуру типа
- ghci пожаловался на тип
- Я удалил сигнатуру типа
- ghci принял функцию
- Я проверил выведенный тип
- выводимый тип был точно таким же, как и тип, который я пытался дать
- Я был очень огорчен
- Я обнаружил, что могу воспроизвести проблему в любом выражении let
- Скрежетзубов;решил проконсультироваться с экспертами по 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
делает что-то совсем другое.