Dart and language evolution.

Recently at work I’ve been working with a language called Dart together with a UI framework called Flutter. I have always heard positive things about Dart but never got around to working with it or trying it out. And now after a few months of working in it I asked myself, why do people like this? After searching around a bit on the web it turns out my initial assumption that people like the language might have been the result of marketing and publicity that the language got around its release. Articles like this called Dart the “Javascript Killer” while this article names Dart the number one worst language to learn, which makes me more curious what Dart really is about.

Do note that I am not a language designer nor have deep knowledge in language design. The things I talk about in this post are just the things I’ve bumped into while working with Dart I’m not claiming to be an expert, this is merely a beginners perspective.

Type system

When working with Dart and what inspired me to write this at all was when I had sent a pull request to one of my colleagues. This pull request expected a new set of fields to be returned from an API call to our backend. My colleague only tested the app without pulling the latest changes to the backend and as expected that specific API call didn’t return the new field. This resulted in the member which in this specific case was a boolean, got the value of null. After some research I found out that booleans, integers and other primitive types indeed can take on the value of null in Dart.

void main() {
  bool b = null;
  
  if (b) {
    print('B is true');
  }
  else {
    print('B is false');
  }
}

This above application results in an error: Uncaught Error: Assertion failed: "boolean expression must not be null

For someone like me who mostly writes code in C/C++ and C# on a daily basis, now having to write if (b != null && b) in my if statements feels super weird. Looking further into this problem it is rooted in how Dart’s type system is set up. The first line in our example assigns null to our boolean which is an instance of the Null class. So Null is a type in Dart and not a bool and since we can assign null to our bool there is an inconsistency in the type system. If Dart was aiming to be a dynamically typed language this would be fine but they specifically state in their documentation that they have “A sound type system” which means it should never be able to get into a state where an expression evaluates to a value that doesn’t match the expression’s static type. Looking at our example this means a static type of bool should be guaranteed to evaluate to a bool at runtime, which doesn’t happen in our example application.

Dart has what they call “Sound null safety” a feature which is opt-in and unfortunately I cannot use due to some of the legacy code that the project I work on depends on.

Typescript is like Dart, an optionally typed language, for example the following code snippet is allowed and can compile:

let anExampleVariable : Boolean = null;
console.log(anExampleVariable)

However the compiler will always complain when you assigning null to a Boolean variable since they are two completely different types. In Typescript both null and undefined are primitive values, something that Dart seems to lack. I think that the root cause of this problem is that in Dart, everything is part of the common Object type. Primitive types are the base building blocks in programming. For everything to inherit from an object type introduces a lot of complexity together with the “modularity” of being able to modify or introduce new “base building blocks” to the application. Looking at the diagram above I would assume that we would be able to create a new Currency type that inherits from num. I have never been in a codebase that does this but I can only imagine that it would be a great cognitive load.

Java is a language that greatly enforces object oriented programming and James Gosling one of the founders of the language received a question: “Why are there primitive types in Java? Why wasn't everything just an object?” to which James responded:

Totally an efficiency thing. There are all kinds of people who have built systems where ints and that are all objects. There are a variety of ways to do that, and all of them have some pretty serious problems. Some of them are just slow, because they allocate memory for everything. Some of them try to do objects where sometimes they are objects, sometimes they are not (which is what the standard LISP system did), and then things get really weird. It kind of works, but it's strange. Just making it such that there are primitive and objects, and they're just different. You solve a whole lot of problems.

I totally agree with James here. But since Dart is mainly trying to compile to JavaScript this probably doesn’t matter that much.

Interfaces

Dart uses implicit interfaces, every class implicitly defines an interface containing all the instance members of the class and of any interfaces it implements. Looking at the following class:

class Person {
  final _name;

  String greet(String who) => 'Hello, $who. I am $_name.';
}

In this example both _name and the method greet is part of an invisible interface that other classes that implement this class will be able to access.

In Dart there is no interface keyword hence you cannot declare interfaces in Dart. Instead you have two different keywords `implements` to implement the implicit interface that Dart creates for your class and the `extend` that works as usual.

I tried searching the web to find an answer to what problem implicit interfaces actually solves but all I could find was this link which states “Implicit interfaces are a powerful tool in Dart to avoid having to repeat the contract of a class when it can be trivially inferred from the signatures of an implementation of that contract.” I see this as a potential problem as an interface is a contract where you specify the intent of how to use something. Mixing this with the actual implementation is not the same thing, and sure you save some time having to write code for the contract but I can see this actually saving you time in the long run for example in the case when someone implements an interface not intended to be implemented.

Interfaces is a common concept which exists in most object oriented programming languages but Dart takes a different approach to how they work. New keywords and implicit actions taken by the language once again adds cognitive load and complexity to your applications. C# suffers from a similar problem in my opinion but on a different level which you can read more about here. Ways to work around this in Dart could be naming conventions like the ones you see in C# where you prepend your class name with an “I” but all it takes is one person to use extends on your interface and you have a problem in your codebase. The best solution in my opinion is to create explicit interfaces using abstract classes but I would much rather not even have this be part of the language.

Enums

I personally love Enums, they allow me to map integer values or strings to an actual type. In my C applications it is very common to see something like:

enum Type 
{
	TYPE_NONE,
	TYPE_HUMAN,
	TYPE_MONSTER,
	TYPE_MAX,
};

struct Entity 
{
	Type type;
};

void SomeFunction(Entity *e)
{
	switch(e->type)
{
	case TYPE_NONE:
{
	// Do stuff for none
} break;
case TYPE_HUMAN:
{
	// Do stuff for human
} break;
...

This is great and allows me to make very clear code which in my opinion is easy to read. You can quite simply do this with Dart as well but something you cannot do is give for example TYPE_HUMAN the value of 2 explicitly. Dart allows no custom values within enums. If you want to specify custom values on your enum you have to use what is called an extension which looks like this:

enum Type {
  TYPE_NONE,
	TYPE_HUMAN,
	TYPE_MONSTER,
	TYPE_MAX,
}

extension TypeValue on Type {
  int get value {
    switch(this) {
      case Type.TYPE_NONE:
        return 0;
      case Type.TYPE_HUMAN:
        return 2;
      case Type.TYPE_MONSTER:
        return 3;
      case Type.TYPE_MAX:
        return 4;
    }
  }
}

You can then access the integer value through Type.TYPE_HUMAN.value. I guess this is fine but this is once more a case where Dart is moving away from the standard conventions that programming languages use and I find that quite problematic for someone like me who is new to Dart. It produces a lot of friction for me as a developer and for new developers it would be very confusing starting out with Dart and then having to work with basically anything else.

Thinking more about this problem I remember when I worked with Java on a daily basis I really enjoyed how enumerations worked in that language. Solving the problem of having an enumeration of strings you could do something like the following:

public enum Country
{
    DE(“Germany”),
    IT(“Italy”),
    SE(“Sweden”),
 
    private String name;
 
    Country(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
}

I constantly miss this pattern in C# and I think it would fit really well in a language like Dart as well.

Language evolution

My takeaway point from this post Is that I see cases where Dart tries to break away from the norm in programming by doing things in its own way. The problem I have with this is that Dart are doing these things without them actually solving any real problems. I like writing code and I don’t really need my programming language to have cases where it implicitly does things for me because sooner or later I will still have to write more code to solve other problems anyway like with the enumerations.

I was unfortunately unable to find any original design documents for Dart when writing this and it was unbelievably hard to find information at all. I don’t know if the reason behind this is the low community engagement but perhaps it is and that might also be the reason to why I’m having trouble finding information.

There are different versions of Dart (1 and 2) where the later version actually solves some of the problems with the type system for example but this is also at the same time very confusing because a lot of the resources online are still targeting Dart 1 which makes me super confused.

Evolving programming languages is in a way necessary and actually do bring a lot to the table. For example C# introduced LINQ in 2007, I think this is a healthy type of evolution where object oriented languages bring in good parts from more functional programming languages and actually benefited a lot of developers including myself in terms of productivity and readable code. In the case of adding new keywords and implicitly creating an interface for me is not helping me and instead counter productive because I have to unlearn a common thing in most programming languages I use just to write a few lines of code less which I then instead have to write when I want to associate values with enums.