vavr-io / vavr

vʌvr (formerly called Javaslang) is a non-commercial, non-profit object-functional library that runs with Java 8+. It aims to reduce the lines of code and increase code quality.
https://vavr.io
Other
5.76k stars 637 forks source link

make public: λModule.ReflectionUtil #1239

Closed jknack closed 8 years ago

jknack commented 8 years ago

Hi,

Thanks for javaslang! My project http://jooby.org promotes promotes the use of javaslang by making it a required dependency but also using javaslang as part of my API.

I wonder if you could make λModule.ReflectionUtil a public class and/or expose their method some way or another.

I'm planning to use the λModule.ReflectionUtil methods for defining routes and will love to reuse your wonderful methods.

danieldietrich commented 8 years ago

Hi Edgar,

I saw your announcement of adding Javaslang as dependency to jooby. Thanks for using it!

The λModule.ReflectionUtil was removed starting with Javaslang 2.0.0. Which version do you currently use?

I chatted with Brian Goetz on Twitter. The lambda serialization is not a stable feature that applications should use to get runtime informations about functions. It is a hack. Javaslang used it for the old 1.x Match API. The new 2.0 Match API does not need it any more.

I'm sure that your use-case can be expressed directly, without these hacks. Do you have some code that I can take a look at? What is the API you are modelling hand how the semantics are specified? Maybe I can show you a way how to express it effectively with Javaslang.

jknack commented 8 years ago

jooby is on javaslang 2.0.0... and didn't realize the class was removed from 2.x. I have a proof of concept working example with λModule.ReflectionUtil :)

Today, jooby supports script/inline routes. They look like this:

get("/", req -> {
  return ...;
});

If you need access to a HTTP param, then:

get("/", req -> {
  int foo = req.param("foo").intValue();
  return ...;
});

It's not bad and the param API is good...

Now, saw you were able to detect param types and hack type erasure in 1.x with that I was able to say for example:

get("/:id", (String id, Integer foo) -> {
  // ...
  return ...;
});

This way the route looks more "natural" and don't force users to get a reference to the request.

The idea was to extend jooby router with your CheckedFunction[X], detect the param types and call the param API behind the scene.

make sense?

danieldietrich commented 8 years ago

Yes, I understand. I've studied the jooby docs and also had a look at other http rooting impls.

I like jooby's take on rooting by defining handlers 'on-the-fly' in an instance initializer without the need of controller classes. Indeed, I also see no other possibility than dynamically inspecting the lambda types at runtime to parse and convert the request parameters.

Statically compiling rooting files like the Play Framework or using compile time annotations @GET("/:id") go not adhere with the philosophy of jooby.

So I think I can support you by adding lambda reflection as a public feature. I will release it in 2.1.0.

I think reflection should be used with caution. To make it a little bit harder to use that feature I could place it in the interface λ, which is hard to type :)

interface λ {

    static Type getType(Serializable lambda) {
        // ...
    }

    interface Type {
        int getParameterCount();
        String getParameterName(int index); // <--- I think that should be also possible
        Class<?> getParameterType(int index);
        Class<?> getReturnType();
    }
}

What do you think? Would that help?

danieldietrich commented 8 years ago

@jknack But beware, there are special cases:

class App {
    {
        final String doh = ...;
        get("/:id", (String id, Integer foo) -> {
            // do sth with 'doh'
            return ...;
        });
    }
}

Here the reflected lambda type will have 3 parameters: String id, Integer foo and String ???. I don't know if the name of a captured arg can be also reflected. Need to take a look...

But I think it might also be possible to provide a function

boolean isParameterCaptured(int index);

Update: The captured args can be muted completely, we do not have to deal with them.

jknack commented 8 years ago

Awesome! It works for me, but... if you already removed it from 2.0.0 I feel bad if you will add it just for me. I can probably copy what you did from 1.x branch and add it to jooby.

Anyway, your call... if you add it, I will reuse it. Otherwise, will copy what you did ;)

Thanks a lot @danieldietrich!

danieldietrich commented 8 years ago

It is ok, I will add it to the λ interface, it is a general purpose functionality there. The getType() of (Checked)Function1..(Checked)Function8 will not be added. I don't want to have it as standard functionality for arbitrary functions because it is fragile. But under certain circumstances it might make sense to do lambda reflection.

ruslansennov commented 8 years ago

FYI I have working prototype of gwt-module for Javaslang. I believe it will be merged in master branch sometime in the future. Reflection is one of problem that cannot be resolved.

danieldietrich commented 8 years ago

Info: There are JVM changes in the pipeline ("Project Panama" / new Vector API, however, no due date) that require runtime lambda reflection (see this video).

danieldietrich commented 8 years ago

There are several problems with this kind of reflection.

  1. Brian Goetz told me that it is 'thin ice'. He would not build features upon lambda reflection.
  2. it does only work for lambdas. If we pass a method reference, it will throw at runtime
  3. it is relatively slow
  4. gwt does not support reflection

Therefore we will not ship this feature with Javaslang. If it is really needed I recommend to copy/paste the code Javaslang once contained (must have been in a RC before 2.0.0 final)