2017-01-31 2 views
2

Tenez compte « composition » fonctions avec la signature suivante (pseudo):En utilisant des lentilles pour composer les fonctions

(a1 -> a2 -> ... -> an -> r) -> 
(s -> ai) -> 
a1 -> a2 -> ... -> ai -> ... an -> r 
where i in [1..n] 

Bien sûr, nous ne pouvons pas écrire ce qui est ci-dessus dans Haskell, mais voici un exemple concret:

f4change3 :: 
    (a1 -> a2 -> a3 -> a4 -> r) -> 
    (s -> a3) -> 
    a1 -> a2 -> s -> a4 -> r 
f4change3 f g x1 x2 x3 x4 = f x1 x2 (g x3) x4 

Comme vous pouvez le voir, il y a une collection de n fonctions pour chaque fonction d'arité n, donc le nombre de fonctions dont nous avons besoin croît de façon quadratique avec l'arité.

Je pourrais juste écrire ceux dont j'ai besoin, mais d'abord, je ne veux pas réinventer la roue, donc ce serait bien de savoir si la bibliothèque a déjà fait cela. Mais aussi, alors que j'ai à peine utilisé des lentilles, j'ai lu un peu à leur sujet, et ce genre de problème semble être leur allié, mais je ne sais pas exactement comment le faire. Un exemple de code serait génial si c'est possible.

+1

Je ne comprends pas la question ... – mb14

+0

Je pense que vous demandez essentiellement sur http://conal.net/blog/posts/semantic-editor-combinators – Carl

+0

Vous ne pouvez pas utiliser * modèle Haskell * pour cette? –

Répondre

5

Comme mentionné dans les commentaires, Conal Elliott's semantic editor combinators donne un très bel outil pour écrire ces fonctions.Revoyons seulement deux de ses combinateurs:

argument :: (a' -> a) -> ((a -> b) -> (a' -> b)) 
argument = flip (.) 

result :: (b -> b') -> ((a -> b) -> (a -> b')) 
result = (.) 

En d'autres termes: argument modifie l'argument d'une fonction avant que la fonction est appelée, et result modifie la valeur de retour d'une fonction après son appel. (Voir le blog lui-même pour plus d'explications-intuition de ces renforcer combinateurs.) Disons que nous avons une fonction avec un type comme

a1 -> a2 -> a3 -> a4 -> a5 -> b 

et nous voulons changer a5. Notez que nous pouvons parenthésée cela, bien sûr:

a1 -> (a2 -> (a3 -> (a4 -> (a5 -> b)))) 

Donc, si nous voulons atteindre a5, comment devrions-nous atteindre dans cette structure? Eh bien, nous le ferons en atteignant à plusieurs reprises dans le result, agissant alors sur le argument: le type de résultat de cette fonction est a2 -> (a3 -> (a4 -> (a5 -> b))), dont le résultat est a3 -> (a4 -> (a5 -> b)), dont le résultat est a4 -> (a5 -> b), dont le résultat est a5 -> b, dont l'argument est a5. Cela nous donne le code directement:

arg5 :: (a5' -> a5) 
    -> (a1 -> a2 -> a3 -> a4 -> a5 -> b) 
    -> (a1 -> a2 -> a3 -> a4 -> a5' -> b) 
arg5 = result . result . result . result . argument 

Espérons qu'il est tout à fait clair comment généraliser ce pour modifier d'autres arguments: il suffit de modifier le nombre de fois que vous appelez result.

-3

Cette bibliothèque est appelée base. Et vous n'avez même pas besoin d'importer quoi que ce soit, tout est déjà dans Prelude. Tout ce dont vous avez besoin est un peu d'imagination et de créativité. Et vous pouvez facilement écrire votre fonction de la manière suivante:

f4change3 :: 
    (a1 -> a2 -> a3 -> a4 -> r) -> 
    (s -> a3) -> 
    a1 -> a2 -> s -> a4 -> r 
f4change3 = flip . ((flip . ((.) .)) .) 

Après plusieurs années de formation et la lecture de ce code, vous vous remplir à l'aise avec ce genre de code. Et il ne vous faudra que quelques heures pour comprendre ce que cette implémentation fait réellement. Mais les programmeurs de gourou Haskell ne lisent pas les implémentations car tout est clairement clair à partir du type.

+0

Cela ne répond pas à la question. Votre implémentation est juste une version sans point de 'f4change' dans la question. Le PO cherche un moyen de le généraliser. – duplode

+0

Quelqu'un n'a pas de blagues et d'ironie ... Vous ne devriez pas être trop sérieux ici :) – Shersh

+0

Eh bien, ce n'est pas tout à fait juste: j'allais écrire un commentaire spécifiquement sur le deuxième paragraphe, mais je me suis arrêté dans temps :) – duplode

1

Bon, voici la vraie réponse. Si vous ne voulez pas juste rire et que vous voulez résoudre le problème, vous pouvez passer à la prochaine étape.

Votre mise en œuvre peut être raccourcissent un peu:

f4change3 :: 
    (a1 -> a2 -> a3 -> a4 -> r) -> 
    (s -> a3) -> 
    a1 -> a2 -> s -> a4 -> r 
f4change3 f g x1 x2 = f x1 x2 . g 

Ce n'est pas très utile et vous pouvez passer en mode plein sans le point (voir ma réponse précédente).

Je souhaite Haskell avait nommé des arguments pour que vous puissiez tout simplement écrire semblable à -XRecordWildCards:

f4change3 :: 
    ((a1 :: a1) -> (a2 :: a2) -> (a3 :: a3) -> (a4 :: a4) -> r) -> 
    ((s :: s) -> a3) -> 
    (a1 :: a1) -> (a2 :: a2) -> (s :: s) -> (a4 :: a4) -> r 
f4change3 f g = f{ a3 = g{..}, .. } 

Pour ces cas particuliers, lorsque nous voulons les noms des arguments soient les mêmes que les noms des variables de type syntaxe peut être même beaucoup -Plus court. Mais malheureusement, nous ne verrons pas ces caractéristiques douces dans le futur le plus proche (ou jamais).

Mais vous avez posé des questions sur la solution avec des lentilles. Vous pouvez en effet réaliser quelque chose de similaire en utilisant des lentilles. La seule chose que vous devez modifier dans vos signatures de type est de représenter les arguments pour f comme type de données avec quatre champs. Voici le code complet:

{-# LANGUAGE TemplateHaskell #-} 

import   Control.Lens (makeLenses, (%~)) 

data FArg a1 a2 a3 a4 = FArg 
    { _a1 :: a1 
    , _a2 :: a2 
    , _a3 :: a3 
    , _a4 :: a4 
    } 

makeLenses ''FArg 

f4change3 :: 
    (FArg a1 a2 a3 a4 -> r) -> 
    (s -> a3) -> 
    FArg a1 a2 s a4 -> r 
f4change3 f g = f . (a3 %~ g) 

Peut-être une solution sophistiquée en utilisant des lentilles est acceptable, mais ici, nous au moins atteindre compréhensibilité (si vous familiariser avec les objectifs de cours). Mais habituellement, personne ne le fait.