Как <$ = (fmap. const) в Functor вообще работает в Haskell?

Я знаю, что оператор точки (.) принимает две функции, которые принимают аргумент соответственно, и третий аргумент для второго аргумента.

Его тип (.) :: (b -> c) -> (a -> b) -> a -> c.

Например, take 1 . drop 2 $ [1,2,3,4,5].

Но как (fmap . const) может работать?

Функция fmap требует два аргумента, а функция const принимает два аргумента и затем возвращает первый аргумент.

Логично, что fmap должна принять выходные данные, которые являются первым аргументом, принимаемым функцией const.

Типы двух функций:

fmap :: Functor f => (a -> b) -> f a -> f b
const :: a -> b -> a

fmap в (fmap . const) 2 (Just 4) должен получить выходные данные 2, возвращаемые функцией const, поэтому это не сработает, но на самом деле это работает.

Что случилось?

« fmap в (fmap . const) 2 должен получить выходные данные 2, возвращенные функцией const» — нет, fmap в (fmap . const) 2 получает выходные данные \_ -> 2, возвращенные функцией const. const x _ = x по сути то же самое уравнение, что и const x = \_ -> x.

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

Ответы 3

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

Просто унифицируйте типы:

fmap         :: Functor f =>      (a -> b) -> (f a -> f b)
const        ::              b -> (a -> b)
fmap . const :: Functor f => b             -> (f a -> f b)

Итак, fmap . const берет x :: b и производит fmap (const x) :: f a -> f b.

В вашем примере

  (fmap . const) 2 (Just 4)
= ((fmap . const) 2) (Just 4)
= fmap (const 2) (Just 4)
= Just (const 2 4)
= Just 4

Это непонятно, как это на самом деле работает, пожалуйста, взгляните на комментарии под ответом @willeM_Van Onsem. Итак, (a -> b) также может быть (a -> (x -> z)), или в случае fmap в операторе точки это также может быть Functor f => ((a -> b) -> (f a -> f b))? Как алгебраический тип может быть типом значения или типом функции?

Akari 09.07.2024 09:53

Да, переменные типа, такие как b, обозначают любой (мономорфный) тип, а не только алгебраические типы данных. Вот почему они настолько эффективны: вы можете скомпоновать любые две функции, если их домен и кодомен совпадают.

Naïm Favier 09.07.2024 10:59

Функции также являются значениями; можно сказать, что в этом весь смысл функционального программирования.

Naïm Favier 09.07.2024 11:08

Кроме того, чтобы более полно понять всю картину, основываясь на полиморфной природе переменных типа и мономорфном выводе типа выражения (также применение функции, применение композиции функции), композиция fmap . const, ее тип такой: (.) :: Functor f => ((f b -> a) -> (f b -> f a)) -> (((a -> (f b -> a)) -> ((((a -> f b) -> f a))))),

Akari 10.07.2024 17:48

где приложение (fmap . const) 2 (Just 4) выглядит так, что оператор точки принимает четыре аргумента, но на самом деле это не так, его результатом является функция, которая заботится о четвертом аргументе... Таким образом, выражение для приложения типа fmap . const $ 2 (Just 4) недопустимо, но должно быть (fmap . const) 2 (Just 4), или, кроме того, без упрощения скобок, которое разрешено компилятором, ((fmap . const) 2) (Just 4). Я думаю, это слишком много, это трудная часть, чтобы быть функциональным. @Наим Фавье, я правильно это понимаю?..

Akari 10.07.2024 17:49

Не совсем: экземпляр (.), который мы здесь используем, имеет тип ((a -> b) -> (f a -> f b)) -> (b -> (a -> b)) -> (b -> (f a -> f b)).

Naïm Favier 10.07.2024 17:51

Его отредактировали неправильно, даже для моего неправильного понимания, это должно было быть (.) :: Functor f => ((f b -> a) -> (f b -> f a)) -> ((a -> (f b -> a)) -> (f b -> f a), а не (.) :: Functor f => ((f b -> a) -> (f b -> f a)) -> (((a -> (f b -> a)) -> ((((a -> f b) -> f a))))). Я также попробовал вашу подпись типа (.) :: Functor f => ((a -> b) -> (f a -> f b)) -> (b -> (a -> b)) -> (b -> (f a -> f b)) (.) f g = \x -> f (g x) в Haskell, она действительно работает.

Akari 10.07.2024 18:23

Функция fmap требует два аргумента, а функция const принимает два аргумента и затем возвращает первый аргумент.

Нет, каждая функция принимает один параметр и иногда в результате выдает функцию. fmap :: Functor f => (a -> b) -> (f a -> f b), принимает одну функцию типа a -> b, а затем создает функцию, которая сопоставляет f a с f b.

Теперь тот же трюк необходим, когда мы работаем с const :: c -> d -> c, это сокращение от const :: c -> (d -> c), то есть снова функция, которая принимает значение, а затем создает функцию, которая сопоставляет любое значение с данным значением.

Итак, что же такое fmap . const? Это сокращение от \x -> fmap (const x). Таким образом, он запрашивает значение x, а затем выдает fmap (const x). Таким образом, это означает, что fmap сопоставит любое значение с x. Таким образом, это означает, что если мы работаем, например, с x = 42 и списком [1,4,2,5], мы получаем fmap (const 42) [1,4,2,5], а это значит, что мы сопоставляем любой элемент списка с 42, поэтому [42, 42, 42, 42].

«Так что же такое fmap . const? Это сокращение от \x -> fmap (const x)», на мой взгляд, это не является строго логическим выводом из сигнатуры типа (b -> c) -> (a -> b) -> a -> c. Мне все еще нужно изучить реализацию оператора точки, как объяснялось в других ответах.

Akari 09.07.2024 09:25

Вот почему это сбивает с толку, я не могу понять, глядя непосредственно на сигнатуру типа оператора точки. Мне сказали, что сигнатура типа расскажет, как работает функция/оператор, но очевидно, что в некоторых подобных ситуациях это не так, даже с точки зрения каррирования. Это проблема. Меня смутила его подпись типа. Теперь, если посмотреть на то, как реализован оператор точки, становится очень ясно:

Akari 09.07.2024 09:28

Или это может означать, что (a -> b) также, вероятно, будет обозначать, что он принимает аргумент и, вероятно, возвращает другую функцию, например (a -> (x -> z))., в fmap, принимаемом точкой в ​​качестве первого аргумента, она будет (a -> b) равна Functor f => ((a -> b) -> (f a -> f b)), тогда какой смысл в ищем сигнатуры типов, если бы алгебраический тип мог означать не только тип значения, но и тип функции..

Akari 09.07.2024 09:46

Да, b может быть любого типа, это никак не ограничено. Сюда входят и типы функций, потому что почему типы функций здесь могут быть разными?

cafce25 09.07.2024 10:29

@Akari, технически, этот тип расскажет вам все, что вам нужно знать, поскольку существует только одна (всего) функция с этой сигнатурой типа согласно параметрическим/свободным теоремам. Полагаю, это вопрос опыта.

Naïm Favier 09.07.2024 10:53

@Akari Главное, к чему вам нужно привыкнуть, чтобы чувствовать себя комфортно в Haskell, — это перестать думать о функциях отдельно от значений. «Функция» описывает подкатегорию значений, а не что-то совсем другое. Функции — это значения. Точно так же, как «список» описывает подкатегорию значений, а не что-то еще. Поэтому, когда вы видите что-то, что применимо к любому типу значения (например, к переменной типа, такой как b), это включает в себя возможность (но не необходимость) применения этого к функциям. Так что на самом деле тип сказал вам, что это может быть функция; вот что означают переменные типа!

Ben 10.07.2024 04:14

Функциональная композиция . определяется как:

f . g = \x -> f (g x)

Заменив . его определением в fmap . const, вы получите эквивалентное выражение:

\x -> fmap (const x)

Аналогично, если вы замените fmap . const в выражении (fmap . const) 2 его эквивалентом \x -> fmap (const x), вы получите:

(\x -> fmap (const x)) 2

Наконец, применив аргумент 2 выше:

fmap (const 2)

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