Есть ли удобный способ использовать шаблон в качестве функции предиката?

10

Недавно я сталкивался с ситуациями, когда мне нужно передать предикатную функцию в другую функцию, и довольно часто логика, которую я ищу, по существу "соответствует ли это значение этому шаблону?"

Похоже, что сопоставление с образцом предпочтительнее в объявлениях, doблоках и списках, но есть ряд функций, которые принимают предикат a -> Bool, где было бы очень удобно как-то передать шаблон. Так , например, takeWhile, until, find, spanи т.д.

До сих пор я делал \a -> case a of MyCons _ -> True; otherwise -> Falseили писал именованную функцию а-ля, let myPred (MyCons _) = True; myPred _ = False inно они оба кажутся ужасно уродливыми и не очень идиоматичными. «Очевидный» (и неправильный) путь был бы чем-то вроде этого, \(MyCons _) -> Trueно это, естественно, выдает ошибку за частичность, и даже тогда кажется, что должен быть более чистый путь.

Есть ли более лаконичный / чистый способ сделать это? Или я поступаю совершенно неправильно?

Дэвид Сэмпсон
источник
1
Может быть, это вещь «личного вкуса», но, если вам нужен только этот предикат в одном месте, я был бы весьма доволен letпредложением, которое вам не нравится - хотя я предпочитаю эквивалентное whereпредложение, чтобы оно не загромождало основное определение. Конечно, если вам понадобится эта утилита более одного раза, вы определите ее как функцию верхнего уровня.
Робин Зигмонд
Это конечно хорошо работает. Мой вопрос был несколько мотивирован тем, насколько впечатляюще лаконичен Хаскелл. Обычно кажется, что идиоматичный Haskell очень мало дублирует идеи и сводит пух к минимуму. Так что даже не обязательно, что я думаю, что let myPred...стиль плохой , но он кажется гораздо более многословным, чем я ожидал от очень простой идеи, которая заставляет меня задуматься, не лаю ли я не на том дереве.
Дэвид Сэмпсон
2
Вы могли бы взглянуть на призмы (с объектива). Они как первоклассные композитные узоры
Луки
1
Я думаю, что нам нужно увидеть пример того, где вы используете этот тип функции более высокого порядка. Часть меня хочет сказать, что проблема в дизайне, который требует такого предиката.
Чепнер
способ Haskell98 для этого состоит в том, чтобы определить функцию сопоставления регистра (деконструкции) для вашего типа данных, например, maybe :: b -> (a -> b) -> Maybe a -> bи bool :: a -> a -> Bool -> aзатем использовать ее с булевы производящими функциями в качестве аргумента (ов). например myCons z f (MyCons x) = f x ; myCons z f _ = z, затем позвоните myCons False (const True) aMyConsValue. это почти то, что вы написали, просто есть еще один уровень «косвенности» / «абстракции» через функциональные аргументы, встроенный в него.
Уилл Несс

Ответы:

7

Вы можете использовать расширение языка LambdaCase \case MyCons _ -> True; _ -> False, хотя это не спасает столько символов.

Я полагаю, что вы могли бы написать ряд функций constructedWith :: (Generic a) => (b -> a) -> a -> Bool, constructedWith2 :: (Generic a) => (b -> c -> a) -> a -> Boolно я недостаточно компетентен в Generics, чтобы реализовать его без нескольких часов тестирования. Я попробую это и отредактирую свой ответ, если смогу разобраться или это тупик.

РЕДАКТИРОВАТЬ: Да, вы можете сделать это! Вот ссылка на мой код, который реализует все это с нуля:

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

Однако лучше использовать что-то вроде http://hackage.haskell.org/package/generic-deriving-1.13.1/docs/Generics-Deriving-ConNames.html для всего общего кода.

Исаак Вайс
источник