Какой смысл "const" в прелюдии Haskell?

Просматривая прелюдию Haskell, я видит функцию const:

const x _ = x

Кажется, я не могу найти ничего важного в отношении этой функции.

Какой смысл? Может ли кто-нибудь указать пример использования этой функции?

Ответ 1

Это полезно для перехода к функциям более высокого порядка, когда вам не нужна вся их гибкость. Например, оператор монадической последовательности >> может быть определен в терминах монадического оператора связывания как

x >> y = x >>= const y

Это несколько более аккуратно, чем использование лямбда

x >> y = x >>= \_ -> y

и вы даже можете использовать его без точек

(>>) = (. const) . (>>=)

хотя я не рекомендую это в этом случае.

Ответ 2

Чтобы добавить к Hammar отличный прямой ответ: простые функции, такие как const и id, действительно полезны в качестве функции высшего порядка по той же причине, по которой они являются фундаментальными в исчислении комбинаторов SKI.

Не то чтобы я думал, что функции прелюдии haskell были смоделированы сознательно после этой формальной системы или чего-то еще. Просто создать богатые абстракции в haskell очень легко, поэтому вы часто видите, что эти типы теоретических вещей оказываются практически полезными.

Shameless плагин, но я писал о том, как Аппликативный экземпляр для (->) на самом деле являются S и K комбинаторы здесь, если что - то, что вы находитесь в.

Ответ 3

Простым примером использования const является Data.Functor.(<$). С помощью этой функции вы можете сказать: у меня здесь функтор с чем-то скучным в нем, но вместо этого я хочу иметь в нем эту интересную вещь, не меняя формы функтора. Например.

import Data.Functor

42 <$ Just "boring"
--> Just 42

42 <$ Nothing
--> Nothing

"cool" <$ ["nonsense","stupid","uninteresting"]
--> ["cool","cool","cool"]

Определение:

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

или написано не как бессмысленно:

cool <$ uncool =  fmap (const cool) uncool

Вы видите, как const используется здесь, чтобы "забыть" о вводе.

Ответ 4

Еще одно использование - реализовать функции-члены класса, у которых есть фиктивный аргумент, который не следует оценивать (используется для разрешения неоднозначных типов). Пример, который может быть в Data.bits:

instance Bits Int where
  isSigned = const True
  bitSize  = const wordSize
  ...

Используя const, мы явно говорим, что определяем постоянные значения.

Лично мне не нравится использование фиктивных параметров, но если они используются в классе, то это довольно хороший способ записи экземпляров.

Ответ 5

Я не могу найти что-нибудь подходящее в отношении этой функции.

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

Например, следующая (неэффективная!) Реализация length,

length' = foldr (\_ acc -> 1 + acc) 0

может быть переписан как

length' = foldr (const (1+)) 0

что, возможно, более элегантно.

Выражение const (1+) действительно семантически эквивалентно \_ acc → 1 + acc, потому что оно принимает один аргумент, отбрасывает его и возвращает раздел (1+).

Ответ 6

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

Скажем, мы хотим переписать структуру 2-х кортежей в другую структуру из 2-х кортежей. Я мог бы выразить это так:

((a,b),(c,d)) ⇒ (a,(c,(5,a)))

Я могу дать прямолинейное определение с сопоставлением с образцом:

f ((a,b),(c,d)) = (a,(c,(5,a)))

Что делать, если я хочу бессмысленное (молчаливое) решение для такого переписывания? Некоторое мышление и потихоньку позже, ответ заключается в том, что мы можем выразить любые перезаписи с помощью (&&&), const, (.), fst, snd. Обратите внимание, что (&&&) - от Control.Arrow.

Решение примера с использованием этих функций:

(fst.fst &&& (fst.snd &&& (const 5 &&& fst.fst)))

Обратите внимание на сходство с (a,(c,(5,a))). Что, если мы заменим &&& на ,? Затем он читает:

(fst.fst, (fst.snd, (const 5, fst.fst)))

Обратите внимание, что a - это первый элемент первого элемента, и это то, что fst.fst проекты. Обратите внимание, что c - это первый элемент второго элемента, и это то, что fst.snd проекты. То есть, переменные становятся путём к их источнику.

const позволяет ввести константы. Интересно, как название соответствует значению!

Затем я обобщил эту идею с помощью Applicative, чтобы вы могли написать любую функцию в бессмысленном стиле (пока у вас есть анализ случаев, доступный как функции, такие как maybe, either, bool). Опять же, const играет роль введения констант. Вы можете увидеть эту работу в пакете Data.Function.Tacit.

Когда вы начинаете абстрактно, в цели, а затем работаете над реализацией, вы можете быть удивлены ответами. То есть любая одна функция может быть столь же загадочной, как и любая винтовка в машине. Однако, если вы вернетесь, чтобы показать всю машину, вы можете понять контекст, в котором этот инструмент необходим.

Ответ 7

Допустим, вы хотите создать список Nothings равный длине строки. Поскольку const возвращает свой первый аргумент, независимо от второго, вы можете сделать:

listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing

или, более конкретно:

listOfNothing st = map (const Nothing) st