Questions about functions as value type in Go

xuanbao · 2017-10-31 02:00:08 · 611 次点击    
这是一个分享于 2017-10-31 02:00:08 的资源,其中的信息可能已经有所发展或是发生改变。

I read Rob Pike's wonderful post on errors being values and promptly went and wrote an incredibly simple wrapper to abstract my pains away from simple, parameterless functions that only return an error.

Cleaned up a lot of my code and it was great. What wasn't great was when 1 of 2 things happened.

1) The function had parameters

2) The function returns multiple types

I tried an incredibly naive approach with something that was essentially

type ParameterFunc func(params ...interface{}) error

As you might well imagine, passing something like json.Unmarshal(someBytes, &someValue) doesn't work. I was optimistic (and as stated, naive).

So. Is there a neat solution here that I'm not thinking about for the general case?

I've written things like type SomeBusinessHandler struct that have methods like func (z *SomeBusinessHandler) Persist(someValue SomeValueType) error that wrap all of the persist calls in a manner somewhat similar to bufio.Scanner.

But that's specific to the task at hand (which is alright), I'm just curious if there's a trick out there I haven't considered. I briefly considered reflection, but that sounds like a lot of overhead for something that can be solved in the design layer


评论:

jerf:

Is there a neat solution here that I'm not thinking about for the general case?

For the general case, no.

There are specific cases where you can do some things. For specific types, you can provide an error-handling function. Note how a function that returns two parameters can be directly fed into a function that expects two parameters. Go does not have first-class tuples, but it has some traces of it in the syntax.

You can create an object like an io.Writer that, once it errors, is guaranteed to continue erroring with the same error on every call. Users can then write code that blindly powers through a series of operations and just check the last error. However you'll need to document this, because in general I would not assume a given object necessarily works that way.

I'm not saying this is all the special-case solutions, just two that I've seen and leap to mind without me going out and searching. Feel free to post more in replies.

Reflection could do the job, but most segments of the Go community will look at you funny, and say bad things about your github repo if you try to post something that "solves" the error problem via reflection. You will certainly take a non-trivial performance hit, if nothing else.

earthboundkid:

Just write closures around the other functions to convert them into func() error.

stone_henge:
YourErrorMajig(func () error {
    return json.Unmarshal(someBytes, &someValue)
})
bkeroack:

Stop fighting the idiom. You'll be much happier if you write Go as it was designed to be written, instead of trying to force brevity or generic abstractions over everything. The teammates that end up working on your code later will also be much happier.

hobbified:

Don't pretend that there aren't large portions of the problem space where "Go as it was designed to be written" simply doesn't exist.

stone_henge:

That is just bad advice. Go is designed to be written like errors are just any value, because it recognizes that there is no one-size-fits-all solution to handling errors.

From the linked article, by one of the designers of Go:

Regardless of whether this explanation fits, it is clear that these Go programmers miss a fundamental point about errors: Errors are values.

Values can be programmed, and since errors are values, errors can be programmed.

Of course a common statement involving an error value is to test whether it is nil, but there are countless other things one can do with an error value, and application of some of those other things can make your program better, eliminating much of the boilerplate that arises if every error is checked with a rote if statement.

Idiomatically, it is very common with for example functions that repeatedly call an error producer until it doesn't produce an error.

hobbified:

Nope, that's pretty much what you get. Go function types are exact, they don't have any ability to move in covariant or contravariant directions. If you declare something as a func(...interface{}), then you'd better give it something that can take an arbitrary number of arbitrary things; a func([]byte, interface{}), which takes two things, of specific types, doesn't do the job. You can always produce a wrapper function that takes ...interface{}, asserts that the args it actually got are what json.Unmarshal wants, unpacks them, and passes them on, but it will inevitably be ugly.

JRMWarfare:

A function's (or method's) type includes it's specific parameter types. func (params ...interface {}) is different than a function with explicit parameters. What you can do, however, is create a function that accepts an error value and does something with it if non-nil. You could then call it like doSomethingOnError (json.Unmarshal (data, &target))

mf192:

I’ve found this pkg quite useful. Just throwing this into the mix.

https://github.com/pkg/errors

E,g., errors.Wrapf(err, “could not get file: [%v]”, file)

Further reading: Blog: https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully Video: https://youtu.be/lsBF58Q-DnY


入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

611 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传