-
-
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
Rethink flatMap et al. #488
Comments
I can pick this up - and will come back tomorrow with some ideas, ok ? |
I'll take a break :) |
@patrox ok, this will be fun! |
@patrox if any questions (e.g. regarding existing code) - I'm here! |
you've more than deserved it :) |
@ruslansennov you deserved some 🍻 :) |
@patrox it's really a good idea |
@danieldietrich, @ruslansennov, regarding I found that when we run any transformation function (like But, in case of So When applying So to summarize:
Does it make any sense ? |
I focused for now on |
Hi @patrox, thanks for investigating it!! I think we cannot decide whether our map/flatMap returns a CharSeq (aka WrappedString) or Vector, depending on the result type of the mapping function. Adding an additional // this call is ambiguous
CharSeq.empty().map(c -> 'c');
// this is additional
public CharSeq map(CharUnaryOperator mapper) {
Objects.requireNonNull(mapper, "mapper is null");
if (isEmpty()) {
return this;
} else {
final char[] chars = back.toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] = mapper.apply(chars[i]);
}
return CharSeq.ofAll(chars);
}
}
// this comes from FilterMonadic
@Override
public <U> Vector<U> map(Function<? super Character, ? extends U> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
Vector<U> result = Vector.empty();
for (int i = 0; i < length(); i++) {
result = result.append(mapper.apply(get(i)));
}
return result;
}
// the compiler can't distinguish between this and Function<Character, Character>
@FunctionalInterface
interface CharUnaryOperator {
char apply(char c);
}
// factory method
static CharSeq ofAll(char[] array) {
Objects.requireNonNull(array, "array is null");
return new CharSeq(String.valueOf(array));
} If we want our traversables to have common map methods, we need to keep the one that returns Additionally it makes sense to map chars to chars. To circumvent the ambiguities, we could rename the char mapper to Analog, flatMap could be public CharSeq flatMapChars(CharFunction<? extends CharSequence> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
if (isEmpty()) {
return this;
} else {
final StringBuilder builder = new StringBuilder();
back.chars().forEach(i -> builder.append(mapper.apply((char) i)));
return new CharSeq(builder.toString());
}
}
@FunctionalInterface
interface CharFunction<R> {
R apply(char c);
} /cc @ruslansennov |
yes, I believe that I use too strong words, because when I was writing 'should return' I meant: 'it would be good if ...', but unfortunately it seems that java is missing a lot of scala's 'magic' :) |
Yes, it seems the Scala compiler can derive statically the return type... |
I'll play with the methods which you proposed and let you know what my experience was ... Maybe something new will pop up. |
Alright! 👍 |
@patrox do you think I can commit these methods or will it produce merge conflicts at your site? |
I will do it to get the Array in here... |
@danieldietrich, you can merge them without any concerns. |
SummarizedWe want uniform
|
To make it more concrete, here is an example: interface List<T> extends LinearSeq<T>, Value<T> {
// mapM, map ...
@Override // defined in Value or MonadOps
<U> List<U> flatMapM(Function<? super T, ? extends Value<? extends U>> mapper);
@Override // defined in LinearSeq
<U> List<U> flatMap(Function<? super T, ? extends Iterable<? extends U>> mapper);
}
interface Map<K, V> extends Traversable<Entry<K, V>>, Value<Entry<K, V>> {
// flatMapM, flatMap ...
@Override // defined in Value or MonadOps
<U> Set<U> mapM(Function<? super Entry<K, V>, ? extends U> mapper);
@Override // defined in Map
<U, W> Map<U, W> map(BiFunction<? super K, ? super V, ? extends Entry<? extends U, ? extends W>> mapper);
} And then: // = List(2, 4)
List.of(1, 2, 3, 4).flatMapM(i -> i % 2 == 0 ? new Some<>(i) : None.instance);
// = List(1, 1, 2, 2, 3, 3, 4, 4)
List.of(1, 2, 3, 4).flatMap(i -> Vector.of(i, i)); |
While applying changes I'm also applying additional minor changes. E.g. renaming
|
The following is taken from a pull request comment:
With
flatMap
we have a TODO in general but not here, with this pull-request.Example:
Our flatMap works totally different. We need to check what is possible with Java, what makes sense and what is the expected behavior of flatMap. Maybe we need a Monad.unit(val) and Monoid.combine(m1, m2)...
The text was updated successfully, but these errors were encountered: