Я думаю, что здесь происходит следующее: в стандартном выводе типа Damas - Milner, пусть привязки являются единственным местом, где происходит обобщение типа. Сигнатура типа, используемая в вашем неудачном примере, - это сигнатура типа шаблона , которая «очевидным образом ограничивает тип шаблона». Теперь в этом примере не «очевидно», должно ли это ограничение происходить до или после обобщения, но ваш неудачный пример демонстрирует, я думаю, что оно применяется до обобщения.
Конкретнее: в привязке let let x = id in ...
происходит то, что тип id
forall a. a->a
создается в монотипе, скажем a0 -> a0
, который затем присваивается типу x
и затем обобщается как forall a0. a0 -> a0
. Если, как мне кажется, сигнатура типа шаблона проверяется перед обобщением, то, по сути, она просит компилятор проверить, является ли монотип a0 -> a0
более общим, чем политип forall a. a -> a
, а это не так.
Если мы переместим сигнатуру типа на уровень привязки, let x :: forall a. a-> a; x = id in ...
сигнатура проверяется после обобщения (поскольку это явно разрешено для обеспечения полиморфной рекурсии), и ошибка типа не возникает.
Является ли это ошибкой или нет, я думаю, вопрос мнения. Кажется, нет реальной спецификации, которая бы указала нам, каково здесь правильное поведение; Есть только наши ожидания. Я бы предложил обсудить этот вопрос с людьми из GHC.