Почему в Haskell тип (a -> a) может принимать тип k (a -> a) в качестве параметра?

Я изучаю Haskell, выполняя упражнение fp-course (https://github.com/system-f/fp-course)

При заполнении (*>) в Application.hs. Я не могу понять ответ следующим образом.

-- | Apply, discarding the value of the first argument.
-- Pronounced, right apply.
--
-- >>> (1 :. 2 :. 3 :. Nil) *> (4 :. 5 :. 6 :. Nil)
-- [4,5,6,4,5,6,4,5,6]
--
-- >>> (1 :. 2 :. Nil) *> (4 :. 5 :. 6 :. Nil)
-- [4,5,6,4,5,6]
--
-- >>> (1 :. 2 :. 3 :. Nil) *> (4 :. 5 :. Nil)
-- [4,5,4,5,4,5]
--
-- >>> Full 7 *> Full 8
-- Full 8
--
-- prop> \a b c x y z -> (a :. b :. c :. Nil) *> (x :. y :. z :. Nil) == (x :. y :. z :. x :. y :. z :. x :. y :. z :. Nil)
--
-- prop> \x y -> Full x *> Full y == Full y
(*>) ::
  Applicative k =>
  k a
  -> k b
  -> k b
(*>) ka kb = (pure id) <$> ka <*> kb

Например. k это List.

Тип (pure id) будет List (a -> a)
Тип <$> будет (a -> b) -> List a -> List b
Тип k a будет List a

  1. Почему <$> первый тип параметра (a -> a) может принимать (pure id) какой тип List (a -> a)

  2. Я не знаю, почему (pure id) <$> ka может превратиться 1 :. 2 :. 3 :. Nil в id :. id :. id :. Nil

Может ли кто-нибудь помочь? Спасибо!

Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
79
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Использование pure используется только в контексте (->) a экземпляра Applicative. Действительно [src]:

instance Applicative ((->) r) where
    pure = const
    (<*>) f g x = f x (g x)
    liftA2 q f g x = q (f x) (g x)

Таким образом, это означает, что pure id здесь эквивалентно const id. Вместо этого, возможно, было бы лучше использовать const id.

Причина, по которой он выберет (->) r, только для области pure id, что является важным аспектом, заключается в том, что fmap (или <$>), таким образом, нужна функция с левой стороны. Единственный способ добиться этого — позволить pure id работать с instance Applicative (->) r.

Но важно подчеркнуть, что экземпляр, который он выбирает для аппликации pure, не имеет ничего общего с Applicative k, который будет выбран для <$> и <*>.

Мы используем const id, чтобы использовать return id, независимо от элемента в списке, мы возвращаем id. Таким образом, вероятно, было бы более ясно с:

(*>) :: Applicative k => k a -> k b -> k b
(*>) ka kb = const id <$> ka <*> kb

или проще:

(*>) :: Applicative k => k a -> k b -> k b
(*>) ka kb = (id <$ ka) <*> kb

Хотя обычно я не комментирую лишние скобки, это случай, который на 100% предназначен для работы так, как он работает. Все аппликационные операторы имеют инфиксный номер 4, а <|> — инфиксный номер 3. Это было специально выбрано для того, чтобы аппликативный/альтернативный синтаксический анализатор можно было писать без круглых скобок для операторов синтаксического анализа. Я думаю, что использование этих операторов без дополнительных скобок так же стоит запомнить, как и использование арифметики без дополнительных скобок.

Carl 16.04.2023 17:44

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