Live data from Hacker News

Leveraging the Go Type System

gopherguides.com

51–60 of 112 posts

Re: Leveraging the Go Type System

#51
Personally, I think the only part that makes sense is making a type to hide the implementation. That String() method is kinda nasty: It's O(n), and you need to edit it every time a category gets added/removed/changed.

I think it'd make more sense to just make a idiomatic struct:

    type Genre struct {
      id int
      name string
    }
    func New(id int, name string) Genre {
      return Genre {id, name}
    }
Then, you can define your categories normally:

    var (
      Adventure = genre.New(1, "Adventure")
      Comic     = genre.New(2, "Comic")
      // etc
    )
And have a O(1) String() method that doesn't need to be edited every time a new category gets added/removed/changed:

    func (g Genre) String() string {
      return g.name
    }
You can also change the implementation of the type without breaking consumers (e.g. maybe you want a pointer to a struct instead of a struct)

This also means that you don't accidentally leak an "is-a" relationship between the nominal type and the underlying implementation type

    var foo Genre = Adventure
    foo + 1 // ought to throw a compilation error!

Re: Leveraging the Go Type System

#52

Earlier quoted context omitted.

One of Go's strengths is that it should make it easier to swing between the optimized abstraction and the less-optimized abstraction. If you start with that data represented as strings and then find yourself backed into a performance corner and need to change the representation, Go types and type-based compile-time method selection can make it a bit easier to make that change. Even in this era of fast computers and p…

That's a great point. Anyone who has spent significant time with Go knows how easy it is to do a large refactor (or even a minor one) due to it being a types language. And yes, you may not start with ints, but you could easily add code for the marshal/unmarshal later on to convert those strings to ints for serialization purposes. And it wouldn't require a change to any of your other code, not to mention that if you s…

Go has many shortcomings that make it difficult and extremely annoying to do refactorings. Things like no constructors where types are declared as follows

    A {
        Field1: field1,
        Field2, field2,
    }
Now adding a new field to A would require ensuring that all code paths initialize Field3, otherwise you're going to have silent errors at runtime. This has been solved ages ago in Java and C# and similar languages by means of constructors.

Re: Leveraging the Go Type System

#53
"Make Genre an int instead of string to save space, but then bake the same strings into the String method of Genre anyway."

I don't understand why you would ever want to possibly save space in a database at the cost of consuming it in your binaries instead. You're almost certain to deploy more copies of your binaries than you are of your database.

Re: Leveraging the Go Type System

#54
post #51

Personally, I think the only part that makes sense is making a type to hide the implementation. That String() method is kinda nasty: It's O(n), and you need to edit it every time a category gets added/removed/changed. I think it'd make more sense to just make a idiomatic struct: type Genre struct { id int name string } func New(id int, name string) Genre { return Genre {id, name} } Then, you can define your categorie…

> Then, you can define your categories normally:

> var (

> Adventure = genre.New(1, "Adventure")

> Comic = genre.New(2, "Comic")

> // etc

> )

Yet there is nothing preventing anyone from reassigning Adventure to genre.New(2, "Comic") or some other arbitrary value. The fact that golang doesn't have the equivalent of Java's `final` is just poor design.

Re: Leveraging the Go Type System

#55
I have used Go in production for enterprise/business systems since 2015 and what I miss most is true sum types that really limits the state space of composite types. It’s being discussed somewhat actively for Go 2, and I would love to see more support for the idea. It’s really useful for business logic.

Re: Leveraging the Go Type System

#56
post #51

Personally, I think the only part that makes sense is making a type to hide the implementation. That String() method is kinda nasty: It's O(n), and you need to edit it every time a category gets added/removed/changed. I think it'd make more sense to just make a idiomatic struct: type Genre struct { id int name string } func New(id int, name string) Genre { return Genre {id, name} } Then, you can define your categorie…

> Then, you can define your categories normally: > var ( > Adventure = genre.New(1, "Adventure") > Comic = genre.New(2, "Comic") > // etc > ) Yet there is nothing preventing anyone from reassigning Adventure to genre.New(2, "Comic") or some other arbitrary value. The fact that golang doesn't have the equivalent of Java's `final` is just poor design.

There's nothing preventing someone from editing the source code to `const Comic = Adventure` either. Either you have access to edit String() AND have access to mess with the definitions in source code, or you don't have access to either. The idea that `final` can protect you against yourself is kinda silly IMHO.

Re: Leveraging the Go Type System

#57

Earlier quoted context omitted.

That's a great point. Anyone who has spent significant time with Go knows how easy it is to do a large refactor (or even a minor one) due to it being a types language. And yes, you may not start with ints, but you could easily add code for the marshal/unmarshal later on to convert those strings to ints for serialization purposes. And it wouldn't require a change to any of your other code, not to mention that if you s…

Go has many shortcomings that make it difficult and extremely annoying to do refactorings. Things like no constructors where types are declared as follows A { Field1: field1, Field2, field2, } Now adding a new field to A would require ensuring that all code paths initialize Field3, otherwise you're going to have silent errors at runtime. This has been solved ages ago in Java and C# and similar languages by means of c…

A constructor is just a function though... If I add a new field to a Java class and fail to add its initialization to the constructor, I have the exact same problem because Java initializes the field to its default value when the constructor is called.

You are correct that every situation where the struct in Go is initialized "bare" would need to be addressed if a field is added, but Go considers this a feature, not a bug (and, conversely, considers "bare initialization" of structs at dozens of places in your code to be bad practice if that struct could ever grow new fields). If you're bare-initializing structs, you're comfortable using them in a "loosey-goosey" context where zero-initialized fields are permitted (or, better, useful... https://www.youtube.com/watch?v=PAAkCSZUG1c&t=6m25s). In Go, the issue of required structure is addressed by wrapping the struct in an interface and then providing a function in the package that can create an instance of the interface. Used in that way, you get something very similar to a Java class (though Go doesn't force you into the "everything is a class" paradigm that Java demands).

Re: Leveraging the Go Type System

#58

The points about iota are interesting. I always define the first constant as "unknown" when defining a set of iota-driven constants. That way the zero value is "unknown" so if I create a new struct with no initialiser it doesn't accidentally inherit a value I didn't mean it to have, and instead gets the "unknown" value. It also doesn't touch on the other useful "system" funcs to include on a type (Scan, Value, Marsha…

Agreed. There are a couple of clever ways to handle iota, but as my next article suggests, in general, I steer away from them if you really just need constants. I didn't go into any of the Scan/Value/Marshal etc as it was a little beyond the scope of this article. I can certainly do a follow up article on it though Thanks for the feedback!

Yeah I read the article about iota. It was interesting, and there's stuff there I agree with, but on the whole iota is too useful not to use (imho).

Re: Leveraging the Go Type System

#59
post #36

Earlier quoted context omitted.

That's a great point. Anyone who has spent significant time with Go knows how easy it is to do a large refactor (or even a minor one) due to it being a types language. And yes, you may not start with ints, but you could easily add code for the marshal/unmarshal later on to convert those strings to ints for serialization purposes. And it wouldn't require a change to any of your other code, not to mention that if you s…

This perspective ignores the fact that there is no shortage of typed languages that don’t have all the—in 2021—inexcusable downsides of golang.

There is unfortunately a shortage, still in 2021, of languages that have a null set of inexcusable downsides.

I will trade - unhappily - ADTs for value types, automatic memory management, some language-level concurrency support, a compiler that builds our largest project in under two minutes, and a community large enough I can spend my time training new hires on fundamentals and business problems and not tool onboarding.

Re: Leveraging the Go Type System

#60
post #15

The points about iota are interesting. I always define the first constant as "unknown" when defining a set of iota-driven constants. That way the zero value is "unknown" so if I create a new struct with no initialiser it doesn't accidentally inherit a value I didn't mean it to have, and instead gets the "unknown" value. It also doesn't touch on the other useful "system" funcs to include on a type (Scan, Value, Marsha…

I feel like there are times when zero values can make things awkward (e.g. boolean flags often need to be expressed in the "negative" form because the zero value is false), but this is a great idea for iota!

Totally agree. Time zero values are my bane. I've started using sql.NullTime for all time values regardless, just so I'm clear about what is what.

* yes I know time.IsZero() is a thing, and the semantics are similar, but I want the compiler to warn me if I'm trying to use a time without checking if it's actually initialised first.

Post reply on HN