2016-06-04 13 views
5

sto cercando di definire classi in Java che sono simili a funtori di Haskell. Con la presente, un funtore è definito come:Funtori 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); 

} 

Se voglio realizzare un funtore Identity Functor over types, devo scrivere qualcosa di simile

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

Il problema con questo codice è che Id<X> non corrisponde al tipo di EndoFunctor<X>. Come potrei stabilire fmap nel EndoFunctor dell'interfaccia tale che se qualsiasi tipo K<T> implementa EndoFunctor<T> e viene data una funzione di mappa T->U, quindi K<U> viene restituito come un valore, senza alcuna fusione di caratteri (cioè da quando so che il mio oggetto è un Id<T>, allora il risultato di fmap "deve essere" Id<U>, e quindi ho abbattuto il risultato di tipo EndoFunctor<U> a tale tipo)?

+0

C'è un motivo per cui non è stato utilizzato 'EndoFunctor FMAP (Funzione mappa)', invece? In questo modo, ogni istanza utilizzerà le sue variabili di istanza. I.e, 'return new Id <> (map.apply (this.witness))'. – afsantos

+0

Bene, non penso che questa osservazione possa rispondere alla mia domanda. La tua mappa ('fmap2 (x)') è definibile come 'fmap (x, this)', quindi questo non risolve il problema del tipo. – jackb

+0

A proposito, ho definito la funzione in questo modo per farla somigliare più alla definizione di un funtore, cioè [F: (a-> b) -> (Fa -> Fb)] (http: // latex.codecogs.com/gif.download?F%5Ccolon%20%28a%5Cto%20b%29%5Cto%20%28Fa%5Cto%20Fb%29), dove F in questo caso è EndoFunctor. – jackb

risposta

7

Potreste usare 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

Questo approccio risolve altri problemi (con cui non ti annoierò) che ho in seguito sul mio codice. Grazie – jackb

2

Il problema non è che Id<X> non corrisponde EndoFunctor<X>, ma che quando si è tentato di ignorare fmap aver effettuato i tipi di parametri più specifici, e di conseguenza la firma del metodo non corrisponde più alla firma del metodo di fmap in EndoFunctor

Ciò significa che Id<X> nella sua forma attuale non attuare pienamente l'interfaccia EndoFunctor<X>. Quando si implementa un'interfaccia, deve essere possibile interagire con la classe senza aver bisogno di sapere che si tratta di un'interfaccia diversa.

O seguire il consiglio nei commenti sulla rimozione di questo parametro del metodo e utilizzando la variabile di istanza o modificare la firma in Id<X> in public <Y extends Type> Id<Y> fmap(Function<X, Y> map, EndoFunctor<X> fx) per renderla compatibile con l'interfaccia.

+0

Il tuo consiglio ha un problema simile a quello sopra descritto: in questo modo, permetto solo un tipo restituito specifico. – jackb

8

Come ho potuto determinare FMAP nell'interfaccia EndoFunctor tale che se qualsiasi tipo K implementa EndoFunctor e una funzione cartina T-> U è dato, allora K viene restituito come valore, senza typecasting (cioè, poiché So che il mio oggetto è un ID, quindi il risultato di fmap "deve essere" un ID e quindi ho downcast il risultato del tipo EndoFunctor in tale tipo)?

Non è possibile; questo è chiamato polimorfismo di tipo superiore, e Java non lo supporta (pochissime lingue lo fanno). La risposta di Jorn Vernee si ottiene il più vicino possibile in Java, ma che l'interfaccia consente di scrivere

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

e non funzionerà se si vuole scrivere il codice generico su EndoFunctor s invece di lavorare con un specificaEndoFunctor come Id.

+0

In realtà, speravo che l'esecuzione di alcuni tipi di sollevamento avrebbe contribuito a questo problema. La tua risposta è formalmente corretta, mentre la precedente mi ha aiutato a programmare. Peccato che non possa accettare due risposte allo stesso tempo. – jackb

Problemi correlati