Frage

Ich habe mit einer statisch typisierten Sprache wie Scala oder Haskell gibt es keine Möglichkeit zu schaffen oder bietet eine Lisp apply Funktion gelesen, dass:

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

oder vielleicht

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

Ist das eine Wahrheit?

War es hilfreich?

Lösung

Eine vollständige Übernehmen ist schwierig, in einer statischen Sprache.

In Lisp GILT wendet eine Funktion auf eine Liste von Argumenten. Sowohl die Funktion und die Liste der Argumente Argumente anzuwenden.

  • anwenden kann jede Funktion verwenden. Das bedeutet, dass dies kein Ergebnis Typ sein könnte und alle Argumenttypen.

  • GILT nimmt beliebige Argumente in beliebiger Länge (in Common Lisp die Länge durch eine Implementierung spezifischen konstanten Wert beschränkt ist) mit willkürlich und möglicherweise verschiedenen Typen.

  • APPLY gibt jede Art von Wert, der von der Funktion zurückgegeben wird es als Argument bekam.

Wie würde eine Art Überprüfung, dass ohne ein statisches Typsystem zu untergraben?

Beispiele:

(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

Interaktion Beispiel:

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

Nun ein Beispiel mit der Funktion entfernen. Wir werden den Charakter einer aus einer Liste von verschiedenen Dingen entfernen.

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

Beachten Sie, dass Sie auch selbst anwenden anwenden können, gelten, da eine Funktion.

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

Es gibt auch eine kleine Komplikation, da die Funktion anzuwenden, um eine beliebige Anzahl von Argumenten entgegennimmt, bei denen nur die letzte Argument braucht eine Liste zu sein:

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

Wie damit umgehen?

  • entspannt statische Typprüfung Regeln

  • restrict GILT

Eine oder beide oberhalb haben in einem typischen statisch Typ getan werden geprüft Sprache zu programmieren. Weder gibt Ihnen eine voll statisch geprüft und voll flexibel GILT.

Andere Tipps

Es ist durchaus möglich, in einer statisch typisierte Sprache. Das ganze java.lang.reflect Dingen ist über das zu tun. Natürlich Reflexion unter Verwendung gibt Ihnen so viel Art Sicherheit, wie Sie mit Lisp haben. Auf der anderen Seite, während ich weiß nicht, ob es statisch typisierten Sprachen unterstützen solche Funktion ist, scheint es mir, es getan werden könnte.

Lassen Sie mich zeigen, wie ich meine, Scala erweitert werden könnte, sie zu unterstützen. Lassen Sie uns zunächst sehen, ein einfacheres Beispiel:

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

Dies ist ein echter Scala Code, und es funktioniert, aber es wird für jede Funktion nicht arbeiten, die beliebige Arten empfängt. Zum einen wird die Notation T* eine Seq[T] zurückgeben, die eine homegenously typisierte Sequenz. Es gibt jedoch heterogen-typisierten Sequenzen, wie die hList .

Also, lassen Sie uns zuerst versuchen zu nutzen HList hier:

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

Das funktioniert noch Scala, aber wir haben eine große Einschränkung auf f sagen, es eine HList erhalten müssen, statt einer beliebigen Anzahl von Parametern. Lassen Sie uns sagen, dass wir @ verwenden, um die Umwandlung von heterogenen Parameter HList die gleiche Art und Weise * Konvertiten aus homogenen Parameter Seq zu machen:

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

Wir sprechen hier nicht über Real-Life-Scala mehr, sondern eine hypothetische Verbesserung zu. Das sieht vernünftig zu mir, außer dass T sollte ein Typ vom Typ Parameter Notation sein. Wir könnten vielleicht nur verlängern sie die gleiche Art und Weise:

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

Für mich sieht es aus wie das funktionieren könnte, obwohl das Naivität meinerseits sein kann.

Lassen Sie uns betrachten eine alternative Lösung, eine je nach Vereinheitlichung der Parameterlisten und Tupel. Sagen wir mal Scala hatte schließlich Parameterliste vereinigt und Tupel, und dass alle Tupel waren Unterklasse einer abstrakten Klasse Tuple. Dann könnten wir schreiben:

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

Es. eine abstrakte Klasse Tuple zu machen wäre trivial, und die Tupel / Parameterliste Einigung ist nicht weit hergeholt Idee.

Der Grund, warum Sie nicht, dass in den meisten statisch typisierten Sprachen tun können, ist, dass sie fast alle wählen einen Listentyp haben, die auf einheitliche Listen beschränkt ist. typisierten Racket ist ein Beispiel für eine Sprache, die über Listen sprechen können, die nicht einheitlich sind typisierte (zB hat es einen Listof für einheitliche Listen und List für eine Liste mit einer statisch bekannten Länge, die ungleichmäßig sein kann) - aber immer noch weist es eine begrenzte Art (mit den Einheitslisten) für Schläger des apply, da die realen Typ ist äußerst schwierig zu kodieren.

Es ist trivial in 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, typenlos:

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

Vielleicht ich gemischte oben funcall und apply, so:

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

Es ist möglich, Schreib apply in einer statisch typisierten Sprache, solange Funktionen eine bestimmte Art und Weise eingegeben werden. In den meisten Sprachen haben Funktionen einzelnen Parameter entweder durch eine Ablehnung beendet (das heißt ohne variadische Aufruf) oder eine typisierte annehmen (das heißt variadische Aufruf möglich, aber nur dann, wenn alle weiteren Parameter des Typs T sind). Hier ist, wie Sie könnte dieses Modell in 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]]

Beachten Sie, dass dies nicht Wohlgeformtheits- erzwingt (obwohl Typ Schranken für die exist tun, glaube ich), aber Sie bekommen die Idee. Dann haben Sie apply wie folgt definiert:

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

Ihre Funktionen, dann in Bezug auf die Typenliste Dinge definiert:

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

wird:

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

Und variadische Funktionen wie folgt:

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

Diese werden:

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

Das einzige Problem bei all der ist, dass in Scala (und in den meisten anderen statischen Sprachen), Typen sind nicht erste Klasse genug, um die Isomorphismen zwischen jeder cons-Stil Struktur und einem festen Länge Tupel zu definieren. Da die meisten statischen Sprachen keine Funktionen in Bezug auf die rekursive Typen repräsentieren, haben Sie nicht die Flexibilität, Dinge wie diese transparent zu machen. (Makros würde dies ändern, natürlich, sowie eine angemessene Darstellung von Funktionstypen in erster Linie ermutigend. Allerdings apply wirkt sich negativ auf die Leistung aus offensichtlichen Gründen verwenden.)

In Haskell, gibt es keinen Datentyp für Multi-Typen-Listen, obwohl ich glaube, dass Sie so etwas wie dieser zusammen WHITH der mysteriöse Typeable typeclass hacken. Wie ich sehe, sind Sie für eine Funktion suchen, die eine Funktion übernimmt, eine, die genau wie nötig, um die gleiche Menge an Werten enthält, die durch die Funktion und gibt das Ergebnis zurück.

Für mich, das sieht sehr vertraut Haskells uncurryfunction, nur, dass es dauert ein Tupel statt einer Liste. Der Unterschied ist, dass ein Tupel immer die gleiche Anzahl der Elemente hat (so (1,2) und (1,2,3) sind von verschiedenen Arten (!)), Und es Inhalte können beliebig eingegeben werden.

Die uncurry Funktion hat diese Definition:

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

Was Sie brauchen, ist eine Art von uncurry, die in einer Art und Weise überlastet ist, eine beliebige Anzahl von params bereitzustellen. Ich denke an so etwas wie folgt aus:

{-# 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

Das funktioniert aber nur, wenn alle Beteiligten Typen sind dem Compiler bekannt. Leider ein fundep Zugabe veranlasst den Compiler zu Müll Kompilierung. Da ich kein Haskell Guru bin, domeone vielleicht sonst weiß, Howto dieses Problem zu beheben. Leider weiß ich nicht, wie man diese leichter archieve.

Resümee: apply ist nicht sehr einfach in Haskell, obwohl möglich. Ich denke, Sie werden es nie brauchen.

Bearbeiten Ich habe eine bessere Idee jetzt, gib mir 10 Minuten und ich Ihnen etwas whithout diese Probleme.

versuchen Falten. sie sind wahrscheinlich ähnlich dem, was Sie wollen. schreibt nur einen Sonderfall davon.

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

Übrigens ist foldr1 funktionell äquivalent zu foldr mit dem Speicher als das Element der Liste initialisiert.

gibt es alle Arten von Falten. sie alle tun technisch die gleiche Sache, wenn auch auf unterschiedliche Weise, und könnten ihre Argumente in unterschiedlicher Reihenfolge tun. foldr ist nur eine der einfacheren.

dieser Seite , ich lese, dass „Übernehmen ist wie FUNCALL, mit der Ausnahme, dass sein letztes Argument eine Liste sein soll,. die Elemente dieser Liste behandelt werden, als ob sie zusätzliche Argumente für eine FUNCALL waren“

In Scala können Funktionen haben varargs (variadische Argumente), wie die neueren Versionen von Java. Sie können eine Liste (oder eine beliebige Iterable Objekt) umwandeln in mehr Vararg Parameter die Schreibweise :_* Beispiel für die Verwendung:

//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)

In der Tat, auch Java kann dies tun. Java varargs kann entweder als eine Folge von Argumenten oder als Array übergeben werden. Alles was Sie konvertieren Sie Ihre dasselbe Java List auf ein Array haben würde tun zu tun.

Der Vorteil einer statischen Sprache ist, dass es verhindern würde, eine Funktion auf die Argumente von falschen Typen anzuwenden, so dass ich denke, es ist natürlich, dass es schwieriger sein würde, zu tun.

eine Liste von Argumenten und einer Funktion, in Scala gegeben, ein Tupel wäre am besten fängt die Daten, da sie speichern können Werte verschiedenen Typen. Mit dem im Verstand tupled hat eine gewisse Ähnlichkeit mit 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

Für Funktion ein Argument gibt es eigentlich apply:

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

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

Ich denke, wenn man so gelten als Mechanismus denke, ein First-Class-Funktion auf ihre Argumente anwenden, dann ist das Konzept in Scala gibt. Aber ich vermute, dass apply in Lisp mächtiger ist.

Für Haskell, es dynamisch zu tun, siehe Data.Dynamic und dynApp insbesondere: http://www.haskell.org/ghc/docs/6.12.1/html/libraries/base/Data-Dynamic.html

Sehen Sie seine dynamische Sache für Haskell, in C, Leere Funktionszeiger können auf andere Typen gegossen werden, aber Sie würden den Typ angeben, müssen sie zu werfen. (Ich glaube, nicht Funktionszeiger in einer Weile getan)

Eine Liste in Haskell können nur Werte eines Typs, so dass Sie nicht lustige Sachen wie (apply substring ["Foo",2,3]) tun könnte. Weder hat Haskell variadische Funktionen, so (+) kann immer nur zwei Argumente nehmen.

Es ist eine $ Funktion in Haskell:

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

Aber das ist nur dann wirklich nützlich, da es sehr niedrige Priorität hat, oder als Höfs, das um.

Ich stelle mir Sie so etwas wie dies vielleicht in der Lage zu tun, mit Tupel-Typen und fundeps obwohl?

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

Ich denke, das ist eine Art ‚uncurryN‘, ist es nicht?

Bearbeiten : Das ist eigentlich nicht kompilieren; ersetzt durch @ FUZxxl Antwort.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top