Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Go 2: ability to create Dynamic Proxies like in Java #41897

Closed
jarrodhroberson opened this issue Oct 10, 2020 · 6 comments
Closed

proposal: Go 2: ability to create Dynamic Proxies like in Java #41897

jarrodhroberson opened this issue Oct 10, 2020 · 6 comments
Labels
Milestone

Comments

@jarrodhroberson
Copy link

I have a Java application that uses Dynamic Proxies to remove lots of boilerplate code, and we all know Java is notorious for repetitive boilerplate code. It depends on the Dynamic Proxy. Basically the ability to implement Interface methods at runtime.
By allowing me to implement Interfaces and just delegate the method calls to a Map<String,Object> or a map[string]interface{} in Go semantics. Here is an example of what I created to do this for me.

I understand why you can not implement methods at runtime in Go given the compiled nature of the language. But the lack of this language feature is causing me to have to write/generate thousands of lines of code to do something as simple as mapping methods to values in a map. ( well deeply nested maps of maps and arrays and array of maps of maps, etc )

For a simple example:

Lets say I have the following Interface

type Person interface {
    FirstName() string
    LastName() string
    Age() string
}

and I had a map[string]interface{} with that data, with the Dynamic Proxy Invocation Handler I can just tell it to map the method names to the keys in an the map like so.

@Override
    protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable
    {
        final String methodName = method.getName();
        if (this.values.containsKey(methodName)) { return this.values.get(methodName); }
        else { throw new IllegalArgumentException(methodName + " does not exist in " + Joiner.on(',').join(values.keySet())); }
    }

and then I can just provide any interface and any map and it will do the translation for me:

public static <T> Builder<T> as(@Nonnull final Class<T> cls)
    {
        return new Builder<T>()
        {
            @Override public T from(@Nonnull final Map<String, Object> map)
            {
                return Reflection.newProxy(cls, new MapBackedJavaBeanProxy(map));
            }
        };
    }

These are just the relevant snippets the full code has all the relevant error handling and casting.

I found this article and at first I thought it did what I needed, but it only allows for mapping from one instance to another instance of the same struct with the same interface methods.

More and more digging made me realize I need to do some reflection and create an implementation at runtime to delegate the calls to the map, and that I found out is impossible to implement methods at runtime.

@gopherbot gopherbot added this to the Proposal milestone Oct 10, 2020
@ianlancetaylor
Copy link
Contributor

If I understand this correctly, you want the ability to add a function that handles any method invocation on some value. This approach means that there is no static type checking at all.

You can already do part of this using the reflect package. Given a reflect.Value, the reflect package can look up a method by name, and call it.

What you can't do is define a method dynamically and call it with static code. As Go is a statically typed language, it seems very unlikely that that would ever be permitted.

@ianlancetaylor ianlancetaylor changed the title proposal: Ability to create Dynamic Proxies like in Java proposal: Go 2: ability to create Dynamic Proxies like in Java Oct 10, 2020
@ianlancetaylor ianlancetaylor added v2 A language change or incompatible library change LanguageChange labels Oct 10, 2020
@beoran
Copy link

beoran commented Oct 12, 2020

One way to do this is to embed a Go interpreter like this one: https://github.com/traefik/yaegi. This allows you to define Go functions at runtime although indirectly, through the interpreter.

I feel this may be an XY question though. Why do you actually want to do this? If we know your ultimate goal, we might be able to better advise you on how such a thing is done in Go, as opposed to trying to copy the Java approach.

@ianlancetaylor
Copy link
Contributor

Based on the discussion above, and the emoji voting, this is a likely decline. Leaving open for four weeks for final comments.

@ianlancetaylor
Copy link
Contributor

No further comments.

@jarrodhroberson
Copy link
Author

I thought it was pretty clear, that this is the desired behavior. There is no XY. This is what I want and why I want to be able to do it that way.

Lets say I have the following Interface

type Person interface {
    FirstName() string
    LastName() string
    Age() string
}

and I had a map[string]interface{} with that data, with the Dynamic Proxy Invocation Handler I can just tell it to map the method names to the keys in an the map like so.

being able to intercept the call to FirstName() and get the function name and be able to return the value in the map by return map[functionName]interface{} is pretty straightfoward and clear.

This is an EXTREMELY powerful ability. It allows you to put type safety onto unsafe things like map[string]interface{} with a single implemenation.

Lots of things like Google Datastore and other key value stores, graph stores and document stores; use a generic map[string]interface{} representation and you either have to code lots of boilerplate mappings to get a type safe interface for the Entity or you have to generate a bunch of boilerplate code like it is 1995 and you are using Visual Basic 4 to get data from an RDBMS and map it to objects.

@cocotyty
Copy link

I thought it was pretty clear, that this is the desired behavior. There is no XY. This is what I want and why I want to be able to do it that way.

Lets say I have the following Interface

type Person interface {
    FirstName() string
    LastName() string
    Age() string
}

and I had a map[string]interface{} with that data, with the Dynamic Proxy Invocation Handler I can just tell it to map the method names to the keys in an the map like so.

being able to intercept the call to FirstName() and get the function name and be able to return the value in the map by return map[functionName]interface{} is pretty straightfoward and clear.

This is an EXTREMELY powerful ability. It allows you to put type safety onto unsafe things like map[string]interface{} with a single implemenation.

Lots of things like Google Datastore and other key value stores, graph stores and document stores; use a generic map[string]interface{} representation and you either have to code lots of boilerplate mappings to get a type safe interface for the Entity or you have to generate a bunch of boilerplate code like it is 1995 and you are using Visual Basic 4 to get data from an RDBMS and map it to objects.

Have a look at this https://github.com/cocotyty/dpig

@golang golang locked and limited conversation to collaborators Jul 16, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants