Open sviperll opened 8 years ago
Thanks for creating this ticket.
I think the general solution is just nested calls to accept method (this is what Haskell/ML compilers internally do for you):
class Map<K, V> {
Map<K, V> put(final K k, final V v) {
return accept(new MapVisitor<Map<K, V>, Map<K, V>, K, V>) {
Map<K, V> EmptyMap() {
return InsertAssoc(Pair.Pair(k, v), this);
}
Map<K, V> InsertAssoc(final Pair pair, final Map<K, V> ts) {
return pair.accept(new PairVisitor<K, V, Map<K, V>>() {
Map<K, V> Pair(final K k1, final V v1) {
return k.equals(k1) ? InsertAssoc(Pair.Pair(k, v), ts) : InsertAssoc(pair, ts.put(k, v))
}
});
}
});
}
}
But with your particular example there are at least two work arounds.
class Map<K, V> {
Map<K, V> put(final K k, final V v) {
return accept(new MapVisitor<Map<K, V>, Map<K, V>, K, V>) {
Map<K, V> EmptyMap() {
return InsertAssoc(Pair.Pair(k, v), this);
}
Map<K, V> InsertAssoc(final Pair pair, final Map<K, V> ts) {
return k.equals(pair.getFst()) ? InsertAssoc(Pair.Pair(k, v), ts) : InsertAssoc(pair, ts.put(k, v));
}
})
}
}
@GenerateValueClassForVisitor
@Visitor(selfReferenceVariableName = "SELF", resultVariableName = "M")
public interface MapVisitor<M,SELF,A,B> {
M EmptyMap();
M InsertAssoc(A key, B value, SELF argMap);
}
Not intending to pursue a dispute of comparison of different libraries targetting the same problem areas, what's derive4j standpoint on this topic, @jbgi?
@nobeh there will be no fundamental difference with derive4j, only the lambda syntax can make things more readable:
@Data
public abstract class Map<A,B> {
public interface MapVisitor<A,B, M> {
M EmptyMap();
M InsertAssoc(Pair<A, B> argPair, Map<A,B> argMap);
}
public abstract <M> M accept(MapVisitor<A,B, M> visitor);
public static <A, B> Map<A,B> put(Map<A, B> ms, A k, B v) {
return ms.accept(visitor(
() -> InsertAssoc(pair(k, v), EmptyMap()),
(argPair, argMap) -> k.equals(argPair.left())
? InsertAssoc(pair(k, v), ms)
: InsertAssoc(argPair, put(ms, k, v))
));
}
}
Now if you want pattern guards syntactic sugar you may want to use https://github.com/aol/cyclops/tree/master/cyclops-pattern-matching or http://javaslang.github.io/javaslang-docs/2.0.0-RC3/#_match
Also with derive4j you can nest fluent pattern matches when cases have only one argument.
Thanks for the note. Actually, we've had a few trials with a few libraries include aol-cyclops, others such as motif. In the end none of them actually help us in the context of deep pattern matching.
Our motivation is in the context of generating programming language source code such as Java and Haskell from ABS that is a concurrent object modelling language. For a language such as Haskell, this is quite straightforward. However, for a language such as Java, it becomes an obstacle.
I have not had javaslang under my radar. I will give it a try.
cc @rudi, @bezirg, @vlad-serbanescu
With pure visitors you can probably somewhat solve the problem by implementing all possible destruction patterns in visitor with abstract class or default methods.
Like this
abstract class AbstractMapVisitor<A, B, R> implements MapVisitor<A, B, R> {
abstract R EmptyMap();
R InsertAssocWithPairAndEmptyMap(A key, B value);
R InsertAssocWithPairAndInsertAssoc(A key, B value, Pair<A, B> entry2, Map<A, B> map);
R InsertAssocWithPair(final A key, final B value, Map<A,B> argMap) {
return argMap.accept(new MapVisitor<A, B, R>() {
R EmptyMap() {
return InsertAssocWithPairAndEmptyMap(key, value);
}
R InsertAssoc(Pair<A, B> entry2, Map<A, B> map) {
return InsertAssocWithPairAndInsertAssoc(key, value, entry2, map);
}
});
}
R InsertAssoc(Pair<A, B> argPair, final Map<A,B> argMap) {
return argPair.accept(new PairVisitor<A, B, R>() {
R Pair(A first, B second) {
return InsertAssocWithPair(first, second, argMap);
}
});
}
}
So your original put method will look like this:
class Map<A, B> {
Map<A, B> put(A key, B value) {
return accept(new AbstractVisitor<A, B, Map<A, B>>() {
Map<A, B> EmptyMap() {
return Map.InsertAssoc(Pair.Pair(key, value), Map.EmptyMap());
}
Map<A, B> InsertAssocWithPair(A key1, B value1, Map<A, B> map) {
if (key.equals(key1))
return Map.InsertAssoc(Pair.Pair(key, value), map);
else
return Map.InsertAssoc(Pair.Pair(key1, value1), map.put(key, value));
}
});
}
}
Following @nobeh comment in #15