Просматривая прелюдию Haskell, я видит функцию const
:
const x _ = x
Кажется, я не могу найти ничего важного в отношении этой функции.
Какой смысл? Может ли кто-нибудь указать пример использования этой функции?
Просматривая прелюдию Haskell, я видит функцию const
:
const x _ = x
Кажется, я не могу найти ничего важного в отношении этой функции.
Какой смысл? Может ли кто-нибудь указать пример использования этой функции?
Это полезно для перехода к функциям более высокого порядка, когда вам не нужна вся их гибкость. Например, оператор монадической последовательности >>
может быть определен в терминах монадического оператора связывания как
x >> y = x >>= const y
Это несколько более аккуратно, чем использование лямбда
x >> y = x >>= \_ -> y
и вы даже можете использовать его без точек
(>>) = (. const) . (>>=)
хотя я не рекомендую это в этом случае.
Чтобы добавить к Hammar отличный прямой ответ: простые функции, такие как const
и id
, действительно полезны в качестве функции высшего порядка по той же причине, по которой они являются фундаментальными в исчислении комбинаторов SKI.
Не то чтобы я думал, что функции прелюдии haskell были смоделированы сознательно после этой формальной системы или чего-то еще. Просто создать богатые абстракции в haskell очень легко, поэтому вы часто видите, что эти типы теоретических вещей оказываются практически полезными.
Shameless плагин, но я писал о том, как Аппликативный экземпляр для (->)
на самом деле являются S
и K
комбинаторы здесь, если что - то, что вы находитесь в.
Простым примером использования 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
используется здесь, чтобы "забыть" о вводе.
Еще одно использование - реализовать функции-члены класса, у которых есть фиктивный аргумент, который не следует оценивать (используется для разрешения неоднозначных типов). Пример, который может быть в Data.bits:
instance Bits Int where
isSigned = const True
bitSize = const wordSize
...
Используя const, мы явно говорим, что определяем постоянные значения.
Лично мне не нравится использование фиктивных параметров, но если они используются в классе, то это довольно хороший способ записи экземпляров.
Я не могу найти что-нибудь подходящее в отношении этой функции.
Во многих других ответах обсуждаются относительно эзотерические (по крайней мере, для новичка) применения const
. Вот простой: вы можете использовать const
чтобы избавиться от лямбды, которая принимает два аргумента, отбрасывает первый, но делает что-то интересное со вторым.
Например, следующая (неэффективная!) Реализация length
,
length' = foldr (\_ acc -> 1 + acc) 0
может быть переписан как
length' = foldr (const (1+)) 0
что, возможно, более элегантно.
Выражение const (1+)
действительно семантически эквивалентно \_ acc → 1 + acc
, потому что оно принимает один аргумент, отбрасывает его и возвращает раздел (1+)
.
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.
Когда вы начинаете абстрактно, в цели, а затем работаете над реализацией, вы можете быть удивлены ответами. То есть любая одна функция может быть столь же загадочной, как и любая винтовка в машине. Однако, если вы вернетесь, чтобы показать всю машину, вы можете понять контекст, в котором этот инструмент необходим.
Допустим, вы хотите создать список Nothings
равный длине строки. Поскольку const
возвращает свой первый аргумент, независимо от второго, вы можете сделать:
listOfNothings :: String -> [Maybe Char]
listOfNothings = (map . const) Nothing
или, более конкретно:
listOfNothing st = map (const Nothing) st