2016-06-04 14 views
5

Próbuję zdefiniować klasy w Javie, które są podobne do funktorów Haskella. Niniejszym, funktorem jest zdefiniowany jako:Funktory w Javie

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

} 

Jeśli chcę wdrożyć Identity Functor over types funktor, muszę napisać coś podobnego

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)); 
    } 
} 

problem z tym kodem jest, że Id<X> nie pasuje do typu EndoFunctor<X>. Jak mogę określić fmap w EndoFunctor interfejs taki, że jeśli każdy rodzaj K<T> realizuje EndoFunctor<T> i funkcją map T->U jest podana, wtedy K<U> jest zwracany jako wartość, bez rzutowania (to jest, ponieważ wiem, że moim celem jest Id<T>, następnie wynik fmap "musi być" Id<U>, a zatem I downcast wynik typu EndoFunctor<U> do tego typu)?

+0

Czy istnieje powód, dla którego nie użyłeś zamiast tego 'EndoFunctor fmap (funkcja mapa)? W ten sposób każda instancja użyje swoich zmiennych instancji. Na przykład 'return new Id <> (map.apply (this.witness))'. – afsantos

+0

Cóż, nie sądzę, aby ta obserwacja mogła odpowiedzieć na moje pytanie. Twoja mapa ('fmap2 (x)') jest definiowalna jako 'fmap (x, this)', więc to naprawdę nie rozwiązuje problemu typu. – jackb

+0

Nawiasem mówiąc, zdefiniowałem funkcję w ten sposób, aby przypominała bardziej definicję funktora, czyli [F: (a-> b) -> (Fa -> Fb)] (http: // latex.codecogs.com/gif.download?F%5Ccolon%20%28a%5Cto%20b%29%5Cto%20%28Fa%5Cto%20Fb%29), gdzie F w tym przypadku to EndoFunctor. – jackb

Odpowiedz

7

Można użyć CRTP:

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

To podejście rozwiązuje inne problemy (z którymi nie będę Cię zanudzał), że mam później swój kod. Dzięki – jackb

2

Problemem nie jest to, że Id<X> nie pasuje EndoFunctor<X>, ale kiedy próbowali zastąpić fmap dokonaniu typy parametrów bardziej szczegółowe, a co za tym idzie podpis metoda nie pasuje do podpisu metody fmap w EndoFunctor

oznacza to, że Id<X> w to obecną formę nie w pełni implementować interfejs EndoFunctor<X>. Podczas implementowania interfejsu musi istnieć możliwość interakcji z klasą bez konieczności poznania, że ​​jest to inny interfejs.

Należy postępować zgodnie z poradami w komentarzach dotyczących usuwania tego parametru metody i używając zmiennej instancji lub zmodyfikować podpis w Id<X> do public <Y extends Type> Id<Y> fmap(Function<X, Y> map, EndoFunctor<X> fx), aby był zgodny z interfejsem.

+0

Twoja rada ma podobny problem jak opisany powyżej: w ten sposób zezwalam tylko na jeden określony zwracany typ. – jackb

8

w jaki sposób można określić FMap w interfejsie EndoFunctor takie, że jeżeli typ K realizuje EndoFunctor i funkcję mapę T> U podane, to wówczas K jest zwracany jako wartość bez rzutowania (czyli od Wiem, że mój obiekt jest identyfikatorem, a wynik fmap "musi być" identyfikatorem, a więc odrzucam wynik typu EndoFunctor do tego typu?

Nie można; nazywane jest to polimorfizmem o wyższym czynniku, a Java nie obsługuje go (bardzo mało języków). Odpowiedź Jorn Vernee dostaje ci najbliżej można w Javie, ale interfejs pozwala na pisanie

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) { ... } 
} 

i nie będzie działać, jeśli chcesz napisać kod rodzajowy nad EndoFunctor s zamiast pracy z specyficznyEndoFunctor, takich jak Id.

+0

Właściwie, miałem nadzieję, że wykonanie niektórych typów podnoszenia pomogłoby w tej sprawie. Twoja odpowiedź jest poprawna formalnie, podczas gdy poprzednia pomogła mi w kodowaniu. Szkoda, że ​​nie mogę przyjąć dwóch odpowiedzi w tym samym czasie. – jackb