Live data from Hacker News

Things about programming I learned with Go

mjk.space

151–157 of 157 posts

Re: Things about programming I learned with Go

#151

Earlier quoted context omitted.

Okay, but its not, not from the perspective the best practice comes from. The most common use cases for object inheritance can be delivered with object composition instead. This is much more like loop vs recursion.

The dominant use case for inheritance is polymorphism, which composition cannot do.

You'd use an interface for polymorphism, much better.

That said, I have to thank you for your suggestion of doing sum types with inheritance. I hadn't thought of it, and the use case presented itself at work yesterday. So I learned something new, thanks. It worked well, an abstract class with shared fields, and a derived class for every type in my sum type with distinct fields added to them. Now a variable of the abstract type is effectively constrained to one of the set of its derived children. Limit this to one level and you've got a pretty nice simulated sum type. Just need to remember to handle all cases when working with it. It worked like a charm, wouldn't have thought of it without your comment.

Re: Things about programming I learned with Go

#152
post #4

> Authors of Go wanted to give users more flexibility by allowing them to add their logic to any structs they like. Even to the ones they’re not authors of (like some external libraries). Except that you can't. "You "cannot define new methods on non-local type[s],". You have to compose them. That makes the struct function definition syntax a bit moot in my opinion.

Yes, you're right. I was somehow convinced that this is possible. Which is BTW not a good idea anyway (or at least I really don't like similar concept in Ruby - monkey patching).

Do you know why Go authors decided to put method definitions outside types definitions? Technically I don't see anything to stop the syntax from supporting the otherwise.

Re: Things about programming I learned with Go

#153

Earlier quoted context omitted.

The dominant use case for inheritance is polymorphism, which composition cannot do.

You'd use an interface for polymorphism, much better. That said, I have to thank you for your suggestion of doing sum types with inheritance. I hadn't thought of it, and the use case presented itself at work yesterday. So I learned something new, thanks. It worked well, an abstract class with shared fields, and a derived class for every type in my sum type with distinct fields added to them. Now a variable of the abs…

> You'd use an interface for polymorphism, much better.

Interface inheritance is simply the special case of inhering a purely abstract class. I've never seen a good argument why restricting abstract classes to purely abstract methods is worthwhile and several that speak against it [1, 2]. Languages that separate implementation and interface inheritance (such as Sather) have been tried, but never caught on, because it just leads to a lot of code duplication in order to write the interface twice. It can be useful to have inferred interfaces (as in Dart or OCaml), but there's nothing inherently better about using interfaces over more general abstract classes.

A common use case is to represent types of the form `T * A | T * B`, which (without implementation inheritance) just leads to code duplication for `T` or extra destructuring efforts (if you turn it into a representation of form `T * (A | B)`).

[1] Example 1: it gets in the way of doing Design by Contract as part of the interface of a class, even if you want interface-only inheritance.

[2] Example 2: Abstract classes with significant implementation parts show up all the time in design patterns. (Note that this is not about whether design patterns are good or bad, just that they reflect observed common practice).

Re: Things about programming I learned with Go

#154
post #107

Earlier quoted context omitted.

I was mostly thinking of Java. Ignoring an exception would be done with try {....} catch (Exception e) {}, while in Go, you would do: x,_ := f(...). And if you want to check a returned error I think the if err!=nil {...} is still a bit less typing than the Java version, and you don't have to declare checked exceptions you might throw.

In Go, if you call three functions you need to repeat the x, err := do_something() if err != nil { return nil, err } ceremony three times. In Java it happens by default, because the language designers agreed this is by far the most common case. Ignoring an error is almost always a serious mistake, so the fact that Go makes it easy and not blatant is not a good thing.

Yes, I certainly was not advocating ignoring any error. Which is, why exceptions are of dubious use. If you only check exceptions around a larger block of function calls, or every several levels of the call stack, then you might miss the exact source of the exception, unless the exception type is unambiguous. That is, why I find the Go version tedious for sure, but still better than to wrap single function calls into try... catch.

Re: Things about programming I learned with Go

#155
post #82

Earlier quoted context omitted.

Not true. You can model sums with inheritance along with an exclusivity constraint, but it's a weird model and subtyping is more general. Further, the idea of each variant being its own type is inherently a subtyping sort of idea. Sums don't give names to their conponents, only distinctions.

> You can model sums with inheritance along with an exclusivity constraint, but it's a weird model and subtyping is more general. You don't need an exclusivity constraint. Exclusivity is purely a modularity concern; you will still have a finite number of variants in any given software system; traditional ADTs and exclusivity just limit the declaration of variants to a single module. See also "open sum types" vs. "clo…

I'm aware of polymorphic variants and row types and the like. My concern is one of modularity in that I consider a running system pretty dead, extensions during coding are where language features and their logics are interesting. Closing your sums is valuable to consumers: they have complete induction principles, e.g.

Open sums and row types are a little different in that they represent a fixed/closed type but retain enough information to extend it more conveniently and to see it as structurally related to other (open) sums/products. This is no doubt super useful, but I see it more as an application sitting atop polymorphism rather than a fundamental concept.

Finally, I am exactly confusing the language mechanism with the type it intends to model because exactly now we have to think about things as a mechanism and model. This is where breakdowns occur.

Anyway, I doubt there's a real difference of opinion here. I'm very familiar with the concepts you're discussing, but perhaps argue that they are not as fundamental as regular, closed sums/products and language support for those simplest building blocks is important.

Re: Things about programming I learned with Go

#156
post #149

Earlier quoted context omitted.

>ceremony three times. In Java it happens by default, What happens by default? Not clear. (I know Java, but not up to date with recent versions.) >Ignoring an error is almost always a serious mistake Agreed.

In Java, an exception outside a catch block automatically stops the method and raises the exception to the caller, and then their caller, and so on. In Go you have to write this after every function call (except the tiny minority that can only panic).

Thanks. I did know that about Java (having used it), just that it was not clear to me from your earlier wording, that that is what you were saying. NP.

Re: Things about programming I learned with Go

#157

Earlier quoted context omitted.

You'd use an interface for polymorphism, much better. That said, I have to thank you for your suggestion of doing sum types with inheritance. I hadn't thought of it, and the use case presented itself at work yesterday. So I learned something new, thanks. It worked well, an abstract class with shared fields, and a derived class for every type in my sum type with distinct fields added to them. Now a variable of the abs…

> You'd use an interface for polymorphism, much better. Interface inheritance is simply the special case of inhering a purely abstract class. I've never seen a good argument why restricting abstract classes to purely abstract methods is worthwhile and several that speak against it [1, 2]. Languages that separate implementation and interface inheritance (such as Sather) have been tried, but never caught on, because it…

What you gain by following more constrained constructs is knowing you can't accidentally use it for something else then what you needed it for.

Take or. You don't need it to be a language construct, the more general conditionals can do it: if y doX else if z doX. While that's more general and thus more powerful, its less expressive. When you mean or use or, less chance of doing it wrong and the intent is clearer and unambiguous to other readers.

Can you define your notation here, I don't follow it. Why do you want a type T * A | T * B ? This I doubt is the real use case, that's already your solution to a use case. Give me a concrete example.

Post reply on HN