Sauf si vous voulez aller de piratage autour avec typeclasses, ce qui est préférable pour des expériences de pensée et de preuve de concept, vous venez de ne pas généraliser à plusieurs arguments. N'essayez pas.
Quant à votre question principale, c'est le plus élégamment résolu avec Conal Elliott sémantique de l'éditeur combinators. D'une sémantique de l'éditeur du combinator est une fonction avec un type comme:
(a -> b) -> F(a) -> F(b)
Où F(x) est une expression faisant intervenir x. Il y a aussi "contravariant" éditeur combinators qui prennent un (b -> a)
à la place. Intuitivement, un éditeur de combinateur sélectionne une partie de certains des plus grands de la valeur à opérer. Celui dont vous avez besoin est appelée result
:
result = (.)
Regardez le type de l'expression que vous essayez de faire fonctionner sur:
a -> a -> Bool
Le résultat (arrivée) de ce type est a -> Bool, et le résultat qui est de type Bool, et c'est ce que vous essayez d'appliquer not
de. Alors appliquez not
à la suite du résultat d'une fonction f
, vous écrivez:
(result.result) not f
Cette belle généralise. Voici un peu plus de combinators:
argument = flip (.) -- contravariant
first f (a,b) = (f a, b)
second f (a,b) = (a, f b)
left f (Left x) = Left (f x)
left f (Right x) = Right x
...
Donc, si vous avez une valeur x
de type:
Int -> Either (String -> (Int, Bool)) [Int]
Et vous voulez appliquer l' not
de la valeur Bool, vous venez de préciser le chemin d'accès pour vous y rendre:
(result.left.result.second) not x
Oh, et si vous avez obtenu de Foncteurs encore, vous remarquerez qu' fmap
est un éditeur combinator. En fait, le ci-dessus peut être orthographié:
(fmap.left.fmap.fmap) not x
Mais je pense que c'est plus clair d'utiliser les noms étendues.
Profitez de.