As a newbie to Go, some of the syntactic rules seem confusing or “shoehorned” in.
For example, if exported functions and types have to be upper case, then why are builtin functions like “len” lower case and still usable by all packages? Isn’t it breaking a rule?
The Receiver method concept feels like an afterthought which was added to give some “class” like behavior to declared types, after deciding we are not going to have classes. It just doesn’t feel like it was done to improve readability.
My OCD maybe showing here, but until I can reconcile this, I feel like I can’t proceed with go at all
All C-style languages that I can think of have builtins. You don’t have to import anything to use the int type in Python, Java, C, C++, C#, etc. Builtin functions like append, cap, len, etc. aren’t like any other functions in the language: They are like macros or templates/generics. The compiler translates len(mySlice) into a different operation than len(myMap). You couldn’t implement these functions yourself; you need support from the compiler. They could have implemented these builtins to be within a package, just like any other C-like language could have put their builtins into a package, but I don’t see any benefit (to me, go.Int, lang.Int, system.Int are not better than just int).
I’m not sure what you mean about the receiver concept; it doesn’t have anything to do with classes.
The point about the “len” was, if the rule is that any type or function has start with an uppercase letter to be considered “public”, then why is the builtin package the exception?
Regarding the comment I made on receivers: according to the Go playground, “Go doesn’t have classes. However, you can define methods on types” and then goes on to define methods, which are “functions with receiver types” … hence the class comparison in my comment.
Looking further into other features like interfaces, the language feels like it tries to have class-like behavior and inheritance while shunning it at the same time.
The builtin package is the exception because it’s “built in.” No matter what package you’re in, you call the builtin len function with just len(thing). My point about int is that it’s a built in type like len is a built in function. You can’t build your own int type without the built in int type. lenjust is special just like slices and maps just are special in that they are the only generic types in the language. You can’t write a generic stack type in Go, but you can build a stack type on top of slices. The builtin types are fundamental building blocks that you build larger constructs around/on top of.
The language designers could have decided to have no built in types or functions and instead have a sys or go, etc. package so that you have things like sys.Int or sys.Append, etc., but they didn’t. They also didn’t out any methods on built in types which is why it’s slice = append(slice, value) instead of slice.Append(value).
Having methods without classes is not an afterthought. It was deliberate. Go’s designers didn’t want inheritance, but they did want polymorphism. They wanted compile-time type safety but they didn’t want types to need to list every interface they wanted to implement. They didn’t want operator or function overloading, etc.
I didn’t like Go when I started using it in 2016. Up until this year, I preferred C# or Python. A common complaint about Go is that the language seems to be arbitrarily opinionated about its features or design decisions. That’s probably true in some ways. For me, now that I’m used to its quirks, it’s easier for me to write boring concurrent code that’s easy to read, reason about and change. It’s also probably made me a better C# and Python programmer