4 ms·
In your examples there, am I not having to create a new type per case? So a Reply<String> and a Reply<Customer> will require me to create two classes?
by AndrewDucker 14y ago
In your examples there, am I not having to create a new type per case? So a Reply<String> and a Reply<Customer> will require me to create two classes?
- ansible 14y agoYou'd just want to have your types implement the interface for Reply, and provide a method with the same signature that clients can use. Here's what I think you want, written in (as far as I am able to) idiomatic Go: http://play.golang.org/p/KI79ynfRg2 http://play.golang.org/p/KI79ynfRg2
- quatrevingts 14y agoThis means that your objects are aware of every "container" that might hold them. This does not scale, and it certainly doesn't work for reusing data structures.
- Evbn 14y agoIt scales in the sense that all types are extensible: you can make A implement B by adding the appropriate definitions anywhere, not just in the file that defines B. Rubyists call this monkey patching and they love it (but Ruby monkey patching can do wackier things beyond adding new methods)
- quatrevingts 14y agoBut I can only make A implement B in the file that defines A, at least according to the comments here: https://news.ycombinator.com/item?id=5443344 https://news.ycombinator.com/item?id=5443344
- AndrewDucker 14y agoSo every type that can be returned from a service call has to implement Reply? That's acres of template code being duplicated all over the place - and if I need to fix a defect in it I need to change it in every place! That strikes me as terribly unmaintainable.
- grey-area 14y agoSo every type that can be returned from a service call has to implement Reply? Yes, if you want it to actually be a Reply, it has to implement Reply. That's acres of template code being duplicated all over the place No, if you need to reuse code you can use other techniques like embedding (not shown in examples). The features and restrictions differ from many other OO languages but I think the creators of Go deserve a little more credit than you are giving them - they do not produce unmaintainable code (see std lib for Go), worked on a large scale with C++ OOP previously, designed the forking Unix operating system, and are not unaware of the ramifications of the decisions taken.
- ansible 14y agoWell, you can implement methods based on the Reply interface instead. As long as there are common fields or methods that all the types have, you can have a single CheckStatus() that uses those. Maybe I don't know what exactly you want. The Foo and Bar in my example have quite different CheckStatus() methods because those two structs are quite different. If there were more similar, then you could share more code.
- grey-area 14y agoIn your examples there, am I not having to create a new type per case? So a Reply<String> and a Reply<Customer> will require me to create two classes? In the second example no, in the first example, if you want two types to conform to an interface, you need two types - Go interfaces require an implementation. In real code this is not really a limitation - types require minimal boilerplate in Go, and the complete definition can be one line (e.g. type StringReply string). You can create them very easily, and wrap standard types in them (see the error type example NegativeSqrtError). I'm not too familiar with C# generics I'm afraid so am guessing as to what you want to do. Go already has support for generic maps and lists which can take any type or interface (as in your dictionary example), so that's not really a problem. Maybe someone else who knows both better will jump in. If you wanted to treat two types (string and Customer) in Go as the same sort of thing (a Reply), you can do this with an interface. Then you can work with anything which conforms to that interface. Let's say you have this reply interface: type Reply interface { Reply() string } That requires a Reply method returning a string. You want to pass around both a string and a Customer (using say its name) as a reply? Define what each does when it replies - this is required so that the compiler knows you are passing the right sort of type, and so that you can actually use them as replies (NB you can't add to built-ins, so you have to add a new StringReply type): type Customer struct {Name string} func (c *Customer) Reply() string { return fmt.Sprintf("%s",c.Name) } type StringReply string func (s StringReply) Reply() string { return string(s) } http://play.golang.org/p/uLgSpbTx24 http://play.golang.org/p/uLgSpbTx24 so you could have a list []Reply or a map[string]Reply, or some custom collection type which can only have Replies added to it and nothing else. If you want to reuse code between types, you can have helper methods or embedded types (NB only reusing code, not data, this is not inheritance). Code above is trivial of course, and if you see code repeated in other examples it'll be because of these are little toy examples - you can use composition of types or helpers which act on interfaces to avoid repeating yourself. In real code you tend not to have quite such simple interfaces or types. If you feel we're not getting at what you want, posting a C# example of something you think would be impossible in Go would be helpful, otherwise you can assume that what most of what you can do in C# etc can easily be handled in Go as well even if the concepts don't map 1-1.
- AndrewDucker 14y ago