Equivalente della corrispondenza del modello idiomatico in Java

9

Sto costruendo un simulatore che analizza alcuni eventi da STDIN e li "esegue". Il mio background è principalmente la programmazione funzionale in questi giorni, quindi mi è sembrato naturale fare qualcosa del genere:

data Event = Thing1 String Int | Thing2 Int | Thing3 String String Int
Parse :: String -> [Event]
Simulate :: [Event] -> [Result]

dove simulare sarebbe

case event
  of Thing1 a b => compute for thing one
   | Thing2 a => compute for thing two

ecc. Qual è il modo idiomatico di fare questo genere di cose in Java? Googling mi ha indirizzato verso le classi annidate e lo schema dei visitatori, ma nel mio tentativo sembra piuttosto pesante. Il tipo di cancellazione sembra essere in lotta con me, difficile. Potresti mostrarmi una descrizione di come sarebbe fatto correttamente?

    
posta closeparen 10.05.2016 - 17:41
fonte

5 risposte

9

L'autore di "Programmazione funzionale in Scala" offre una buona illustrazione del meglio che è possibile ottenere in Java in un modo sicuro:

link

Essenzialmente, usa una codifica della Chiesa dei casi per assicurare che il compilatore si lamenterà se ne manca qualcuno.

I dettagli non sono prontamente riassunti e in effetti sono così ben coperti nell'articolo che non ha senso riprodurli qui (questo è ciò che i collegamenti ipertestuali sono per giusto?).

    
risposta data 10.05.2016 - 19:29
fonte
4

What is the idiomatic way to do this sort of thing in Java?

Non c'è davvero una cosa del genere, dato che Java (il linguaggio) è fondamentalmente un imperativo.

Se è possibile eseguire sulla JVM, ma non limitarsi alla lingua Java , è possibile esaminare Scala, che otterrebbe qualcosa di simile a quanto sopra usando abbinamento di pattern .

Altrimenti, penso che tu sia ridotto a far corrispondere manualmente i vari casi e metodi di chiamata a seconda dei casi, o forse definire sottotipi di "Evento" e usare il polimorfismo per invocare metodi particolari per ogni sottotipo.

    
risposta data 10.05.2016 - 17:46
fonte
1

Dai un'occhiata al link che è una libreria "pattern matching" simile a Scala per Java 8.

Non è bello come ML / Erlang / Haskell, ma sembra ancora molto più dichiarativo di molti altri.

    
risposta data 10.05.2016 - 20:26
fonte
0

Potresti usare un enum e un'interfaccia, sovrascrivendo il metodo di simulazione, in questo modo:

interface Event {
  void simulate()
}

enum MyEvents implements Event {
  THING1 {
    @Override
    void simulate() {
    //...
    }
  },
  THING2 {
    @Override
    void simulate() {
    //...
    }
  },
}

Supponiamo che tu abbia Event event . Quindi puoi usarlo in due modi:

event.simulate();

o

switch(event) {
  case THING1:
    //..
    break;
  case THING2:
    break;
}

Ma il costruttore viene chiamato automaticamente dalla JVM, quindi per memorizzare anche i parametri lì dovrai aggiungere proprietà con accessors ecc.

In alternativa, puoi codificare i tuoi eventi come stringhe costanti e utilizzare il costrutto switch , nel qual caso dovresti fare

string event = args[0];
switch(event){
  case THING1:
    int a = args[1];
    //...
    break;
  case THING2:
    int a = args[1];
    int b = args[2];
    break;
}

ecc. Ma sì, non c'è nulla di nativo che imita direttamente la corrispondenza del modello: (

    
risposta data 10.05.2016 - 19:14
fonte
0

Il modello di visitatore o il suo equivalente di codifica della chiesa è la strada da percorrere. È piuttosto dettagliato in Java, ma si spera che strumenti come Derive4J (un processore di annotazioni che mantengo) o Adt4J può generare lo standard. Usando questo strumento il tuo esempio diventa:

import java.util.function.Function;
import org.derive4j.Data;

@Data
public abstract class Event {

  interface Cases<X> {
    X Thing1(String s, int i);
    X Thing2(int i);
    X Thing3(String s, String s2, int i);
  }

  abstract <X> X match(Cases<X> cases);

  static Function<Event, Result> Simulate =
      Events.cases().
          Thing1( (s, i    ) -> computeForThingOne(s, i)       ).
          Thing2( (i       ) -> computeForThingTwo(i)          ).
          Thing3( (s, s2, i) -> computeForThingThree(s, s2, i) );

}

Derive4J genera la classe Events che fornisce una sintassi fluente di corrispondenza del modello (con un controllo completo che tutti i casi sono gestiti).

    
risposta data 28.08.2016 - 16:43
fonte

Leggi altre domande sui tag