Git Product home page Git Product logo

Comments (10)

smillies avatar smillies commented on June 1, 2024

The same thing happens with an empty list, i. e. constructed like this:

List<Integer> list = Collections.emptyList();

from cyclops-integration.

johnmcclean avatar johnmcclean commented on June 1, 2024

At the moment cyclops doesn't treat List as a full Monad (there is no reason why it can't, it just needs a specific Comprehender for List). What it does do is convert List to Stream in certain circumstances.

If you do this instead, your test will pass.

    @Test
 public void test() {
    List<Integer> list = Arrays.asList(1,2,3);
    Monad<Stream<Integer>, Integer> m = Monad.of(list.stream());
    AnyM<Integer> any = m.anyM();
    AnyM<Integer> mapped = any.flatMap(e -> any.unit(e));
    List<Integer> unwrapped = mapped.asSequence().toList();
    assertEquals(list, unwrapped);
}

Or alternative to avoid using Monad, you can use

  AsAnyM.anyM(list);  

AsAnyM provides an overloaded method that performs the conversion from List to Stream. But we should also add a native Comprehender for List also (feature enhancement).

from cyclops-integration.

smillies avatar smillies commented on June 1, 2024

I think neither alternative will work for me, because I don't know when I'm dealing with a list. It's only known at runtime. I'm trying to do something like this:

static <E, M> Function<E, AnyM<E>> extend(Function<E, M> lookup, Supplier<M> defaultValue) {
    return s -> Monad.of(Optional.ofNullable(lookup.apply(s)).orElseGet(defaultValue)).anyM();
}

This code extends the given partial function to a total function and lifts the result to some monad. The element type is E, the container type would be M, if that were allowed. At runtime M might be Optional or List.

I'm trying to hide my internal use of Cyclops, so having clients using AnyM is out of the question.
Using Streams would also not work, because I can't put them in the lookup table behind the function, since they can be consumed only once. Switching everything to Suppliers would again appear arbitrary to my clients.

I guess a comprehender would do its work dynamically at runtime. Would it be difficult to write a List comprehender? Are there guidelines that I could follow if I wanted to try that myself? How is a custom comprehender activated?

from cyclops-integration.

johnmcclean avatar johnmcclean commented on June 1, 2024

You have a few options that might help

AsAnyM.convertToAnyM takes an Object as a parameter and does it's best to convert it into a supported Monad type (so List will become a Stream). E.g. this would wrap a Stream inside an AnyM when lookup returns a List (leaving Optional as Optional).

 static <E, M> Function<E, AnyM<E>> extend(Function<E, M> lookup, Supplier<M> defaultValue) {
     return s -> AsAnyM.convertToAnyM(Optional.ofNullable(lookup.apply(s)).orElseGet(defaultValue));
 }

I don't know if this solves any traverse once issues you have with Stream (there is a Streamable construct that can lazily capture and replay the values of a Stream. It may actually make more sense if our Collection converter converted to it rather than the traverse once Stream (but it currently doesn't).

Writing your own Comprehender

All you need to do is implement this interface, and register it as JDK Service : https://github.com/aol/cyclops/blob/master/cyclops-monad-api/src/main/java/com/aol/cyclops/lambda/api/Comprehender.java

For List we would have to provide map & flatMap implementations, which at least initially could be done via stream() and map/flatMap followed by collect. (getTargetClass() should simply return List.class).

Comprehenders get picked up via the JDK Service Loader mechanism. There is a file in META-INF/services called com.aol.cyclops.lambda.api.Comprehender that details the implementations for a jar. You just add your new Comprehender implementation class there.

https://github.com/aol/cyclops/blob/master/cyclops-monad-api/src/main/resources/META-INF/services/com.aol.cyclops.lambda.api.Comprehender

If you do write one, it would be great to see it submitted back! (If not I could write one - but I can also always help with any questions).

from cyclops-integration.

smillies avatar smillies commented on June 1, 2024

Thank you, convertToAnyM has been very helpful! When I have time, I might still try writing a Comprehender. Of course I'll let you know if I do.

Meanwhile, I have another question: I think it would be useful to have a uniform way to get back the type over which an AnyM was originally constructed. For example, when wrapping a List in AnyM, I'd expect unwrap() to give me a List, but it doesn't, it gives me a SequenceImpl. Thus I find myself writing utility functions like this:

static <E> Optional<E> unwrapOpt(AnyM<E> elem) {
    return elem.unwrap();
}

static <E> List<E> unwrapList(AnyM<E> elem) {
    return elem.asSequence().toList(); 
}

This isn't nice, because it leaks the existence of AnyM to my clients. Instead of unwrapList(myMethod()), I'd much rather define

<M,E> M myMethod() {
    AnyM<E> elem = ...
    return elem.unwrap();
}

I guess that AnyM just doesn't remember what object type was used to construct it.

BTW: I fear a GitHub issue may not be the appropriate forum to discuss such things. Should I direct my questions someplace else?

from cyclops-integration.

johnmcclean avatar johnmcclean commented on June 1, 2024

Here is fine, but there is also a GitHub chat room if you prefer.

AnyM.unwrap should return whatever the Monad type currently is. Because we don't have a Comprehender for List it gets converted into a Stream (in fact a cyclops.SequenceM which extends Stream, and SequenceImpl implements SequenceM). If we add the List comprehender we can also make sure the CollectionConverter does not to convert Lists to Streams (SequenceM in practice). That way AnyM.unwrap would always return a List in your case.

I'll add a specific issue for a List Comprehender.

from cyclops-integration.

johnmcclean avatar johnmcclean commented on June 1, 2024

By the way, you can write utility functions that will safely unwrap to any type (there are built in ones for Optional, Sequence / Stream and CompletableFuture). Your optional example would fail if the AnyM didn't wrap an Optional, but this should always work :-

 static <E> Optional<List<E>> unwrapOpt(AnyM<E> elem) {
     return elem.toOptional();
  }

If elem was originally an Optional with a single value it will now be an Optional with that value wrapped in a List. If elem was a List, that List will be embedded in our Optional.

from cyclops-integration.

smillies avatar smillies commented on June 1, 2024

Thanks for explaining that. I didn't see that SequenceImpl was just a Stream. So the client can pass in a lookup function that yields a list, and must remember to manually collect(toList()) in the end. I just need to document that bit of behaviour. And once there's a List comprehender, things will be simpler. When do you think the 6.1.0 milestone will be reached?

from cyclops-integration.

johnmcclean avatar johnmcclean commented on June 1, 2024

Should have something available within the next week.

from cyclops-integration.

johnmcclean avatar johnmcclean commented on June 1, 2024

Released

from cyclops-integration.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.