From
Tuple.of(1,2,3)
List.of(1,2,3)
To
tuple(1,2,3)
list(1,2,3)
By providing a sort of Aliases like this :
public final class Aliases {
public static <A, B> Tuple2<A, B> tuple(A a, B b) {
return Tuple.of(a, b);
}
public static <A, B, C> Tuple3<A, B, C> tuple(A a, B b, C c) {
return Tuple.of(a, b, c);
}
public static <T> List<T> list(T element) {
return List.of(element);
}
public static <T> List<T> list(T... elements) {
return List.of(elements);
}
// many more...
}
I like the idea of static importing, but not sure an Aliases would be the place to do it: it would have a very low cohesion.
@ronanM yes, great idea! We have to take care to return the general types instead of special types, e.g. Option.none() and Option.some() return Option etc.
I would place them in javaslang.API (which is generated) instead of Aliases. Maybe we should also provide f1(Function1), ..., f8(Function8) as shortcut for Function1.of(Function1), ... (this would go adhere with this feature request).
@paplorinc You are right (low cohesion) but on the other hand it would be great to just
import static javaslang.API.*
and have everything at hand :-)
If we don't want to include all variants for all primitives and wrappers and iterables etc, it might work :)
Yes, we should cover:
Function0, ..., Function8 - f0, ..., f8CheckedFunction0, ..., CheckedFunction8 - chk_f0, ..., chk_f8 _<-- better names?_ or just cf0, ...cf8?Tuple0, ..., Tuple8 - tuple(), ..., tuple(T1, ..., T8) (but no aliases like pair etc!)Either - left, rightFuture- future (successful, failed would be too confusing, see Try)Lazy - lazyOption - option, some, none, nothingTry - try_ or _try, success, failureValidation - valid, invalidcharSeq, array, vector, (not stack), list, stream, queue, hashMap, ...)
:+1: (btw, the tree should be updated with BitSet and PriorityQueue)
Yes, I will create an issue (or I will forget it...)
See javaslang/javaslang-docs#25
The scope changed:
@paploric stated:
public static <T> $OptionType<T> some(T value) {
do we need these?
What's the difference between importing statically Option or API?
I mean these are already available to us by importing Option statically.
This is a valid point. Several types already have the static factory methods with similar names:
We still have additional problems here - some API is ambiguous, e.g.
// returns a success
public static <T> $FutureType<T> future(T result) { ... }
// returns a failure
public static <T> $FutureType<T> future(Throwable exception) { ... }
It was a design decision to use consequently use of throughout our whole codebase, e.g.
Tuple.of(1, true, "nice")List.of(1, 2, 3)Try.of(() -> computation())Main reasons:
of API. It looks familiar to the user.new - that is what we really want. But the upper-case names are already taken by the Pattern Match API in order to deconstruct objects.But: The of notation is not concise enough.
Like @paplorinc said, we do not need to duplicate the existing API. What we really want are shortcuts for the of factory methods only.
I would like to have first char upper-case static factory method names within javaslang.API, e.g.
Tuple(1, 2), Tuple(1, 2, 3, 4)Function1(obj::methodRef), CheckedFunction1(obj::methodRef)Try(() -> computation()), Option(value), Future(() -> computation())List(1, 2, 3)HashMap(1, "a", 2, "b", 3, "c")This would go adhere with API.Match() and API.Case() etc.
To this day static factory methods for (most) collection _interfaces_ were out of scope, e.g.
Seq(1, 2, 3)Set(1, 2, 3)Map(1, "a", 2, "b", 3, "c")But maybe it would be nice to have these too.
Q: So, what to do with the names of generated patterns? They will clash with the new API.
A: We will break backward compatibility and change the annotation processor of javaslang-match. We will generate a preceding $ for patterns. This goes adhere with the use of $ for standard patterns like
$() - any$(value) - equality$(predicate) - conditionExample:
T result = Match(value).of(
Case($Tuple($(1), $(true), $("nice")), (v1, v2, v3) -> ...),
Case($List($(1), $()), (head, tail) -> ...)
);
We determine these _specialities_:
$Tuple($(Tuple(1, 2, 3)) will contain a $Tuple pattern and a Tuple constructor. Hard to read?$List($(), $()) deconstructs _head_ element and _tail_ listList(v1, v2) constructs a List containing two values v1, v2However, the first char upper case constructors can be already introduced but they will raise problems when used in conjunction with the Match API (, e.g. import static javaslang.API.*;).
oh boy, I really love breaking changes :D
Yes, breaking the lib is a hard decision.
I think evolution is not possible without changing existing things. Java doesn't do it. Scala did it in the past but got more conservative. Swift 3 will include many breaking changes to make the language better.
We use semantic versioning. Major versions break things by definition. But major versions should not appear often.
Our users need a reliable lib. Having that said, we need to maintain the 2.x streamline. The 3.x streamline will be the next evolution step. I can't foresee all possibilities. Evolution is a feedback-loop considering the outer world.
There is one thing that still bothers me - we will have mixed upper case and lower case factory methods, e.g.
API.Option() but lower case Option.some(), Option.none()This idea is still unfinished... We need to solve this until 3.0.0
@paplorinc asked:
There is one thing that still bothers me - we will have mixed upper case and lower case factory methods, e.g.
Could you please explain the need for upper-camel? If it's simply to model
Scala's companion constructor, we could simply translate that to statically-importable lowercase static factories, e.g.option,some,checkedFunction.
AboutAPI, well, it could have a prefix of e.g.$or_, but still lowercase, e.g.$option.
These are my thoughts:
Deconstructors (read: unapply patterns) and constructors are _dual_. This should be reflected by the name. I think we have two options: both upper- or both lower case. Distinguishing them is mandatory. A prefix should work fine ('$' would integrate well for the patterns).
These are our options:
Some()/$Some() vs some()/$some()javaslang.API or all within related types or a hybrid solutionEach type has several of and ofAll methods in general. When adding new factory methods we concentrate on of(T) and of(T...).
Looking at Option we will take one of these approaches:
API.Option(T), API.Option(T...) and Patterns.$Option()API.option(T), API.option(T...) and Patterns.$option()Option.Option(T), Option.Option(T...) and Patterns.$Option()Option.option(T), Option.option(T...) and Patterns.$option()I like to just import static javaslang.API.* and then start to write Javaslang using Match, For and almost all of the Values, Tuples and (Checked)Functions.
Option.some(...) instead of some(...). This would go adhere with of and ofAll.javaslang.API is intended to be used as static import _only_. E.g. we write For and Match instead of API.For and API.Match.For and Try(() -> computation) instead of for and try(() -> computation()).API.Some() and API.None() in addition to Option.some() and Option.none().In other words Some(...) and Option.some() are different in the way they are used, the former as static import, the latter full qualified. The case indicates the usage.
Patterns are prefixed upper-case, e.g. $Some(...).
May be this issue must be in milestone 2.1.0 as PR?
Right!
Most helpful comment
Yes, breaking the lib is a hard decision.
I think evolution is not possible without changing existing things. Java doesn't do it. Scala did it in the past but got more conservative. Swift 3 will include many breaking changes to make the language better.
We use semantic versioning. Major versions break things by definition. But major versions should not appear often.
Our users need a reliable lib. Having that said, we need to maintain the 2.x streamline. The 3.x streamline will be the next evolution step. I can't foresee all possibilities. Evolution is a feedback-loop considering the outer world.