"Eta reduce" не всегда проводится в Haskell?

Я обнаружил, что могу сказать

{-# LANGUAGE RankNTypes #-}
f1 :: (forall b.b -> b) -> (forall c.c -> c)
f1 f = id f

(и HLint скажите, что я могу сделать "Eta уменьшить" здесь), но

f2 :: (forall b.b -> b) -> (forall c.c -> c)
f2 = id

не удалось скомпилировать:

Couldn't match expected type `c -> c'
            with actual type `forall b. b -> b'
Expected type: (forall b. b -> b) -> c -> c
  Actual type: (forall b. b -> b) -> forall b. b -> b
In the expression: id
In an equation for `f2': f2 = id

На самом деле у меня аналогичная проблема в более сложной ситуации, но это самый простой пример, о котором я могу думать. Таким образом, либо HLint не дает надлежащего указания здесь, либо компилятор должен обнаружить эту ситуацию, не так ли?

UPDATE

Другой вопрос о повторении выглядит аналогичным. Однако, хотя оба ответа весьма полезны, меня не удовлетворяют, поскольку они, похоже, не затрагивают суть вопроса.

Например, мне даже не разрешено назначать id предлагаемым типом ранга 2:

f2 :: (forall b.b -> b) -> (forall c.c -> c)
f2 = id :: (forall b.b -> b) -> (forall c.c -> c)

Если проблема заключается только в типе, то явное обозначение типа должно решить его (идентификатор имеет тип a -> a, и он был ограничен (forall b.b -> b) -> (forall c.c -> c). Поэтому, чтобы оправдать это использование, (forall b.b -> b) должен соответствовать (forall c.c -> c) > и это верно). Но приведенный выше пример показывает, что это не так. Таким образом, это истинное исключение из "eta reduce": вам нужно явно добавлять параметры для обеих сторон, чтобы преобразовать значение типизированного значения ранга 1 в значение с типом 2. 2.

Но почему существует такое ограничение? Почему компьютер не может автоматически унифицировать тип ранга 1 и тип 2-го ранга (забыть о типе вывода, все типы могут быть заданы с помощью обозначений)?

Ответ 1

Я не уверен, что HLint вообще знает RankNTypes, возможно, нет.

Действительно, eta сокращение часто невозможно с этим расширением. GHC не может просто унифицировать a->a и (forall b.b -> b) -> (forall c.c -> c), иначе это полностью испортило бы возможность вывода своего типа для Rank1-code 1. OTOH, это не проблема для объединения (forall b.b -> b) с аргументом a; результат равен (forall b.b -> b), который соответствует (forall c.c -> c).


1 Рассмотрим map id [(+1), (*2)]. Если id было разрешено иметь тип, с которым вы имеете дело, компилятор может создать другой выбор экземпляра для полиморфных функций Num, что, безусловно, не должно быть возможным. Или это должно быть? Я не уверен, думая об этом...

Во всяком случае, я уверен, что он доказал, что с RankNTypes полный вывод типа невозможен, поэтому, чтобы получить его, по крайней мере, в подмножестве Rank1, GHC обычно должен по умолчанию использовать это как возможный полиморфный выбор.