Overall Go feels to me like some version compiled PHP with better concurrency support.
Overall Go feels to me like some version compiled PHP with better concurrency support.
- simple (so not Scala, Haskell, Perl 6, etc.; no Rust either, unfortunately)
- clean (nice syntax, preferably Python inspired, but consistent)
- modern (generics, some functional features, string interpolation, etc.)
- decent concurrency/parallelism story
- good IDE, preferably supported by the core dev team
- compilation to a (possibly static) native binary
- decently big ecosystem
- and a big enough community of contributors or commercial backing from at least 1 stable entity
- also preferably didn't have a lot of historical baggage
From what I gather C# might actually be close to this, 2-3 years from now, if the .Net Core transition happens smoothly and the OSS community adopts it... Who knows, maybe even Kotlin 2019 or so?
- much simpler than Scala while still being much more expressive than Java
- clean syntax, with a higher consistency than e.g. Java
- Generics, Higher Order Functions, Multiline-Strings, Destructuring, ADTs, Pattern Matching
- Coroutines (experimental but production-ready)
- IntelliJ Community Edition (or Ultimate)
- not yet but will be possible with Kotlin Native
- has its own ecosystem but also has great interoperability with Java
- stable for quite some time, big community
- well, a little bit for reasons of Java-interoperability; on the other hand
Most of the stuff in Kotlin looks more like less boiler plate (data classes) than "more expressive".
* Top level declarations
* Sealed classes
* Coroutines (in Java this is currenty only possible with Bytecode manipulation)
* Inlined Functions and Reified Generics
* Covariant Collections
* Tail Recursion
It's implemented with some bytecode hackery and hence limited to inlined functions [0], which makes it significantly less useful.
Funny how a misguided design decision from the Java 1.5 days (running generic 1.6 code with a 1.5 jre) still haunts us today...
[0] https://kotlinlang.org/docs/reference/inline-functions.html#...
What of these would you call "more expressive"? I'd agree with sealed classes, they express a limited set of possible classes. Perhaps tail recursion as I can express problems in way of recursion without stack overflows. I don't think coroutines are more expressive than Futures/get, only a runtime optimization for many concurrent executions.
callbacks -> futures/promises -> coroutines
Because callbacks are a completely different style of code, your async looks totally different. Futures and promises are better, but still make it difficult to ever break out of the async context, coroutines make the difference between async and normal code nonexistant, and allow clean escape from an async context.
Interesting. I thought the lack of proper tail recursion is a JVM limitation. Hence why Clojure uses loop/recur to handle it by translating it to a loop rather then as an actual tail call. Which means things like mutually recursive functions are not possible in Clojure without blowing the stack.
How does Kotlin handle that? Is it internally rewriting to a loop or trampoline, or did they find some way to actually make proper tail calls?
Yes. You add a `tailrec` modifier to your function. The compiler then checks if the last op in the function is a call to itself. If it is, it gets replaced with a loop. If not, you get a warning that `tailrec` is ignored.
Little things that are a apin in Java and elsewhere are quick and natural in Kotlin:
- nullable types are smoother than Options - null checks are relatively painless - the stdlib works with you, not against - named parameters and optional parameters means no need for builders - etc. Etc.
"nullable types are smoother than Options - null checks are relatively painless"
Might be the case, but Options are more expressive in that I can combine them and restrict them with other type classes to express larger constraints. Reusabilty is higher e.g. with Monad transformers like OptionT.
So nullable types are less code, but also less expressive as building blocks of larger constructs.
They might be also less expressive because they conflate two concepts: Not initialized and not-there, whereas Option expresses not-there and _ (in Scala) expresses not-initialized.
"named parameters and optional parameters"
Not sure how this more expressive?
It's as fun as writing Python (with a similar syntax), but it has essentially all the goodies you want from Lisp when you need them, runs as fast as equally optimized C, produces standalone native binaries, _or_ standalone JavaScript if that's your thing.
> except the ecosystem/commercial backing
If this is not the most important feature on that list, here is one more vote for Nim. Give it a try, it might (pleasantly) surprise you!
It should happen soon (TM), but from what I understand it will be just another version, just this time renamed to 1.0 - this basically means you could start using it more seriously even now.
Nim itself does not come with an IDE, but does include nimgrep and nimsuggest which underlie intelligence and other features in every IDE setup.
There’s also c2nim which will generate the bindings for you (though it might need some hand holding if the header files make extreme use of macros)
Go is showing itself to be a good compromise especially with the simplified nature of deployment and runtime stuff. It’s like a safer, higher level C, with a decent runtime library. That has so much mileage.
I’ve learned one thing as well over the last 15 years of using .net as well. An IDE is a monolithic brain damager. If you have to do something outside the scope of it, you’re shot. Give me an extensible text editor and shell.
Now I’m going to duck for cover!
Crazy attracts crazy, now you know.
What about Java? Its easy, modern in the way you describe, has a good concurrency story, multiple great IDEs, can compile to a static binary with commercial JVMs, or experimentally in OpenJDK9. Really big ecosystem. Huge commercial backing. It just maybe fails at historical baggage and syntax.
Kotlin fixes those last two. And Java 10 will also fix a good chunk of the syntax complaints.
In all fairness though, I think you're focused on nitpicks that have no practical benefits. You're asking for a language with a better user experience. Which is great, as it can make programming more pleasant, and I'm all for it.
That said, I'd focus on language changes that provide functional value. Haskell adds a powerful correctness layer at a low expressiveness cost. Go adds a useful CSP concurrency mechanism with green threads, as well as being friendly to containerisation. Erlang and co add a great deal of reliability features and massive parallelism with first class actors. Clojure gives you powerful metaprogramming, state of the art immutability and a similar CSP layer as Go, while maintaining the access to Java's large ecosystem. Kotlin adds null type checking, and better functional primitives. Rust gives you safe memory access without the performance overhead of a GC. Chapel allows for super parallel computing. Scala gives you OOP with the safety layer of Haskell on it. Nim gives you faster python, with type safety. Crystal gives you faster Ruby with type safety. Etc.
Those are the languages that challenge the status quo, and have a chance at bringing real value in terms of the average output they result in when used to program with.
A slightly better UX for Java or Python is a hard sell. Its like the famous Unix quote: "the most dangerous enemy of a better solution is an existing codebase that is just good enough." - Eric S. Raymond
Java, C#, Python, Ruby, C++ are all good enough, even UX wise, to be displaced by something that doesn't offer compeling functional value. At least, in my opinion.
In a couple years, .NET Core will be even more stable and will surely have convinced the stragglers in the community to adopt it. (I'm seeing this is already mostly the case, because everyone likes not being locked into Windows.)
I'm a pretty big fan of .NET Core (coming from Node.JS) and I really do expect it to grow a lot in the next few years as people discover how easy and sane it is.
Mono has always supported "compilation to a (possibly static) native binary".
Windows 8 introduced "compilation to a dynamic native binary" on the Windows Store via the Bartok compiler used in Singularity.
Windows 10 adopted "compilation to a static native binary" with .NET Native, based on Midori compiler toolchain.
The .NET Core team has CoreRT for the same purpose.
C# used to be that but I don't think that's the direction anymore.
No language ships a cross-platform GUI toolkit in its standard library.
Just looked at wikipedia examples and the code is full of special chars and shortened keywords. Doesn't look nice or simple to me, tbh.
As it stands, I would rather bet on OCaml.
I expect they want a native binary (possibly even statically linked) so they can trivially distribute the artefact without requiring that the VM be installed or bundled which is a PITA, in which case "compiles to bytecode" is useless.
- arrays with slices (that could also be used for strings) and Cilk Plus array notation [1]
- maybe actual string type with separate concatenation operator (possibly space as awk uses and C for literal strings) it could probably be folded to improved arrays
- standard SIMD vector types so that most use for operator overload disappears
- cleaned up standard library
- actual macros so the remaining use for operator overload disappears
- module system (so you would generally not need make and could easily compile to static binary)
- already you can build with static musl libc [2]
- something like libdill [3] or libmill [4] in stdlib
- there are multiple good IDEs, debuggers, analyzers
- one of biggest ecosystems and communities
- historical baggage mostly cleaned up
All commercial JDKs had this option in one form or the other, and many of them are still around.
If you want to try this for free on open source projects, Excelsior JET is one option.
Well, there was GCJ, but I think that hurt more than it helped.
Once you get used to it, though, you'll probably want to write idiomatic F#.
basically go is missing from your list is generics and string interpolation.
I love that Go is a language that can be mastered in a matter of months; that it has such a simple "get up and running" story; that its tools mostly do the right thing by default; and that almost everything is straightforward. In most other languages, I have to figure out what build system to use, how to script it, how to specify and download dependencies, what testing library to use, how to actually run the tests, how (if it's possible at all) to get a static binary, etc--and then I usually have to learn a complex language on top of all of that. For the most part, Go just works out of the box.
I think folks also underappreciate Go's runtime--it has a fast garbage collector and an awesome goroutine scheduler that all get statically compiled into your application binary. Mostly, I want a functional language that compiles to Go so I can still leverage Go's awesome tooling, runtime, and deployment model, but with things like generics and algebraic data types. I've started prototyping it too, and so far it's coming along smoothly (modulo all the things I'm learning about building a programming language). On that note, there is some effort going on in the Go community to build a VM for Go, which would probably make an even more attractive compilation target, at least from a toolchain perspective. Even if my project doesn't take off (and it likely won't), I think this is a low-effort way to address Go's most significant complaints.
[^1]: Well, "most" judging by the significance many of Go's critics place on semantically versioned dependencies
If you have to test file io or DB interaction, you can either do the adapter pattern or, as I prefer, just actually use the filesystem and DB but with some test infrastructure around it.
Example:
> type Bar interface{
> Walk() (int, error)
> }
>
> func Visit(b Bar) (string, error) { ... some code ...}
In the production code, you call Visit with some struct that matches the interface (has a Walk method). In tests, you create a new struct like fakeBar that also matches the interface. You can have your fakeBar return any string or error you like and you can verify that Walk behaves the way you need it to.Hope that helps.
...and simpler syntax and without the quirks. But that's one of the best descriptions of Go I've seen so far.