Apakah ada cara yang nyaman untuk menggunakan pola sebagai fungsi predikat?

10

Saya baru-baru ini mengalami situasi di mana saya harus melewati fungsi predikat ke fungsi lain, dan cukup sering logika yang saya cari pada dasarnya adalah "apakah nilai ini cocok dengan pola ini?"

Pencocokan pola tampaknya lebih disukai dalam deklarasi, doblok, dan daftar pemahaman, tetapi ada sejumlah fungsi yang mengambil predikat a -> Bool, di mana akan sangat berguna untuk melewati suatu pola. Sebagai contoh, takeWhile, until, find, span, dll

Sejauh ini saya sudah melakukan \a -> case a of MyCons _ -> True; otherwise -> False, atau menulis fungsi bernama a la let myPred (MyCons _) = True; myPred _ = False intetapi mereka berdua tampak sangat jelek dan tidak terlalu idiomatis. Cara "jelas" (dan salah) akan menjadi sesuatu seperti \(MyCons _) -> Truetetapi itu melemparkan kesalahan karena menjadi parsial, secara alami, dan bahkan kemudian rasanya seperti harus ada cara yang lebih bersih.

Apakah ada cara yang lebih ringkas / bersih untuk melakukan hal semacam ini? Atau apakah saya melakukan sesuatu yang sepenuhnya salah?

David Sampson
sumber
1
Mungkin ini adalah "selera pribadi" tetapi, jika Anda hanya membutuhkan predikat ini di satu tempat, saya akan cukup senang dengan letklausa yang tidak Anda sukai - walaupun saya lebih suka whereklausa yang setara sehingga ini tidak mengacaukan definisi utama. Tentu saja jika Anda membutuhkan utilitas ini lebih dari sekali maka Anda akan mendefinisikannya sebagai fungsi tingkat atas.
Robin Zigmond
Ini tentu saja berfungsi dengan baik. Pertanyaan saya agak termotivasi oleh bagaimana Haskell biasanya ringkas dan mengesankan. Biasanya terasa seperti Haskell idiomatik memiliki duplikasi ide yang sangat sedikit, dan menjaga fluff seminimal mungkin. Jadi bahkan belum tentu saya pikir let myPred...gaya itu buruk , tetapi rasanya jauh lebih jelas daripada yang saya harapkan untuk ide yang sangat sederhana, yang membuat saya bertanya-tanya apakah saya menggonggong pohon yang salah.
David Sampson
2
Anda mungkin melihat prisma (dari lensa). Mereka seperti pola komposisi kelas satu
luqui
1
Saya pikir kita perlu melihat contoh di mana Anda menggunakan jenis fungsi tingkat tinggi ini. Sebagian dari saya ingin mengatakan bahwa masalahnya adalah pada desain yang membutuhkan predikat seperti itu.
chepner
cara Haskell98, untuk ini, adalah mendefinisikan fungsi case-matching (deconstructing) untuk tipe data Anda, seperti maybe :: b -> (a -> b) -> Maybe a -> bdan bool :: a -> a -> Bool -> a, kemudian gunakan dengan fungsi penghasil Boolean sebagai argumen. misalnya myCons z f (MyCons x) = f x ; myCons z f _ = z, lalu telepon myCons False (const True) aMyConsValue. ini hampir seperti yang Anda tulis, hanya memiliki satu tingkat "tipuan" / "abstraksi" lagi melalui argumen fungsional, dimasukkan ke dalamnya.
Will Ness

Jawaban:

7

Anda dapat menggunakan LambdaCase Ekstensi Bahasa untuk digunakan \case MyCons _ -> True; _ -> False, meskipun ini tidak menyimpan banyak karakter.

Saya percaya Anda bisa menulis serangkaian fungsi constructedWith :: (Generic a) => (b -> a) -> a -> Bool, constructedWith2 :: (Generic a) => (b -> c -> a) -> a -> Booltapi saya tidak cukup kompeten dengan Generics untuk mengimplementasikannya tanpa beberapa jam menguji semuanya. Saya akan mencoba ini, dan mengedit jawaban saya jika saya bisa mengetahuinya, atau jika itu jalan buntu.

EDIT: Ya, Anda bisa melakukannya! Berikut ini tautan ke kode saya, yang mengimplementasikan semuanya dari awal:

https://repl.it/@lalaithion/ConstructedWith

Namun, menggunakan sesuatu seperti http://hackage.haskell.org/package/generic-deriving-1.13.1/docs/Generics-Deriving-ConNames.html untuk semua pemipaan kode generik mungkin lebih baik.

Izaak Weiss
sumber