implementa la funzione cons in Java - digita la domanda di sicurezza

7

Sto lavorando su una piccola libreria funzionale scritta in Java, che riproduce lo stile funzionale della programmazione. Sono bloccato con un cast di tipo indesiderato in una delle mie definizioni di metodo e mi piacerebbe un po 'd'aiuto.

Ok, quindi non abbiamo funzioni di prima classe in Java, quindi li definisco come oggetti, con un metodo 'apply ()' come questo:

public abstract class Function2<R,T1,T2> {
    public abstract R apply(final T1 paramOne, final T2 paramTwo);
}

Quindi definisco il mio metodo contro come oggetto Function2, che posso passare in giro come se fosse una funzione in un'altra lingua che lo supporta:

public static <T> Function2<List<T>, T, List<T>> cons(){
    return new Function2<List<T>, T, List<T>>(){
        @Override
        public List<T> apply(T x, List<T> xs) {
            return new NonEmptyList<T>(x, xs);
        }
      };
    }

Ho omesso la mia struttura di lista per brevità; Supponiamo che sia una struttura di dati di lista funzionale di stile con tutto il solito head / tail / etc. operazioni.

Quindi voglio implementare una funzione come "reverse", che restituisce un elenco di elementi in ordine inverso. Io uso foldl1 (piega una lista non vuota da sinistra) per ottenere questo e passa la funzione cons come parametro a fold1 in questo modo:

public static <T> List<T> foldl( Function2<List<T>, T, List<T>> f,
                                 List<T> acc,
                                 List<T> xs ){
    if(xs.isEmpty()){ return acc; }
    return foldl(f, (f.apply(xs.head(), acc)), xs.tail());
}    

public static <T> List<T> reverse(List<T> xs){
    // how do I avoid this cast??
    return foldl( (Function2) cons(), new EmptyList(), xs); 
} 

Ma quando passo il mio oggetto "cons ()" in "reverse", ho bisogno di lanciarlo come una Funzione2, e non ho idea di come evitare di farlo. Ho provato tutti i tipi di problemi con i tipi ... Sento che questa è semplicemente una mancanza di esperienza da parte mia con il sistema di tipo Java ... chiunque?

PS. Sono a conoscenza di altre librerie funzionali in Java, volevo solo fare la mia piccola esperienza di apprendimento.

EDIT - OK, quindi ora sto usando un normale "foldl" per recuperare un elenco, ma devo ancora eseguire il cast? Il tipo di ritorno di "contro" è allineato a "foldl" ...

    
posta lwm 10.03.2013 - 16:54
fonte

2 risposte

7

Il motivo per cui non puoi passare contro come argomento a foldl1 senza casting è che il tipo di contro semplicemente non corrisponde a quello di foldl1 dell'argomento di funzione e il tuo cast è in realtà illegale - ma, purtroppo, deselezionato, in modo da non ottenere subito un'eccezione (alla fine ne otterrai una).

Hai definito foldl1 per prendere una funzione che prende due argomenti dello stesso tipo e che i contro non lo fanno. cons prende un T e un elenco di T s. Quando foldl1 chiama per la prima volta cons , la chiamerà con il primo e il secondo elemento della lista come argomenti. Poiché nessuno di questi è un elenco, ciò causerà il lancio di ClassCastException .

Quindi cons non è semplicemente un argomento valido per foldl1 . Non solo non c'è modo di evitare il cast, non c'è nemmeno modo di far funzionare il cast.

Un altro motivo per cui non è possibile utilizzare foldl1 per invertire un elenco è il suo tipo di ritorno: foldl1 restituisce un T dove T è il tipo di elemento dell'elenco su cui si sta chiamando. Pertanto, se utilizzi foldl1 in un elenco di T s, il risultato sarà un T . Ma chiaramente il risultato di invertire un elenco di T s dovrebbe essere un altro elenco di T s, non un semplice T .

Se vuoi invertire un elenco utilizzando una piega e contro, devi definirlo in termini di foldl , non foldl1 . foldl consente al tipo di risultato di essere diverso dal tipo di elemento della lista e ti permette di usare qualcosa di diverso dal primo elemento della lista come valore iniziale dell'accumulatore (di cui hai bisogno perché, come ho detto, l'accumulatore deve essere una lista).

    
risposta data 10.03.2013 - 19:34
fonte
3

Ho esaminato questo materiale in precedenza e alla fine ho rinunciato , perché anche i tipi di funzioni semplici come fold e map sono stati così complicati da rendere divertente la programmazione con loro in Java.

Ma il vero dolore inizia quando hai bisogno di tipi più gentili, come quello di fmap .

fmap :: Functor f => (a -> b) -> f a -> f b

Cioè, se non vuoi solo astrarre il tipo di elemento (di una lista, per esempio), ma anche il tipo di contenitore (Functor), simboleggiato da f sopra.

Btw, è divertente: finché non si esegue la programmazione funzionale, tale esigenza di tipi più elevati non si pone mai, forse perché non si hanno nemmeno funzioni di ordine superiore. Ma quando li hai, ti rendi conto abbastanza presto che ora vuoi fare un passo avanti. A questo proposito, sarà interessante vedere come funziona Java 8. Con le funzioni lambda, soddisfano un bisogno tanto atteso, ma non sono sicuro che sapranno che apriranno la scatola di Pandora. Ma i tipi più elevati richiederebbero una sostanziale riscrittura del sistema di tipi Java ... o almeno credo.

    
risposta data 10.03.2013 - 22:40
fonte

Leggi altre domande sui tag