8
votes

Qu'est-ce qu'un système général pour écrire une fonction dans le style pointue?

Je travaille à travers le 20 exercices de haskell intermédiaires pour le moment, ce qui est assez amusant exercer. Il implique la mise en œuvre de diverses instances du fonctionnement code> Foncteur code> et monad code> (et des fonctions qui prend Foncteur code> s et monad code> s arguments) mais avec des noms mignons comme Furry code> et brumeux code> pour dissimuler ce que nous faisons (fait un code intéressant).

J'ai essayé de faire quelques De cela dans un style ponctuel, et je me demandais s'il y a un régime général pour transformer une définition point-ful (?) dans une définition sans points. Par exemple, voici le typeclass pour Misty code>: p> xxx pré>

(les fonctions Unicorn code> et banane code> sont retour code> et >> = code>, au cas où il ne serait pas évident) et voici mon implémentation de Apple code> (équivalent à Flip AP CODE>): P>

appleTurnover :: (Misty m) => m (a -> b) -> m a -> m b
appleTurnover = flip apple

banana1 :: (Misty m) => (a -> b) -> m a -> m b
banana1 =  appleTurnover . unicorn

banana2 :: (Misty m) => (a -> b -> c) -> m a -> m b -> m c
banana2 f = appleTurnover . banana1 f

banana3 :: (Misty m) => (a -> b -> c -> d) -> m a -> m b -> m c -> m d
banana3 f x = appleTurnover . banana2 f x

banana4 :: (Misty m) => (a -> b -> c -> d -> e) -> m a -> m b -> m c -> m d -> m e
banana4 f x y = appleTurnover . banana3 f x y


2 commentaires

Il est connecté à l'élimination de l'abstraction que vous faites pour transformer les expressions de calcul de Lambda en combinateurs. Vous voudrez peut-être également consulter l'autonome Outil PointFree (également disponible en tant que @pl dans Lambdabot ).


Une discussion connexe J'avais avec un ami l'autre jour . Vous pourriez le trouver intéressant.


5 Réponses :


5
votes

Oui! L'une des astuces est d'écrire vos points dans la notation de préfixe plutôt que d'infixer. Ensuite, vous devriez être capable de trouver de nouvelles choses qui ressemblent à la composition de la fonction. Voici un exemple:

banana4 f x y = appleTurnover . banana3 f x y
              = (.) appleTurnover ((banana3 f x) y)
              = ((.) appleTurnover) . (banana3 f x) $ y
banana4 f x = ((.) appleTurnover) . (banana3 f x)
            = (.) ((.) appleTurnover) (banana3 f x)
            = ((.) ((.) appleTurnover)) ((banana3 f) x)
            = ((.) ((.) appleTurnover)) . (banana3 f) $ x
banana4 f = ((.) ((.) appleTurnover)) . (banana3 f)
          = (.) ((.) ((.) appleTurnover)) (banana3 f)
          = ((.) ((.) ((.) appleTurnover))) (banana3 f)
          = ((.) ((.) ((.) appleTurnover))) . banana3 $ f
banana4 = ((.) ((.) ((.) appleTurnover))) . banana3
        = (((appleTurnover .) .) .) . banana3


3 commentaires

C'est également un bon moyen de rendre vos fonctions totalement illisibles, bien sûr ...


Donné retour est appelé Unicorn il semble que l'OP n'est pas inquiet à propos de ça = p.


@Claudiu: Eh bien, ça vient des exercices que l'OP est en train de faire, qui dérivent fondamentalement des trucs comme monad à partir de la mise en place, en utilisant des noms très stupides pour les définitions.



2
votes

Il y a un package PointeFree qui prend une définition de fonction HASKELLL et tente de ré-écrire dans un style Pointefree . Je suggérerais d'expérimenter avec de nouvelles idées. Voir Cette page Pour plus de détails; Le colis est disponible ici .


0 commentaires

11
votes

Comme illustré par l'utilitaire PointPlare , il est possible de faire une telle conversion automatiquement. Cependant, le résultat est plus souvent obscurcié que l'amélioration. Si son objectif est d'améliorer la lisibilité plutôt que de le détruire, le premier objectif devrait être d'identifier pourquoi une expression a une structure particulière, trouvez une abstraction appropriée et construisez des choses de cette façon.

La structure la plus simple est simplement enchaînant des choses ensemble dans un pipeline linéaire, ce qui est une composition de fonction simple. Cela nous amène assez loin juste seul, mais comme vous l'avez remarqué, cela ne gère pas tout.

Une généralisation est de fonctionner avec des arguments supplémentaires, qui peuvent être construits progressivement. Voici un exemple: définir onresult = (. (.)) . Maintenant, appliquer Onresult N fois à une valeur initiale de ID vous donne la composition de fonction avec le résultat d'une fonction N-ARY. Nous pouvons donc définir comp2 = onresult (.) , puis écrivez comp2 non (&&) pour définir une opération NAND.

Une autre généralisation - qui englobe ce qui précède, réellement - est de définir les opérateurs qui appliquent une fonction à un composant d'une valeur plus grande. Un exemple ici serait premier et second dans contrôler.arrow , qui fonctionne sur 2-tuples. Les combinaisons de rédaction sémantique sont basées sur cette approche.

Un cas légèrement différent est lorsque vous avez une fonction multi-arguments sur certains types B et une fonction a -> b et devez les combiner dans un Fonction multi-arguments utilisant A . Pour le cas commun des fonctions 2-ary, le module data.function fournit le combinateur sur , que vous pouvez utiliser pour écrire des expressions telles que comparer `on` fst pour comparer 2-tuples sur leurs premiers éléments.

C'est une question plus délicieuse lorsqu'un argument unique est utilisé plus d'une fois, mais il existe des modèles récurrents significatifs qui peuvent également être extraits. Un cas commun annonce ici appliquer plusieurs fonctions à un seul argument, puis collectionne les résultats avec une autre fonction. Ceci arrive à correspondre à l'instance d'application pour les fonctions, qui nous permet d'écrire des expressions telles que (&&) <$> (> 3) <*> (<9) pour vérifier Si un numéro tombe dans une plage donnée.

La chose importante, si vous souhaitez utiliser l'une d'entre elles en code réel, est de penser à ce que l'expression signifie et comment cela se reflète dans la structure. Si vous faites cela, puis le refactue dans le style pointut gratuit à l'aide de combinaisons significatifs, vous ferez souvent l'intention du code plus clair que ce n'en serait autrement, contrairement à la sortie typique de PointPlare < / code>.


0 commentaires

3
votes

J'utilise le système de réécriture de trimestre suivant: xxx pré>

Il est incomplet (lire pourquoi dans des livres sur la logique combinatoire), mais il suffit: P>

Voici Banana2: p> xxx pré>

réécrite en tant que lambda: p> xxx pré>

écrire (.) dans le style préfixe: p>

banana2 = ((.) appleTurnover) . banana1


0 commentaires

0
votes

Etant donné que le style Poinfree est le style des combinaisons, il suffit d'appliquer des définitions de combinaisons connues, les lire à l'envers pour effectuer la substitution: xxx pré>

temps liftmx code>, liftax code>, séquence code>, Sequencea code> peut simplifier les choses. J'envisageais aussi pli dollar code>, non enfoncé code>, itérer code>, jusqu'à code> comme des combinaisons de base. P>

Souvent, à l'aide de sections de l'opérateur vous aide également: P>

((f .) . g) x y = f (g x y)
((. f) . g) x y = g x (f y)

(((f .) .) . g) x y z = (f .) (g x y) z = f (g x y z)
(((. f) .) . g) x y z = (. f) (g x y) z = g x y (f z)


0 commentaires