-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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: decorator support, like python decorator #36669
Comments
You can do something similar by using higher order functions or simply pointers. For example:
https://play.golang.org/p/dsolvSezkqf (It does give an error since http doesn't work on the playground.) . IMO; decorators are a misfeature of Pyton, in Go we generally don't need such a confusing way to call a function before another though magic syntax. |
Yep, the previous example can be achieved by higher order functions. But in the case of auto dependency injection, you have to do it manually if without decorator. type BookRepository interface {
GetBook(id int) (*Book, error)
CreateBook(book *Book) error
}
type bookService struct {
@di.Inject // `di.Inject(bookRepository)` will be called right after `bookService` instance is created
bookRepository BookRepository
}
func (s *bookService) CreateBook(title, author string) error {
book := NewBook(title, author)
return s.boookRepository.CreateBook(book)
}
func main() {
bookRepository := NewBookRepository()
di.Provide(bookRepository)
bookService := new(bookService) // `di.Inject(bookService.bookRepository)`will be called right after `new`, so bookService.bookRepository is not nil here now.
bookService.CreateBook("Jane Eyre", "Charlotte Bronte") // won't panic
} // package di
var container map[reflect.Type]interface{}
func Provide(obj interface{}) {
typ := reflect.TypeOf(obj)
container[typ] = obj
}
// decorator `Inject` injects a previous provided instance to obj, which might be nil before
func (obj interface{}) @Inject() {
typ := reflect.TypeOf(obj)
obj = container[typ]
} As far as this feature is concerned, maybe there is a balance between confusing, beauty, productivity, etc. |
Like @beoran , I can't see the point of I don't know what |
@ianlancetaylor I've updated the previous comment with the code of // Decorator `@Env` sets value to a int variable from System Environment
func (intVar *int) @Env(name string, defaultValue int) {
if intVar != nil && *intVar != 0 {
return
}
val, ok := os.LookUpEnv(name)
if !ok {
*intVar = defaultValue
return
}
intVal, err := strconv.Atoi(val)
if err != nil {
*intVar = defaultValue
return
}
*intVar = intVal
}
type Config struct {
@Env(/*name=*/ "HTTP_PORT", /*defaultValue=*/ 80) // `httpPort.Env("HTTP_PORT", 80)` will be called right after a new instance of `Config` is created.
httpPort int
}{}
var config Config // config.httpPort now equals to 80, because of decorator `@Env` is executed right after variable `config` initialization. Of course, things can be done without decorators. But the decorator syntax allows us to more conveniently alter variables, functions, etc. Refers: |
Go favors an explicit programming style. If you write out what the program does, everybody reading the program understands what happens. It's true that this leads to more verbose code than in some other programming languages. Go considers the benefit in readability to be worth the extra code. If we take that as a given, I have no idea what benefit Go gets from adding decorators. As far as I can tell, a decorator is a way to concisely invoke a function. Go already has ways to invoke a function. In your |
There are ways to do dependency injection in Go as well, either using reflection, or using code generation, such as this tool/library: |
For language change proposals, please fill out the template at https://go.googlesource.com/proposal/+/bd3ac287ccbebb2d12a386f1f1447876dd74b54d/go2-language-changes.md . When you are done, please reply to the issue with Thanks! |
Proposal: Go 2: function decorator support After above discussion, maybe only function decorator is a simple and useful sugar. Other decorators, like variable decorator is not so predictable that disobey GoLang's design principle.
Before: package main
import (
"fmt"
"log"
"time"
)
type Handler func(string) error
func Recover(h Handler) Handler {
return func(arg string) error {
defer func() {
if r := recover(); r != nil {
log.Printf("recoved from: %v", r)
}
}()
return h(arg)
}
}
func Async(h Handler) Handler {
return func(arg string) error {
go h(arg)
return nil
}
}
func Log(h Handler) Handler {
return func(arg string) error {
err := h(arg)
log.Printf("called f with %s, returns %v", arg, err)
return err
}
}
func hello(name string) error {
fmt.Printf("Hello, %s\n", name)
panic("Ouch")
}
var Hello = Async(Log(Recover(hello)))
func main() {
Hello("Beoran")
time.Sleep(time.Second)
} After: package main
import (
"fmt"
"log"
"time"
)
type Handler func(string) error
func (h *Handler) @Recover() {
wrapped := func(arg string) error {
defer func() {
if r := recover(); r != nil {
log.Printf("recoved from: %v", r)
}
}()
return h(arg)
}
*h = wrapped
}
func (h *Handler) @Async() {
wrapped := func(arg string) error {
go h(arg)
return nil
}
*h = wrapped
}
func (h *Handler) @Log() {
wrapped := func(arg string) error {
err := h(arg)
log.Printf("called f with %s, returns %v", arg, err)
return err
}
*h = wrapped
}
@Async
@Log
@Recover
func Hello(name string) error {
fmt.Printf("Hello, %s\n", name)
panic("Ouch")
}
func main() {
Hello("Beoran")
time.Sleep(time.Second)
}
@gopherbot please remove label WaitingForInfo |
Thanks for filling out the template. This proposal introduces a new way to do something that the language can already do. Perhaps if we had used this style initially this would be a good fit. But we have years of code that does not use this new approach. This new approach does not seem to provide enough advantage over the existing approach to warrant inclusion. Also, there is little support for this based on emoji voting. For these reasons this is a likely decline. Leaving open for four weeks for final comments. |
No further comments. |
Decorator may make writing GoLang projects more efficient. I hope GoLang support some kind of decorator.
The text was updated successfully, but these errors were encountered: