Live data from Hacker News

Things about programming I learned with Go

mjk.space

81–90 of 157 posts

Re: Things about programming I learned with Go

#81
post #69

Earlier quoted context omitted.

A sum type `T = A | B` means that a value of type `T` can be either of type `A` or of type `B`. Such a type is used to express polymorphism. A product type (from "Cartesian product", i.e tuples) `T = A * B` means that a value of type `T` has a component that is of type `A` and another component that is of type `B`. It is used to aggregate parts into a whole. > Yes, I could go and research functional programming langu…

>A sum type `T = A | B` means that a value of type `T` can be either of type `A` or of type `B`. Such a type is used to express polymorphism. Taking this example (in English / pseudocode): Define a class Animal. Define Dog as a subclass of Animal. Define Cat as a subclass of Animal. Case 1) Now if we have a variable a1 that can, at runtime, contain (or refer to) either a Dog or an Animal instance. Case 2) And if we h…

What languages are you familiar with? It might help with explaining the concepts.

Since we're on a Go thread, I'll point out that Go's structs and tuples are both examples of product types.

Re: Things about programming I learned with Go

#82

Earlier quoted context omitted.

Inheritance is not a way to express sum types, it's a form of subtyping. A sum type is like a discriminated union, it can only be one thing at a time. Subtyping allows a value to have multiple (related) types simultaneously, which is much more expressive. I suppose you can use one level of single inheritance to emulate a sum type, but you could just as well emulate it with a discriminated union in Go, e.g. a struct w…

> Inheritance is not a way to express sum types, it's a form of subtyping. A distinction without a difference. This is probably most visible in languages like Scala and Kotlin, which implement algebraic data types by way of inheritance. That inheritance creates a subtyping relationship is irrelevant; there's a similar subtyping relationship between variants (or groups of variants) and the overarching type using a tra…

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.

Re: Things about programming I learned with Go

#83
post #79
post #69

Earlier quoted context omitted.

>A sum type `T = A | B` means that a value of type `T` can be either of type `A` or of type `B`. Such a type is used to express polymorphism. Taking this example (in English / pseudocode): Define a class Animal. Define Dog as a subclass of Animal. Define Cat as a subclass of Animal. Case 1) Now if we have a variable a1 that can, at runtime, contain (or refer to) either a Dog or an Animal instance. Case 2) And if we h…

I'm not sure if you can really express it in OO. Sum types are used to represent a closed set of variants. Inheritance like that isn't closed. In languages that have both OO and ML/Haskell-style type systems like Scala, the fact that the set is closed is denoted by the keyword 'sealed'. example sealed trait Color final case object Red extends Color final case object Green extends Color Color can only be Red OR Green.…

>Color can only be Red OR Green. In your example you can have an Animal, a Dog, a Cat, and other things can inherit Animal and create more variants.

True. I used traditional OO style inheritance in my example, and my question was about understanding the relationship between it and sum types, based on rbehrends' comment that I was replying to. Interesting, did not know this. So you are saying that sum types are sort of like inheritance with some limits - pre-defined types only. Sort of like an enum (though those do not have inheritance in them) but for classes or types.

Re: Things about programming I learned with Go

#84
post #69

Earlier quoted context omitted.

>A sum type `T = A | B` means that a value of type `T` can be either of type `A` or of type `B`. Such a type is used to express polymorphism. Taking this example (in English / pseudocode): Define a class Animal. Define Dog as a subclass of Animal. Define Cat as a subclass of Animal. Case 1) Now if we have a variable a1 that can, at runtime, contain (or refer to) either a Dog or an Animal instance. Case 2) And if we h…

What languages are you familiar with? It might help with explaining the concepts. Since we're on a Go thread, I'll point out that Go's structs and tuples are both examples of product types.

Most familiar with Python currently. Done some Ruby and Java and C and Pascal earlier. Some D and a bit of C++ and a bit of Go. I do understand that structs and tuples are examples of product types (because the range of the values for a struct or tuple is the Cartesian product of all the possible values for each field). My question was mainly about sum types as described by rbehrends, was trying to relate them in my mind (somewhat, if it makes sense) to traditional inheritance as in Python, Java or C++.

Re: Things about programming I learned with Go

#85
post #67

Earlier quoted context omitted.

I'm about 8 months in to using Go for a few largish projects and I'd say these are probably the two biggest things I still struggle a bit with. (not generics as others seem to obsess about) On errors, I'm really of two minds. In a way, it is a lot like how Java started with checked exceptions, it forced you to deal with the error. But at some point most people decided that was annoying and switched to runtime excepti…

Checked exceptions suck. Every method has to explicitly throw them up to a higher level where they can be handled causing tons of useless boilerplate. Its much better to just let unchecked exception bubble to a higher level of the app. This is a best practice in java so letting the exception bubble all the way back to the user is just poor programming.

I have to google what the "checked" actually means. But in Java you can throw parent classes, in Python you don't need to declare them at all. And honestly having a bad implementation is still better than no implementation. The main point here is to get correct, helpful error reporting so people understand what's going on. If I spend another 4 hours debugging an error message that was actually just a warning and not a biggy at all I'm going to throw up.

Re: Things about programming I learned with Go

#86
post #34
post #20

I'm no Go expert, so the following may just come from having not used it enough. The two things that shocked me out of continuing with Go were exception handling and package management. Exception handling is basically not implemented. Instead the Go developers dediced to add half a step from return codes towards exceptions and work with that. And now the community seems to have decided to just reraise everything that…

If you want a stack trace with your errors you can use errors.Wrap()[1]. This repo should replace the stdlib errors package. 1. https://github.com/pkg/errors/blob/master/errors.go#L180

Thanks! Can this also be added to already compiled tools somehow? E.g. kubernetes uses a lot of go components, but of course I only receive them in compiled form.

Re: Things about programming I learned with Go

#87
post #82

Earlier quoted context omitted.

> Inheritance is not a way to express sum types, it's a form of subtyping. A distinction without a difference. This is probably most visible in languages like Scala and Kotlin, which implement algebraic data types by way of inheritance. That inheritance creates a subtyping relationship is irrelevant; there's a similar subtyping relationship between variants (or groups of variants) and the overarching type using a tra…

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. "closed sum types", because it can be beneficial to have extensible sum types [1]. Not all sum types are closed; see polymorphic variants and extensible variants in OCaml.

Also, do not confuse the language mechanism used to specify a type with the type itself.

I do agree that inheritance is a generalization of algebraic data types.

> 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.

Try polymorphic variants in OCaml (mentioned above); or GADTs:

  # type _ t = Int: int -> int t | String: string -> string t;;
  type _ t = Int : int -> int t | String : string -> string t
  # Int 0;;
  - : int t = Int 0
  # String "";;
  - : string t = String ""
There's nothing inherent about summands not having a distinct declared type in ML and Haskell, only convention. Obviously, they do have distinct actual types.

Edit: A practical use case is the representation of nodes for an abstract syntax tree. An `Ast` type can benefit from having abstract `Expr`, `Statement`, `Declaration`, `Type`, etc. subtypes that group the respective variants together in order to get proper exhaustiveness checks, for example.

[1] See the question of how to type exceptions in Standard ML; in OCaml, this led to the generalization of exception types to extensible variants.

Re: Things about programming I learned with Go

#88
post #79
post #69

Earlier quoted context omitted.

>A sum type `T = A | B` means that a value of type `T` can be either of type `A` or of type `B`. Such a type is used to express polymorphism. Taking this example (in English / pseudocode): Define a class Animal. Define Dog as a subclass of Animal. Define Cat as a subclass of Animal. Case 1) Now if we have a variable a1 that can, at runtime, contain (or refer to) either a Dog or an Animal instance. Case 2) And if we h…

I'm not sure if you can really express it in OO. Sum types are used to represent a closed set of variants. Inheritance like that isn't closed. In languages that have both OO and ML/Haskell-style type systems like Scala, the fact that the set is closed is denoted by the keyword 'sealed'. example sealed trait Color final case object Red extends Color final case object Green extends Color Color can only be Red OR Green.…

>final case object Red extends Color

On a side note, that syntax seems a little counter-intuitive - the extends word seems to indicate that Red is a subclass of Color, but from what you said seems more like Red and Green are values for Color. So it seems like an enum would be more appropriate here (for this use of Red, Green and Color)?

Re: Things about programming I learned with Go

#89
post #80
post #77

Earlier quoted context omitted.

> not generics as others seem to obsess about One reason people obsess about generics is specifically because of error handling. With generics, you could implement Result and Option types, which make error handling significantly more sane.

You'd need generics and algebraic types to implement Result/Option

False. That feature allows a more efficient implementation, but (T, error) (or an equivalent struct) can be reasoned about in much the same way as Result. You don't need a tagged union when you can use the nil-ness of one of the two values in the tuple as the tag. Similarly, Option is just a wrapper around a nullable T.

Re: Things about programming I learned with Go

#90
post #88
post #79

Earlier quoted context omitted.

I'm not sure if you can really express it in OO. Sum types are used to represent a closed set of variants. Inheritance like that isn't closed. In languages that have both OO and ML/Haskell-style type systems like Scala, the fact that the set is closed is denoted by the keyword 'sealed'. example sealed trait Color final case object Red extends Color final case object Green extends Color Color can only be Red OR Green.…

>final case object Red extends Color On a side note, that syntax seems a little counter-intuitive - the extends word seems to indicate that Red is a subclass of Color, but from what you said seems more like Red and Green are values for Color. So it seems like an enum would be more appropriate here (for this use of Red, Green and Color)?

A Scala “object” declaration declares a singleton; it's an object, but you can also define object-specific class features.

If the instances need behavior (which in practice they often do), this is useful. Presumably, in the example, they would in a substantive program, but the behavior is irrelevant to the illustration and thus omitted.

Post reply on HN