Question

Je l'ai lu avec un langage typé statiquement comme Scala ou Haskell il n'y a aucun moyen de créer ou de fournir une fonction apply Lisp:

(apply #'+ (list 1 2 3)) => 6

ou peut-être

(apply #'list '(list :foo 1 2 "bar")) => (:FOO 1 2 "bar")
(apply #'nth (list 1 '(1 2 3))) => 2

Est-ce une vérité?

Était-ce utile?

La solution

est difficile POSTULER complète dans une langue statique.

Dans Lisp POSTULER applique une fonction à une liste d'arguments. La fonction et la liste des arguments sont des arguments à appliquer.

  • Appliquer peut utiliser toutes les fonctions. Cela signifie que cela pourrait être tout type de résultat et tout type d'argument.

  • APPLIQUER prend des arguments arbitraires de longueur arbitraire (en Common Lisp la longueur est limitée par une valeur constante spécifique de mise en oeuvre) avec des types arbitraires et éventuellement différentes.

  • APPLIQUER rendements tout type de valeur retournée par la fonction qu'il a comme argument.

Comment peut-on vérifier de type sans subvertir un système de type statique?

Exemples:

(apply #'+ '(1 1.4))   ; the result is a float.

(apply #'open (list "/tmp/foo" :direction :input))
; the result is an I/O stream

(apply #'open (list name :direction direction))
; the result is also an I/O stream

(apply some-function some-arguments)
; the result is whatever the function bound to some-function returns

(apply (read) (read))
; neither the actual function nor the arguments are known before runtime.
; READ can return anything

Exemple d'interaction:

CL-USER 49 > (apply (READ) (READ))                        ; call APPLY
open                                                      ; enter the symbol OPEN
("/tmp/foo" :direction :input :if-does-not-exist :create) ; enter a list
#<STREAM::LATIN-1-FILE-STREAM /tmp/foo>                   ; the result

Voici maintenant un exemple avec la fonction SUPPRIMER. Nous allons démonter le caractère d'une liste des choses différentes.

CL-USER 50 > (apply (READ) (READ))
remove
(#\a (1 "a" #\a 12.3 :foo))
(1 "a" 12.3 :FOO)

Notez que vous pouvez également appliquer lui-même appliquer, car appliquer est fonction.

CL-USER 56 > (apply #'apply '(+ (1 2 3)))
6

Il y a aussi une légère complication parce que la fonction prend un POSTULER nombre arbitraire d'arguments, où seuls les derniers besoins des arguments à une liste:

CL-USER 57 > (apply #'open
                    "/tmp/foo1"
                    :direction
                    :input
                    '(:if-does-not-exist :create))
#<STREAM::LATIN-1-FILE-STREAM /tmp/foo1>

Comment traiter cela?

  • assouplir les règles de contrôle de type statique

  • Limiter APPLIQUER

L'un ou les deux ci-dessus devra être fait dans un type statique typique vérifié langage de programmation. Ni vous donnera un entièrement statiquement vérifié et entièrement flexible Appliquer.

Autres conseils

Il est parfaitement possible dans un langage typé statiquement. L'ensemble thingy java.lang.reflect est de faire cela. Bien sûr, en utilisant la réflexion vous donne la sécurité comme beaucoup de type que vous avez avec Lisp. D'autre part, alors que je ne sais pas s'il y a des langues statiquement typé supportant cette fonctionnalité, il me semble que ce pourrait être fait.

Permettez-moi de montrer comment je figure Scala pourrait être étendu pour le soutenir. Tout d'abord, nous allons voir un exemple plus simple:

def apply[T, R](f: (T*) => R)(args: T*) = f(args: _*)

Ceci est vrai le code Scala, et cela fonctionne, mais il ne fonctionnera pas pour toute fonction qui reçoit les types arbitraires. D'une part, la notation T* renverra un Seq[T], qui est une séquence homegenously typée. Cependant, il y a des séquences typées, hétérogènes tels que le HList .

Alors, d'abord, nous allons essayer d'utiliser HList ici:

def apply[T <: HList, R](f: (T) => R)(args: T) = f(args)

C'est Scala travaille toujours, mais nous avons mis une grande restriction f en disant qu'il doit recevoir un HList, au lieu d'un nombre arbitraire de paramètres. Disons que nous utilisons @ pour effectuer la conversion de paramètres hétérogènes à HList, de la même manière * convertis de paramètres homogènes à Seq:

def apply[T, R](f: (T@) => R)(args: T@) = f(args: _@)

Nous ne parlons pas de la vie réelle Scala plus, mais une amélioration hypothétique à elle. Cela ressemble à moi raisonnablement, sauf que T est censé être un type par la notation de paramètre de type. Nous pourrions peut-être étendre tout de la même façon:

def apply[T@, R](f: (T@) => R)(args: T@) = f(args: _@)

Pour moi, il semble que cela pourrait fonctionner, bien que peut-être la naïveté de ma part.

Considérons une solution de rechange, une fonction de l'unification des listes de paramètres et tuples. Disons que la liste des paramètres a finalement unifié Scala et tuples, et que toutes les lignes étaient sous-classe à une Tuple de classe abstraite. Ensuite, nous pourrions écrire ceci:

def apply[T <: Tuple, R](f: (T) => R)(args: T) = f(args)

. Faire une Tuple classe abstraite serait triviale, et la liste tuple / paramètre unification est pas une idée farfelue.

La raison pour laquelle vous ne pouvez pas le faire dans la plupart des langues est statiquement typé qu'ils choisissent presque tous d'avoir un type de liste qui est limitée aux listes uniformes. dactylographié Racket est un exemple pour une langue qui peut parler de listes qui ne sont pas uniformément typé (par exemple, il a une Listof pour les listes uniformes et List pour une liste d'une longueur statiquement connue qui peut être non uniforme) - mais elle attribue un type limité (avec des listes uniformes) pour le apply de Racket, puisque le réel type est extrêmement difficile à encoder.

Il est trivial à Scala:

Welcome to Scala version 2.8.0.final ...

scala> val li1 = List(1, 2, 3)
li1: List[Int] = List(1, 2, 3)

scala> li1.reduceLeft(_ + _)
res1: Int = 6

OK, sans type:

scala> def m1(args: Any*): Any = args.length
m1: (args: Any*)Any

scala> val f1 = m1 _
f1: (Any*) => Any = <function1>

scala> def apply(f: (Any*) => Any, args: Any*) = f(args: _*)
apply: (f: (Any*) => Any,args: Any*)Any

scala> apply(f1, "we", "don't", "need", "no", "stinkin'", "types")
res0: Any = 6

Peut-être que je mélange jusqu'à funcall et apply, donc:

scala> def funcall(f: (Any*) => Any, args: Any*) = f(args: _*)
funcall: (f: (Any*) => Any,args: Any*)Any

scala> def apply(f: (Any*) => Any, args: List[Any]) = f(args: _*)
apply: (f: (Any*) => Any,args: List[Any])Any

scala> apply(f1, List("we", "don't", "need", "no", "stinkin'", "types"))
res0: Any = 6

scala> funcall(f1, "we", "don't", "need", "no", "stinkin'", "types")
res1: Any = 6

Il est possible de apply d'écriture dans un statiquement typé, aussi longtemps que les fonctions sont tapés d'une manière particulière. Dans la plupart des langues, les fonctions ont pris fin paramètres individuels, soit par un rejet (à savoir pas d'invocation variadique), ou un dactylographiée acceptent (à savoir l'invocation variadique possible, mais seulement lorsque tous les autres paramètres sont de type T). Voici comment vous pouvez modéliser cette Scala:

trait TypeList[T]
case object Reject extends TypeList[Reject]
case class Accept[T](xs: List[T]) extends TypeList[Accept[T]]
case class Cons[T, U](head: T, tail: U) extends TypeList[Cons[T, U]]

Notez que cela ne l'applique pas bien formedness (bien que des bornes de type existent-ils pour cela, je crois), mais vous voyez l'idée. Ensuite, vous avez apply défini comme ceci:

apply[T, U]: (TypeList[T], (T => U)) => U

Vos fonctions, puis, sont définies en termes de la liste des choses du type:

def f (x: Int, y: Int): Int = x + y

devient:

def f (t: TypeList[Cons[Int, Cons[Int, Reject]]]): Int = t.head + t.tail.head

et les fonctions variadique comme ceci:

def sum (xs: Int*): Int = xs.foldLeft(0)(_ + _)

devenir ceci:

def sum (t: TypeList[Accept[Int]]): Int = t.xs.foldLeft(0)(_ + _)

Le seul problème avec tout cela est que Scala (et dans la plupart des autres langages statiques), les types ne sont pas assez de première classe pour définir les isomorphismes entre toute structure contre-style et un tuple de longueur fixe. Parce que la plupart des langages statiques ne représentent pas des fonctions en termes de types récursifs, vous n'avez pas la possibilité de faire des choses comme celle-ci de manière transparente. (Macros changerait cela, bien sûr, ainsi que d'encourager une représentation raisonnable des types de fonction en premier lieu. Cependant, l'utilisation apply un impact négatif sur la performance pour des raisons évidentes).

Dans Haskell, il n'y a pas de type pour les listes multi-types, bien que je crois, que vous pouvez pirater quelque chose comme ça ensemble cinque le mystérieux Typeable de classe de types. Comme je le vois, vous êtes à la recherche d'une fonction qui prend une fonction, un qui contient exactement la même quantité de valeurs selon les besoins de la fonction et renvoie le résultat.

Pour moi, cela semble très familier à Haskells uncurryfunction, juste qu'il faut un tuple au lieu d'une liste. La différence est qu'un tuple a toujours le même nombre d'éléments (donc (1,2) et (1,2,3) sont de différents types (!)) Et son contenu peut-il être dactylographiée arbitraire.

La fonction uncurry a cette définition:

uncurry :: (a -> b -> c) -> (a,b) -> c
uncurry f (a,b) = f a b

Qu'est-ce que vous avez besoin est une sorte de uncurry qui est surchargée de manière à fournir un nombre arbitraire de params. Je pense à quelque chose comme ceci:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}

class MyApply f t r where
  myApply :: f -> t -> r

instance MyApply (a -> b -> c) (a,b) c where
  myApply f (a,b) = f a b

instance MyApply (a -> b -> c -> d) (a,b,c) d where
  myApply f (a,b,c) = f a b c

-- and so on

Mais cela ne fonctionne que si tous les types impliqués sont connus pour le compilateur. Malheureusement, l'ajout d'un fundep provoque le compilateur à la compilation des déchets. Comme je ne suis pas un gourou haskell, domeone peut-être autre sait, howto résoudre ce problème. Malheureusement, je ne sais pas comment archieve faciliter la tâche.

Resümee: apply est pas très facile à Haskell, bien que possible. Je suppose que, vous ne serez jamais besoin.

Modifier J'ai maintenant une meilleure idée, donnez-moi dix minutes et je vous présenter quelque chose whithout ces problèmes.

essayer plis. ils sont probablement similaires à ce que vous voulez. il suffit d'écrire un cas particulier de celui-ci.

haskell: foldr1 (+) [0..3] => 6

incidemment, foldr1 est fonctionnellement équivalent à foldr avec l'accumulateur initialisé comme l'élément de la liste.

il y a toutes sortes de plis. ils font tous la même chose sur le plan technique, mais de différentes manières, et pourraient faire leurs arguments dans des ordres différents. foldr est juste l'un des plus simples.

cette page , je lis que « Appliquer est tout comme funcall, sauf que son dernier argument doit être une liste;. les éléments de cette liste sont traitées comme si elles étaient des arguments supplémentaires à un funcall »

Dans Scala, les fonctions peuvent avoir varargs (arguments variadique), comme les versions les plus récentes de Java. Vous pouvez convertir une liste (ou tout autre objet Iterable) en plus de paramètres vararg en utilisant la notation :_* Exemple:

//The asterisk after the type signifies variadic arguments
def someFunctionWithVarargs(varargs: Int*) = //blah blah blah...

val list = List(1, 2, 3, 4)
someFunctionWithVarargs(list:_*)
//equivalent to
someFunctionWithVarargs(1, 2, 3, 4)

En fait, même en Java peut le faire. Java varargs peut être transmis soit comme une séquence d'arguments ou d'un tableau. Tout ce que vous auriez à faire est de convertir votre Java List à un tableau pour faire la même chose.

L'avantage d'un langage statique est qu'il vous empêche d'appliquer une fonction aux arguments de types incorrects, donc je pense qu'il est naturel que ce serait plus difficile à faire.

Étant donné une liste d'arguments et d'une fonction, à Scala, un tuple serait mieux saisir les données, car il peut stocker des valeurs de différents types. Dans cet esprit tupled a une certaine ressemblance avec apply:

scala> val args = (1, "a")
args: (Int, java.lang.String) = (1,a)

scala> val f = (i:Int, s:String) => s + i
f: (Int, String) => java.lang.String = <function2>

scala> f.tupled(args)
res0: java.lang.String = a1

Pour la fonction d'un argument, il est en fait apply:

scala> val g = (i:Int) => i + 1
g: (Int) => Int = <function1>

scala> g.apply(2)
res11: Int = 3

Je pense que si vous pensez que d'appliquer le mécanisme pour appliquer une première fonction de la classe à ses arguments, le concept est là dans Scala. Mais je pense que apply en Lisp est plus puissant.

Pour Haskell, de le faire de façon dynamique, voir Data.Dynamic et dynApp en particulier: http://www.haskell.org/ghc/docs/6.12.1/html/libraries/base/Data-Dynamic.html

Voir son truc dynamique pour haskell, en C, les pointeurs de fonction vides peuvent être casted à d'autres types, mais vous devez spécifier le type de jeter à. (Je pense, n'ont pas fait des pointeurs de fonction en temps)

Une liste Haskell ne peut stocker des valeurs d'un type, de sorte que vous ne pouviez pas faire des choses drôles comme (apply substring ["Foo",2,3]). Ni ne Haskell ont des fonctions variadique, si (+) ne peut jamais prendre deux arguments.

Il y a une fonction $ en Haskell:

($)                     :: (a -> b) -> a -> b
f $ x                   =  f x

Mais ce qui est vraiment utile parce qu'il a priorité très faible, ou passant autour de entreprises avec siège social.

Je suppose que vous pourriez être en mesure de faire quelque chose comme ceci en utilisant des types de tuple et fundeps bien?

class Apply f tt vt | f -> tt, f -> vt where
  apply :: f -> tt -> vt

instance Apply (a -> r) a r where
  apply f t = f t

instance Apply (a1 -> a2 -> r) (a1,a2) r where
  apply f (t1,t2) = f t1 t2

instance Apply (a1 -> a2 -> a3 -> r) (a1,a2,a3) r where
  apply f (t1,t2,t3) = f t1 t2 t3

Je suppose que c'est une sorte de « uncurryN », est-il pas?

Modifier : cela ne compile pas réellement; remplacé par @ réponse de FUZxxl.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top