-
Notifications
You must be signed in to change notification settings - Fork 134
AnyM
AnyM is a special type of functor in cyclops-react. It is a functor that abstracts over monads (and has two monadic sub-types AnyMValue and AnyMSeq). That is, it is an interface that can wrap any monad type. This allows us to write common code that can accept an Optional, CompletableFuture, Stream or List from the JDK as well as any of the cyclops-react datatypes (extended collections, LazyFutureStreams, FutureW, Try, Xor, Ior, FeatureToggle, Reader or Maybe). Via the cyclops integration modules we can also abstract over types from Javaslang, Functional Java, RxJava, Reactor and Guava.
We split the AnyM type into 2 sub-types AnyMValue and AnyMSeq. AnyMValue represents any monad that ultimately resolve to a single value (such as Maybe / Optional / Either / Try / Future / Reader) and AnyMSeq represents any monad that ultimately resolves to a sequence of values.
AnyM itself is not a monad, we can't safely represent the flatMap method on it, but it's two sub-types are. Both AnyMValue and AnyMSeq implement flatMap.
Use AnyM to combine Optionality (Optional) and Exception handling (Try). AnyM can be used to augment functionality missing from the original implementation (for example Applicative like combining in java.util.Optional or monadic flatMapping in guava's Optional).
AnyMValue<Integer> tryMonad = AnyM.fromTry(Try.success(20));
AnyMValue<Integer> optMonad = AnyM.fromOptional(Optional.of(10));- using flatMap (monad methods)
intMonad.flatMap(i->tryMonad.map(s->s+i))
.map(i->i*2);
//AnyMValue[60]- using combine (Applicative Functor methods)
intMonad.combine(tryMonad,s->(s+i)*2);
//AnyMValue[60]We can use AnyM to inject the ability to schedule emission from a Functional Java (or Javaslang Stream), for example
import static com.aol.cyclops.functionaljava.FJ.stream;
stream(Stream.stream(1,2,3)).schedule("* * * * * ?", Executors.newScheduledThreadPool(1))
.connect()
.forEach(System.out::println)
AnyM types need to accept AnyM as a return type from the transforming function in a flatMap operation. For monads that store a single value this means making a decision about what data to accept from non-scalar monads (monads that represent a sequence of values). Truncating the sequence may cause some runtime surprises for users, so we separated AnyM into AnyMValue and AnyMSeq.
If you wish to abstract across both Values and Sequences the method flatMapFirst method will only accept the first element from a sequence when performing a flattening transform into a value. E.g.
AnyM.fromOptional(Optional.of(10))
.flatMapFirst(i->AnyM.fromStream(ReactiveSeq.range(0,i));
//AnyM[0]
AnyM.fromStream(Stream.of(10))
.flatMapFirst(i->AnyM.fromStream(ReactiveSeq.range(0,i));
//AnyM[0..10]The visit method on AnyM allows users to safely determine whether this AnyM type is a value or a seq (without instanceofing or casting) e.g.
String type = AnyM.fromOptional(Optional.of(10))
.visit(v->"value",s->"seq");
//"value"
String type = AnyM.fromStream(Stream.of(10))
.visit(v->"value",s->"seq");
//"seq"oops - my bad