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