2016-06-04 17 views
5

Ich versuche Klassen in Java zu definieren, die Haskell Funktoren ähnlich sind. Hierdurch wird ein Funktor ist definiert als:Funktoren in Java

/** 
* Programming languages allow only (just simply enough) endofunctor, that are functors from and to the same category. 
* In this case, the category is the one of the datatypes (in here Type, in order to make it more clear) 
*/ 
public interface EndoFunctor<X extends Type> extends Type { 

    /** 
    * The basic implementation for any element fx 
    * @param map Transformation function for the type parameter 
    * @param fx Element of the current class 
    * @param <Y> Target type 
    * @return  transformed element through map 
    */ 
    <Y extends Type> EndoFunctor<Y> fmap(Function<X,Y> map, EndoFunctor<X> fx); 

} 

Wenn ich einen Identity Functor over types Funktors implementieren wollen, ich habe

so etwas wie
public class Id<X extends Type> implements EndoFunctor<X> { 
    protected X witness; 
    Id(X witness) { this.witness = witness; } 
    @Override 
    public <Y extends Type> Id<Y> fmap(Function<X, Y> map, Id<X> fx) { 
     return new Id<>(map.apply(fx.witness)); 
    } 
} 

Das Problem mit diesem Code zu schreiben ist, dass Id<X> nicht den Typ überein EndoFunctor<X>. Wie kann ich fmap in der EndoFunctor Schnittstelle so bestimmen, dass, wenn jede Art K<T> implementiert EndoFunctor<T> und eine Kartenfunktion T->U gegeben ist, dann K<U> als Wert zurückgegeben wird, ohne eine Schublade gesteckt (das heißt, da ich weiß, dass mein Objekt ein Id<T> ist, dann das Ergebnis von fmap "muss" ein Id<U> sein, und daher habe ich das Ergebnis vom Typ EndoFunctor<U> auf diesen Typ gesenkt?

+0

Gibt es einen Grund, warum Sie 'EndoFunctor fmap (Funktion map)' nicht stattdessen verwendet haben? Auf diese Weise würde jede Instanz ihre Instanzvariablen verwenden. Ie, 'return new Id <> (map.apply (this.witness))'. – afsantos

+0

Nun, ich denke nicht, dass diese Beobachtung meine Frage beantworten könnte. Ihre Map ('fmap2 (x)') kann als 'fmap (x, this)' definiert werden, was das Typproblem nicht wirklich löst. – jackb

+0

Übrigens habe ich die Funktion auf diese Weise definiert, um sie der Definition eines Funktors ähnlicher zu machen, nämlich [F: (a-> b) -> (Fa -> Fb)] (http: // latex.codecogs.com/gif.download?F%5Ccolon%20%28a%5Cto%20b%29%5Cto%20%28Fa%5Cto%20Fb%29), wobei F in diesem Fall der EndoFunctor ist. – jackb

Antwort

7

könnten Sie CRTP verwenden:

interface EndoFunctor<X extends Type, T extends EndoFunctor<X, T>> extends Type { 
    <Y extends Type> EndoFunctor<Y, ?> fmap(Function<X,Y> map, T fx);  
} 

class Id<X extends Type> implements EndoFunctor<X, Id<X>> { 
    protected X witness; 
    Id(X witness) { this.witness = witness; } 

    @Override 
    public <Y extends Type> Id<Y> fmap(Function<X, Y> map, Id<X> fx) { 
     return new Id<>(map.apply(fx.witness)); 
    } 
} 
+0

Dieser Ansatz löst andere Probleme (mit denen ich dich nicht langweilen werde), die ich später auf meinem Code habe. Danke – jackb

2

Das Problem ist nicht, dass Id<X> nicht EndoFunctor<X> übereinstimmen, aber das, wenn Sie versucht haben, fmap außer Kraft setzen Sie die Parametertypen präziser gemacht haben und damit die Methodensignatur keine Spiele mehr die Methodensignatur von fmap in EndoFunctor

das bedeutet, dass Id<X> in seiner derzeitigen Form die EndoFunctor<X> Schnittstelle nicht vollständig implementieren. Bei der Implementierung einer Schnittstelle muss es möglich sein, mit Ihrer Klasse zu interagieren, ohne dass Sie wissen müssen, dass es sich um eine andere Schnittstelle handelt.

Entweder befolgen Sie den Hinweis in den Kommentaren zum Entfernen dieses Methodenparameters und verwenden Sie die Instanzvariable, oder ändern Sie die Signatur in Id<X> zu public <Y extends Type> Id<Y> fmap(Function<X, Y> map, EndoFunctor<X> fx), um es kompatibel mit der Schnittstelle zu machen.

+0

Ihr Rat hat ein ähnliches Problem wie das oben beschriebene: indem ich dies tue, erlaube ich nur einen bestimmten zurückgegebenen Typ. – jackb

8

Wie kann ich fmap in dem EndoFunctor Schnittstelle bestimmen, so dass, wenn jeder Typ K implementiert EndoFunctor und eine Kartenfunktion T-> U gegeben ist, dann K als Wert zurückgegeben wird, ohne eine Schublade gesteckt (das heißt, seit Ich weiß, dass mein Objekt eine Id ist, dann muss das Ergebnis von fmap "eine Id sein, und daher habe ich das Ergebnis vom Typ EndoFunctor auf diesen Typ reduziert."

Sie können nicht; Dies wird höher-kinded Polymorphismus genannt, und Java unterstützt es nicht (sehr wenige Sprachen tun). Jorn Vernee Antwort bekommt man die nächstgelegene Sie in Java, aber das Interface ermöglicht

class NotId<X extends Type> implements EndoFunctor<X, Id<X>> { 

    @Override 
    public <Y extends Type> ADifferentEndoFunctorAgain<Y> fmap(Function<X, Y> map, Id<X> fx) { ... } 
} 

zu schreiben und wird nicht funktionieren, wenn Sie den Code generic über EndoFunctor s schreiben wollen, anstatt zu arbeiten mit einem spezifischenEndoFunctor wie Id.

+0

Eigentlich hatte ich gehofft, dass die Durchführung einiger Überheblichkeitstypen zu diesem Problem beigetragen hätte. Ihre Antwort ist formal korrekt, während die vorherige mir geholfen hat zu programmieren. Schade, dass ich zwei Antworten nicht gleichzeitig annehmen kann. – jackb