Existe uma maneira conveniente de usar um padrão como uma função predicada?

10

Recentemente, estive enfrentando situações em que preciso passar uma função predicada para outra função, e muitas vezes a lógica que estou procurando é essencialmente "esse valor corresponde a esse padrão?"

A correspondência de padrões parece ser preferida em declarações, doblocos e compreensões de lista, mas há várias funções que usam um predicado a -> Bool, nas quais seria muito útil passar de alguma forma em um padrão. Por exemplo, takeWhile, until, find, span, etc.

Até agora eu venho fazendo \a -> case a of MyCons _ -> True; otherwise -> Falseou escrevendo uma função nomeada à la, let myPred (MyCons _) = True; myPred _ = False inmas ambas parecem terrivelmente feias e não muito idiomáticas. O caminho "óbvio" (e errado) seria algo parecido, \(MyCons _) -> Truemas isso gera um erro por ser parcial, naturalmente, e mesmo assim parece que deve haver um caminho mais limpo.

Existe uma maneira mais sucinta / limpa de fazer esse tipo de coisa? Ou estou fazendo as coisas da maneira errada?

David Sampson
fonte
11
Talvez isso seja algo de "gosto pessoal", mas, se você precisar desse predicado em um só lugar, eu ficaria muito feliz com a letcláusula que você não gosta - embora eu prefira a wherecláusula equivalente para que isso não atrapalhe a definição principal. Obviamente, se você precisar deste utilitário mais de uma vez, você o definiria como uma função de nível superior.
Robin Zigmond
Certamente funciona bem. Minha pergunta foi um pouco motivada pela maneira como Haskell é impressionantemente sucinto. Geralmente parece que Haskell idiomático tem muito pouca duplicação de idéias e mantém o mínimo de cotão. Portanto, nem sempre é necessário que eu pense que o let myPred...estilo é ruim , mas parece muito mais detalhado do que eu esperaria de uma idéia muito simples, o que me leva a pensar se estou latindo na árvore errada.
David Sampson
2
Você pode dar uma olhada nos prismas (da lente). Eles são como padrões de
composição de
11
Acho que precisaríamos ver um exemplo de onde você está usando esse tipo de função de ordem superior. Parte de mim quer dizer que o problema está no design que requer esse predicado em primeiro lugar.
chepner 8/01
a maneira Haskell98, para isso, é definir a função de correspondência de casos (desconstrução) para seu tipo de dados, como maybe :: b -> (a -> b) -> Maybe a -> be bool :: a -> a -> Bool -> a, em seguida, usá-la com funções de produção booleana como argumento (s). por exemplo myCons z f (MyCons x) = f x ; myCons z f _ = z, então ligue myCons False (const True) aMyConsValue. isto é quase o que você escreveu, apenas possui mais um nível de "indireção" / "abstração" por meio de argumentos funcionais, inseridos nele.
Will Ness

Respostas:

7

Você pode usar a extensão de idioma LambdaCase para usar \case MyCons _ -> True; _ -> False, embora isso não salve muitos caracteres.

Acredito que você possa escrever uma série de funções constructedWith :: (Generic a) => (b -> a) -> a -> Bool, constructedWith2 :: (Generic a) => (b -> c -> a) -> a -> Boolmas não sou competente o suficiente com a Generics para implementá-la sem algumas horas testando as coisas. Vou tentar isso e editar minha resposta se eu puder descobrir ou se é um beco sem saída.

EDIT: Sim, você pode fazê-lo! Aqui está um link para o meu código, que implementa tudo do zero:

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

No entanto, usar algo como http://hackage.haskell.org/package/generic-deriving-1.13.1/docs/Generics-Deriving-ConNames.html para todo o encanamento de código genérico pode ser melhor.

Izaak Weiss
fonte