Java Stream API mit echten Funktionstypen #298

Closed
opened 2024-03-18 21:22:44 +00:00 by i21023 · 10 comments
Collaborator

Ich habe das Gefühl, dass die echten Funktionstypen für Lambda Ausdrücke Probleme mit Java Libraries wie der Stream API haben.

Beispiel:

import java.util.List;
import java.util.ArrayList;
import java.lang.Integer;
import java.lang.System;
import java.io.PrintStream;
import java.util.stream.Stream;
import java.util.function.Function;

public class Foo{
    void foo(){
        List<Integer> list = new ArrayList<>();
        var a = list.stream();
        a.map(x -> 2*x);
    }
}

Der JavaTX Compiler müsste ja einen ensprechenden FunN$$-Typen für die Lambda Expression x -> 2*x anlegen. Die map Funktion der Stream API verlangt aber als Eingabe ein funktionales Interface vom Typen java.util.function.Funtion und nicht den FunN Typen. Zumindest scheitert der Code mit einer großen Anzahl an unaufgelösten Constraints, ich weiß nicht, ob es wirklich daran liegt.

Zum Beispiel kompiliert folgender Code mit javac ja auch nicht, weil MyFuncInterface keine java.util.functions.Function ist.

import java.util.List;
import java.util.ArrayList;
import java.lang.Integer;
import java.util.stream.Stream;
import java.util.function.Function;

public class Foo{
    void foo(){
        List<Integer> list = new ArrayList<>();
        MyFuncInterface<Integer, Integer> timestwo = x -> 2*x;
        var a = list.stream();
        a.map(timestwo);
    }
}

interface MyFuncInterface<T, R>{
    R apply(T p);
}

Wenn ich allerdings die Lambda Expression explizit in ein java.util.functions.Function Typen speichere, kompliliert der Code, dann ist aber gewöhnlicher Java Code mit einem gewissen Mehraufwand und keinen echten Funktionstypen.

import java.util.List;
import java.util.ArrayList;
import java.lang.Integer;
import java.util.stream.Stream;
import java.util.function.Function;

public class Foo{
    void foo(){
        List<Integer> list = new ArrayList<>();
        var a = list.stream();
        Function<Integer, Integer> timestwo = x -> 2*x;
        a.map(timestwo);
    }
}
Ich habe das Gefühl, dass die echten Funktionstypen für Lambda Ausdrücke Probleme mit Java Libraries wie der Stream API haben. **Beispiel:** ```java import java.util.List; import java.util.ArrayList; import java.lang.Integer; import java.lang.System; import java.io.PrintStream; import java.util.stream.Stream; import java.util.function.Function; public class Foo{ void foo(){ List<Integer> list = new ArrayList<>(); var a = list.stream(); a.map(x -> 2*x); } } ``` Der JavaTX Compiler müsste ja einen ensprechenden FunN$$-Typen für die Lambda Expression `x -> 2*x` anlegen. Die map Funktion der Stream API verlangt aber als Eingabe ein funktionales Interface vom Typen `java.util.function.Funtion` und nicht den FunN Typen. Zumindest scheitert der Code mit einer großen Anzahl an unaufgelösten Constraints, ich weiß nicht, ob es wirklich daran liegt. Zum Beispiel kompiliert folgender Code mit javac ja auch nicht, weil `MyFuncInterface` keine `java.util.functions.Function` ist. ```java import java.util.List; import java.util.ArrayList; import java.lang.Integer; import java.util.stream.Stream; import java.util.function.Function; public class Foo{ void foo(){ List<Integer> list = new ArrayList<>(); MyFuncInterface<Integer, Integer> timestwo = x -> 2*x; var a = list.stream(); a.map(timestwo); } } interface MyFuncInterface<T, R>{ R apply(T p); } ``` Wenn ich allerdings die Lambda Expression explizit in ein `java.util.functions.Function` Typen speichere, kompliliert der Code, dann ist aber gewöhnlicher Java Code mit einem gewissen Mehraufwand und keinen echten Funktionstypen. ```java import java.util.List; import java.util.ArrayList; import java.lang.Integer; import java.util.stream.Stream; import java.util.function.Function; public class Foo{ void foo(){ List<Integer> list = new ArrayList<>(); var a = list.stream(); Function<Integer, Integer> timestwo = x -> 2*x; a.map(timestwo); } } ```
Owner

Hier muss die Integration von Functional Interfaces und Function types noch implementiert werden (vgl. Kap 6 im angehängten Paper) implementiert werden. Hinweis: FunN* im Paper ist heute FunN$$.

Hier muss die Integration von Functional Interfaces und Function types noch implementiert werden (vgl. Kap 6 im angehängten Paper) implementiert werden. Hinweis: FunN* im Paper ist heute FunN$$.
Owner

Der Umweg über

List<Integer> list = new ArrayList<>();
var a = list.stream();
Function<Integer, Integer> fun = x -> 2 * x;
a.map(fun);

funktioniert, es liegt also an den Constraints irgendwo.

Der Umweg über ```java List<Integer> list = new ArrayList<>(); var a = list.stream(); Function<Integer, Integer> fun = x -> 2 * x; a.map(fun); ``` funktioniert, es liegt also an den Constraints irgendwo.
Owner

Ich konnte das Problem etwas weiter eingrenzen:

public <R, T> Stream<R> takes(Stream<T> s, Function<? super T, ? extends R> fun)

So in etwa sieht die Definition von map aus.
Wenn ich das ganze etwas abändere:

public <R, T> Stream<R> takes(Stream<T> s, Function<T, ? extends R> fun)

dann funktioniert es. Es scheint also ein Problem mit ? super X zu geben.

Ich konnte das Problem etwas weiter eingrenzen: ```java public <R, T> Stream<R> takes(Stream<T> s, Function<? super T, ? extends R> fun) ``` So in etwa sieht die Definition von map aus. Wenn ich das ganze etwas abändere: ```java public <R, T> Stream<R> takes(Stream<T> s, Function<T, ? extends R> fun) ``` dann funktioniert es. Es scheint also ein Problem mit `? super X` zu geben.
dholle referenced this issue from a commit 2024-03-19 14:20:05 +00:00
dholle added the
Unify
confirmed
in progress
labels 2024-03-21 09:56:49 +00:00
dholle referenced this issue from a commit 2024-03-25 11:14:31 +00:00
Owner

Die Implementierung gefällt mir noch nicht besonders gut...
@stan vielleicht hast du eine bessere Idee wie man das machen kann. So wie es jetzt ist wird es nachher schwer den Unify auszugliedern.

Die Implementierung gefällt mir noch nicht besonders gut... @stan vielleicht hast du eine bessere Idee wie man das machen kann. So wie es jetzt ist wird es nachher schwer den Unify auszugliedern.
Owner

Gut Erkannt. Der Unify benutzt jetzt Typen aus dem package ast.syntaxtree. Zum Beispiel RefType.
Der Unify sollte allerdings nur seine eigenen Datenstrukturen verwenden (UnifyType). Die Typen in den Constraints müssen vor der Übergabe an den Unify Algorithmus in dessen Datenstruktur konvertiert werden.

Gut Erkannt. Der Unify benutzt jetzt Typen aus dem package ast.syntaxtree. Zum Beispiel `RefType`. Der Unify sollte allerdings nur seine eigenen Datenstrukturen verwenden (`UnifyType`). Die Typen in den Constraints müssen vor der Übergabe an den Unify Algorithmus in dessen Datenstruktur konvertiert werden.
Author
Collaborator

Der Umweg über

List<Integer> list = new ArrayList<>();
var a = list.stream();
Function<Integer, Integer> fun = x -> 2 * x;
a.map(fun);

funktioniert, es liegt also an den Constraints irgendwo.

Das funktioniert aber auch nur im Spezialfall bei dem Input und Output den gleichen Typ haben. Bei einer Lambda-Funktion, die z.B. einen Integer als Input und einen Boolean als Output hat, gibt es unaufgelöste Constraints.

Function<Integer, Boolean> filter = x -> x > 3;
//oder direkt mit dem funktionalen Interface Predicate, dass z.B. von java.util.stream.Stream.filter(...) erwartet wird
Predicate<Integer> filter2 = x -> x > 3; 
Unresolved constraints: [[(java.lang.Boolean <. java.lang.Integer, )
> Der Umweg über > ```java > List<Integer> list = new ArrayList<>(); > var a = list.stream(); > Function<Integer, Integer> fun = x -> 2 * x; > a.map(fun); > ``` > funktioniert, es liegt also an den Constraints irgendwo. Das funktioniert aber auch nur im Spezialfall bei dem Input und Output den gleichen Typ haben. Bei einer Lambda-Funktion, die z.B. einen Integer als Input und einen Boolean als Output hat, gibt es unaufgelöste Constraints. ```java Function<Integer, Boolean> filter = x -> x > 3; //oder direkt mit dem funktionalen Interface Predicate, dass z.B. von java.util.stream.Stream.filter(...) erwartet wird Predicate<Integer> filter2 = x -> x > 3; ``` ``` Unresolved constraints: [[(java.lang.Boolean <. java.lang.Integer, ) ```
dholle reopened this issue 2024-03-26 16:02:10 +00:00
dholle referenced this issue from a commit 2024-03-26 16:31:55 +00:00
Author
Collaborator

Ich glaube durch die Änderungen in RuleSet in 8fdfbf875b geht es aktuell gar nicht mehr

Ich glaube durch die Änderungen in RuleSet in 8fdfbf875b geht es aktuell gar nicht mehr
i21023 reopened this issue 2024-03-26 19:21:51 +00:00
Owner

Kannst du ein neues Beispiel geben welches nicht funktioniert?

Kannst du ein neues Beispiel geben welches nicht funktioniert?
Author
Collaborator

Kannst du ein neues Beispiel geben welches nicht funktioniert?

z.B. das Beispiel von oben:

Der Umweg über

List<Integer> list = new ArrayList<>();
var a = list.stream();
Function<Integer, Integer> fun = x -> 2 * x;
a.map(fun);

funktioniert, es liegt also an den Constraints irgendwo.

oder mit Predicate statt Function:

Predicate<Integer> pred = x -> x > 5;

oder ohne direkte Zuweisung zu einem Functional Interface:

List<Integer> list = new ArrayList<>();
list.stream().map(x -> 2*x);
> Kannst du ein neues Beispiel geben welches nicht funktioniert? z.B. das Beispiel von oben: > Der Umweg über > ```java > List<Integer> list = new ArrayList<>(); > var a = list.stream(); > Function<Integer, Integer> fun = x -> 2 * x; > a.map(fun); > ``` > funktioniert, es liegt also an den Constraints irgendwo. oder mit Predicate statt Function: ```java Predicate<Integer> pred = x -> x > 5; ``` oder ohne direkte Zuweisung zu einem Functional Interface: ```java List<Integer> list = new ArrayList<>(); list.stream().map(x -> 2*x); ```
Owner

Ich hoffe es ist jetzt gefixt. Ich hatte noch alte class files rumliegen.

Ich hoffe es ist jetzt gefixt. Ich hatte noch alte class files rumliegen.
Sign in to join this conversation.
No Milestone
No project
No Assignees
4 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: JavaTX/JavaCompilerCore#298
No description provided.