Hello!

I would like to share my use for generics, as there have been a public
callout for feedback:

> Second, we know that many people have said that Go needs generics, but we 
> don’t necessarily know exactly what that means. Does this draft design 
> address the problem in a useful way? If there is a problem that makes you 
> think “I could solve this if Go had generics,” can you solve the problem when 
> using this tool?

My goal is to build a caller-friendly API for an engine, which
orchestrates processing of various commands. Each command has got a
specific input and output type. The engine, besides other things,
wraps command execution with various aspects (transactions,
performance logging, security checks, queueing,... and possibly
load-balancing to different nodes). Therefore, commands aren't
implemented as plain functions, but are implemented as handlers
identified by keys.

I had been playing with generics, and this the solution, which I came
up with (for simplistic, reduced, engine scratch) :
https://go2goplay.golang.org/p/1CRFNDCI6Lg

    type Engine struct {
        Commands map[string]func(input interface{}) interface{}
    }

    func (e *Engine) Invoke(key string, input interface{}) interface{} {
        fmt.Printf("INVOKING COMMAND: %s\n", key)
        return e.Commands[key](input)
    }

    type Command(type I, O) struct {
        Key     string
        Handler func(input I) O
    }

    func (c Command(I, O)) GenericHandler(input interface{}) interface{} {
        return c.Handler(input.(I))
    }

    func (c Command(I, O)) Invoke(engine *Engine, input I) O {
        return engine.Invoke(c.Key, input).(O)
    }

    var c1 = Command(int, string){
        Key: "C1",
        Handler: func(n int) string {
            return fmt.Sprintf("first command output %d", n)
        },
    }

    var c2 = Command(int32, int64){
        Key: "C2",
        Handler: func(n int32) int64 {
            return int64(n * 3)
        },
    }

    type C3Input struct{ a, b int }
    type C3Output struct{ x, y int }

    var c3 = Command(C3Input, C3Output){
        Key: "C3",
        Handler: func(i C3Input) C3Output {
            return C3Output{i.b, i.a}
        },
    }

    func main() {
        engine := &Engine{
            Commands: map[string]func(input interface{}) interface{}{
                c1.Key: c1.GenericHandler,
                c2.Key: c2.GenericHandler,
                c3.Key: c3.GenericHandler,
            },
        }

        var (
            // explicit types to demonstrate static type-checking
            c1Result string   = c1.Invoke(engine, 5)
            c2Result int64    = c2.Invoke(engine, 7)
            c3Result C3Output = c3.Invoke(engine, C3Input{11, 13})
        )

        fmt.Printf("Result #1: %#v\n", c1Result)
        fmt.Printf("Result #2: %#v\n", c2Result)
        fmt.Printf("Result #3: %#v\n", c3Result)
    }

Above example doesn't thoroughly demonstrate the use of returned
variables, and static type checking. But, the goal is, that:

- input type is statically type-checked, when calling Invoke,
- output type is returned with correct static type, and can be used
for auto-completion and static type-checking.

Maybe, the engine's API can be designed in a better way. However, I
hope this feedback will be useful for designing Go's generics.

Thank you for the attention!

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CA%2BNaYLPxF7twitScA-TAXpnhX0-jdP8kt2gbVCS-OYX9BR5stQ%40mail.gmail.com.

Reply via email to