dave4420 ударил его, но я думаю, что следующие замечания могут быть полезны.
Существуют правила, которые вы можете использовать для достоверного «переписывания» типа в другой тип, совместимый с оригиналом. Эти правила предусматривают замену всех вхождений переменного типа с каким-либо другим типом:
- Если у вас есть
id :: a -> a
, вы можете заменить a
с c
и получить id :: c -> c
. Этот последний тип также можно переписать в оригинал id :: a -> a
, что означает, что эти два типа эквивалентны. Как правило, если вы заменяете все экземпляры переменной типа другой переменной типа, которая не встречается в оригинале, вы получаете эквивалентный тип.
- Вы можете заменить все вхождения переменной типа конкретным типом. I.e., если у вас есть
id :: a -> a
, вы можете переписать это на id :: Int -> Int
. Последний, однако, не может быть переписан обратно к оригиналу, поэтому в данном случае вы , специализирующийся на типа.
- В более общем смысле, чем второе правило, вы можете заменить все вхождения переменной типа любым типом, конкретным или переменным. Например, если у вас есть
f :: a -> m b
, вы можете заменить все вхождения a
на m b
и получить f :: m b -> m b
. Поскольку это тоже нельзя отменить, это также специализация.
Этот последний пример показывает, как id
может использоваться как второй аргумент >>=
. Таким образом, ответ на ваш вопрос заключается в том, что мы можем переписывать и выводить типы следующим образом:
1. (>>=) :: m a -> (a -> m b) -> m b (premise)
2. id :: a -> a (premise)
3. (>>=) :: m (m b) -> (m b -> m b) -> m b (replace a with m b in #1)
4. id :: m b -> m b (replace a with m b in #2)
.
.
.
n. (>>= id) :: m (m b) -> m b (indirectly from #3 and #4)
Что произойдет, если 'a'' 'm b', поскольку' id' заставляет его быть? Это должно ответить на ваш вопрос. –
Этот аромат магии часто называют «унификацией» :) –