I love programming languages. Every one of them has this catchy thing that I wished the others had. Lately, I've been distracted by the question of which language would I use if I would start a new side project.
Before reading, there are a couple of things worth knowing. While most of my short career I had created production apps using Java, JS, and Ruby, I constantly learn new languages and frameworks. I believe that languages and language (or framework) specific communities bring new ideas to the table that you can later use in other situations: Functional programming can teach you a lot about object-oriented programming, and working full time on a Rails app will teach you a lot about testing (if you'll practice it). The problem with learning all the awesome concepts from each language is that you eventually find yourself frustrated from looking for all the good features of these other languages in one language.
Another key thing to know is that I use the terminal all the time. Most of my time on a computer is split between a browser and a terminal. Yes, I code in Vim. I like type systems that don't have nulls (I like optional types) and that are strong. I don't like Java's type system for that matter, though the latest version of Java I used was Java 6, so there's a good chance some things have changed! Java's type system was the reason why I tried Ruby because the Ruby community always talked about how much they are different than Java. It always felt like when I write Java code, I'm helping the compiler, instead of the other way around.
The last thing I want to say is that the language I'm looking for may not be the language you're looking for! In this article, I'll cover up some languages I used for the last couple of months, maybe for serious work and/or for fun projects.
You see, I just love Ruby. Ruby is a great, expressive language with lots of mature libraries (we call them "gems"), that can help you get your app up and running very quickly. Rails are very mature and easy to use. Tests are baked into the community core more than any other language I've ever used. Ruby is a pure OOP language, so most of the code will share the same API style‚??-‚??classes, no matter which libraries you'll choose. The community is powerful as well‚??-‚??Ruby devs seem to contribute code to already-made libraries instead of writing their own library each time (ActiveRecord and Sequel as an example of awesome well-spread libraries). This consensus in terms of libraries helps with making extensions to libraries, and I usually give the Rails Admin gem as an example of making life easier.
Ruby, in terms of speed, isn't at its best. Deployments are usually heavy and take time to load. Practicing Rails is awesome and so much fun, but in reality, it is both time-consuming and money-consuming to run Rails apps: especially on "serverless" container platforms like Heroku, AWS ECS because of RAM, disk, traffic and boot time, where you pay extra for all of these things. Running Rails locally is totally fine and Bundler is awesome‚??-‚??but sometimes, there are issues and pitfalls with the "hot reloading" mechanism, which Rails uses because the startup time can take on a medium-size app 5-10 seconds.
No wonder this is the main language we use at Wix.
Lately, I started to learn Swift to bump up my iOS development game. Previously it was at level zero because all I knew was to how to make React Native apps‚??-‚??which is totally fine, but I wanted to experience something new.
Swift is a statically-typed, compiled language. It was originally made for developing apps for the Apple ecosystem, but was open sourced and now can be used to develop executables with on Linux systems. One of the most prolific npm library authors I know, Sindre Sorhus, stated that he wants to do more Swift work. I can understand him! Swift's fast startup time and helpful compilation process can assure you that the chances for runtime errors are getting lower and lower. You don't have NULL, you have Optional types. You can't throw without telling that your function throws, but not verbosely as Java's throws declaration, and there is nice syntactic sugar around it in the form of "try that and wrap in optional, in case of failure". You have pattern matching that works perfectly with Swift's enums, which makes it very powerful. They also have type inference, that doesn't include method definition, but I guess it's fine. Woohoo. A great language!
Why Swift isn't my winner? It isn't that easy or fun to use Swift in an editor that isn't XCode. I usually use Vim and I feel very slow with other editors. I tried to use VSCode or Atom, but it wasn't as good. Maybe, eventually, I'll write a Swift CLI tool that will help me write editor plugins and make the developer experience awesome myself, but right now‚??-‚??it isn't there. Swift also doesn't have a static compilation yet, so you need to have the environment set up with Swift in order to use CLIs. It's fine for Mac apps, but servers are Linux, and I want my binaries to be self-contained.
I am fascinated by this fairly-new syntax for OCaml, made by Facebook. The whole toolchain feels mature and awesome. OCaml's package manager, OPAM
, has emojis, which shows that the tools for the old-looking language aren't old at all. Merlin
and the OCaml
language server are awesome too, and even works amazingly good with Vim. There's a fully-working autocomplete engine(!), go-to-definition, hover type definition, and more features. Amazing development tools that are decoupled from an editor is such a powerful thing to have for a language.
Reason can be compiled to JS using BuckleScript, which generates performant JS from Reason/OCaml code. This is awesome because now you have fully typed systems with great JS interop and you can use the libraries you want. I'm hooked. Actually, the only thing I dislike about it is that I have to create lots of type definitions just to use dependencies, but usually, it is okay: we don't have to model the whole module, just the input, and output of a specific function/class/method we use. Because Reason isn't purely functional (you can have side-effects)‚??-‚??to me, Reason feels like the best way of building JS right now.
Reason can compile to bytecode/native as well. Using pure OCaml/Reason means that you won't have runtime errors if your code compiles, it can be statically built too with a small footprint and fast startup. and it builds VERY FAST. I mean, wow. FAST. FAST.
The biggest problem I noticed while trying to build a native Reason app was that I had no idea what people are doing and how to use some libraries. Most of them are OCaml, but since OCaml and Reason are interchangeable, I just used the Chrome extension to read things as Reason code. Still, it wasn't as clear. There is OCaml code that can't be converted to Reason, maybe due to lack of PPX in the Chrome extension. PPX is, from what I understand, an extension to the syntax‚??-‚??basically macros that turn code from one syntax version to another. You can think about it as a Babel plugin or something like that. Native Reason/OCaml doesn't have multi-core support yet, but for doing concurrent processes you can use Lwt, which is a promise-like library. There is still no simple Lwt guide/blog post that I've found!
Also, it seems that for native OCaml/Reason development, the entry level is very high, and it can be frustrating. The community doesn't explain things and takes knowledge forgiven, and mostly send the reader to look at the implementation or interfaces, but I have a strong belief that this will change eventually because it is just the beginning of the conversion of JS devs.
Go is a fantastic language. It is easy to learn, it is fast to compile and run, it has goroutines and simple concurrency using CSP
. It has multicore support and you can compile a static binary that will run on a plain Linux system with fast startup time. It has type inference for variable declarations, but not function definitions. It has interfaces and it looks like it has a professional community with good foundations.
The fact that Go has lots of strong modules and apps written in it, like Docker, Kubernetes, CockroachDB, means you can possibly make a binary that contains the infrastructure inside your app, for small and easy distributions (like on a raspberry pi). This is very powerful.
Its lack of generics (that should be added in the next major version) feels weird to me, in terms of using common data structures (graph, trees) and algorithms: It forces to write things every time or use a code generation step, which can also work, but I would rather have the compiler do that for me. Also, I don't fully understand the new module system, called VGO, but I guess we'll hear more about it and more simple guides will be available at the community will become more familiar with it. Finally and personally, I think that the language itself isn't pretty. I know, I know. This is not a good reason why not to use a language, but it makes me avoid fully test it or make a side project with it. It's just not fun, it's simple, boring and good. I'm sure that eventually, I'll end up using it for a production system and I'll love it. You know, the taste can change!
We've started with Ruby, so let's finish it with Crystal.
Crystal is another fairly-new language‚??-‚??still not 1.0‚??-‚??that looks almost like Ruby, but it is compiled and statically typed and fast! It supports OOP like Ruby does and has lots of nice features in it, like type inference, optional types, CSP for concurrency and compile-time macros, like Golang codegen, but inside the compiler. There are a couple of new web frameworks for Crystal too, like Lucky and Amber. There's Kemal which is Sinatra, but for Crystal. ORMs are available too, and because Crystal resembles Ruby (with a touch of Elixir), you can find ORMs that use almost the same APIs, along with type safety. This is a big thing!
Since the language is still young, it still has time until it will be production ready. I'd like Crystal concurrency to use all the cores available, like Go, instead of having to manually fork in order to do that. I would rather that methods that throw exceptions would return result type instead, so error handling would be explicit. I wish that Enums could have values in them, so we could use Enums just like Swift enums and OCaml variants. Better editor support could help too: autocompletion and type hints on hover have been an awesome addition. Moreover, in Scry, the language server's autocompletion works on the standard library but not on user code. I also have a small fear that Crystal wouldn't make it to version 1.0. But I really hope that it will.