Який сенс "const" у Прелюдії Haskell?


92

Переглядаючи прелюдію Хаскелла, я бачу функцію const :

const x _ = x

Здається, я не можу знайти нічого відповідного щодо цієї функції.

В чому справа? Хтось може навести приклад, де ця функція може використовуватися?


10
Приклад: backgroundColor :: Text -> Colorце для менеbackgroundColor = const White
Zhen

Відповіді:


83

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

x >> y = x >>= const y

Це дещо акуратніше, ніж використання лямбди

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

і ви навіть можете використовувати його безточково

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

хоча я особливо цього не рекомендую.


9
+1. Це також часто виникає при використанні комбінаторів синтаксичного аналізатора.
Fred Foo

47
Так, це більше "генератор функцій" - я використовую його з одним аргументом, і це дає мені функцію (беручи один аргумент), яка завжди повертає постійне значення. Тож map (const 42) [1..5]результати в [42, 42, 42, 42, 42].
стюсміт

2
Стюсміт: Ви зрозуміли. constкорисно застосовувати до одного аргументу, щоб отримати функцію, де вона потрібна (наприклад, перехід до map).
Conal

8
@stusmith: Ви можете використовувати це цікавими способами:head = foldr const (error "Prelude.head: empty list")
чемпіон

27

Додамо до чудової прямої відповіді Hammar: скромні функції подобаються constі idдійсно корисні як функції вищого порядку з тієї самої причини, що вони є фундаментальними в обчислювальному комбінаторі SKI .

Не те, що я думаю, що функції прелюдії haskell були змодельовані свідомо після цієї формальної системи чи чогось іншого. Просто створити багаті абстракції в Haskell дуже просто, тому ви часто бачите, що такі типи теоретичних речей постають практично корисними.

Shameless плагін, але я писав про те , як аплікативного екземпляр для (->)фактично є Sі Kкомбінатори тут , якщо це така річ , ви в.


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тут використовують, щоб "забути" про введені дані.


21

Здається, я не можу знайти нічого відповідного щодо цієї функції.

Багато інших відповідей обговорюють відносно езотеричні (принаймні для новачка) програми const. Ось простий: ви можете використати, constщоб позбутися лямбди, яка бере два аргументи, відкидає перший, але робить щось цікаве з другим.

Наприклад, наступне (неефективне, але повчальне) здійснення length,

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

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

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

що, можливо, більш елегантно.

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


4
Мені знадобилося 5 хвилин, щоб зрозуміти, як це працює :)
Мукеш Соні

15

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

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

Використовуючи const, ми прямо заявляємо, що визначаємо постійні значення.

Особисто мені не подобається використання фіктивних параметрів, але якщо вони використовуються в класі, то це досить приємний спосіб написання екземплярів.


Аргументи проксі-сервера справді набагато кращі, і, орієнтуючись на нещодавні GHC, програми типу виконують трюк акуратно.
dfeuer

2

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 .

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


2

Скажімо, ви хочете створити список, Nothingsрівний довжині рядка. Як 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


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],[]]
 λ> 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.