A complete example (followed by the code it replaced): ``` package main
import ( "fmt" "os" ) func getMotd() (string, error<-) { motd, error<- := os.ReadFile("/etc/motd") return "MOTD:\n---\n" + string(motd) + "\n---\n", nil } func getPwd() (string, error<-) { pwd, error<- := os.Getwd() return "PWD:\n" + pwd + "\n", nil } func action() error { motd, error<- := getMotd() pwd, error<- := getPwd() fmt.Println(motd, pwd) return nil } func main() { if err := action(); err != nil { fmt.Printf("ERROR: %s\n", err) } } ``` The Go Playground (golang.com) <https://play.golang.com/p/nk3ePQhjEWC> or main could be lazy and let `error<-` panic when err != nil: ``` func main() { action() } ``` The current go code would be: ``` package main import ( "fmt" "os" ) func getMotd() (string, error) { motd, err := os.ReadFile("/etc/motd") if err != nil { return "", err } return "MOTD:\n---\n" + string(motd) + "\n---\n", nil } func getPwd() (string, error) { pwd, err := os.Getwd() if err != nil { return "", err } return "PWD:\n" + pwd + "\n", nil } func action() error { motd, err := getMotd() if err != nil { return err } pwd, err := getPwd() if err != nil { return err } fmt.Println(motd, pwd) return nil } func main() { if err := action(); err != nil { fmt.Printf("ERROR: %s\n", err) } } ``` https://play.golang.com/p/2_pk-FNv-dk On Tuesday, December 27, 2022 at 3:52:41 PM UTC-8 Oliver Smith wrote: > Discussing implementations of the (returns..., error) idiom from golang in > other languages, I noted a common thing: non-go developers often > conceptualized this as a back channel and didn't intuit that it's just a > return-value convention. > > This lead me to a new idea for addressing the excess verbosity of the > majority of trivial error handling in golang, > that is primarily just an additional reflection tag for functions and a > mechanism to leverage that from calling > sites. > > The error slot, written `error<-`. > > As a return type, it is just the type `error` but tags the function as > having return field N be recognized as the error slot. > ``` > func ReadFile1(filename string) ([]byte, bool, error) > // *can* optionally be written > func ReadFile2(filename string) ([]byte, bool, error<-) > ``` > > - Actual type is just 'error', > - One per function, > - Position doesn't matter, > - Named/anoymous, > - reflection gains a method that gives -1 for no error slot otherwise >= 0 > denoting which return field is official error slot, > - parsing: very context-specific cases, potentially even a dedicated > token/symbol, > > Beyond syntax support, this requires no compatibility breaking changes, > but one such break IS proposed: > > - Failing to forward/capture the error slot of a method in a function > without an error slot be a compile error, > > ``` > // slotted > func AnError() error<- { return io.EOF } > > // old code, unslotted > func OldError() error { return io.EOF } > > // old code, unslotted > func OldFunction() error { > OldError() // valid as currently > > AnError() // compile error suggesting `_ = AnError()` > > _ = AnError() // valid but vet warned > > return AnError() // valid > } > ``` > > Second, a change to provide an implicit nil-or-return shorthand: replace > the field's capture on the left side with "error<-". > > ``` > func Old() (int, error, bool, error) // Who knows > func New() (int, error<-, bool, error) // They have their reasons > > func Caller() (err error<-) { // Must be slotted to use new > syntax. > // Old code is still absolutely valid. > if i, e1, b, e2 := Old(); e1 != nil { > return e1 > } > > // Also still absolutely valid. > if i, e1, b, e2 := New(); e1 != nil { > return e2 > } > > // Because Old doesn't specify which is a slot, we can use either: > _, error<-, _, _ = Old() > // aka: if _, err, _, _ = Old(); err != nil { return } > > _, _, _, error<- = Old() > // aka: if _, _, _, err = Old(); err != nil { return } > > // Slotted functions are constrained to the error slot return tho. > _, error<-, _, _ = New() // valid > // _, _, _, error<- = New() // invalid, slot mismatch > } > ``` > > A final benefit of marking your function slotted is that if you fail to > capture a call slot yourself, it introduces an implicit panic > > ``` > func Panicy() error<- { > Old() // no change > New() // if new's error slot != nil, panics. vet warning. > } > ``` > > Some of the people I discussed this with immediately went to method > chaining, but I think the above change actually already > brings that approach to a more beautiful middle ground: > > > A couple folks asked me about chaining: Special-case a method returning > one value and an error slot: > > ``` > func OpenFile(name string) (*File, error<-) > func (f *File) ReadRaw() (*RawStuff, error<-) > func (r *RawStuff) Decode() (*Data, error<-) > > // Old Style > func StructFromFile1(name string) (data *Data, err error) { > var file *File > var raw *RawStuff > > file, err = OpenFile(name) > if err == nil { > raw, err = file.ReadRaw() > if err == nil { > data, err = raw.Decode() > } > } > > return > } > > // Rusty, too terse and prone to creating hard to decipher multi-part > mega-statements. > func StructFromFile2(name string) (*Data, error<-) { > return OpenFile(name).ReadRaw().Decode() > } > > // A properly go-beautiful style: > func StructFromFile3(name string) (*Data, error<-) { > file, error<- := OpenFile(name) > raw, error<- := file.ReadRaw() > return raw.Decode() > } > ``` > -- 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/36564865-4624-4dc4-89fe-ad509b033108n%40googlegroups.com.