Go turns three
blog.golang.org
blog.golang.org
However, the lack of generics seems like a pretty bad oversight. It's annoying to rewrite common functional operations (e.g. map, reduce) over and over again with slightly different argument types (or sprinkling type casts all over your code).
The lack of generics doesn't feel like that. It strikes me more as an oversight than anything else. This is especially true because some builtins (i.e. make) are generic functions -- they're just special, and you aren't allowed to make your own.
"I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. In recent years, a number of program analysers like PREfix and PREfast in Microsoft have been used to check references, and give warnings if there is a risk they may be non-null. More recent programming languages like Spec# have introduced declarations for non-null references. This is the solution, which I rejected in 1965."
Kotlin and Rust seem to have an interesting approach. In Kotlin, you have to explicitly declare nullable types, and Rust does not allow references/pointers to be null in the first place if I am not mistaken. I have just recently started learning more about Scala, and although it allows nulls to be passed, they are not idiomatic, and I am guessing it would not be too hard to have an automatic style checker that catches uses of null (at least those that do not interface to Java libraries).
Languages like Kotlin are missing the point.
That's a bit harsh. JetBrains and the developers of the compiler are certainly aware of the Maybe/Option approach, they chose not to use it. Their approach is not as composable as Haskell's but much more practical and more readable in my opinion.
If anything, the fact that Option is still used so rarely in Scala is an indication that maybe, that experiment has failed.
And I do not agree that the Kotlin approach is more practical, unless by that you mean it's close to being useless.
The thing to understand about Nulls is that most often they denote non-fatal errors that have to be either explicitly handled or otherwise they get ignored. That's why nulls are evil, because NPEs take developers by surprise, at the worst possible time.
Monads are useful for dealing with errors. It's a tool, or a design pattern if you will, that makes the developer either deal with exceptional state or make it somebody else's problem. Developers that are not familiar with monads are doomed to implement it badly [1]
If you're interested for more and are a little familiar with Scala, I've attached an URL for an awesome explanation of this topic [2]
[1] https://gist.github.com/3889970
[2] http://james-iry.blogspot.ro/2007/09/monads-are-elephants-pa...
So, exactly like exceptions.
To be honest, I haven't looked much at Clojure. Do their containers/data structures use the immutable versions by default (kind of similar to what Scala does?)
The Rust programming language seems to have something going in that area. Immutability is the default, you have to explicitly declare mutable data members. Similar with methods, you have the option of declaring `self` to be mutable (which seems cleaner than C++'s approach).
There are no annotations to specify immutability, you have to trust whoever implemented the class you're using to provide the guarantees that are in their documentation. To some extent, that makes this a static vs dynamic typing discussion, but my point is that type annotations are realistically not necessary if you chose immutability as the default and provide a limited set of well known mutation operations. These operations are generally annotated with a "@", "!", or "." for "dereference current value", "side effectful", and "java interop" respectively.
For a language that uses multiple return values to indicate error nilable pointers are a requirement. With no constructors a non-nillable pointers would become a strange rarely used feature.
'defer' is currently very useful in loops, open a bunch of files in a loop and schedule them all to be closed at the end of the function. If defer was block scoped this would be impossible.
The error handling with sum/option types is an idea that usually comes from people that haven't used Go or at least haven't thought about how it would work at all. It doesn't work. Languages that use sum/option types for errors are universally functional languages that don't mutate function parameters. The io.Reader interface is a good example of this problem. Read(p []byte) (n int, err error) You could make an option type of 'n' and 'err' but since the caller owns 'p' you can't prevent them from using it without checking for the error.
It's non-trivial to transfer features between languages, they all interact with each other and have dependencies on each other.
However, sum types would still give extra safety in most cases.
Also, it is possible to differentiate a type of allocation and a type of a usable buffer, such that "io.Reader" could take an allocation as an argument and return a sum type of an error and a usable buffer. The allocation should be an opaque type not usable as a buffer unless io.Reader converts it to such.
For other features (explicit nullability, sum types, etc) the cost-benefit is very clearly in favor of having those features, at least according to all PL researchers and most users who have experience with languages that have these features.
In a vacuum, sure. But sum types don't interact in obvious ways with Go interfaces.
And FYI, I enjoy both Haskell and Go. Each have their own strengths (and weaknesses). Haskell's expressiveness and safety are a pure joy to work with. But the simplicity in the language design of Go make working with the language very productive. (I very rarely find myself in a situation in Go in which I have to think about "how do I express this idea using Go". Unlike Python and Haskell, which are huge languages.)
The cost-benefit analysis of a language feature MUST be performed in the context of a language and its stated goals.
Why couldn't it work at both the function and block level? I too would like to use defer at a block level. I find myself creating functions just to gain usage of defer, or not using it when I'd like to.
Not oversights but deliberate decisions.
http://commandcenter.blogspot.de/2012/06/less-is-exponential...
- constants are just numbers
- no const or other type annotations
Type System Tyranny
- Clunky typing:
- Makes programming harder (think of C's const: well-intentioned but awkward in practice)
Yet they still blessed a few builtin collections with generics without waiting to "get generics Right"
In this case, wouldn't it have been better to ship 1.0 with generics?
Retrofitting generics in a language already released with an existing code base sure doesn't seem like the best way to make sure the feature is "done right".
http://blogs.msdn.com/b/dsyme/archive/2011/03/15/net-c-gener...
The lack of generics was not an oversight but an explicit design decision.
Personally I would go with interface{} and type cast.
Idiomatic Go (currently) is to use a for loop for map and reduce/fold operations.
There is going to be a for loop one way or the other. The question is n occurrences of for loop or one occurrence of for loop in a generic_map func followed by n-1 occurrences of call to generic_map and type casting.
As for performance, function calls themselves incur a performance penalty. That doesn't necessitate putting all code inline. If the profiler says that interface{} and type casting incurs a significant performance penalty(it does incur a penalty but does it matter?), then I would go for inline for.
The language itself is pretty cool, but most of our work is still web application development (broadly defined). Go isn't particularly suited for that, and even if it were, it doesn't have a mature framework for doing webdev yet.
Really? Web development is one of Go's strengths IMO, and the standard library was clearly designed with web development in mind. i.e., the HTML template library, the net/http library, etc.
There are also a few third party frameworks out there. They may or may not be mature depending upon your definition, but gorilla looks promising.
Personally, I've used Go for some prototyping work internally. I thought it was a great language to write in. Sadly I haven't had a chance to do something in production with it myself yet!
I've been keeping a close eye on Revel as well (http://robfig.github.com/revel/), which is a Play-like web framework under active development.
What isn't easy is reading and understanding the documentation. The jump between the Go tour and the Go docs is gaping. I wish some five star technical writers would take a look and fix it since I feel that is Go's biggest problem right now.
I do think that sometimes the more complex packages are aided by examples and I think the std lib could take on a few more examples. Fortunately, http://gobyexample.com someone is already doing that.
+1 to you pjlmp
Haskell We Love You