Странная ошибка типа в let-выражении Haskell - в чем проблема?

Сегодня я наткнулся на неприятное кое-что в Haskell.

Вот что произошло:

  1. Я написал функцию в ghci и присвоил ей сигнатуру типа
  2. ghci пожаловался на тип
  3. Я удалил сигнатуру типа
  4. ghci принял функцию
  5. я проверил предполагаемый тип
  6. предполагаемый тип был точно таким же, как и тип, который я пытался дать ему
  7. Я был очень огорчен
  8. Я обнаружил, что могу воспроизвести проблему в любом let-выражении
  9. Скрежет зубов ; решил проконсультироваться со специалистами в 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-блок.

Для бонусных баллов:
есть ли в стандартном дистрибутиве Haskell функция, которая делает это? Я был удивлен, что filterM делает нечто совсем другое.

10
задан Matt Fenwick 5 October 2011 в 14:30
поделиться