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

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

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

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

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


person David Sampson    schedule 07.01.2020    source источник
comment
Возможно, это личный вкус, но если вам нужен этот предикат только в одном месте, я был бы вполне доволен предложением let, которое вам не нравится, хотя я предпочитаю эквивалентное предложение where, чтобы оно не загромождало основное определение. Конечно, если вам понадобится эта утилита более одного раза, вы должны определить ее как функцию верхнего уровня.   -  person Robin Zigmond    schedule 08.01.2020
comment
Это, безусловно, работает нормально. Мой вопрос был отчасти мотивирован тем, насколько лаконичным обычно является Haskell. Обычно кажется, что в идиоматическом Haskell очень мало дублирования идей и сведено к минимуму ерунда. Так что даже не обязательно, что я считаю стиль let myPred... плохим, но он кажется гораздо более многословным, чем я ожидал, для очень простой идеи, что заставляет меня задуматься, не ошибаюсь ли я .   -  person David Sampson    schedule 08.01.2020
comment
Вы могли бы взглянуть на призмы (от объектива). Они похожи на первоклассные составные шаблоны.   -  person luqui    schedule 08.01.2020
comment
Я думаю, нам нужно увидеть пример того, как вы используете этот тип функции высшего порядка. Часть меня хочет сказать, что проблема в дизайне, который в первую очередь требует такого предиката.   -  person chepner    schedule 08.01.2020
comment
способ 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. это почти то, что вы написали, просто есть еще один уровень косвенности/абстракции через функциональные аргументы, встроенные в него.   -  person Will Ness    schedule 09.01.2020


Ответы (1)


Вы можете использовать языковое расширение 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 для всей общей сантехники кода может быть лучше.

person Izaak Weiss    schedule 07.01.2020