21 ms·
Things Go needs more than generics
- mmargerum 5y agoOnly things I'd like are Switch operator a<b ? "neg" : "pos" A coalesce operator myval ?? "default value" generic map/reduce/filter
- mastrsushi 5y agoAre generics not essential in a modern industry language? How else can you avoid rewriting data structures? Void pointers and polymorphic parameters expect the programmer know the underlying type.
- racingmars 5y agoGiven how successful Go has been without generics, it appears generics are not essential in a modern industry language. I'm happy generics are coming to Go, but living without them hasn't been difficult. (Like the article author, I feel like I'm jumping through more ugly hoops due to lack of a proper enum type than I am due to lack of generics.)
- stickfigure 5y agoWe went through all this with Java in 2004. Lots of people saying "we don't need generics", then a few years later nobody can imagine writing code without them. You may not yet know what you're missing, but you will.
- codemac 5y agoThe same can be said of C or JavaScript or any other language that is successful. Saying they have flaws doesn't somehow undo their success. Programming languages are a human phenomenon, as is their success. They are popular in spite of their flaws, not because of them.
- ferdowsi 5y agoFlaws often represent design tradeoffs. Rust's lengthy compile times are a tradeoff for a highly sophisticated type system, where Go made the opposite choice (a simple type system for fast compiles). As a web engineer I prefer the latter for a rapid development/debug cycle.
- xfer 5y agoOcaml's compiler is very fast(not including extreme edge cases), It has nothing to do with "highly sophisticated type system". It's mostly llvm. As a web engineer you should look at rescript/reason before making claims about compile times.
- deleted 5y ago[deleted]
- jpgvm 5y agoIt really depends on the types of programs you are writing. A lot of CRUD style "microservices" being built these days don't ever construct any complex in-memory structures, instead deferring that to databases etc. Also many system-ish programs, CLIs and the like also don't really need complex structures or benefit from authoring them + the algorithms that traverse them in a program-specific way. That said, I'm not a fan of Go but generics are far from the only reason why I'm not a fan and it could probably exist as a somewhat useful language without them indefinitely.
- silisili 5y agoIn real world usage, no, it's not essential. There are times when you wish you had them, but they are typically conversion type functions. Those just don't happen that often for a lot of applications. It's easy to show some contrived add func, and how you need one of every single number type in Go. But in practice, people rarely ever need such a function.
- klodolph 5y agoThe problem with void pointers is that if you make a mistake, you get undefined behavior. If you make a bad cast in Java, Go, or C#, instead, it fails. You get nil, you get an exception, or you get a panic, depending on how you do the cast, and which language you're using. None of these result in undefined behavior. If you look at Java and C#, neither had generics either. You used casts, just like Go. It's safe, because the casts are safe (unlike void pointers). People started using Java and C# anyway, even though they didn't have generics. Java then added generics in J2SE 5.0, which came out in 2004, when Java was nearly 10 years old. A year later, in 2005, C# added generics as well, with the release of C# 2.0. Given that Go is about 11 years old now, it doesn't seem so out of place.
- pjmlp 5y agoC# had generics already, they just weren't fully baked for 1.0 release and they decided to postpone them instead of delaying the .NET 1.0 release. That is one of the reasons why the CLR already had most of the infrastrucure for proper handling of generic code instead of being a compiler only sugar. "How generics were added to .NET" https://mattwarren.org/2018/03/02/How-generics-were-added-to-.NET/ https://mattwarren.org/2018/03/02/How-generics-were-added-to...
- al2o3cr 5y ago"Firstly, it is not an obvious way to declare an enum if you are at all familiar with their use in other languages." It's the same way C does it, other than saying "const" instead of "enum". Explicitly casting int -> enum is "recommended" but not required or enforced.
- pyentropy 5y ago> In particular, there is no shortcut for the zero value for a struct value (not a pointer) — you have to specify the struct name followed by an empty set of braces There's no such thing as a struct name. Structs are unnamed. The only way to have a zero valued struct... is, obviously, to instantiate it with its members as zero values. Exactly what struct{members}{} is. `type X struct { age int }` just makes X refer to a particular type of a struct, but this also works: var john = struct { age int } { 16 }
- LukeShu 5y agoYou are mistaken. The struct type very literally has a name: type X struct { age int } print(reflect.TypeOf(X{}).Name()) But a name isn't the only thing that may be "magically" bound to a struct. Methods too; if you define methods on `X`, then your `john` won't have those methods. I encourage you to re-read the language spec, paying particular attention to the relationship between "defined types" and "underlying types".
- pyentropy 5y agoThe spec very clearly settles the issue: 1. A struct is a sequence of named elements, called fields, each of which has a name and a type. // An empty struct. struct {} // A struct with 6 fields. struct { x, y int u float32 _ float32 // padding A *[]int F func() } 2. A type declaration binds an identifier, the type name, to a type. The new type is called a defined type. It is different from any other type, including the type it is created from. type TreeNode struct { left, right *TreeNode value *Comparable } -- type X struct {} is simply a type named X, of underlying type struct{} which (I'm refering to the struct here) cannot have a name - because it is an ordered sequence [(name, type), (name2, type2), ...], not a product type (X, [...]). I don't know why you bring methods to this, as if methods are somehow connected to structs. In your reflection example: Name() returns: (1) the name of a defined type (2) the empty string for all other cases It is designed for all types, and is of no particular meaning to the struct type, more than it is to int64 or float64. Would you say "ints have names"? You can create a type with a name and/or methods having such underlying types too. I don't wanna nitpick in any case - my point was structs are not an exception like OP mentions. The brace syntax is a composite literal, and maps, arrays, slices all use it. The latter just don't use it for creating zero valued objects, because they are dynamic and are nil valued. Both declaring a value without a specific value (var john struct{} or var john X) or a literal (struct{}{} and X{}), seem concise to me. Unless we bring inference to the table: map[string]struct{age int}{"A": {15}, "B": {18}}
- welder 5y agoI don't agree, especially on the second point. Having strings default to empty instead of nil is a feature not a flaw. What Go actually needs: Nil-Safe Types! [1] Programmers can work around verbose error handling(3) and lack of enums(1), but forget to check for nil before using a pointer... CRASH! And that's something the compiler doesn't warn about. [1]: https://wakatime.com/blog/48-go-desperately-needs-nil-safe-types https://wakatime.com/blog/48-go-desperately-needs-nil-safe-t...
- stonemetal12 5y agoWhy is null even an expected value for strings? I get that it is a design decision for a low level language like C, but as soon as you go up a level the fact that strings are implemented as pointers to character arrays should be an implementation detail not visible to the language's user. Need a Null\Nil string? Use a pointer to a string, just like you would for an integer.
- oh-4-fucks-sake 5y agoBecause there's value in being able to differentiate between when a value is "truly absent" vs. a zero-length string. To take a trivial example with a simple API: a null string can carry the semantic that a value was (intentionally or not-intentionally) omitted in a request by a user/FE. What if that request schema grows to the point where there could be dozens of intentionally-omitted/optional fields--you then get into this awkward kind of scenario where either the FE must include those key-values as empty strings or the API must be able to interpret those omitted values and coerce them into empty strings. Even if a framework or whatever is abstracting that away from you--the can is getting kicked somewhere for someone to have to deal w/ the fact that other systems/langs--and their contracts--do, very much, have the concept of null.
- stonemetal12 5y agoWhy strings and not integers and doubles? Why strings and not structs? You have given plenty of reasons why nullability in general can be useful, but not why it is mandatory for strings to be nullable. Unless the core of your argument is C has null strings so now everyone must support that poor decision for interop purposes. Personally I prefer how Protocol buffers handles not present strings compared to C. If you stick to std::string, C++ isn't bad either.
- davidbanham 5y agoI would love great enum support to be added to Go. I want to have a case statement for an enum’s value and have the compiler assure me that I’ve covered all possible cases.
- mjevans 5y agoI would rather make that check request-able but not default. E.G. any case (including default) in a switch statement, or anywhere else, could have a keyword such as FORBIDDEN and at compile-time a check would result in: A warning if it is unprovable this is unreachable with a fatal exception if it is reached during runtime; an error if it is reachable (in the default case for your input handling this would inform you of the need to update the code); nothing (it is proven OK).
- davidbanham 5y agoWhat I’m asking for is basically just sum types. In my mind the behavior of switch would be totally unchanged unless you use an enum as the subject. If there are possible values you don’t want to handle that’s totally fine, you just need to be explicit about it by having a blank default handler or similar.
- andrewjf 5y agoI wholeheartedly agree. Exhaustive compile-time checking of enum variants is extremely helpful (and absolutely a required feature if sum types exist). So that when you're adding a variant to the enum, the compiler will fail if you missed handling the new variant.
- maxpert 5y agoI don’t agree with this. Right now writing a basic LRU cache requires casting gloop in your code. The issues (specially 2 & 3) in article are manageable. But generics are genuinely holding me back from writing reusable code.
- deleted 5y ago[deleted]
- ferdowsi 5y agoAn issue with a Rust-like ? operator is that it is sanctifying the "result, error" return values. It's entirely possible to return three or four values from a Go method (if not common). With generics Go is opening up the possibility of honest sum types. But, to be honest, I'm one of those people who are not upset by Go's error verbosity. The error shortcuts in Rust confuse me more often than not.
- kelnos 5y agoI don't think that's a problem. The semantics of `?` would likely be defined for any number of (including zero) non-error return values, followed by an error value as the last return value. And when you use `?`, the call site will return however many non-error return values there are. func foo() error { ... } func bar() (string, error) { ... } func baz() (string, int, error) { ... } func doStuff() error { foo()? s1 := bar()? s2, i2 := baz()? }
- akira2501 5y agoGo authors have explicitly stated that while the branches are clunky, they are so intentionally to make the points where your code branches much more obvious. I wasn't sold at first, but 6 months ago I decided to use it for a project where it's ease of cross-compilation was the deciding factor, and since then I have to agree. Anyways, you're entirely free to use a named return value in the second (or later) part of a multi valued := statement. An example is noted below, the clunky and obvious branches are there, but the intent and flow is obvious at a glance. Example: https://play.golang.org/p/56Jv9Cvrw-s https://play.golang.org/p/56Jv9Cvrw-s
- unscaled 5y agoThe "Go authors" (by that I assume you mean the Go team) have very explicitly stated that they view the current way Go does error handling as a major problem[1][2]. [1] https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling-overview.md https://go.googlesource.com/proposal/+/master/design/go2draf... [2] https://github.com/golang/go/issues/32437#issuecomment-512035919 https://github.com/golang/go/issues/32437#issuecomment-51203...
- silisili 5y agoYou can get close enough to an enum by making your own type and a few values. If some fool typecasts their own values, that's on them but also easily checked for. The way nil works is pretty simple. Unexpected cases are cases that didn't understand how nil works. Errors as values is how the language works, and how I prefer it. No magic, make sure each step works. When I hammer a nail, I can tell immediately if I miss it, and deal with it. I don't have a catchall that sees I wasn't wearing safety glasses and makes some random hammer error. Then again, I also don't think generics are a good thing, so maybe I'm just against change to the language at all.
- andrewjf 5y ago> The way nil works is pretty simple. Unexpected cases are cases that didn't understand how nil works. I mean sure, but wouldn't it just be strictly better if there was just not null values and you specifically had to handle the present vs non-present case (i.e. Haskell's "maybe" type or other sum type construct)? Any non-trivial codebase is going to have programming errors where "nil" crashes the program where the compiler could literally have told you that your code was incomplete. > Errors as values is how the language works, and how I prefer it. No magic, make sure each step works. When I hammer a nail, I can tell immediately if I miss it, and deal with it. I don't have a catchall that sees I wasn't wearing safety glasses and makes some random hammer error Not following the analogy, but seems to rely on writing perfect code the first time, which is not possible. Nor are any of the proposed improvements "magic". > Then again, I also don't think generics are a good thing, so maybe I'm just against change to the language at all. With respect, it definitely sounds like it. Which is fine, but there's serious design problems with go that can be improved over time, and it's not a personal affront.
- bootlooped 5y agoI don't like the inconsistency of forcing you to assign all of the return values of a function (even if you just use underscore), but not doing that for range, map access, or taking something off a channel.
- Everlag 5y agoI would be surprised and saddened if `?` as described in the blog was added to go. Error wrapping is critical for operating a service and staying sane; an operator encouraging `if err != nil { return nil, err }` is a step backwards. Consider this situation(it may sound familiar): you get paged at 4am for 500s from a service, check the logs, see 'file does not exist'. go doesn't attach stacktraces to errors by default. go doesn't enforce that you wrap errors with human context by default. How do you debug this? Pray you have metrics or extra logging enabled by log level that can give some degree of observability. This error could have been 'opening config: file does not exist' or 'initializing storage: opening WAL: file does not exist' or even just decorated with a stacktrace. Any of those are immediately actionable. Now, if go decided to make error wrapping a first class citizen with a fancy '?' operator, I'd be excited. However, I doubt that will happen because go is definitely not rust-like in its design.
- GauntletWizard 5y agoI would love a syntactic sugar for 'if err == nil {Errors.Wrap(err, *What I was trying to do")}'. As a SRE, making each part of the stack explicit (or explicitly non-explicit) is invaluable for understanding debugging. I'm A-ok with some forms of error throwing, but they need to be clear and understood.
- unscaled 5y agoI think you meant the code: if err != nil { return fmt.Errorf("What I was trying to do: %w") } That's the correct and standard way to do error wrapping in Go since Go 1.13[1]. There is also Dave Cheney's pkg/errors[2] which does define "errors.Wrap()", but that has been superseded by the native functionality in Go. [1]: https://go.dev/blog/go1.13-errors https://go.dev/blog/go1.13-errors [2]: https://github.com/pkg/errors https://github.com/pkg/errors
- GauntletWizard 5y agoThe code I'm primarily working on is old enough that Errors.Wrap is the standard - Though refactoring that is definitely on the radar :)
- klodolph 5y agoThe article is hosted on some kind of shitty blog platform which doesn't let me copy quotes from it. Anyway, there was a point about how tedious it was to type, return SomeStructName{}, err This is what "goreturns" does. Concise error handling would be "nice to have" but to be honest, I prefer Go's current error handling over exceptions, most of the time.
- kaba0 5y agoWhy do you prefer them over exceptions? What is the preferred way of handling errors deep in the stack in a unified place (eg. By displaying an error page)?
- andreygrehov 5y ago1. Native enums - yes. 2. Nil - no. > The way I see it, there is a simple solution staring us in the face — make nil a placeholder for the zero value for any type. [...] Sure, it might be a little odd to see nil used in place of 0 for integer values, but I think this is a small price to pay [...] So is writing `return MyLongTypeName{}, errors.New("...")`. The power of Go is its explicitness. It's a slippery road to introduce an unintended behavior. The existing use cases for nil are extremely clear. There is no need to change that. I agree that it's annoying to write `return MyLongTypeName{}, errors.New("...")`, but it gives you a chance to think about a better name of a struct and normalize errors. Suddenly, an ugly looking line of code becomes short and nice `return Type{}, ErrInvalidOp`. Maybe Go would benefit from a reserved keyword that would represent a default value of a type, eg return default, ErrInvalidOp var x int = default var y string = default but again, this is just a syntactic sugar and it may introduce uglier code when used in conjunction with explicit values: return default, ErrInvalidOp ... return 0, ErrInvalidOp // was it intentional to write 0 here? can it be replaced with default? is there a difference between 0 and default? 3. Concise error handling - no What if you have more than two return values? func Foo(x int) (int, error, error) y := Foo(x)? Which error is being checked, the second or the third one? What if the second is nil, but the third one is not? Anyways, concise error handling has already been discussed by the community million times. Nobody can suggest good solution. When that happens, the best solution is to leave things alone, because, well, doing nothing is also a solution.
- masklinn 5y ago> Native enums - yes. I feel it’d be easier to have “sealed interfaces”: Go already has type switches, so the only missing pieces are defining sealing and adding match completeness support to type switches. > The power of Go is its explicitness. And yet it’s got plenty of implicitness. Though I guess you’re fight in that most of them are footguns? > 3. Concise error handling - no Then you don’t use concise error handling? Seems pretty easy.
- masklinn 5y ago> you’re fight right, obviously.
- jagger27 5y agoI wouldn’t mind type safe enums that take advantage of the good parts of Go, but the rest of the ideas are truly awful. enum Language { Arabic `json:"ar"` English `json:"en"` French `json:"fr"` Spanish `json:"es"` // ... } `json:"language"` That would be useful and Go-like.
- deleted 5y ago[deleted]
- mindwok 5y agoIMO all of these these are minor conveniences (except maybe Enums). Go does not need these more than generics.
- jicea 5y agoI’ve a very small working experience with Go, tried it on few personal projects to test the language. Globally I really like it, and I’ve been pleased by a lot of « features » (simplicity, explicitness, readability of the standard lib etc…). One thing that I miss was an immutability story. You have values/references and, again I’m a beginner, I’ve the impression that passing references are favored. Wouldn’t it be nice if the compiler was able to check if you can mutate a reference or not? There are some proposals [1] that are discussed [2], that can be a nice evolution. [1] https://github.com/romshark/Go-1-2-Proposal---Immutability https://github.com/romshark/Go-1-2-Proposal---Immutability [2] https://github.com/golang/go/issues/27975 https://github.com/golang/go/issues/27975
- tapirl 5y agoAgree, immutable values should be the top one in the needed-list. There are tons of immutable value proposals: https://github.com/go101/go101/wiki/Go-immutable-value-proposal-list https://github.com/go101/go101/wiki/Go-immutable-value-propo...
- phplovesong 5y agoAgreed o the Enum part. It kind of sucks that if i expose a package level const (like a enum-ish thing) its not enforced by the compiler. package options const ( OK = iota WARNING FATAL ) Later in the code import ( "package/options" ) func main() { ok := options.OK // iota value 1 useOption(ok) // OK useOption(1) // ALSO OK } I should be able to enforce that only values from the exposed options can be used on a type level.
- mariusor 5y agoBut you can do that if you create a type for the enum values : type errorLevel int const ( OK errorLevel = iota WARNING FATAL ) ...
- marcus_holmes 5y agoThe compiler doesn't enforce only those values being acceptable. I can do: myerrorlevel := errorlevel(3458967478) and the compiler won't complain at all. It would be good if there was some way of specifying that this type can only accept these values. Thinking about it, it's the same as nil-safe types - being able to specify to the compiler that this type cannot accept a nil value.
- mbnull 5y agounless errorlevel is exported as ErrorLevel you can only do so inside the declaring package.
- mariusor 5y agoIndeed, that was the idea behind keeping it unexported. From an external package you can only access the explicit values and the functions that accept them as parameters. The type is opaque for the developer.
- 5y ago
- unscaled 5y agoThere have been many[1] proposals for streamlining error handling in Go, including some coming from the Go Team, like the check/handle proposal from Marcel van Lohuizen[2] and the try proposal from Robert Griesemer[3]. If anything, these proposals have been even more controversial than generics. The "try" proposal in particular, was probably the mildest proposal of them all, but it garnered a lot of criticism which amounted to "Go error handling is perfect as it is". Personally speaking, I vehemently disagree with the criticism. I think that error handling go is a meteor-crashing-into-earth level disaster and anyone happy with that must be completely out of their minds. But there does seem to be sizable (or at least very vocal) group of Go users who believe this boilerplate help understanding the code better, and I don't think we can reach common ground. It makes sense. Due to its design, Go naturally attracts people who we believe in can call "Imperative Explicitness Maximalism". This is the belief that the code is easier to read the closer the code is to to description to the way your machine executes code[4]. The people who want to reduce boilerplate as much as possible may be motivated by various beliefs, but I think the most credible belief is that imperative explicitness hides the _purpose_ of your code behind nitty-gritty implementation details. From my perspective, in cases where the implementation is trivial (like error handling), you're adding nothing to understanding the implementation, while completely hiding the purpose and — at the same time — significantly increasing the signal-to-noise ratio in your code. So you get zero readability gains at the price of two significant readability losses. --- [1] https://github.com/golang/go/issues/40432 https://github.com/golang/go/issues/40432 [2] https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md https://go.googlesource.com/proposal/+/master/design/go2draf... [3] https://github.com/golang/go/issues/32437 https://github.com/golang/go/issues/32437 [4] Of course, even this group has a limit. They tend to view things like memory management and out-of-order execution as out-of-scope. Some of them may even complain that Rust — a language that is very explicit about memory — is too implicit about other things.
- namelosw 5y agoThere are several ways to deal with nil in language design: 1. The "default value" approach as Go, or how Java treats primitive types: It's simpler and safer. 2. Universal nil, or how Java handles non-primitive types: Bad idea. It's Sir Tony Hoare's "million-dollar mistake". It leads to unsafe code and feels pretty backward for a modern language. 3. Union/sum type/algebraic data types: The Haskell and Rust approach. It solves the problem faithfully but does introduce mental overhead. Whether it's suitable for languages like Go which strive for simplicity is subject to debatable. As for Enum and Exception, in principle, it's the same situation that there are three camps of ideas: 1. A minimal approach which is the Golang we have. For enum is there is no enum. For error handling is treating errors as return values. 2. A mainstream approach like Java and alike. For enum is enum like in Java. For error handling its exceptions and ad-hoc syntaxes like "?." operators. 3. A full solution like Rust and Haskell. For enum, it's ADT. For error handling it's also ADT-based Either type and monadic operators. It's interesting that how often these three camps talk past each other, and even more challenging to hold the community together and make the right decision for the language.
- marcus_holmes 5y agoEnums: yes, absolutely, good call Universal Nil: no. we need less nil not more. Nil-safe types would be a better answer. Error-checking: If you're not going to do anything with the error, then have an editor snippet for the ? boilerplate. Better still, start handling the error. The boilerplate is a Go anti-pattern. I think we do need more clarity about the relationship between slices and arrays: it's not clear at the moment when two slices share a backing array or not, and therefore whether changes to one slice will change the other. I'm kinda looking forward to generics, but also dreading it. Most newbie gophers don't understand how powerful duck-typed interfaces are and what they can do with them, and I have a feeling generics will be used for all sorts of situations that could be better handled with interfaces.
- Joker_vD 5y agoIt's not universal nil, it's more universal zero. This proposal, in one way or another, ― using "_", or "{}", or "nil" ― has been around since 2016 at least, because zero-initializing a struct is indeed way too verbose. > Better still, start handling the error. Most of the time, you can do this only two-three levels higher on the call stack, and even here it's most likely to be wrapping up of "return nil, fmt.Errorf("frobbing bar: %w", err)" sort. And yes, I am too excited and scared of the generics. I'd like to see generic map- and channel-related algorithms and patterns, and sort.Interface is an ugly hack, even if an ingenious one.
- marcus_holmes 5y ago> because zero-initializing a struct is indeed way too verbose hmm. I don't have the problem you're describing, because I generally declare the return struct at the start of the function and then return that. E.g. func something() (mystruct,error){ result := mystruct{} if badthing() then { return result, fmt.Errorf("bad thing happened: %w",err) } //do something to result before returning it return result } I must admit I don't understand how the proposal works fully. Would `x==nil` return true for an int with value 0? Both answers to this seem wrong to me.
- 5y ago
- a_c 5y agoOn nil, time to refresh history https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/ https://www.infoq.com/presentations/Null-References-The-Bill...
- deleted 5y ago[deleted]
- jillesvangurp 5y agoMaking go more like other languages might be nice if you like those other languages. But then, there are probably valid reasons for go language designers to not have gone down that path. I can work with Go when I have to. But it's not my main language. I appreciate the low barrier to entry. There's a ruthless pragmatism to the language that just means that whether you like it or not, that's how you do things in Go. Go-fmt is a great idea. Shuts down discussions about style that are fundamentally a waste of everybody's time. The suggestions that the author makes are unremarkably reasonable and at the same time controversial to people used to the Go way. People get entrenched that way. It's like tabs vs spaces or curly braces vs. begin/end (or parentheses). Enums are a good idea though. Should be entirely controversial. But clearly people have dismissed that idea because it is such an obvious and easy thing to do. I'd be curious about the reasons for that. Error handling has been controversial since the language emerged with people loving it and hating it. IMHO it would clean up the language a lot if they acknowledged the simple reality that the most copied bit of go code is basically people deciding to not do anything substantial with the error other than logging it. Having the option to do something productive is nice. Making it required to spell out that you are not going to is silly. Java had a similar thing with checked exceptions. That's thankfully missing from Kotlin (which I use a lot) and it streamlines code quite a bit without really affecting stability. You still deal with the error somewhere. Just not right here right now. I mention Kotlin here because I'm eagerly awaiting Kotlin native getting to the point where it becomes stable enough so people can write command line tools in it like people do with Go. JVM based Kotlin or Kotlin script are just not great for that (startup overhead, dependencies). Kotlin native actually works well on IOS currently but it is a bit lacking for command line usage; mainly because of lacking functionality in the library ecosystem. Just not a huge priority for Jetbrains apparently. But very fixable.
- jackpeterfletch 5y agoI'm a recent newcomer to GO as a Kotlin native. Initially I was taken back by the language, I felt we'd stepped back 15 years in language and design principles. I decried the lack of many of these features. Today I feel more like the language has simply been misunderstood, and possibly because of its association with Google, wrongfully applied in places where it isn't the right fit. It seems to been adopted, in many place as a fast system level alternative to higher level languages such as Kotlin or C#, when in actuality I think its niche is in providing a safer, more convenient alternative to C. Great for smaller, performance critcal applications, or things that where the bundle size is important, such as operating system utilities, parts of the networking stack, small fast lambda functions, caching servers, etc. But not domain heavy application servers. So with that in mind im not sure how I feel with GO current roadmap. Its now a language being adapted to be more suitable for applications I _feel_ it was never intended for.
- dustinmoris 5y ago> Secondly, since the type is based on an int there is nothing at the type level saying that your pseudo-enum can only take the enum values you want — it can take any possible value that the integer can represent. That is literally how enums work in most languages AFAIK. Even in .NET an enum is just an integer and you can in fact assign any integer value to an enum and therefore break things. Proof: https://dotnetfiddle.net/lp51Dh https://dotnetfiddle.net/lp51Dh Re error handling: In my opinion people who complain about having to write too many if statements in Go because of error handling just don't get it. I rarely ever want to bubble an err all the way up in my stack. In most cases I can either handle my error directly or if it's an error which cannot be dealt with at the current level then often I actually want to wrap it into a more high level application error and log the low level error or map it to something meaningful, distinguishing internal errors from user errors. People who look for a simple syntax to not deal with errors and just let them bubble all the way up simply do it wrong and need to learn how to write good code.
- Cthulhu_ 5y agoPersonally I'd rather have proper enum support instead of generics at this point, I don't have a use case for generics yet.
- betimsl 5y agoUniversal nil?
- bojanz 5y agoOptional function parameters also deserve a mention. Their absence results in semi-ugly workarounds even in the standard library[1]. [1] https://bojanz.github.io/optional-parameters-go/ https://bojanz.github.io/optional-parameters-go/
- jordiburgos 5y agoPoint 1. Totally agree, there must be a way to specify a limited list of values of one type. Point 2. It's not clear what the operator '?' does for a person new to the language. It will introduce confusion. Point 3. Yes, I don't even know why it has to be that way.
- VWWHFSfQ 5y ago> Point 2. It's not clear what the operator '?' does for a person new to the language. It will introduce confusion. I mean, it's not clear what pretty much anything (except the most basic stuff) does for a person new to the language. Has to be documented, like anything else.
- mmargerum 5y agoDon't agree with any of these. Keep go simple.
- jdc0589 5y agodefinitely disagree with the 'Concise Error Handling section', at least without a lot of enhancements to errors first. yea, there's a lot of error handling code in Go, but a lot of the time its not just "if err != nil return err". errors are a simple interface type with an 'Error() string' function. This is NOT ENOUGH METADATA for real usable errors a lot of the time. Lots of codebases are littered with stuff like 'if err != nil return errors.WithStack(err)' simply to attach a stack-trace to an error coming from an external dependency. Sure, the enhancement proposed in the article doesn't preclude you from continuing to do that, but it doesn't solve the actual shortcomings of errors either in my opinion. Errors in golang != exceptions common in other languages, I get that, but that doesn't mean I want to have to remember to attach standard metadata to every possible place an error bubbles up in my application. It's a consistency and simplicity problem too since some places you can just bubble an error up transparently, and sometimes you've got to wrap it with metadata for reasons that aren't obvious just by looking at the immediate error handling code. Thats a problem.
- AtNightWeCode 5y agoEnums are a pain in C# and Java. Not a fan. The error handling is what it is. Not a fan. It is really bad. The second point I don't even understand. Never had that issue. I think the problems with Go becomes obvious when you spend some time reading Go code. Try reading the code to Kubernetes and so on.
- lamontcg 5y agoI'd add operator overloading and static method dispatch at least. For mathematical types it is much better to write `M * v` rather than `M.VecMultiply(v)` for clarity. I gather that Go people freak out over syntactic sugar like `cout << "foo\n'` but they've thrown away the ability to better express mathematical notation. And the language itself cheats and internally can do that and has the kind of almost useless complex type, but you can't extend it to add vectors, quaternions and octonians that use algebraic operators. You don't need this for writing a grpc web service, but if you start doing ML or game programming this becomes important, and I think there's one of those koan quotes out there about how the code you write should strive for clarity, and I'd argue that function calls are less clear, and the reason why we write `1 + 2` instead of `1.AddInt(2)`.
- cratermoon 5y agoPlease no on the Universal nil. Meaningful zero values for types are essential. It may be confusing to some that nil is a valid zero value for some types and not others, in which case that needs to be refined, but having a type that can either be a value, a zero value, or a nil introduces complexity where there should be simplicity. Having a zero value enables, for example, Unconditional Programming: https://www.youtube.com/watch?v=AnZ0uTOerUI https://www.youtube.com/watch?v=AnZ0uTOerUI
- nunez 5y agoPersonally, I find writing tests in Go to be extremely painful outside of trivial assertion tests. The minute you need to start introducing test doubles, spies, or mocks, you're abusing interface{}. Compared to Ruby's rspec or even Python's pytest where creating these objects is fairly straightforward, writing new code with Golang often feels like a chore. I get that a lot of this difficulty stems from Go being a compiled language, but I still wish that writing tests were easier. (I'm really thankful that Ginkgo and Gomega exist though!)