Time consuming hashCode calculation (of immutable objects)?
These objects are used in a HashMap?
No problem!
class MyClass {
// may also be Lazy.of(this::timeConsumingOperation)
Time consuming hashCode calculation (of immutable objects)?
These objects are used in a HashMap?
No problem!
class MyClass {
// may also be Lazy.of(this::timeConsumingOperation)
public interface RedBlackTree<T> { | |
static <T extends Comparable<T>> EmptyNode<T> empty() { | |
// HERE: | |
return new EmptyNode<>(T::compareTo); | |
} | |
class EmptyNode<T> implements RedBlackTree<T> { | |
EmptyNode(Comparator<? super T> comparator) { |
class FunctionalQueueImpl<T> implements Queue<T> { | |
// ... | |
@Override | |
public <C> Map<C, Queue<T>> groupBy(Function<? super T, ? extends C> classifier) { | |
return foldLeft(HashMap.empty(), (map, t) -> { | |
final C key = classifier.apply(t); | |
final Queue<T> queue = map.getOption(key).orElse(Queue.empty()); | |
return map.put(key, queue.enqueue(t)); |
Regarding functional programming in Java, the most important design rule is to avoid mutability. We use it only in places, where it makes sense and we use it internally, only, i.e. mutability does not leak to the outside as public API. Within the last years I deduced these three simple rules to achieve it:
i. Don't try to create purely functional programs with Java. Simply spoken, Java is not the right language for that.
ii. It is most important to follow these rules consistently and make no exceptions other than stated.
import javaslang.Lazy; | |
import javaslang.Value; | |
import javaslang.control.Try; | |
class PeekSomeValues {{ | |
Seq<Value<String>> values = List.of( | |
// a lazy value | |
Lazy.of(() -> "test"), |
/* / \____ _ _ ____ ______ / \ ____ __ _ _____ | |
* / / \/ \ / \/ \ / /\__\/ // \/ \ / / _ \ Javaslang | |
* _/ / /\ \ \/ / /\ \\__\\ \ // /\ \ /\\/ \__/ / Copyright 2014-now Daniel Dietrich | |
* /___/\_/ \_/\____/\_/ \_/\__\/__/___\_/ \_// \__/_____/ Licensed under the Apache License, Version 2.0 | |
*/ | |
package javaslang.concurrent; | |
import javaslang.collection.Queue; | |
import javaslang.control.None; | |
import javaslang.control.Option; |
Calling factory methods of same name for single and multiple elements may raise ambiguities problems. This is the case for the actual java.util.stream.Stream#of(T) and #of(T...)
API.
These are the relevant cases (compiling with -Xlint:all -Werror
):
1. It is hard to create a Stream<T[]>
Stream.of(arrayOfObj)
calls Stream.of(T...)
instead of Stream.of(T)
.
Goals: We want to reduce the use of unchecked casts by allowing 'unsound' types.
Non-Goals: It is not a goal to change Java's type system to fully support co- and contra-variance.
Motivation: Unchecked types spread all over our code are reality (see below).
Notes: In the case of immutable objects this approach will work. In the case of mutability we are able to create a counter-example:
void counterExample(Set<? extends Object> input) {
// an interface for Monads of type M<T> | |
public interface Monad<M extends Kind1<M, ?>, T> extends Kind1<M, T>, Functor<T> { | |
// maps all T to M<U> and flattens these M<U> to one M<U> | |
<U> Monad<M, U> flatMapM(Function<? super T, ? extends Kind1<M, U>> mapper); | |
@Override | |
<U> Monad<M, U> map(Function<? super T, ? extends U> mapper); | |
// lifts a function T -> R to a function M<T> -> M<R> |