-
Notifications
You must be signed in to change notification settings - Fork 17.6k
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: deriving code ala Haskell #54799
Comments
This comment was marked as resolved.
This comment was marked as resolved.
You can probably achieve that without a language change. Just have a type parametered Node and a type switch in the definition of Show and Read for the different implementations? |
I don't think this is really eliminating the need to write boilerplate code. If I want to use the above code example to write: type MyToy struct {
n Node
...
}
node := &MyToy{n: &MathNode{Pi}}
fmt.Println(node.n.Show()) |
This comment was marked as resolved.
This comment was marked as resolved.
You can use embedding if you want MyToy to expose the same interface as Node (method promotion) type MyToy struct {
Node
...
} |
type Node interface deriving (Show, Read) I don't understand what that means. |
This is a language suggestion that stands for type Node interface {
Show() string
Read(string) bool
} |
Sure, but this proposal is to have the compiler autogenerate code for really common-use functions, i.e. Show, Read, or even more (see Haskell deriving capabilities) for algebraic types. |
Where are the full function signatures for |
I added the signatures to the example above: type Node interface deriving (Show, Read) Is equivalent to type Node interface {
Show() string
Read(string) bool
} |
But where/how do you define |
Well, this is not really magic since Haskell shows that it is possible to autogenerate canonical functions, Show, Read and the Go compiler also has the information about simple and algebraic types (structs) at compile time. This can be used to facilitate autogeneration functions Show, Read... at compile time. |
@fsouza Going by my vague memory of Haskell, I believe the intent is that type Node interface deriving (Show, Read) is roughly like type Node interface {
Show
Read
}
type Show interface {
Show() string
}
type Read interface {
Read(string) bool
} but also At least for |
Stricly, there is not a 1:1 analogy to Haskell, but in Haskell 'deriving' is used in conjunction with defining data types. Haskell doesn't have interfaces but type classes, that's not too important here. Indeed, there would be still some magic here such that the GO compiler would be able to generate a good enough default implementation for all simple and struct data types. |
In general, I don't think the feature is a good fit for Go. The fact that it'd require assist from the compiler makes it worse IMO. Like, I think that it wouldn't be as bad if this proposal also included information about this could be implemented by libraries, but at that point you'll probably be proposing macros, which have been proposed before. I don't think the use case for parsing/building syntax trees is common enough in the Go community to justify baking such "magic" into the compiler. |
Personally I think |
Needed derivation a while ago. My usecase:
Besides generic return errors.New("the action is not allowed for the user").UserID(userID) The problem we should implement these methods for both Logger and Error. It would be nicer to have something like type Contexter interface {
Int(key string, value int)
Int8(key string, value int8)
...
Bool(key string, value bool)
Any(key string, value any)
}
// UserID extension method for Contexter.
func (c Contexter) UserID(userID string) Contexter {
return c.Str("user-id", userID)
} Both logger and error instance implements
would add |
The custom logger can't embed the default logger? |
Indeed, |
Reply to my post post? Well, not exactly, the following is possible: return errors.New("error message").Str("key", "value").UserID(userID)
...
log.Err().Str("key", "value").UserID(userID).Msg("error message") It needs and supports chaining, which is exactly the same for logger and custom errors with the only difference: |
Ok I see. I am not sure that the problem can even be solved by the current proposal since the method signatures are different. |
@sirkon I believe the crux of your problem is the desire for method call chaining. If not for that you could just define a normal function like so, yes? func UserID(c Contexter, userID string) Contexter {
return c.Str("user-id", userID)
} To that end, just wondering if something like this would meet your needs. type ContexterOption func(Contexter) Contexter
type Contexter interface {
...
With(ContexterOption) Contexter
}
func UserID(userID string) ContexterOption {
return func(c Contexter) Contexter { return c.Str("user-id", userID) }
} Then instead of |
@rittneje it will of course, and I considered this way at one point. Not with closures, options were a struct like type Option struct {
Name string
Type OptionType
// Only one of these value fields below are to be used depending on the Type
Str string
Int int64
Flt float64
Any interface{}
} and a set of constructors. But, it is too verbose to my taste and I used a code generator instead in the end, that builds a code in logging and error repos using .go file like below as a reference: var (
// UserID ...
UserID string
// RegionID ...
RegionID string
// Time ...
Time time.Time
) |
@fkuehnel, code generation, as with "go generate" or home-rolled tools, is the "correct" way to accomplish these tasks, in Go. Another example would be Google's grpc generator tool, github.com/golang/protobuf/protoc-gen-go, which is capable of generating simple CRUD grpc services, based on the proto specs alone. As stated above, this is the documented approach for such use-cases. It is satisfactory for both testability and correctness, if not ergonomics. There are many things about Go which are not to anyone's taste in particular. It's unfortunate, but given that I'm one of the few Gophers in a Python shop that's been "transitioning" for longer than I've been there, I have a healthy appreciation for how deliberate the Golang dev team is. |
I really like this idea, keep the core language lean but 'go generate' can do the heavy lifting! |
Based on the discussion above, and the emoji voting, this is a likely decline. Leaving open for four weeks for final comments. |
No further comments. |
It's already discussed in 19412 that sum (discriminated union) types are nothing for GO 1.xx. However, unrelated to this is the fact that I find myself frequently writing boilerplate code for the interface type replacement of a discriminated union:
And this is just a small example of how it could go.
Now to the Haskell deriving inspired proposal:
The idea here is like in Haskell to have a well-defined way to generate common implementations, i.e. Show, Read for algebraic types. This makes code reviews much easier and helps avoid making errors. Obviously, writing and reading less code is beneficial. Here, one could eliminate most of the boilerplate code with the 'deriving' syntax,
but one could still choose to implement custom versions if needed. This means it would be backward compatible to GO 1.xx versions:
The text was updated successfully, but these errors were encountered: