2009-07-16 12 views
5

Ich habe etwas Scala-Code, der mit zwei verschiedenen Versionen einer typenparametrisierten Funktion etwas raffiniert macht. Ich habe dieses von meiner Anwendung sehr vereinfacht, aber am Ende ist mein Code voll von Anrufen des Formulars w(f[Int],f[Double]), wo w() meine magische Methode ist. Ich würde gerne eine magische Methode wie z(f) = w(f[Int],f[Double]) haben - aber ich kann keine Syntax wie z(f[Z]:Z->Z) zu arbeiten, wie es aussieht (für mich) wie Funktionsargumente kann nicht ihre eigenen Typparameter haben. Hier ist das Problem als Scala-Code-Snippet.Kann Scala freie Typparameter in Argumenten erlauben (sind Scala-Typparameter Bürger erster Klasse?)?

Irgendwelche Ideen? Ein Makro könnte das machen, aber ich glaube nicht, dass das ein Teil von Scala ist.

object TypeExample { 
    def main(args: Array[String]):Unit = { 
    def f[X](x:X):X = x    // parameterize fn 
    def v(f:Int=>Int):Unit = { }  // function that operates on an Int to Int function 
    v(f)        // applied, types correct 
    v(f[Int])      // appplied, types correct 
    def w[Z](f:Z=>Z,g:Double=>Double):Unit = {} // function that operates on two functions 
    w(f[Int],f[Double])    // works 
// want something like this: def z[Z](f[Z]:Z=>Z) = w(f[Int],f[Double]) 
// a type parameterized function that takes a single type-parameterized function as an 
// argument and then speicalizes the the argument-function to two different types, 
// i.e. a single-argument version of w() (or wrapper) 
    } 
} 
+0

Sie möchten, dass das Argument von z parametrisiert wird? –

+0

Ja, ich möchte, dass das Argument von z() eine parametrisierte Funktion ist (wie mein f) und dann z() den Typparameter an zwei verschiedene Werte bindet. – jmount

+0

Was würde total rocken wäre eine Funktion, die eine parametrisierte Funktion f in nehmen und eine Klasse mit einem Mitglied auf f [Int] und die andere auf f [Double] zurückgeben würde. – jmount

Antwort

6

Sie können es wie folgt tun:

trait Forall { 
    def f[Z] : Z=>Z 
} 

def z(u : Forall) = w(u.f[Int], u.f[Double]) 

oder mit Strukturtypen:

def z(u : {def f[Z] : Z=>Z}) = w(u.f[Int], u.f[Double]) 

Aber dies wird langsamer sein als die erste Version, da es Reflexion verwendet.

EDIT: Dies ist, wie Sie die zweite Version verwenden:

scala> object f1 {def f[Z] : Z=>Z = x => x} 
defined module f1 

scala> def z(u : {def f[Z] : Z=>Z}) = (u.f[Int](0), u.f[Double](0.0)) 
z: (AnyRef{def f[Z]: (Z) => Z})(Int, Double) 

scala> z(f1) 
res0: (Int, Double) = (0,0.0) 

Für die erste Version hinzufügen f1 extends Forall oder einfach

scala> z(new Forall{def f[Z] : Z=>Z = x => x}) 
+0

Dies scheint sinnvoll (mit der Objektdarstellung von Funktionen), aber ich weiß (noch) nicht genug über Scala, um es funktionieren zu lassen. In jedem Fall bekomme ich beim Eintippen von 'z (f)' die Kompilierfehler: fehlendes Argument für Methode f; polymorpher Ausdruck kann nicht mit dem erwarteten Typ instanziiert werden; – jmount

+0

Ich habe ein Anwendungsbeispiel hinzugefügt. –

+0

Das ist großartig! Offensichtlich hat das, was du gesagt hast, einen Sinn ergeben, ich wusste einfach nicht genug, um es anzuwenden. Die Linie, die ich wollte, ist def z (u: {def f [Z]: Z => Z}) = (u.f [Int], u.f [Double]), was perfekt funktioniert. Das philosophische Jucken, das ich befriedigen wollte, bestand darin, 'f' nur einmal zu schreiben (das garantiert, dass beide Spezialisierungen die gleiche Funktion haben), und einige zusätzliche Deklarationen hinzuzufügen, um das zu erreichen, ist überhaupt kein Problem. Vielen Dank. – jmount

2

Ich glaube nicht, was Sie tun möchten, ist möglich.

Edit:

Meine frühere Version fehlerhaft war. Das funktioniert:

scala> def z(f: Int => Int, g: Double => Double) = w(f, g) 
z: (f: (Int) => Int,g: (Double) => Double)Unit 

scala> z(f, f) 

Aber natürlich ist es ziemlich genau das, was Sie haben.

Ich glaube nicht, dass es überhaupt funktioniert, weil Typ-Parameter nur zur Kompilierzeit existieren. Zur Laufzeit gibt es so etwas nicht. Es macht also auch keinen Sinn, eine parametrisierte Funktion zu übergeben, statt einer Funktion mit den von Scala abgeleiteten Typparametern.

Und, nein, Scala hat kein Makrosystem.

+0

Ich mag den Teil, wo Scala die Typen leitet (das ist nett). Danke, dass Sie den Lösungsraum erkundet haben. – jmount

6

Wenn Sie neugierig sind, was Sie hier besprochen heißt "Rang-k-Polymorphismus". See wikipedia. In Ihrem Fall k = 2. Einige übersetzen:

Wenn Sie

f[X](x : X) : X = ... 
schreiben

dann sind Sie sagen, dass f hat Typ "forall XX -> X"

Was Sie für z wollen ist Typ "(für ZZ -> Z) -> Einheit". Dieses zusätzliche Klammerpaar ist ein großer Unterschied. In Bezug auf den Wikipedia-Artikel setzt er den Forall-Qualifier vor 2 Pfeilen anstatt nur 1. Die Typvariable kann nicht nur einmal instanziert und ausgeführt werden, sie muss möglicherweise für viele verschiedene Typen instanziiert werden. (Hier bedeutet "Instanziierung" nicht die Objektkonstruktion, sondern das Zuweisen eines Typs zu einer Typvariablen für die Typprüfung).

Wie die Antwort von alexy_r zeigt, ist dies in Scala mit Objekten statt mit geraden Funktionstypen codierbar, die im Wesentlichen Klassen/Merkmale als Parens verwenden. Obwohl er Sie ein wenig in Bezug auf die es in Ihrem ursprünglichen Code Einstecken hängen gelassen zu haben scheint, so ist es hier:

// dies ist der Code

object TypeExample { 
    def main(args: Array[String]):Unit = { 
    def f[X](x:X):X = x    // parameterize fn 
    def v(f:Int=>Int):Unit = { }  // function that operates on an Int to Int function 
    v(f)        // applied, types correct 
    v(f[Int])      // appplied, types correct 
    def w[Z](f:Z=>Z,g:Double=>Double):Unit = {} // function that operates on two functions 
    w(f[Int],f[Double])    // works 

// Das ist neuer Code

trait ForAll { 
     def g[X](x : X) : X 
    } 

    def z(forall : ForAll) = w(forall.g[Int], forall.g[Double]) 
    z(new ForAll{def g[X](x : X) = f(x)}) 
    } 
} 
+0

Danke, James, ich werde das untersuchen. Ich habe definitiv keine Beschwerden über das Scala-System - ich bin bereit, damit zu arbeiten, um zu bekommen, was ich will. Und ich liebe, dass der Compiler mir sofort sagt, was nicht funktioniert. Meine Absicht ist, wie Sie sagten - das ursprüngliche (nicht vereinfachte) Problem ist eine Funktion, die über verschiedene algebraische Felder berechnet (einige teuer zu arbeiten, einige billig zu arbeiten) und ich wollte, dass Scala zwei spezialisierte typsichere Funktionen aus einem Template erzeugt Anfangsfunktion. Alle anderen beweglichen Teile waren leicht zu arrangieren - außer dieser letzten Transformation. – jmount

+0

'neue ForAll' Syntax ist schöner als das, was ich hatte. Ich habe vergessen, dass es funktioniert. –

+0

Wikipedia-Link: http://en.wikipedia.org/wiki/Parametric_polymorphism#Higher-Ranked_Polymorphism –

Verwandte Themen