Apa gunanya 'const' dalam Haskell Prelude?

94

Melihat melalui Haskell Prelude, saya melihat sebuah fungsi const :

const x _ = x

Sepertinya saya tidak dapat menemukan sesuatu yang relevan tentang fungsi ini.

Apa gunanya? Adakah yang bisa memberi contoh di mana fungsi ini dapat digunakan?

stusmith
sumber
10
Contoh: backgroundColor :: Text -> Coloruntuk sayabackgroundColor = const White
Zhen

Jawaban:

84

Ini berguna untuk meneruskan ke fungsi tingkat tinggi saat Anda tidak membutuhkan semua fleksibilitasnya. Misalnya, operator urutan monadik >>dapat didefinisikan dalam istilah operator ikatan monad sebagai

x >> y = x >>= const y

Ini agak lebih rapi daripada menggunakan lambda

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

dan Anda bahkan dapat menggunakannya tanpa poin

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

meskipun saya tidak terlalu merekomendasikan itu dalam kasus ini.

hammar
sumber
9
+1. Itu juga sering muncul saat menggunakan kombinator parser.
Fred Foo
49
Ahh jadi ini lebih merupakan 'generator fungsi' - saya menggunakannya dengan satu argumen, dan ini memberi saya fungsi (mengambil satu argumen) yang selalu mengembalikan nilai konstan. Jadi map (const 42) [1..5]hasilnya [42, 42, 42, 42, 42].
stusmith
2
stusmith: Anda mengerti. constberguna untuk menerapkan argumen tunggal untuk menghasilkan fungsi yang dibutuhkan (seperti meneruskan ke map).
Conal
9
@stusmith: Anda dapat menggunakannya dengan beberapa cara menarik:head = foldr const (error "Prelude.head: empty list")
rampion
27

Untuk menambah jawaban langsung hammar yang sangat baik: fungsi sederhana seperti constdan idsangat berguna sebagai fungsi orde tinggi untuk alasan yang sama bahwa mereka fundamental dalam kalkulus kombinator SKI .

Bukannya saya pikir fungsi pendahuluan haskell dimodelkan secara sadar setelah sistem formal atau apa pun. Hanya saja membuat abstraksi yang kaya di haskell sangat mudah, jadi Anda sering melihat jenis hal teoretis ini muncul sebagai berguna secara praktis.

Steker tak tahu malu, tapi saya membuat blog tentang bagaimana contoh Aplikatif (->)sebenarnya adalah Sdan Kkombinator di sini , jika itu jenis yang Anda sukai.

jberryman
sumber
8
Nah, kombinator SKI pasti memengaruhi Prelude. Saya ingat berdebat dengan Joe Fasel apakah kombinator S harus dimasukkan atau tidak.
Agustus
4
Kebetulan, ((->) e)juga pembaca monad - dengan Readerdan sejenisnya hanya menjadi newtypepembungkus - dan askfungsinya kemudian id, jadi itu Ikombinatornya juga. Jika Anda melihat sebaliknya di dasar BCKW asli Haskell Curry, B, K, dan Wadalah fmap, return, dan joinmasing-masing.
CA McCann
1
Link blog di jawabannya sudah mati. Sekarang seharusnya mengarah ke sini: brandon.si/code/…
nsxt
22

Contoh sederhana untuk digunakan constadalah Data.Functor.(<$). Dengan fungsi ini Anda dapat mengatakan: Saya memiliki sebuah functor dengan sesuatu yang membosankan di dalamnya, tetapi sebaliknya saya ingin memiliki hal menarik lainnya di dalamnya, tanpa mengubah bentuk dari functor. Misalnya

import Data.Functor

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

42 <$ Nothing
--> Nothing

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

Definisinya adalah:

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

atau ditulis tidak sia-sia:

cool <$ uncool =  fmap (const cool) uncool

Anda melihat bagaimana constdigunakan di sini untuk "melupakan" tentang input.

Landei
sumber
21

Sepertinya saya tidak dapat menemukan sesuatu yang relevan tentang fungsi ini.

Banyak dari jawaban lain membahas aplikasi yang relatif esoteris (setidaknya bagi pendatang baru) const. Ini yang sederhana: Anda bisa menggunakan constlambda yang membutuhkan dua argumen, membuang yang pertama tetapi melakukan sesuatu yang menarik dengan yang kedua.

Misalnya, implementasi (tidak efisien tetapi instruktif) berikut ini length,

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

dapat ditulis ulang sebagai

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

yang mungkin lebih elegan.

Ekspresi const (1+)memang secara semantik setara dengan \_ acc -> 1 + acc, karena mengambil satu argumen, membuangnya, dan mengembalikan bagian tersebut (1+) .

jub0bs
sumber
4
Butuh waktu 5 menit untuk memahami cara kerjanya :)
Mukesh Soni
15

Penggunaan lain adalah untuk mengimplementasikan fungsi anggota kelas yang memiliki argumen dummy yang tidak boleh dievaluasi (digunakan untuk menyelesaikan tipe yang ambigu). Contoh yang mungkin ada di Data.bits:

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

Dengan menggunakan const, kami secara eksplisit mengatakan bahwa kami mendefinisikan nilai konstan.

Secara pribadi saya tidak suka penggunaan parameter dummy, tetapi jika mereka digunakan di kelas maka ini adalah cara yang bagus untuk menulis instance.

Jonas Duregård
sumber
Argumen proxy memang jauh lebih baik, dan saat menargetkan GHC terbaru, jenis aplikasi melakukan triknya dengan rapi.
dfeuer
2

constmungkin hanya implementasi yang Anda cari dalam hubungannya dengan fungsi lain. Inilah contoh yang saya temukan.

Katakanlah kita ingin menulis ulang struktur 2-tupel menjadi struktur 2-tupel lainnya. Saya mungkin mengungkapkan ini sebagai:

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

Saya dapat memberikan definisi langsung dengan pencocokan pola:

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

Bagaimana jika saya menginginkan solusi yang tidak berguna (diam-diam) untuk penulisan ulang semacam ini? Beberapa berpikir dan mengotak-atik nanti, jawabannya adalah kita dapat mengekspresikan penulisan ulang apa pun (&&&), const, (.), fst, snd. Perhatikan itu (&&&)dariControl.Arrow .

Solusi dari contoh yang menggunakan fungsi ini adalah:

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

Perhatikan kesamaannya dengan (a,(c,(5,a))). Bagaimana jika kita ganti &&&dengan ,? Kemudian berbunyi:

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

Perhatikan bagaimana aelemen pertama dari elemen pertama, dan itulah fst.fstproyek. Perhatikan bagaimana celemen pertama dari elemen kedua, dan itulah fst.sndproyek. Artinya, variabel menjadi jalur ke sumbernya.

constmemungkinkan kami untuk memperkenalkan konstanta. Menarik bagaimana nama itu sejajar dengan artinya!

Saya kemudian digeneralisasi ide ini dengan Aplikatif sehingga Anda dapat menulis fungsi apapun dalam gaya sia-sia (asalkan Anda memiliki analisis kasus tersedia sebagai fungsi, seperti maybe, either, bool). Sekali lagi, constmemainkan peran memperkenalkan konstanta. Anda dapat melihat pekerjaan ini di paket Data.Function.Tacit .

Ketika Anda memulai secara abstrak, pada tujuan, dan kemudian bekerja menuju implementasi, Anda akan terkejut dengan jawabannya. Artinya, salah satu fungsi mungkin sama misteriusnya dengan salah satu roda penggerak dalam mesin. Namun, jika Anda mundur untuk menampilkan seluruh mesin, Anda dapat memahami konteks di mana roda gigi itu diperlukan.

erisco
sumber
2

Katakanlah Anda ingin membuat daftar yang Nothingssama dengan panjang string. Saat constmengembalikan argumen pertamanya, tidak peduli argumen kedua, Anda dapat melakukan:

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

atau, lebih jelasnya:

listOfNothing st = map (const Nothing) st
A. Saramet
sumber
0

Katakanlah Anda ingin memutar daftar. Ini adalah cara idiomatik untuk melakukannya di Haskell:

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

Fungsi ini menggabungkan dua larik dengan fungsi tersebut const, yang pertama adalah larik siklik tak hingga, yang kedua adalah larik yang Anda gunakan untuk memulai.

const bertindak sebagai pemeriksaan batas, dan menggunakan larik asli untuk menghentikan larik siklik.

Lihat: Memutar daftar di Haskell

Jameis terkenal
sumber
0

Sepertinya saya tidak dapat menemukan sesuatu yang relevan tentang fungsi ini.

Misalkan Anda ingin membuat semua urutan dari daftar yang diberikan.

Untuk setiap elemen daftar, pada titik tertentu Anda memiliki pilihan Benar (sertakan dalam urutan saat ini) atau Salah (jangan sertakan). Ini dapat dilakukan dengan menggunakan fungsi filterM .

Seperti ini:

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

Misalnya, kami menginginkan semua urutan [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.dll
sumber