Closed danieldietrich closed 8 years ago
Hi Daniel,
There are a couple of things in Cyclops, a relatively new project from Aol, which has a primary focus on interoperability across the different functional style libraries that are emerging, which might be helpful here.
This is still an evolving work in progress - there are a few interfaces that might be helpful for your analysis (either for ideas, reuse or for defining an interoperability standard).
The Monad interface wraps any Java Monad implementation, and makes some attempt to retain type information about the monad's type and the type it targets (i.e. both Stream<String> and String). This can be quite messy in practice. Monad source code
The AnyM class simplifies things by dropping the type information for the Monad being managed (i.e. it doesn't store Stream<String> just String). AnyM source code
In both cases the underlying managed monad instance can be accessed again via unwrap(), and it is possible to flatMap to any monad type, so in the case of AnyM it is always possible to extract a monad of a given type, in a type safe way.
Monad operations are managed under the hood by 'Comprehenders' implementations of the Comprehender interface. These facilititate the existence of the Monad wrapping classes and for comprehensions.
The plan is to create modules in Cyclops for Javaslang that would define Comprehenders for any monads within Javaslang and offer conversion between Javaslang types (and functionaljava, jdk, Guava, TottallyLazy). There are prototypes for some of these conversions already - they could just as easily live in Javaslang as Cyclops (perhaps it would be better?).
What do you think?
@johnmcclean-aol This sounds great. I also see the emerging 'diversification' of functional style libs in the Java eco-system. My intention creating Javaslang was (the same as for everyone else) to add missing functionality to Java, which in turn was designed with the focus to be backward compatible. I would love to see the new libs converging to a standard. Waiting for Java to include things does not help, it will take too long.
I'm currently busy with reviewing two new functional Java books. Soon I'm able to get back to this issue. I will take a look at your project and give you feedback.
@johnmcclean-aol @danieldietrich Hi John, I will take the time to look at this in the next week. I haven't looked at the details of your library, how do you handle the type safety and the lack of kinds in your library?
@danieldietrich @mperry Thanks guys. When I started working on this the goal was to provide away to get your projects, and the JDK/ Guava /TotallyLazy etc, to work together seamlessly!
Mark, under the hood this was very heavily influenced by the fantastic work you did in FunctionalGroovy (e.g. Groovy For Comprehensions). It started off totally dynamic and types have been added gradually. It's a process and it's definitely not complete (particularly for For Comprehensions).
I think type safety for AnyM is reasonably good (although I'm sure there are still bugs / holes to be found), at least if the Monad/ Monad-like class being wrapped takes a single generic parameter. You can create a factory method such as
public static <T> AnyM<T> anyM(CompletableFuture<T> anyM){
return new MonadWrapper<>(anyM).anyM();
}
and calling it will preserve the type e.g.
CompletableFuture<Data> cf;
AnyM<Data> monadWithData = asAnyM(cf);
Types would then change as normal when map / flatMap etc are applied.
AnyM<String> monadWithString = monadWithData.map(data -> data.toString());
One concern would be, if you needed to get back to the wrapped CompletableFuture, AnyM could be anything. But something along these lines should work, and be guaranteed to always return a CompletableFuture (i.e. if monadWithString wraps an Optional, we will still get a CompletableFuture back).
CompletableFuture<String> cf2 = asAnyM(CompletableFuture.completedFuture(""))
.bind( str -> monadWithString.unwrap())
.unwrap();
It's a little long winded, if you wanted to live dangerously, the following would work in our case (but blow up if code elsewhere was passing a Stream or a Try etc).
CompletableFuture<String> cf2 = monadWithString.unwrap();
Internally when a method is called, the actual method to be invoked on the wrapped Monad will be resolved by a Comprehender implementation. If no specific Comprehender has been implemented for the Monad type being wrapped, an InvokeDynamicComprehender will attempt pass on the method invocation.
@danieldietrich Hi Daniel, cyclops-javaslang integration is now available in Maven Central. There are converters to Javaslang types (Tuples, Functions, Streams etc) from Guava, JDK, FunctionalJava and simple-react. But also included are Comprehenders for Javaslang monads (as of Javaslang 1.2.2).
This means you can use Cyclops For Comprehensions (which are now strongly typed) with Javaslang, JDK and Cyclops Monads. E.g. Example below shows a mixed For Comprehension with a JDK CompletableFuture and a Javaslang List.
CompletableFuture<String> future = CompletableFuture.supplyAsync(this::loadData);
CompletableFuture<List<String>> results1 = Do.add(future)
.add(Javaslang.anyM(List.of("first","second")))
.yield( loadedData -> localData-> loadedData + ":" + localData )
.unwrap();
Where this::loadData returns "loaded" the output from
System.out.println(results1.join());
is
List(loaded:first, loaded:second)
With the whole thing being executed asyncrhonously.
You can also use the AnyM abstraction directly with Javaslang monads (AnyM is released as part of Cyclops 5.0.0). E.g. here is AnyM treating a Javaslang Try as Success biased (default behaviour)
assertThat(Javaslang.anyM(Try.of(this::success))
.map(String::toUpperCase)
.toSequence()
.toList(),equalTo(Arrays.asList("HELLO WORLD")));
And a similar example, treating a Javaslang Try as Failure biased
Exception e = new RuntimeException();
assertThat(Javaslang.anyMFailure(new Failure(e))
.toSequence()
.toList(),equalTo(Arrays.asList(e)));
I think we can regard this as something like a first draft, I think we can significantly improve this over time (for example by creating a Javaslang native for comprehension builder) - if there is interest to do so..
These links might be interesting
Cyclops for comprehensions Cyclops for comprehensions Javadoc Cyclops AnyM javadoc
@mperry Hi Mark, I would hope to have a similar module available for FunctionalJava over the next couple of weeks.
@johnmcclean-aol Awesome, thanks for doing all the work. I will check it out.
This may go along with #288. I've a proposal in the pipeline. There will be an article/blog post soon...
This analysis is finished. We re-added the Monad-interface to Javaslang 2.0.0-RC*. We lift functions to monadic functions now.
Here is a Scala example, taken from @travisbrown's abstracted. We need to check what is possible with Java in this direction.
Due to the lack of higher-kinded types, clearly Java's type system is not powerful enough to express this. The intention of this analysis story is, to investigate if we are able to create a Monad abstraction on top of existing classes without modifying them.
And a monad instance for it: