Si tratta di una monade in Java? (parte 2)

2

Il mio primo tentativo è stato su StackOverflow. Raccolgo la risposta per migliorare la mia monade: StackOverflow - È una monade in Java?

Il mio obiettivo è scrivere un esempio di monade. Non sto cercando di risolvere il caso generale, ne esco solo uno per vedere come funziona. Se ho ragione, mi piace molto questo schema. L'ho già usato per rielaborare il codice su cui sto lavorando. Quindi spero sia giusto.

Ero piuttosto severo riguardo le firme e l'amp; non prendere scorciatoie che OO consente.

L'esempio (forzato) è di fornire semantica per rappresentare una relazione di amicizia tra due utenti. Se si verifica un errore o l'amicizia è bloccata, l'elaborazione si interrompe. Ho un codice di esempio che "solleva" Boolean, String e una classe Friend personalizzata nello spazio monadico, quindi penso che sia un segno che sono sulla strada giusta. Ho incluso il codice di esempio che solleva String nello spazio monadico.

La mia domanda è: ho avuto l'implementazione di Monad giusta? Si prega di chiamare i luoghi in cui ho saltato le rotaie!

public class FriendSpace<A> {

    public boolean rejected = false;
    public String errors = "";
    public A original;

    public interface Function<B, MA> {
        MA apply(B b, MA a);
    }

    public FriendSpace<A> unit(A a) {
        FriendSpace<A> that = new FriendSpace<A>();
        that.original = a;
        return that;
    }

    public <B> FriendSpace<A> bind(B b, Function<B, FriendSpace<A>> f) {
        if (! errors.isEmpty()) {
            // we have errors; skip the rest
            return this;
        }

        if (rejected) {
            // No means no
            return this;
        }

        FriendSpace<A> next = f.apply(b, this);

        return next;
    }

    @SuppressWarnings("unchecked")
    public <B> FriendSpace<A> pipeline(B value,
           FriendSpace.Function<B, FriendSpace<A>>... functions) {
        FriendSpace<A> space = this;
        for (FriendSpace.Function<B, FriendSpace<A>> f : functions) {
            space = space.bind(value, f);
        }
        return space;
    }

    // toString omitted to save space
}

Ed ecco un esempio in cui l'input (arbitrario) è una classe People e la classe (arbitraria) che rappresenta lo stato dell'amicizia è String

public class People {

    public People(String from, String to) {
        this.from = from;
        this.to = to;
    }

    public String from;
    public String to;
}

    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        FriendSpace<String> proto = new FriendSpace<String>();

        People people0 = new People("Bob", "Fred");
        FriendSpace<String> space0 = proto.unit("");
        FriendSpace<String> friends0 = space0.pipeline(people0, VERIFY, INVITE, ACCEPT);
        System.err.println(friends0);

        People people1 = new People("Bob", "Jenny");
        FriendSpace<String> space1 = proto.unit("");
        FriendSpace<String> friends1 = space1.pipeline(people1, VERIFY, INVITE, ACCEPT);
        System.err.println(friends1);

        People people2 = new People("Fred", "Jenny");
        FriendSpace<String> space2 = proto.unit("");
        FriendSpace<String> friends2 = space2.pipeline(people2, VERIFY, INVITE, BLOCK);
        System.err.println(friends2);

        People people3 = new People("Bob", "Tom");
        FriendSpace<String> space3 = proto.unit("");
        FriendSpace<String> friends3 = space3.pipeline(people3, VERIFY, INVITE, ACCEPT);
        System.err.println(friends3);
    }

    public interface StringFunction extends FriendSpace.Function<People, FriendSpace<String>> {
    }

    static StringFunction VERIFY = new StringFunction() {
        public FriendSpace<String> apply(People from, FriendSpace<String> to) {
            String KNOWN_USERS = "Bob Fred Jenny";

            if (! KNOWN_USERS.contains(from.from)) {
                to.errors += "Unknown from: " + from.from;
            }

            if (! KNOWN_USERS.contains(from.to)) {
                to.errors += "Unknown to: " + from.to;
            }

            return to;
        }
    };

    static StringFunction INVITE = new StringFunction() {
        public FriendSpace<String> apply(People from, FriendSpace<String> to) {
            // Jenny has blocked Bob
            if ("Jenny".equals(from.to) && "Bob".equals(from.from)) {
                to.errors = "Jenny blocked Bob";
            }
            return to;
        }
    };

    static StringFunction ACCEPT = new StringFunction() {
        public FriendSpace<String> apply(People from, FriendSpace<String> to) {
            // Good to go!
            to.original = "YES";
            return to;
        }
    };

    static StringFunction BLOCK = new StringFunction() {
        public FriendSpace<String> apply(People from, FriendSpace<String> to) {
            to.original = "BLOCK";
            to.rejected = true;
            return to;
        }
    };
    
posta Rob 16.02.2014 - 04:33
fonte

1 risposta

4

Il posto Java e C # cadono nell'implementazione di monadi è se si associa una funzione a -> m b dove a != b non si può return this - è necessario costruire un b predefinito per creare il m b dove in Haskell Nothing è una costruzione m b valida e Left anything può soppiantare Right anything .

Hai qui una o Monade, che è ciò che preferiamo noi OO Forse perché ci permette di intrappolare alcune informazioni con il problema in modo esplicito (le persone di FP tendono a preferire l'implicito e più conciso Nothing della Forse monade).

Risolvi il tuo return this per soddisfare il polimorfismo richiesto in modo che possa richiedere un a -> m b e restituire un m b anche nel caso di errore. Devi restituire nuovo FriendState(something else) perché this potrebbe essere un FriendState<a> ma il tuo bind deve restituire un FriendState<b> , in questo momento l'errore consente solo il ritorno di FriendState<a> che funziona solo quando si associano funzioni che vanno da a -> FriendState<a> .

So che è la parte difficile di un C # che ha già eseguito variazioni sulle monadi in C #, è difficile ottenere la quantità di ADT polimorfismo che ti danno. Questo è un ottimo primo crack!

    
risposta data 19.02.2014 - 19:30
fonte

Leggi altre domande sui tag