J'apprécie la réponse de Sjoerd Visscher, mais les extensions -- en particulier IncoherentInstances
Le système d'alerte, utilisé dans ce cas pour rendre possible une application partielle, peut être un peu décourageant. Voici une solution qui ne nécessite aucune extension.
Tout d'abord, nous définissons un type de données pour les fonctions qui savent ce qu'il faut faire avec un nombre quelconque d'arguments. Vous devriez lire a
comme étant le "type d'argument", et b
comme étant le "type de retour".
data ListF a b = Cons b (ListF a (a -> b))
Nous pouvons ensuite écrire des fonctions (Haskell) qui fusionnent ces fonctions (variadiques). J'utilise la fonction F
pour toutes les fonctions qui se trouvent dans le prélude.
headF :: ListF a b -> b
headF (Cons b _) = b
mapF :: (b -> c) -> ListF a b -> ListF a c
mapF f (Cons v fs) = Cons (f v) (mapF (f.) fs)
partialApply :: ListF a b -> [a] -> ListF a b
partialApply fs [] = fs
partialApply (Cons f fs) (x:xs) = partialApply (mapF ($x) fs) xs
apply :: ListF a b -> [a] -> b
apply f xs = headF (partialApply f xs)
Par exemple, le sum
peut être considérée comme une fonction variadique :
sumF :: Num a => ListF a a
sumF = Cons 0 (mapF (+) sumF)
sumExample = apply sumF [3, 4, 5]
Toutefois, nous voulons également pouvoir traiter les fonctions normales, qui ne savent pas nécessairement quoi faire avec un nombre quelconque d'arguments. Alors, que faire ? Eh bien, comme en Lisp, nous pouvons lancer une exception au moment de l'exécution. Ci-dessous, nous utiliserons f
comme exemple simple de fonction non variable.
f True True True = 32
f True True False = 67
f _ _ _ = 9
tooMany = error "too many arguments"
tooFew = error "too few arguments"
lift0 v = Cons v tooMany
lift1 f = Cons tooFew (lift0 f)
lift2 f = Cons tooFew (lift1 f)
lift3 f = Cons tooFew (lift2 f)
fF1 = lift3 f
fExample1 = apply fF1 [True, True, True]
fExample2 = apply fF1 [True, False]
fExample3 = apply (partialApply fF1 [True, False]) [False]
Bien entendu, si vous n'aimez pas la procédure de définition de lift0
, lift1
, lift2
, lift3
etc. séparément, vous devez activer certaines extensions. Mais vous pouvez aller très loin sans elles !
Voici comment vous pouvez généraliser à une seule personne. lift
fonction. Tout d'abord, nous définissons quelques nombres standard au niveau du type :
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, TypeFamilies, UndecidableInstances #-}
data Z = Z
newtype S n = S n
Nous présentons ensuite la classe de type pour le levage. Vous devez lire le type I n a b
comme " n
copies de a
comme arguments, puis un type de retour de b
".
class Lift n a b where
type I n a b :: *
lift :: n -> I n a b -> ListF a b
instance Lift Z a b where
type I Z a b = b
lift _ b = Cons b tooMany
instance (Lift n a (a -> b), I n a (a -> b) ~ (a -> I n a b)) => Lift (S n) a b where
type I (S n) a b = a -> I n a b
lift (S n) f = Cons tooFew (lift n f)
Et voici les exemples utilisant f
de la version précédente, réécrite à l'aide de l'ascenseur généralisé :
fF2 = lift (S (S (S Z))) f
fExample4 = apply fF2 [True, True, True]
fExample5 = apply fF2 [True, False]
fExample6 = apply (partialApply fF2 [True, False]) [False]
21 votes
Une façon de comprendre pourquoi il échoue est d'essayer d'écrire la signature du type pour
apply
.0 votes
Voir aussi stackoverflow.com/questions/tagged/variadic-functions+haskell
6 votes
C'est en fait mon exemple préféré d'une fonction potentiellement utile qui est incroyablement pénible à écrire dans un langage qui n'a ni types dynamiques ni types dépendants. Heureusement, ce problème ne se pose pas souvent dans la pratique, car la plupart des utilisations réelles peuvent être écrites de différentes manières.
0 votes
En rapport : Impossible de créer une fonction d'application avec un langage statique ?
2 votes
Avec
unsafeCoerce
Tout est possible... même traiter des entiers et des pointeurs et vice versa...2 votes
Je veux juste dire que c'est peut-être un bon exemple de la raison pour laquelle nous AMOUR le typage statique.
1 votes
Bien sûr, vous pouvez voir si en utilisant
f :: [a] -> b
vous convient. Dans ce cas, vous n'avez pas besoinapply
. L'appel de la version à un seul argument fonctionne comme suitf [x]
.