The Coolest Programming Language Features

Jeremy Grifski - Apr 3 '19 - - Dev Community

As the Sample Programs collection grows, I often like to reflect on what I’ve learned. In particular, I enjoy comparing and contrasting languages and their features. After playing with over 100 languages myself, I figured I’d share my personal list of the coolest programming language features.

Now, normally these types of articles are designed as clickbait. For instance, I could have easily titled this article Top 10 Coolest Programming Language Features—and to be fair, I considered it—but, I felt like this article would be better served as a living document. In other words, I plan to make additions and changes as I learn more about programming languages.

So, make sure you check back every once in awhile, and don’t forget to give it a share. At any rate, let’s get right to it.

Features List

For your convenience, I've provided a list of links to each cool feature.

In the following sections, you’ll find an ever-growing collection of cool programming features for your perusal. When you’re done browsing, feel free to share some of your favorites in the comments.

Extension Methods

One of the coolest programming language features I’ve had the chance to explore is extension methods. When I first heard about extension methods, I was implementing Hello World in Kotlin. Of course, they’re completely unnecessary for such a trivial program, but I did get a chance to learn of their existence.

If you’re not sure what extension methods are, they allow us to tack on methods to existing classes without extending the class directly. For instance, let’s say we really like the String class in Java, but we think the class could benefit from a mutation method. Currently, the only way we can create a mutation method is by creating our own class which extends String:

public class StringPlusMutation extends String {
  public String mutate() {
    // Add mutation code
  }
}
Enter fullscreen mode Exit fullscreen mode

Meanwhile, in Kotlin, all we have to do is write a method which directly extends the String class:

fun String.mutate():
  // Add mutation code
Enter fullscreen mode Exit fullscreen mode

Now, anytime we create a String, we can call mutate on it as if mutate were a part of the original set of public methods in String.

Of course, I should mention that extension methods have their drawbacks. For instance, using our example above, what happens if the String API gains a mutate method? That would be one tricky bug, but it’s probably safe to say it’ll rarely happen.

Regardless, I would personally only ever consider using extension methods for rapid prototyping. Let me know when you use them in the comments.

Macros

Another one of the coolest programming language features I’ve come across is macros. When I first discovered macros, I was playing around with Hello World in Rust. After all, the print functionality in rust is implemented as a macro.

For those of you that don’t know, macros are actually a metaprogramming feature. More specifically, macros allow us to modify the language directly by adding rules to the abstract syntax tree. These rules are generated through pattern matching.

That said, the explanation above is pretty dense, so let’s take a look at an example in Rust:

macro_rules! print {
    ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
}
Enter fullscreen mode Exit fullscreen mode

This rule was taken directly from the Rust source code. As we can see, the print macro has one pattern matching case which essentially accepts any number of arguments and attempts to format them before printing.

If the pattern matching syntax is confusing, it’s probably a good idea to learn a bit about regular expressions. The regular expression syntax is similar to the pattern matching syntax.

Of course, as you can probably see, macros are an extremely complex feature. In fact, they are often difficult to write and equally difficult to debug. That’s why, even in Rust’s documentation, macros are considered a feature of last resort.

Automatic Properties

In addition to macros and extension methods, one of the coolest programming language features is automatic properties. I first learned about automatic properties when I discovered them as a bit of syntactic sugar in C#. Since then, I wrote a bit about them in Hello World in C#.

Basically, automatic properties are just shorthand for getters and setters in object oriented programming languages. For instance, let’s say we have a Person class, and we want the class to have some name field. After all, people have names, so it makes sense to give the class this field. Let’s go ahead and implement this in Java:

public class Person {
  private String name;
}
Enter fullscreen mode Exit fullscreen mode

Now, if we want to be able to set the name, we’ll probably want to write a public method to update the private name field. Colloquially, we call these methods setters because they set a property of an object. However, officially, they’re called mutator methods.

In Java, the following creates a mutator method:

public setName(String name) {
  this.name = name;
}
Enter fullscreen mode Exit fullscreen mode

Already we have six lines of code, and we can’t even request the person’s name yet. To do that, we write a getter or accessor method:

public getName() {
  return this.name;
}
Enter fullscreen mode Exit fullscreen mode

In languages with automatic properties, we can completely remove these six lines of boilerplate code. For instance, in C#, the following class is identical to the one we just created in Java:

public class Person
{
  public string Name { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

With automatic properties, we can reduce six lines of code to a single line for every field we want to expose—wonderful!

Optional Chaining

Up next in the list of coolest programming language features is optional chaining. I first discovered optional chaining when I was writing Hello World in Swift. Of course, the feature isn’t all that useful for implementing Hello World, but it’s still an interesting concept to explore.

Before I can really explain optional chaining, we have to get a grasp of what an optional value is. In Swift, variables cannot be empty. In other words, variables cannot store a value of NIL, at least not directly. This is a great feature because we can assume that all variables contain some value.

Of course, sometimes variables need to be NIL. Fortunately, Swift provides that through a boxing feature called optionals. Optionals allow a user to wrap a value in a container which can be unwrapped to reveal either a value or NIL:

var printString: String?
printString = "Hello, World!"
print(printString!)
Enter fullscreen mode Exit fullscreen mode

In this example, we declare an optional String and give it a value of “Hello, World!” Since we know that the variable stores a String, we can unconditionally unwrap the value and print it. Of course, unconditional unwrapping is typically bad practice, so I’m only showing it for the purposes of showing off optionals.

At any rate, optional chaining takes this concept of optionals and applies it to method calls and fields. For instance, imagine we have some long chain of method calls:

important_char = commandline_input.split('-').get(5).charAt(7)
Enter fullscreen mode Exit fullscreen mode

In this example, we take some command line input and split it by hyphen. Then, we grab the fifth token and pull out the seventh character. If at any point, one of these method calls fails, our program will crash.

With optional chaining, we can actually catch the NIL return values at any point in the chain and fail gracefully. Instead of a crash, we get an important_char value of NIL. Now, that’s quite a bit more desirable than dealing with the pyramid of doom.

Lambda Expressions

We likely couldn’t make it through a list of the coolest programming language features without talking about lambda expressions. Now, to be fair, lambda expressions aren’t a new concept (see: Hello World in Lisp). In fact, they’re older than computing. That said, lambda expressions continue to appear in modern languages—even as new features in established languages like Java.

To be honest, the first time I heard about lambda expressions was about three or four years ago when I was teaching Java. Back then, I didn’t really know what lambda expressions were, and I was hardly interested in them at the time.

However, a couple years later, I started playing with Python which has tons of open-source libraries that leverage lambda expressions. So, at some point, I had to use them.

If you’ve never heard of lambda expressions, you may have heard of anonymous functions. For simplicity, they’re synonymous. The subtle difference is that lambdas can be used as data. More specifically, we can package up the lambda expression in a variable and manipulate it as data:

increment = lambda x: x + 1
increment(5)  # Returns 6
Enter fullscreen mode Exit fullscreen mode

In this example, we have essentially created a function, stored it in a variable, and called it like any other function. In fact, we can even make a function return a function, thereby dynamically generating functions:

def make_incrementor(n):
  return lambda x: x + n
addFive = make_incrementor(5)
addFive(10)  # Returns 15
Enter fullscreen mode Exit fullscreen mode

Now, that’s cool!

Gradual Typing

If you’ve been around coding a bit, you’re probably familiar with the two main varieties of typing: static and dynamic. Of course, let’s not confuse these terms with explicit and implicit typing or even strong and weak typing. All three pairs of terms have different meanings.

To me, one of the coolest programming language features is the intersection between static and dynamic typing called gradual typing. Basically, gradual typing is a language feature which allows the user to decide exactly when they want static typing—otherwise, assume dynamic typing.

In many languages that support gradual typing, this feature manifests itself through type annotations. Type annotations are pretty much what you would normally see in an explicitly typed language, but the annotations are optional:

def divide(dividend: float, divisor: float) -> float:
  return dividend / divisor
Enter fullscreen mode Exit fullscreen mode

Here, we have a fully annotated Python function which indicates the expected input and output types. However, we can just as easily remove the annotations:

def divide(dividend, divisor):
  return dividend / divisor
Enter fullscreen mode Exit fullscreen mode

Now, I believe nothing is stopping us from passing pretty much anything into this function in either case. However, the first option opens up opportunities for static type checking which can be integrated into IDEs and other static analysis tools. I’d say that’s a win-win.

While I’ve chosen to use Python as an example, the first time I encountered gradual typing was during my research for the Hello World in Hack article. Apparently, Facebook really wanted to improve on PHPs typing system.

Immutability

Another one of the coolest programming language features is immutability. When I first discovered immutability, I was actually in a course on high-level languages. In this course, we had an assignment where we had to compare and rate the features of about 10 different languages. Of those features, one of them was value semantics, a feature that implies immutability.

Of course, what is immutability? As it turns out, immutability describes variables—or rather constants—that cannot be changed after creation. Often, immutability is manifested in strings in many languages. That’s why it’s usually considered a bad idea to use concatenation in a loop:

my_string = ""
for i in range(10):
  my_string += str(i)  # Generates a new string every iteration
print(my_string)  # Prints "0123456789"
Enter fullscreen mode Exit fullscreen mode

In fact, you may notice that I never use concatenation in my Reverse a String in Every Language series for that exact reason. Of course, language developers are aware of typical pitfalls, so they do a good job of optimizing our mistakes.

Anyway, I had never actually seen a language implement immutability beyond strings until I played with Hello World in Elm. In Elm, variables are immutable just like in math, so the following code snippet throws an error:

a = 2
a = a - 5
Enter fullscreen mode Exit fullscreen mode

Here, the issue is a matter of recursion. First, we define a. Then, we attempt to redefine a as a function of itself. To those of us who typically use imperative languages, we wouldn’t even bat an eye at this syntax. But to a mathematician, something is just deeply wrong with that second equation.

Of course, the beauty in immutability is the ability to trust a value after it is created. At no point will you have to worry about it changing behind your back.

Multiple Dispatch

Like lambda expressions, multiple dispatch isn’t exactly a new programming language feature, but it’s one of the coolest. That’s because, it allows us to do fun things like have different implementations of the same method in an object hierarchy.

Recently, I was reminded of this feature as I was writing the Hello World in Julia article. Apparently, Julia is a numerical analysis language like Python and R, and Julia supports general-purpose programming. Unlike Python, however, Julia supports multiple dispatch.

At this point, I should probably make a distinction between single and multiple dispatch. Single dispatch is a programming language feature which allows users to define the same method in different ways in an object hierarchy. For instance, consider the following:

pikachu.tackle(charmander);
charmander.tackle(pikachu);
Enter fullscreen mode Exit fullscreen mode

Here, we assume we have two objects, pikachu and charmander, in some Pokémon hierarchy. Both objects override some generic tackle method. When we run this code, the JVM is able to dispatch the proper tackle method.

Of course, things can go poorly in a single dispatch language because the method selection is only dynamic for the caller. As for the parameters, we’re stuck relying on static types.

So, let’s say that charmander was instantiated as a generic Pokémon. With single dispatch, pikachu is going to use the generic Pokémon tackle method, not the charmander-specific one. As a result, pikachu could miss the tackle.

With multiple dispatch, this is hardly an issue because the method selection process occurs at runtime. Naturally, the proper tackle method is issued, and pikachu lands the tackle.

If I butchered that explanation, let me know in the comments. For a much better explanation, I recommend Eli Bendersky’s article titled A Polyglot’s Guide to Multiple Dispatch.

Destructuring

Remember when I mentioned pattern matching? Well, destructuring—also known as iterable unpacking—is another form of pattern matching used to extract data from collections of data. Take a look at the following example from python:

start, *_ = [1, 4, 3, 8]
print(start)  # Prints 1
print(_)  # Prints [4, 3, 8]
Enter fullscreen mode Exit fullscreen mode

In this example, we’re able to extract the first element of the list and ignore the rest. Likewise, we can just as easily extract the last element of the list:

*_, end = ["red", "blue", "green"]
print(end)  # Prints "green"
Enter fullscreen mode Exit fullscreen mode

In fact, with pattern matching, we can extract whatever we want from a data set assuming we know it’s structure:

start, *_, (last_word_first_letter, *_) = ["Hi", "How", "are", "you?"]
print(last_word_first_letter)  # Prints "y"
Enter fullscreen mode Exit fullscreen mode

Now, that has to be one of the coolest programming language features. Instead of extracting data by hand using indices, we can write a pattern to match which values we want to unpack or destructure.

Inline Testing

To be honest, I wasn’t sure what to call this feature because I’m not sure it has a formal name. That said, inline testing is one of the coolest programming language features I’ve ever seen.

I first came across inline testing in Pyret, a language designed for programming education. In Pyret, unit tests are a part of the basic syntax. In other words, we don’t have to import any libraries or build up any suites to run tests.

Instead, Pyret includes a couple of clauses for testing within the source code:

fun sum(l):
  cases (List) l:
    | empty => 0
    | link(first, rest) => first + sum(rest)
  end
where:
  sum([list: ]) is 0
  sum([list: 1, 2, 3]) is 6
end
Enter fullscreen mode Exit fullscreen mode

Here, we can see an awesome list sum function. Within the function, there are two basic cases: empty and not empty. In the empty case, the function returns 0. Otherwise, the function performs the sum.

At that point, most languages would be done, and testing would be an afterthought. Well, that’s not true in Pyret. To add tests, we include a where clause. In this case, we test an empty list and a list with an expected sum of 6.

When the code is executed, the tests run. However, the tests are non-blocking, so code will continue to run barring any catastrophic issues.

Regardless, I love this feature just in terms of honest maintainability. Testing is never forgotten in the development process when using Pyret.

Inline Assemblers

Oh, you thought inline testing was the only cool inline feature? Meet inline assemblers: one of the coolest programming language features I first learned about when writing Hello World in D.

As it turns out, an inline assembler is a programming language feature which allows a developer to tap directly into the system architecture. When using a language that has inline assembler support, the following code is completely legal:

void *pc;
asm
{
    pop  EBX         ;
    mov  pc[EBP], EBX ; 
}
Enter fullscreen mode Exit fullscreen mode

Here, we have what looks like a mix between C/C++ and assembly programming, and that’s essentially what this is. We have written code that works directly with the underlying architecture, so we know exactly what’s happening.

I find this feature really interesting because languages tend to opt for abstraction when given the opportunity. With D, we can totally forego the built-in functionality and write a custom assembly-level implementation ourselves.

What Are Your Favorite Features?

Did your favorite feature not make the list of coolest programming language features? Let me know in the comments below, and I’ll check it out! Of course, don’t forget to give this article a share if you enjoyed it. I’m sure others will too.

At any rate, thanks for taking some time to check out my work. If you liked what you saw, consider subscribing to The Renegade Coder. That's where I do most of my writing. In fact, this article is a cross-post from that site, so why not check it out?

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .