-
-
Notifications
You must be signed in to change notification settings - Fork 638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Removed abstractmultimap #1554
Removed abstractmultimap #1554
Conversation
@Override | ||
public <U> Multimap<K, V> distinctBy(Function<? super Tuple2<K, V>, ? extends U> keyExtractor) { | ||
public <U> M distinctBy(Function<? super Tuple2<K, V>, ? extends U> keyExtractor) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@danieldietrich, good job so far, now go get some sleep :p |
zzZZzz |
My approach looks too complicated/unintuitive to me. Example: public final class LinkedHashMap<K, V> implements Multimap<K, V> {
@Override
public LinkedHashMultimap<K, V> put(K key, V value) {
return Multimaps.put(this, delegate, emptyContainer, containerType, builder()::ofMap, key, value);
}
} final class Multimaps {
static <K, V, M extends Multimap<K, V>> M put(
M multimap,
Map<K, Traversable<V>> delegate,
Traversable<V> emptyContainer,
ContainerType containerType,
OfMap<K, V, M> ofMap,
K key,
V value) {
final Traversable<V> values = delegate.get(key).getOrElse(emptyContainer);
final Traversable<V> newValues = containerType.add(values, value);
return (newValues == values) ? multimap : ofMap.apply(delegate.put(key, newValues));
}
} E.g. we create a new You see, this starts to get cumbersome. Putting a value into a Map should be simple. I have the wrong abstraction here. I like @ruslansennov approach to have an Maybe there is another way to circumvent the JDK bug by just overriding all methods of the package private base class and delegating to them. I will test it. public final class LinkedHashMap<K, V> extends AbstractMultimap<K, V, LinkedHashMap<K, V>> {
@Override
public LinkedHashMultimap<K, V> put(K key, V value) {
return super.put(key, value);
}
} This solution is fragile in the sense that we could forget to override a method. We would need a unit test that checks overridden methods via reflection for example. I'm undecided, but I think that I will finish my current refactoring. There are benefits, like fewer type casts... Update: Actually the second |
What if... we just duplicate code. It would simplify things. @Override
public LinkedHashMultimap<K, V> takeUntil(Predicate<? super Tuple2<K, V>> predicate) {
Objects.requireNonNull(predicate, "predicate is null");
return takeWhile(predicate.negate());
} This is a typical code-generator case. We could generate all Multimaps and Maps. Then we have the possibility to apply better optimizations and there are fewer classes. On the other hand that would make it harder to get the hands on these classes for further changes. Will sleep one night over this topic... ...but I already like the idea - we will get rid of unnecessary code and make decisions already at code generation time.
|
This will still take some time. Maybe we should move this issue to the next (bugfix or minor) release in order to ship 2.0.4. In other words method references will not work for Maps/Multimaps. Workaround: use lambdas instead of method references. Good thing: It is a JDK bug, not a Javaslang bug, but should be fixed neartime. I think we still have problems in TreeMap and/or TreeMultimap when the value type is changed by an operation (based on wrong Comparators). But I haven't investigated it yet. Also operations of sorted collections that operate on multiple instances with different underlying Comparators make cause unexpected behavior. But this has to be checked and solved step-by-step. I'm alread on it by re-designing the RedBlackTree with some optimizations and preparation for the Navigable additions... So much to do :) Known bugs are ok. The most important thing is to ship reliable, backward-compatible software. |
We already removed AbstractMap. I wouldn't revert that change - but stop this change here. I will move (the rest of) #1326 to 2.0.5 and go the generator-way. |
I'm trying to follow the changes, but I'm not sure I understand one thing: can't we make the abstractions public instead? If I'm mistaken correctly the problem is that the returned value is a package-private superclass. (also, why isn't multimap simply a |
No. They are only ballast and would pollute the user's view on the Javaslang API.
That's not the problem. The returned value is a generic
We have a public interface, it is
Because Multimaps have methods that operate on I like the idea of generated code. It solved the one and only problem that our AbstractMultimap also solves - factor out duplicate code into a single place. But the generated code is better than the AbstractMultimap. We do not have the generic-hell, no type casts, fewer types within the class hierarchy, etc. There are many benefits. There is only one drawback - the code generator is a little bit harder to write than non-generated code. |
Hmmm, wasn't that your counter-argument against collection specialization? Or can we make the generator source a valid Java source? (i.e write once and forget that this is actually a base for other cases). Otherwise I think this would have a huge maintenance cost (non-standard way of programming, scaring many people away, providing more base for mistakes). |
I did not mean this statement in the very general but only considering our options:
Interesting idea. I have already thought about self-expanding Java code. Xtend language does this with so-called Active Annotations. Scala has Macros. But these things actually hide things like the annotations of many (in)famous Java frameworks. I like to directly write the (generator) code - no hidden magic. Btw - the AbstractQueue does not have this problem. The queues do not need to be generated. I would like to experiment with it (hands-on!) and see how it does feel from the development perspective. Maybe it is not as hard to maintain as we think. The Scala code generator is really easy. |
As long as the source code is not Strings, I don't mind :) |
I don't understand. We check-in the generated source. It is like hand-written code. Unit tests run against it. The unit tests will not be generated (from what I can see now), we use the existing Also our CI build automatically checks if a user accidentally overwrites generated code with manual changes. This breaks the build. Generating the Maps and Multimaps should be safe regarding Java compilation and manual user-changes! |
Obsolete, will be closed. We (most probably) will go the generator way. |
I just meant that source code should be written in an IDE recognizable format. |
The quality of our source code and user experience are very important / priority 1. Javaslang library developer experience is also important but priority 2.
The right workflow will ease the pain of maintaining generator templates:
Phase 1. increases the developer experience during generator-driven development. Phase 2. is a pain in the ass, as always in stringly-driven development. But we need to do it to increase the quality of our library. |
Thanks for clarifying. I like the approach. |
Hi @ruslansennov, @paplorinc,@zsolt-donca,
this is an ongoing (currently dirty) draft of the bugfix of #1326. I created an early PR so that you can track the changes.
This is one of several PRs. The checklist is updated here.
Goal:
Remove all package-private super classes because method-references do not work for default implementations (JDK bug!).
What I did so far:
TODO: