loading...

If you were designing a programming language what would your favourite features be?

dansilcox profile image Dan Silcox Updated on ・2 min read

Having learnt a few different languages over the years I have a shortlist of my favourite features across different languages (note I have included a few things that are not strictly language features, but are commonly used libraries that I think are just great!) - I will list my own thoughts below, but I thought it would also be a fun question to ask the community!

So here's my list in no particular order:

  1. Generics
  2. Arrow functions
  3. Async/Await
  4. LINQ queries
  5. Extension methods
  6. Attributes
  7. Database migrations

What are your thoughts on these features? Would you include them if you were to write a language? What else would you add and why?

Edit: one that is really useful, some would say absolutely key, in modern web development, is native strong-typed JSON serialisation/unserialisation - ie being able to convert easily between object and JSON representation without having to manually map the internal fields, especially where nesting is involved. C# has a great implementation utilising generics for the "to" object type when deserialising - the equivalent serialiser is pretty good too, with easy support for custom converters for things like string representations of enums, etc

Discussion

pic
Editor guide
 

Good question. There's a set of features I can't easily live with nowadays (like generics you mentioned), but let me focus on one of the most neglected language feature:

ADTs (Algebraic Data Types, aka enum / union on steroids, case classes):

Most languages have records, which store various data together. But few has ADTs, which let case selection on a closed set of named records. For example a Haxe-style enum*:

// plain enum so far
enum NumberSystem {
  Roman;  // I, II, ..
  Arabic;  // 1, 2, ..
}

// ADT-y enum
enum MyNumber {
  Numeral(value: Int, writtenIn: NumberSystem);  // I, II, 3, ..
  Textual(value: Int, rawValue: String);  // "one", "hundred", ...
}

var num1: MyNumber = Numeral(1, Roman);
var num2: MyNumber = Textual(25, "twenty-five");

// silly example, a better one would be a render
// function which gives back the original representation
var v = switch(num1) {
  case Numeral(v, _): return v;
  case Textual(v, _): return v;
};

ADTs can be emulated by subclassing to some extent, but then you need to resort to instanceof hacks, which defeats the purpose. Also no guarantee on completeness on cases checked that way.

It also brings a different mindset compared to subclassing: with subclassing, I tried to massage all differences of the subs into a generic interface. With ADTs, I embrace differences and handle them explicitly.

Typically languages with ML (sml, ocaml, mlton..) or Haskell-lineage (haskell, elm, purescript..) have ADTs. Scala has case classes which can be sealed to get similar effect. C has tagged unions which is similar if we squint, but forces manual rolling of tag-to-part mapping.

Apart from this, on the high level I would love an integrated language experience, where all the tooling and syntax work together in unison, batteries-included, with as much upfront guarantees as possible. Ur/web is an experimental (now in maintenance mode) language that kind of promised this for the syntax at least, but in practice didn't satisfy me. Also far from production. From the old times, Turbo Pascal had a nice experience, but it didn't have SQL integrated or any modern language features either.

They say SmallTalk had a nice closed world you could live in, but I have no experience.

*: I don't like Haxe's Color enum example, as the constructors are not orthogonal (red, blue, green, rgb(r,g,b)). Not that they can always be, but this is a particularly bad first example.

 

Thanks for your post, I knew I was going to learn so much by asking this question - never even heard of Haxe! I like the example and I agree that native ADT (or at the very least enum) support is definitely better than using user-land sub-classing for the same thing.

Yes I've heard good things about SmallTalk too, but never used it myself.

 

Haxe is a curiosity I would say - compiler written in Ocaml, compiles very fast, and has a balanced feature set. Has both server- and client-side runtimes (lots of targets actually).

I used it in the past when developed flash games, it (the language) was cleanly superior to ActionScript. ActionScript (and flash) on the other hand came with the Adobe IDE (don't remember the name), and people more interested in writing games than in programming languages produced more shiny stuff. Classic fallacy, writing game engine instead of game..

The limiting factor last time I looked at it was the cross-platform graphics stuff (Neash/NME), which tried to stuff the gfx API behind a flash-like interface. The experience was not always smooth on all targets. Wonder how that fares today.

The original developers, Motion Twin used it in a full-stack fashion. It backed most of their games AFAIK, for example Die2Nite. It seems the author (Nicolas Cannassee, @ncannassee) is up to something again, see youtube.com/watch?v=SfwLOOkcSfo.

 

I'd probably include generics and extension methods as native functionality, along with:

Decorators

Functions that take another function or class as input, mutate it, and then return the result, together with concise syntax for invoking this as part of the definition of a function or class. Typical syntax in languages that implement them looks like:

@decorator2
@decorator1
function doSomething():
    return 4

They're a remarkably useful meta-programming tool (for example, you can implement the concept of mixins as decorators), and having a clear syntax similar to what's used in Python 9and recently added to JavaScript) means that they're easier to work with in version-control than just calling the relevant functions as part of the declaration.

Exception type matching as an implicit part of try/catch clauses.

In essence, I despise languages that make you write code that looks like this:

try {
    doSomething()
} catch (error) {
    if (error instanceof SomeErrorType) {
        handleError(error)
    } else {
        throw error
    }
}

I much prefer the Python approach to this type of thing:

try:
    doSomething()
except SomeErrorType as error:
    handleError(error)

No need to re-throw the error if you can't handle it, and no extraneous conditionals to clutter the code.

Object property accessors as a native language feature

Not many OOP languages lack this feature these days, but I feel it's just too important to not mention as something a language should have. Put simply, sometimes you just need to make it opaque to the outside world that some attribute of some object in your API isn't just a value, but gets computed when you read from it or does extra stuff when you write to it.

Native concurrency support

Probably done similar to how Go handles things (native coroutine support, but it automatically spreads things out across multiple threads at runtime, with the number of threads chosen by default based on the numer of thread's of execution on the system it's running on). I would likely add regular multi-process and multi-thread support as well, but as part of the standard library instead of a native language feature.

Coupled with sane messaging primitives as a native language feature, this could trivially replace any need for Promises and async/await type functionality (in the places where having Promises be used even makes sense that is, some things they get used for in JS and the Web API's are just ridiculous, and could just as easily be synchronous if it weren't for the stupid execution model used in JS).

List/Set/Mapping comprehensions

Quick Wikipedia link.

Essentially, a concise syntax to perform some of the same type of things that map, reduce, and filter do, without needing even a lambda/anonymous function to do it.

A native Set type

I don't often need to make use of set theory constructs in programming, but there are just some things where it makes life so much easier. Having a native Set type makes handling those cases much simpler from the programmer's perspective.


As far as the other stuff you mentioned:

  • Arrow Functions: The shorthand is nice, but the big reason they exist is the insanity that is this in JavaScript. No sane language should have bindings work like that. Without that, you can just use whatever lambda expression syntax you like (and I feel that there are better syntaxes than an arrow operator that might be confused for comparison).
  • Async/await: Covered above. I really just don't like the way it works in JavaScript (it really just says "I'm not dealing with async crap here, whoever called me can deal with it.").
  • LINQ: The concept is nice, but isn't something I think needs to be native to the language itself. I would probably do something equivalent in the standard library, but not in the core language.
  • Attributes: Trivially covered by decorators.
  • Database migrations: Should be part of whatever specific database library you're interacting with. Short of possibly providing native ODBC-type functionality, I would not be including database support as a core language feature (though I'd probably have some form of it in the standard library), and thus would not need schema migration support to be a core language feature.
 

Ah how could I miss decorators! First came across these in typescript, writing angular. Thanks for the well thought out and balanced input!

 
 

I like pointers. Anyone who thinks C can be bettered is delusional.

 

For sure, pointers are crucial to any language, at least "under the hood" if not publicly exposed.

I have a lot of respect for C developers, and for the language itself, however, for me, C (or any particular language for that matter) is not always the right tool for the job. It certainly has a lot of value of course - in embedded engineering, system drivers, even in writing other languages and I'm sure countless other tasks. Would it be the right language in which to write a simple web API? I think not...