Existe-t-il un moyen pratique d'utiliser un modèle comme fonction de prédicat?

10

J'ai récemment rencontré des situations où je dois passer une fonction de prédicat à une autre fonction, et bien souvent la logique que je recherche est essentiellement "cette valeur correspond-elle à ce modèle?"

La correspondance de modèle semble être préférée dans les déclarations, les doblocs et les listes de compréhension, mais il existe un certain nombre de fonctions qui prennent un prédicat a -> Bool, où il serait très pratique de passer en quelque sorte un modèle. Par exemple, takeWhile, until, find, span, etc.

Jusqu'à présent, j'ai fait \a -> case a of MyCons _ -> True; otherwise -> Falseou écrit une fonction nommée à la fois, let myPred (MyCons _) = True; myPred _ = False inmais elles semblent toutes les deux terriblement laides et pas très idiomatiques. La façon "évidente" (et erronée) serait quelque chose comme \(MyCons _) -> Trueça, mais cela jette une erreur pour être partiel, naturellement, et même alors, il semble qu'il doit y avoir une manière plus propre.

Existe-t-il une manière plus succincte / propre de faire ce genre de choses? Ou est-ce que je vais tout à fait mal?

David Sampson
la source
1
C'est peut-être un "goût personnel" mais, si vous n'avez besoin de ce prédicat qu'à un seul endroit, je serais très satisfait de la letclause que vous n'aimez pas - bien que je préfère la whereclause équivalente afin que cela n'encombre pas la définition principale. Bien sûr, si vous finissez par avoir besoin de cet utilitaire plus d'une fois, vous le définirez comme une fonction de niveau supérieur.
Robin Zigmond
Cela fonctionne certainement très bien. Ma question était quelque peu motivée par la brièveté impressionnante de Haskell. On a généralement l'impression que Haskell idiomatique a très peu de duplication d'idées et réduit les peluches au minimum. Donc, ce n'est même pas nécessairement que je pense que le let myPred...style est mauvais , mais il semble beaucoup plus verbeux que ce à quoi je m'attendais pour une idée très simple, ce qui m'amène à me demander si j'aboie le mauvais arbre.
David Sampson
2
Vous pourriez regarder les prismes (depuis l'objectif). Ils sont comme des motifs composables de première classe
luqui
1
Je pense que nous aurions besoin d'un exemple de l'endroit où vous utilisez ce type de fonction d'ordre supérieur. Une partie de moi veut dire que le problème est avec la conception qui nécessite un tel prédicat en premier lieu.
chepner
Pour cela, la méthode Haskell98 consiste à définir la fonction de mise en correspondance (déconstruction) pour votre type de données, comme maybe :: b -> (a -> b) -> Maybe a -> bet bool :: a -> a -> Bool -> a, puis à l'utiliser avec des fonctions de production booléennes comme argument (s). par exemple myCons z f (MyCons x) = f x ; myCons z f _ = z, puis appelez myCons False (const True) aMyConsValue. c'est presque ce que vous avez écrit, il y a juste un niveau supplémentaire "d'indirection" / "abstraction" via des arguments fonctionnels, incorporés dans celui-ci.
Will Ness le

Réponses:

7

Vous pouvez utiliser l'extension de langue LambdaCase à utiliser \case MyCons _ -> True; _ -> False, bien que cela n'enregistre pas autant de caractères.

Je pense que vous pourriez écrire une série de fonctions constructedWith :: (Generic a) => (b -> a) -> a -> Bool, constructedWith2 :: (Generic a) => (b -> c -> a) -> a -> Boolmais je ne suis pas assez compétent avec Generics pour l'implémenter sans quelques heures de test. Je vais essayer cela et modifier ma réponse si je peux le comprendre ou s'il s'agit d'une impasse.

EDIT: Oui, vous pouvez le faire! Voici un lien vers mon code, qui implémente tout à partir de zéro:

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

Cependant, l'utilisation de quelque chose comme http://hackage.haskell.org/package/generic-deriving-1.13.1/docs/Generics-Deriving-ConNames.html pour toute la plomberie de code générique pourrait être meilleure.

Izaak Weiss
la source