Я не могу понять ответ на упражнение в fp-курсе.
https://github.com/system-f/fp-course/blob/7a957a177ccdc9d91323c32d576351dcc1234f2f/src/Course/Monad.hs#L119
-- | Flattens a combined structure to a single structure.
--
-- >>> join ((1 :. 2 :. 3 :. Nil) :. (1 :. 2 :. Nil) :. Nil)
-- [1,2,3,1,2]
--
-- >>> join (Full Empty)
-- Empty
--
-- >>> join (Full (Full 7))
-- Full 7
--
-- >>> join (+) 7
-- 14
join ::
Monad k =>
k (k a)
-> k a
join x = id =<< x
Пока id :: a -> a
и (=<<) :: (a -> k b) -> k a -> k b
Почему id
может быть первым параметром (=<<)
?
Почему ответ join x = id =<< x
?
Спасибо. Пока join (Full (Full 8))
получается (Full 8)
, почему присоединиться (Full 8)
не может получиться 8
? Тип join (Full 8) :: Num (Optional a) => Optional a
тоже странный. Есть ли у a ~ kb
какое-то условие для компилятора?
join :: k (k a) -> k a
как вы должны передать это k a
? Если бы подпись была другой, что должно получиться join Empty
?
Моя вина. Я имею в виду id =<< (Full (Full 8))
оказывается Full 8
. Почему id =<< (Full 8)
не получается 8
когда a ~ k b
. Забудьте присоединиться.
Посмотрите на подпись. (=<<) :: (a -> k b) -> k a -> k b
, k b
не может быть b
«Почему id =<< Full 8
не получается 8
?» Допустим, 8
— это Int
для простоты. Итак, в типе (=<<) :: (a -> k b) -> k a -> k b
мы выбрали k
как Optional
, а a
как Int
, просто написав Full 8
в качестве второго аргумента. Специализируемся, значит, смотрим на (=<<) :: (Int -> Optional b) -> Optional a -> Optional b
. Теперь у нас есть проблема: id
не может иметь тип Int -> Optional b
для любого b
. Так что это за Num
материал? Что ж, компилятор замечает, что 8
может иметь типы, отличные от Int
; назовите этот другой тип t
. (1/?)
Так что теперь, специализировав тип (=<<)
на t
вместо Int
, мы бы получили (=<<) :: (t -> Optional b) -> Optional t -> Optional b
. Ладно, id
может иметь тип t -> Optional b
, если мы выберем t
как Optional b
. Но теперь у нас другая проблема: если мы требуем, чтобы 8 :: t
и t
на самом деле были Optional b
, то мы должны иметь 8 :: Optional b
. Итак, нам нужен способ интерпретировать 8
как значение Optional b
, то есть нам нужен экземпляр Num
. (2/2)
Почему
id
может быть первым параметром(=<<)
?
Возможно, это поможет выстроить типы один к одному, где они должны быть равны. Это соответствует унификации, которую делает компилятор во время проверки типов.
(=<<)
::
(
а1
→
к б
)
→
к
а1
→
к б
id
::
а2
→
а2(=<<) id
::
к
а1
→
к б
↑
к
а2
→
к б
↑
к
(к б)
→
к б
Обратите внимание, что есть две переменные разных типов с именами a
. В GHCi можно поставить :set -fprint-explicit-foralls
, и тогда :type id
покажет forall a. a -> a
. Это показывает локальную область действия параметра a
, например, как \x -> …
определяет область действия x
.
Почему ответ
join x = id =<< x
?
(=<<)
— это комбинация отображения (fmap
/(<$>)
) и выравнивания (join
), то есть f =<< x = join (fmap f x)
. Поскольку join
только выравнивает, мы можем выразить join
, используя (=<<)
, установив часть «отображение» на id
.
id =<< (Full (Full 8))
получаетсяFull 8
. Почему нельзяid =<< (Full 8)
получается8
id =<< Full (Full 8)
означает, что мы отображаем id
на все элементы fmap id (Full (Full 8))
= Full (id (Full 8))
= Full (Full 8)
, а затем сглаживаем Optional (Optional t1)
в Optional t1
для некоторого типа t1
, где 8 :: (Num t1) => t1
.
Если мы попробуем id =<< Full 8
, то сможем отобразить fmap id (Full 8)
= Full (id 8)
= Full 8
, но у нас есть только Optional t1
. Это не ошибка, но добавляется ограничение: t1
должно быть равно Optional t2
для некоторого типа t2
. Синтаксис этого ограничения равенства: t1 ~ Optional t2
. Но нет такого типа, который будет работать, если вы не добавите экземпляр для Num (Optional t2)
. (Это технически возможно, но не стандартно.)
Подсказка:
a ~ k b