В чем смысл const в Haskell Prelude?

94

Просматривая Haskell Prelude, я вижу функцию const :

const x _ = x

Я не могу найти ничего подходящего относительно этой функции.

В чем смысл? Кто-нибудь может привести пример, где можно использовать эту функцию?

стусмит
источник
10
Пример: backgroundColor :: Text -> Colorэто для меняbackgroundColor = const White
Чжэнь

Ответы:

84

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

x >> y = x >>= const y

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

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

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

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

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

хаммар
источник
9
+1. Это также часто возникает при использовании комбинаторов синтаксического анализатора.
Фред Фу,
49
Ах, так что это скорее «генератор функций» - я использую его с одним аргументом, и он дает мне функцию (принимающую один аргумент), которая всегда возвращает постоянное значение. Таким образом map (const 42) [1..5]получается [42, 42, 42, 42, 42].
stusmith
2
Стусмит: Ты понял. constполезен для применения к одному аргументу, чтобы получить функцию там, где он нужен (например, переход к map).
Conal
9
@stusmith: Вы можете использовать это в некоторых интересных целях:head = foldr const (error "Prelude.head: empty list")
rampion
27

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

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

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

Jberryman
источник
8
Что ж, комбинаторы SKI, безусловно, повлияли на Prelude. Я помню, как спорил с Джо Фазелом, нужно ли включать S-комбинатор или нет.
августа
4
Между прочим, ((->) e)это также монада читателя - с Readerи тому подобное, просто являющиеся newtypeоболочками - и askфункция тогда id, так что это тоже Iкомбинатор. Если вы посмотрите на оригинальный вместо BCKW основе Хаскелла Карри, B, Kи Wявляются fmap, returnи joinсоответственно.
CA McCann,
1
Ссылка на блог в ответе мертва. Теперь он должен указывать здесь: brandon.si/code/…
nsxt
22

Простой пример использования 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здесь используется, чтобы «забыть» о вводе.

Landei
источник
21

Я не могу найти ничего подходящего относительно этой функции.

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

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

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

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

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

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

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

jub0bs
источник
4
Мне понадобилось 5 минут, чтобы понять, как это работает :)
Мукеш Сони
15

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

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

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

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

Йонас Дурегард
источник
Аргументы прокси действительно намного лучше, и при нацеливании на недавний GHC приложения типа делают свое дело.
dfeuer
2

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

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

((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 .

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

Эриско
источник
2

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

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

или, точнее:

listOfNothing st = map (const Nothing) st
А.Сарамет
источник
0

Допустим, вы хотите повернуть список. Это идиоматический способ сделать это в Haskell:

rotate :: Int -> [a] -> [a] rotate _ [] = [] rotate n xs = zipWith const (drop n (cycle xs)) xs

Эта функция объединяет два массива с функцией const, первый из которых представляет собой бесконечный циклический массив, а второй - это массив, с которого вы начали.

const действует как проверка границ и использует исходный массив для завершения циклического массива.

См .: Поворот списка в Haskell

Знаменитый Jameis
источник
0

Я не могу найти ничего подходящего относительно этой функции.

Предположим, вы хотите сгенерировать все подпоследовательности данного списка.

Для каждого элемента списка в определенный момент у вас есть выбор: True (включить его в текущую подпоследовательность) или False (не включать). Это можно сделать с помощью функции filterM .

Как это:

 λ> import Control.Monad
 λ> :t filterM
 filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a]
 λ> 

Например, нам нужны все подпоследовательности [1..4].

 λ> filterM  (const [True, False])  [1..4]
 [[1,2,3,4],[1,2,3],[1,2,4],[1,2],[1,3,4],[1,3],[1,4],[1],[2,3,4],[2,3],[2,4],[2],[3,4],[3],[4],[]]
 λ> 
jpmarinier
источник