Не могу понять упражнение Haskell fp-курса

Я не могу понять ответ на упражнение в 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?

Подсказка: a ~ k b

Joseph Sible-Reinstate Monica 20.04.2023 19:01

Спасибо. Пока join (Full (Full 8)) получается (Full 8), почему присоединиться (Full 8) не может получиться 8? Тип join (Full 8) :: Num (Optional a) => Optional a тоже странный. Есть ли у a ~ kb какое-то условие для компилятора?

zichao liu 20.04.2023 19:23
join :: k (k a) -> k a как вы должны передать это k a? Если бы подпись была другой, что должно получиться join Empty?
cafce25 20.04.2023 19:35

Моя вина. Я имею в виду id =<< (Full (Full 8)) оказывается Full 8. Почему id =<< (Full 8) не получается 8 когда a ~ k b. Забудьте присоединиться.

zichao liu 20.04.2023 19:39

Посмотрите на подпись. (=<<) :: (a -> k b) -> k a -> k b, k b не может быть b

cafce25 20.04.2023 19:57

«Почему 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/?)

Daniel Wagner 21.04.2023 22:40

Так что теперь, специализировав тип (=<<) на 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)

Daniel Wagner 21.04.2023 22:43
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
7
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Почему 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). (Это технически возможно, но не стандартно.)

Другие вопросы по теме