Недавно я сталкивался с ситуациями, когда мне нужно передать предикатную функцию в другую функцию, и довольно часто логика, которую я ищу, по существу "соответствует ли это значение этому шаблону?"
Похоже, что сопоставление с образцом предпочтительнее в объявлениях, do
блоках и списках, но есть ряд функций, которые принимают предикат a -> Bool
, где было бы очень удобно как-то передать шаблон. Так , например, takeWhile
, until
, find
, span
и т.д.
До сих пор я делал \a -> case a of MyCons _ -> True; otherwise -> False
или писал именованную функцию а-ля, let myPred (MyCons _) = True; myPred _ = False in
но они оба кажутся ужасно уродливыми и не очень идиоматичными. «Очевидный» (и неправильный) путь был бы чем-то вроде этого, \(MyCons _) -> True
но это, естественно, выдает ошибку за частичность, и даже тогда кажется, что должен быть более чистый путь.
Есть ли более лаконичный / чистый способ сделать это? Или я поступаю совершенно неправильно?
let
предложением, которое вам не нравится - хотя я предпочитаю эквивалентноеwhere
предложение, чтобы оно не загромождало основное определение. Конечно, если вам понадобится эта утилита более одного раза, вы определите ее как функцию верхнего уровня.let myPred...
стиль плохой , но он кажется гораздо более многословным, чем я ожидал от очень простой идеи, которая заставляет меня задуматься, не лаю ли я не на том дереве.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
. это почти то, что вы написали, просто есть еще один уровень «косвенности» / «абстракции» через функциональные аргументы, встроенный в него.Ответы:
Вы можете использовать расширение языка 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 для всего общего кода.
источник