Anton Astashov's blog

Would be nice to write something clever here.

When Dart Is a Good Choice

| Comments

There was a link to flutter.io recently, which went to Hacker News, Reddit, etc. The project itself is on the very early stage, not even alpha, afaik, and it wasn’t an announcement, somebody just posted a link to flutter.io there.

The programming language they use for Flutter is Dart. And I was really surprised by the crowd responses related to Dart – there was so much hate. It seems like there is some confusion among people about what the language and its ecosystem is these days. Since I have some experience with it, I decided to share my thoughts on the language and its ecosystem, what it’s good for and what it’s maybe not the best choice for, again, from my experience.

So, from my perspective, the main selling points, things which are really nicely done in Dart, are:

Async

As I previously mentioned, Dart has really nice async support across its whole ecosystem. Futures and Streams were added to the SDK from the very early versions, so they became standard de-facto, and are used in all the places where asynchronoucity is expected.

I didn’t see any alternative implementations of Futures or Streams in the wild, everybody just uses the SDK’s ones. And majority of packages is built with async APIs, methods return Futures and/or Streams. This makes them nicely composable with each other.

If you’re building a front-end app, you’ll have all the DOM events wrapped into Streams in SDK. If you’re building a web-service, database drivers (e.g. MySql) return the Stream with results as a result of query execution. Loggers give you a Stream with all the log records. You can subscribe/unsubscribe to these streams, map/filter/fold them, etc. All that stuff provides a good foundation for building FRP apps in Dart.

Working with Futures also became pretty pleasant since Dart got async/await support. That allows you to write heavily async/concurrent applications, which look very synchronous. Futures, again, are everywhere – HTTP has Future-based API, unit tests cases will wait if the test returns a Future, web frameworks will also handle it properly if the request handler returns a Future, etc.

There are Zones, which nicely augment the async story of Dart. They give you the ability to wrap any piece of code into a “Zone”, which will keep the async context of that piece of code. So you can catch all the errors (even if they happened in the async part of it), store zone-local values (they will be accessible globally inside the zone), and do a bunch of other things.

Generally, debugging of async apps is painful, because stack traces are messed up, and it’s hard to figure out where we came from to the point where the exception happened. They may look like this:

1
2
3
4
5
6
7
8
Unhandled exception:
Uncaught Error: foo
Stack Trace:
#0      blah.<blah_async_body> (file:///Users/anton/projects/blah/blah.dart:7:3)
#1      Future.Future.microtask.<anonymous closure> (dart:async/future.dart:144)
#2      _microtaskLoop (dart:async/schedule_microtask.dart:43)
#3      _microtaskLoopEntry (dart:async/schedule_microtask.dart:52)
...

Which gives you very little information, just the actual line where the exception happened. You have no clue how the app got to that point. Luckily, there is stack_trace package, which has Chain.capture() method. It uses Zones under the hood, and if you wrap your code with it, the stack traces became way nicer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
blah.dart 7:3        blah.<async>
===== asynchronous gap ===========================
dart:async           _Completer.completeError
blah.dart 10:1       blah.<async>
===== asynchronous gap ===========================
dart:async           Future.Future.microtask
blah.dart            blah
blah.dart 14:15      main.<fn>.<async>.<fn>.<async>.<fn>.<async>
===== asynchronous gap ===========================
dart:async           Future.Future
blah.dart 13:17      main.<fn>.<async>.<fn>.<async>
===== asynchronous gap ===========================
dart:async           Future.Future
blah.dart 12:28      main.<fn>.<async>
===== asynchronous gap ===========================
dart:async           Future.Future.microtask
blah.dart            main.<fn>
package:stack_trace  Chain.capture
blah.dart 11:9       main

It adds some performance and memory penalty, but usually it’s neglectable, and it helps a lot with debugging.

So, IMHO, Dart is really good for building front-end apps or single-threaded back-end services. Unfortunately, multithreading support is pretty immature for now – there are actor-based isolates, but they are very slow (instantiating of an isolate takes almost a second (sic!), and the throughput is about 20000-30000 messages per second on my MacBook Pro).

Staticly typed

It’s nice that there’s no compile-time, with there is static analysis support. Despite the fact the type system is “optional and unsound”, not using type annotations for public methods and functions is considered a bad practice.

The fact the language has source based VM actually simplifies debugging a lot. Despite a decent debugger and other tools, the most convenient way of debugging programs for me is still just throwing print statements. Here, you can easily throw them into the source code of any package or into SDK itself, and immediately see them on page refresh or script restart.

Static analyzer is written as a package, and also there is a daemon (basically headless IDE), which uses it and provides API, which could by used by the IDEs to provide Dart support. The analyzer supports autocomplete, go to definition, find usages, refactoring, etc. This is really cool, because it simplifies writing tools, which require analyzing of the source code. Like:

Static analyzis allows to find a lot of errors, which would be found in runtime otherwise later by end users. The analyzis coverage is okay, but sometimes I’d like it to be stricter. There is a new “strong mode” option in the analyzer coming, which will be stricter, especially related to “dynamic” variables (where the type inference failed, or where it was specifically marked as dynamic), and I’m really looking forward to it.

There is one thing missing, though, and it really hurts. Unfortunately, there’s currently no method generics (just class generics). There is DEP for that though, and it seems like the Dart team is experimenting with them in the new JS transpiler (implemented as annotations for now though), which gives me some hope it will wind up to the VM later as well.

Tooling

Humans are weak. We have hard time keeping more than 7 things in our head simultaneously, we forget things to do, we make mistakes, and cannot fully keep big complex constructs in our head. Complexity of large applications easily go waaay over what average human’s brains can process and keep track of.

So, we rely on tools instead. The programming language should be expressive and allow to build powerful abstractions, which would help us to handle the complexity. But that’s not enough. We also need a code editor, which would help us to navigate through the thousands of lines of code, we need static analyzers, which will catch the errors we made early, we need linters or code formatters, which will make sure we write in the same style within the team, because keeping the code in the same styles reduces its complexity, etc.

Luckily, Dart’s tooling story is very good.

There are 2 main IDEs – Intellij IDEA and Github’s Atom. They are being developed simultaneously. I prefer IDEA for now, it works flawlessly, everything I need from IDE is there – go to definition, find usages, refactoring (I mostly use just renaming though), auto adding imports, integration with dartfmt, debugger, reliable search through class/method names, etc. The only missing thing for me – it doesn’t show a propagated type (if you rely on type inference). This feature is presented in Atom, but it’s missing other stuff, like debugger and search for class/method names. It seems like Atom is developing faster though, so we’ll see how fast it will get the debugger and the search.

There is dartfmt, which autoformats the code for you. It may not match your style preference, but I found it’s just easier to give up and let it style my code for me. A lot of tools also use dartfmt under the hood (e.g. code generator tool source_gen), so it kind of makes sense to use it, so your code and autogenerated code look similar.

There is Observatory, which allows to connect to running Dart processes and debug and profile them, and monitor the metrics.

There is my Crossdart Chrome plugin for Github, which adds ‘go to definition’ and ‘find usages’ functionality to Github’s tree views and pull requests, which is reaaally useful for code reviews.

There is pub, Dart’s package manager. It has pretty simple CLI API, but allows to do all the necessary things for managing dependencies. Seems to be heavily influenced by Ruby’s Bundler. One of the neat features – allows to override dependencies with local ones or just force some version (ignoring requirements of other packages). It’s very useful when your app is very modular, and depends on many internal packages.

There is Dartdocs, the documentation for all the packages on pub.

And there is Crossdart, the hyperlinked source code for all the packages on pub.

And a lot more.

Decent language

Though Dart is a bit conservative language (for my taste), it’s a quite decent OOP language. I like how the language encourages explicitness, provides a solid and well-structured SDK, and offers pretty logical and straightforward semantics. There are also some nice new features, like implicit interfaces (basically you can use any class as an interface), factory constructors, method cascades, etc.

There are also things that I’d expect from a modern language and are missing at the moment, like:

  • Method generics (you can only specify generics on a class)
  • Non-nullable types (like in Swift or Kotlin, String and String?)
  • Value objects and better immutability story overall. To be fair, it’s kind of possible right now, if you just create classes where all the fields are final and use something like vacuum_persistent for immutable maps/sets/vectors, but you potentially can still add mutable objects into your immutable hierarchy. You also have to implement equality, copy method, etc, every time. There are a bunch of packages, which solve that with the code generation (like this one), but it still looks like a workaround. I miss Scala’s case classes and Kotlin’s data classes :)

I should mention though that there are DEPs for Method Generics and for Non-Nullable Types, so there is a hope they will be added eventually.

Summary

From my experience, it was a breeze to use Dart for:

  • Developing large complex web applications working in browser. For example, Montage. Static typing, tools and analyzer made it way easier to create and then maintain that photo book editor.
  • Developing single-threaded services with async IO, which use MySQL, PostgreSQL, Mongo or RethinkDB. If your service is just fetching some data from a database and network, then aggregating it somehow and returning in JSON or HTML (which seems to be a majority of use cases), you can get pretty good performance even on one thread, since all your IO will be async and non-blocking from the top to the bottom, and at the same time won’t look like a callback hell.

What it’s potentially not good for:

  • Multi-threaded services – isolates are not there yet.
  • Something which needs MSSQL – seems like no driver for it
  • Obviously, if you’re tied to another platform, you may want to use something else (like, JVM)
  • There are not many packages on pub (just about 2000), so check first if you have everything you need there.

Choosing a language and a platform is always a tradeoff. There are some things I don’t really like in Dart, but given the decent staticly typed language with good semantics, which feels like interpreted language (because the VM runs the source code, not the bytecode), great tooling, and great async support – there are not many alternatives at the end.

So, check Dart out if you haven’t yet, maybe you’ll find it useful too.

Dart Tricks in dartdocs.org

| Comments

I’ve just finished building new infrastructure for Dartdocs, and Seth asked me to share my experience with Dart there. Obviously, you expect infrastructure for generating Dart docs for all the packages on pub to be written in Dart, though it might be built in any programming language. All it needs in a nutshell is to run a dartdoc shell command, which actually generates the documentation, and then upload the generated files somewhere.

This is not my first Dart project, we use Dart in Mixbook (where I work) pretty heavily, mostly for various client-side projects, but we also have some microservices on backend written in Dart as well. I love Dart, but like with any technology, using it is a tradeoff. It has its own pros (great async support, amazing tooling and ecosystem, staticly typed, but interpreted – no compile time) and cons (type system is not strict enough, IMHO, also a bit conservative for a modern language, i.e. where are my non-nullable types, method generics and immutable value objects?! :)), but for me pros outweight cons, so I use it a lot, and actually pretty happy with it.

The requirements for http://www.dartdocs.org were pretty simple – the infrastructure for generating docs for the Dart pub ecosystem, which is:

  • Efficient – should use the available computing resources efficiently, avoid unnecessary work
  • Scalable – allows to regenerate the documentation for the whole Dart ecosystem quickly (which is thousands and thousands Dart packages), within several hours, in case there is a new version of dartdoc tool.
  • Reliable – should work unattended, restore itself in case of failures, and should be easy to debug in case of failures.

These requirements describe pretty much any web service you usually write :) So, I’ll describe several tricks below I used in dartdocs to achieve these.

Efficiency

As I already said, Dart has really nice async support, for me this is one of its killer features. Futures and Streams were added to SDK from the very early versions, which means all the packages just use the SDK’s ones, and nobody tries to reinvent their own implementations of Futures or Streams. And this is a big deal! A lot of packages are built with concurrency support as well, methods return Futures and Streams, and that makes these packages composable with each other, since every package uses the same implementation of Futures and Streams. Web frameworks, database drivers, unit test packages, file system tools, loggers, http libraries, socket handling, etc – all of them usually support concurrency and non-blocking operations. This allows us to build event-based fully concurrent web services pretty easily (especially after adding async/await support in Dart 1.9!)

So, a workflow for the main dartdocs.org script is:

  • Download all the existing package names and versions from pub (or refresh the list if already downloaded)
  • Download (or refresh) the metadata for already generated packages from Google Cloud Datastore (was it successfully generated or not, generation datetime, etc)
  • Figure out the next batch of packages to generate
  • Actually generate the docs
  • Upload the docs to Google Cloud Storage
  • Update the metadata for the newly generated packages on Google Cloud Datastore

Mostly, all of these are network calls (except actual generation of docs), so it’d be dumb to do that sequently. But thanks to all the things about async and concurrency I desribed above, it could be done pretty easily! The HTTP lib and libs for working with Google Cloud services – they all return Futures, of course, so we can group them and then handle these groups in parallel. E.g., you could implement uploading to Google Cloud Storage in the following way:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Getting the list of all the generated docs files for [package]. This is actually non-blocking too!
var files = await _getListOfFilesForPackage(package);

// Grouping files into groups of 20
var groups = inGroupsOf(files, 20);

for (Iterable group in groups) {
  // Upload every 20 files in parallel, waiting til that batch finishes uploading, then starting a new one
  await Future.wait(group.map((file) {
    var future = _uploadFileToGCS(file);
    return future;
  }));
}

Pretty simple, and easy to read and reason about. There is some room for improvement (you could queue all the uploads, and just make sure you handle 20 at a time, using some task queue, for example), but this example is simple enough to demonstrate the use case.

This non-blocking nature may be not so important for dartdocs.org, but it’s becoming way more important when you use it in web services with HTTP front-end. Usually, you don’t spend a lot of time and CPU on crunching numbers in a request, but instead you just make a lot of network calls to various services and databases, combine data together and return to the requester. Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
@app.Route("/sendNotificationsToUsers", methods: const [app.POST])
sendNotificationsToUsers(@app.Body(app.JSON) List<int> userIds) async {
  Iterable<User> users = await getUsersFromDatabase(userIds);
  Iterable<bool> results = await Future.wait(users.map((user) async {
    Future<bool> result = sendEMailToUser(user);
    return result;
  }));
  if (results.every((r) => r)) {
    return JSON.encode({"status": "successful"});
  } else {
    return JSON.encode({"status": "failure"});
  }
}

If every piece is non-blocking, the app spends very really little computation time on each request and has availability to process other concurrent requests. So you can process a lot of requests concurrently in a single thread. Which is great!

Scalability

Scalability in this context means – if we want to finish regenerating docs faster, we just need to launch more instances in the cloud. So, we need a way to split the work between them, and rebalance the work when we add or remove the instances.

Let’s observe some properties of the pub Dart packages – each package is uniquely identified by its name and version. It’s immutable – the source code of the package never changes. Also, the packages are never being deleted, all their versions will forever stay in pub. So, developers can only add new packages, so the list is always growing, but never shrinking.

I hosted the infrastructure on Google Cloud, on Google Compute Engine (GCE) instances. GCE has the ability to create instance groups, where you can specify how many instances should be run within that group. Each instance within that group will have a unique name, and you can get a list of all the instance names within a group. So, splitting the work in this case is pretty simple – after finishing generating docs for another batch of packages, we ask for the list of currently existing GCE instances within the group, sort it, check what’s the current instance index within the list, and depending on that retrieve the next batch of packages from the whole list. I.e. if we have 5 instances in the group – foo1, foo2, foo3, foo4, and foo5. The current instance name is foo2. Whole number of unhandled packages is 10000. The batch size is 20 packages. Given all that, we’ll take the range 2001-2020 from the list of all unhandled packages as the next batch.

Sometimes, when we increase or reduce number of instances in the group, we may end up with 2 instances generating docs for the same package, but that’s fine – they will produce the same result and rare enough we can ignore that.

Reliability

The scripts should work unattended, and should try to restore themselves after failures. Also, there are a lot of network calls happening during the workflow of the script, so it should be tolerable to network failures, or external service failures. Also, in case of a failure, we need to know why the failure happened. We can achieve that by logging, timeouts, retries, and if we are out of retries, then just fail completely, and let something like monit to get us up and running again.

Retries

It’s actually very simple to do – let’s define this function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const _defaultDurations = const [const Duration(seconds: 3), const Duration(seconds: 5), const Duration(seconds: 15)];
Future retry(body(), {int number: 3, Iterable<Duration> durations: _defaultDurations) async {
  try {
    return await body();
  } catch (error, _) {
    if (number > 0) {
      var duration = durations.first;
      var newDurations = new List.from(durations);
      if (newDurations.length > 1) {
        newDurations.removeAt(0);
      }
      return new Future.delayed(duration, () => retry(body, number: number - 1, durations: newDurations));
    } else {
      rethrow;
    }
  }
}

Now, with any sync or async body(), in case it throws some exception, it will retry several times with specified durations, and then fail. We wrap every single network call into that retry, something like this:

1
2
3
import 'package:http/http.dart' as http;
// ...
var response = retry(() => http.get(url).then((r) => JSON.decode(r.body)));

Unfortunately, we lose the return type of body() in this case, this is would be a great use case for method generics (there is a ticket though).

Every network call in dartdocs.org scripts is wrapped with retry(), and it greatly reduces the number of failures, which may happen just because the network had problems or some service had occasional internal server error.

Timeouts

It’s a good practice to specify meaningful timeouts for the things that are not under your control (like network calls, or external shell commands runs). There is theFuture.timeout() method, which completes a future after specified duration, you use it somewhat like this:

1
2
var response = await http.get(url).timeout(const Duration(seconds: 10));
var json = JSON.decode(response.body);

I used this approach everywhere at first, but then figured out that for the shell scripts it doesn’t really work – if the shell script hangs, timeout fires, and the Dart script continues to run, but that script is not killed, it still hangs. So, a better approach would be to use timeout program, which kills the script and exits with non-zero status after specified timeout.

Logging

Sometimes things go wrong, and we need to know why. To figure out why we get some exception, it’s usually not enough to have just that exception and the stack trace, you usually need to know what happened before that, that’s why we need logging.

The most popular logging package in Dart is named (surprisingly!) logging. The nice thing about it, that it gives a stream with all the log records as part of its API, so you can subscribe to that stream and do whatever you want with it – write it to STDOUT, to a file, etc.

E.g., if we just want to print every log record into STDOUT, it may look something like this:

1
2
3
String format(LogRecord record) => "${record.level} - ${record.message}";

Logger.root.onRecord.map(format).listen(print);

Debugging

Since the code is async from the top to the bottom, in case of failure, what’d you expect to see in the stacktrace? E.g. for this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
import 'dart:async';

Future<Null> blah() async {
  throw 'foo';
}

void main() async {
  var result = await new Future(() async {
    await new Future(() async {
      await blah();
    });
  });
}

Probably something like this:

1
2
3
4
5
6
7
8
9
10
Unhandled exception:
Uncaught Error: foo
Stack Trace:
#0      blah.<blah_async_body> (file:///Users/anton/projects/dartdocsorg/blah.dart:7:3)
#1      Future.Future.microtask.<anonymous closure> (dart:async/future.dart:144)
#2      _microtaskLoop (dart:async/schedule_microtask.dart:43)
#3      _microtaskLoopEntry (dart:async/schedule_microtask.dart:52)
#4      _Timer._runTimers (dart:isolate-patch/timer_impl.dart:394)
#5      _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:414)
#6      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:148)

The first line shows where the exception happened, but we have no idea where we came from when we reached that line, making the stacktrace useless. This is what stack traces usually look like in heavily async programs, and that’s one of the reasons why it’s hard to debug them.

Thankfully, stack_trace package gives you the tools to solve that problem. If you wrap everything into Chain.capture, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 'package:stack_trace/stack_trace.dart';
import 'dart:async';

Future<Null> blah() async {
  throw 'foo';
}

void main() {
  Chain.capture(() async {
    var result = await new Future(() async {
      await new Future(() async {
        await blah();
      });
    });
  }, onError: (error, chain) {
    print(chain.terse);
  });
}

you’ll get way nicer stack traces:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
blah.dart 7:3        blah.<async>
===== asynchronous gap ===========================
dart:async           _Completer.completeError
blah.dart 10:1       blah.<async>
===== asynchronous gap ===========================
dart:async           Future.Future.microtask
blah.dart            blah
blah.dart 14:15      main.<fn>.<async>.<fn>.<async>.<fn>.<async>
===== asynchronous gap ===========================
dart:async           Future.Future
blah.dart 13:17      main.<fn>.<async>.<fn>.<async>
===== asynchronous gap ===========================
dart:async           Future.Future
blah.dart 12:28      main.<fn>.<async>
===== asynchronous gap ===========================
dart:async           Future.Future.microtask
blah.dart            main.<fn>
package:stack_trace  Chain.capture
blah.dart 11:9       main

It adds some performance and memory penalty, but it’s neglectable for the dartdocs.org scripts, and simplifies debugging a lot.

Summary

All in all, it was a pretty straightforward project, and Dart didn’t give me any unpleasant surprises, everything works just fine and as expected, and generates documentation for the new packages every day. Check it out, if you haven’t yet!

Dart Apps With Unidirectional Flow and Persistent Data Structures

| Comments

Recently in JavaScript, and especially in ClojureScript world, one approach of developing front-end apps became pretty popular – using unidirectional app flow and persistent data structures. Something pretty close to Flux, but not really exactly like it. I tried that approach in Textik, and was really happy how it went.

The idea

Here’s the idea – in your app, you have only one place where you store ALL of your application state, some sort of global structure, containing persistent vectors and maps. It is important to use persistent data structures there, as you will see soon, they provide some very important features we use to speed up our app.

So, you have one global data structure, where the state of all your application is stored. Everything is there – what tooltips are shown, what buttons are disabled, all the content of the app, etc. Then, you pass that global data structure to your rendering engine, which renders the whole app, from top to bottom, for the first time. It also sets up various HTML events listeners to the DOM elements, and attaches event handlers, which basically do only one thing – they generate parameters payload, and pass it to Dispatcher.

Dispatcher receives that payload, and figures out what to do with it. It calls various “mutators” – models, which actually will change the global state of the app. Then, Dispatcher calls the rendering engine again, and passes the updated global state to it. The rendering engine rerenders the whole app again, from top to the bottom. But now, it can easily figure out what parts, or what components of the app should be rerendered. We pass particular subtrees of our global state tree to the components while rendering the whole app, and because these are persistent data structures, we can very quickly find out if some particular subtree was changed since last render. So, the rendering engine, using that knowledge, rerenders only parts of the app, which were changed, and because of that – it does that really fast.

And then everything happens in the same way again. We get another event, send the payload to Dispatcher, Dispatcher calls mutators, mutators change the global state, and Dispatcher calls the rendering engine to rerender the app again.

I.e., this way:

1
2
3
4
5
  main -------> render view --------> dispatcher
                    ^                      |
                    |                      |
                    |                      v
                    +------------ change global state

So, this way we get a very simple data flow, all our events go through the same message bus to Dispatcher, the app structure becomes so simple, it is even ridiculous a bit :)

Implementing TodoMVC in Dart

So, I decided to try the same approach with Dart. I took ReactJS as the rendering engine, since it works really close to what I want from it, and there is a nice Dart wrapper for it from Clean Team – react. As for persistent data structures, there is a pub package from VacuumLabs – persistent (but unfortunately it is not in Pub, you should get it from their forked repo from GitHub).

You can check the source code here: https://github.com/astashov/todomvc/tree/master/labs/architecture-examples/react-dart-uniflow/web/dart, and you can play with it here: http://astashov.s3-us-west-2.amazonaws.com/todomvc/index.html (if you want to check it in Dart, use Dartium). This is just usual TodoMVC app.

Let me quickly guide you through the code.

Everything starts with initializing the global state.

1
2
3
4
5
6
7
8
9
10
// data.dart

var _appData = new Data(persist({
    'autoincrement': 0,
    'new-input': '',
    'list': [],
    'filter': 'all',
    'edit': null}));

Data get appData => _appData;

appData will be globally available, and we’ll get the current value of the global state with appData.value. There are a bunch of methods-mutators in the Data class, which will overwrite the value property of appData with the new value. They won’t really mutate the existing value, but recreate the new one, reusing unchanged parts of the old value.

Then, we initialize the app, entering our main() function:

1
2
3
4
5
6
// app.dart

void main() {
  setClientConfiguration();
  rerender();
}

setClientConfiguration() initializes the react library, and then we render the app for the first time. It happens in dispatcher.dart:

1
2
3
4
5
// dispatcher.dart

void rerender() {
  renderComponent(todoAppComponent({'value': appData.value}), querySelector('#todoapp'));
}

Here we call renderComponent, which is a function of the react package, and pass the React component we are going to render and the selector which we are going to append it to.

The components are defined in the components.dart file.

All our components are not just default React components, but instead inherited from our custom _Component class, which we instrumented with the shouldComponentUpdate hook. There we check if the new passed value (our subtree of the global state) was changed since the last render. Again, this check should be very fast because of the persistent data structures properties. Also, as a convention, we are going to send our subtrees as the ‘value’ prop.

Let’s have a look at todoAppComponent – our entry component. All it does is just renders three subcomponents – header, footer and main, and pass parts of the received global tree to them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// components.dart

class TodoApp extends _Component {
  render() {
    return
      div({'id': 'todo-app', 'className': value['filter']}, [
        headerComponent({'value': value["new-input"]}),
        mainComponent({'value': persist({
          'list': value["list"],
          'edit': value["edit"]})}),
        footerComponent({'value': persist({
          'count': value["list"].length,
          'filter': value["filter"],
          'countCompleted': value["list"].where((i) => i["isCompleted"]).length})})]);
  }
}
var todoAppComponent = registerComponent(() => new TodoApp());

Now, let’s have a look e.g. at headerComponent, which renders the input for creating new tasks, and also adds event handlers to the input. It only receives the appData.value['new-input'] value, which defines what should be the value of the input.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// components.dart

class Header extends _Component {

  // ...

  void _inputChange(event) {
    dispatch({'action': 'new-input', 'value': event.target.value});
    setState({'value': event.target.value});
  }

  // ...

  render() {
    return
      header({'id': 'header'}, [
        h1({}, 'todos'),
        input({
          'id': 'new-todo', 'placeholder': 'What needs to be done?', 'autofocus': 'autofocus',
          'value': value, 'onChange': _inputChange, 'onKeyDown': _onKeyDown})]);
  }
}
var headerComponent = registerComponent(() => new Header());

Every single time we enter/remove a character in the input, we fire an event, and its handler calls the dispatch function of Dispatcher. We send the payload to it, which looks like {'action': 'new-input', 'value': input.value}.

There, in Dispatcher, we handle it. For simplicity, I mutate the global state right in Dispatcher, but in a larger application you probably would want to delegate that to a model.

And then, we rerender the app again.

1
2
3
4
5
6
7
8
9
10
11
12
// dispatcher.dart

void dispatch(Map payload) {
  switch (payload["action"]) {
    // ...
    case 'new-input':
      appData.update("new-input", payload['value']);
      break;
    // ...
  }
  rerender();
}

Summary

So, that’s actually it. Of course there are also other functionality – editing items, removing items, marking as completed, etc, but it works just in the same way as I just described. For items, there is the Item model – items.dart, which provides API for CRUD operations for items. But all that stuff works in the same way I just described.

That’s the whole lifecycle of the application. Very simple, very declarative – you basically only need to work with the global state, changing it accordingly, and you don’t really need to care about the view – React will do a great job with rendering the app using the global state.

Another cool side-effect of having the global state – you can always easily reproduce your user’s problems – debugging becomes way easier! If you use something like Airbrake or Rollbar to aggregate exceptions from your users, then you could just attach the global state in JSON to the exception you are about to send to e.g. Rollbar, and then you can easily reproduce the user’s problem just by applying that JSON on your machine, so you’d get exactly the same state of the app where the user was before the exception happened. Kinda cool, huh? :)

There are a bunch of other cool things (e.g. simple undo – you can just save the full state of the app (or part of the state) in a vector every time the change happens, and it will be stored efficiently because of the nature of persistent data structures).

I’m really excited about this approach so far.

Isn’t that like Rails?

If you ever worked with Rails, you can actually see a lot of similar things in this approach. In Rails, we pass our request through router, then router figures out (from URLs and GET params) what controller this request should be sent to, controller calls various models, which change the database, and the render the response. In this case,

  • Dispatcher is router
  • ‘mutators’ are models
  • appData is our database

In models, we also don’t have our internal state, models work with the database, retrieving and saving data into it. Same as in ActiveRecord :)

And the whole lifecycle of the front-end app like this one and a Rails app looks extremely familiar. :)


There are still some things, that could be improved, and some additional persistent data structures, that could be written, like e.g. currently PersistentVector doesn’t efficiently handle inserting/removing elements in the middle of a vector. But still, you already can do a lot with the Dart ecosystem and these 2 packages I was talking about – react and persistent.

So, please try it out, hopefully you’ll find it useful :)

Related links

  • React
  • Persistent – btw, there is a great explanation why persistent data structures are cool, make sure you’ve read that!
  • Source code
  • Demo (Dartium only)
  • Slides of my Textik talk – where I was talking about the Textik’s architecture, which is very close to what I described dhere.

Perfect ClojureScript Development Environment With Vim

| Comments

When I just started to learn ClojureScript, I had hard time trying to find some tutorials which could explain how to set up a nice dev environment for ClojureScript. The good environment is extremely important especially when you just started to play around with the language, it is very useful to get immediate feedback to your changes in the code. But it’s also helpful when you already know what you do, and allows you to iterate quickly on your project.

Later, while working on Textik (check it out, or check the sources), I was able to build something close enough to the ideal environment, which could allow me to iterate very quickly. To simplify life of ClojureScript beginners, here’s a tutorial how to set up that kind of environment.

So, here is a detailed explanation what you need to do from the scratch, or you could just go and clone/fork the skeleton project on Github, if you feel more like tl;dr.

I used Vim for development, but most of this tutorial (except the Vim part) is IDE-agnostic.

To start, let’s create a new Clojure project, called ‘perfection’:

1
$ lein new perfection

It will create a skeleton of a Clojure project. Then, open project.clj file, it should look something like that:

1
2
3
4
5
6
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]])

Add some lines to turn the project into a ClojureScript project:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2277"]] ;; <- Adding ClojureScript

  :jvm-opts ["-Xmx1G"] ;; <- Sometimes ClojureScript compilation fails because available
                       ;; JVM heap space it too low. This line gives it more space

  :plugins [[lein-cljsbuild "1.0.3"]] ;; <- While you can build ClojureScript just by running
                                      ;;    'cljsc', there is a waaay more convenient way -
                                      ;;    using cljsbuild lein plugin

  ;; This is the list of builds 'cljsbuild' will use to compile our ClojureScript
  ;; code into JavaScript. Here we add first build "dev", it will put resulting main
  ;; entry for JavaScript code to resources/public/perfection.js, and all dependencies
  ;; for it to resources/public/out
  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/perfection"]
              :compiler {
                :output-to "resources/public/perfection.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}]})

Now, run

1
$ lein deps

And lein will install all the new dependencies we just added.

Next, delete the file src/perfection/core.clj, create a new file src/perfection/core.cljs, and put simple “Hello World” code into it:

1
2
3
(ns perfection.core)

(.log js/console "Hello world!")

Now we need something to run our compiled JavaScript code. Let’s create the index.html file, which will include the generated JavaScript file. Create resources/public/index.html file and add something like this to it:

1
2
3
4
5
6
7
8
9
10
11
<html>
  <head>
    <title>Perfection - perfect dev environment for ClojureScript</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <script src="out/goog/base.js" type="text/javascript"></script>
    <script src="perfection.js" type="text/javascript"></script>
    <script type="text/javascript">goog.require("perfection.core");</script>
  </body>
</html>

Let’s also add some styles, create the resources/public/css/styles.css CSS file with this content:

1
2
3
body {
  background: silver;
}

Let’s try to run compilation, via ‘cljsbuild’ lein plugin:

1
2
3
4
$ lein cljsbuild auto dev
Compiling ClojureScript.
Compiling "resources/public/perfection.js" from ["src"]...
Successfully compiled "resources/public/perfection.js" in 6.829 seconds.

Now we need some server to serve our files. I usually prefer to use Python’s SimpleHTTPServer:

1
$ python -m SimpleHTTPServer 8000

Next, open a browser and go to http://localhost:8000/resources/public/index.html. Check the console, you should see “Hello World!” there.

Figwheel

Now, you have some running ClojureScript environment, which will automatically and instantly (less than for a second) recompile your files when you change any files in ‘src/perfection’ directory. Sweet. But we want more.

Meet lein-figwheel, amazing lein plugin, which will reload the changed JavaScript files after recompilation in the browser without reloading the page! That is so cool. Let’s add it to our project:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2277"]
                 [figwheel "0.1.3-SNAPSHOT"]]) ;; <- Adding Figwheel

  :jvm-opts ["-Xmx1G"]

  :plugins [[lein-cljsbuild "1.0.3"]
            [lein-figwheel "0.1.3-SNAPSHOT"]] ;; <- Adding also a lein plugin for it

  ;; Figwheel settings. We are going to use 'resource/public' directory as our root
  ;; directory for serving the files by the figwheel server, and also we specify
  ;; where our CSS styles live, so Figwheel could reload them without reloading the
  ;; page when we change them as well!
  :figwheel {
    :http-server-root "public"
    :port 3449
    :css-dirs ["resources/public/css"]}

  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/perfection" "src/figwheel"] ;; <- adding source path where
                                                              ;;    figwheel code will live
              :compiler {
                :output-to "resources/public/perfection.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}]})

We will place the client code for figwheel not in src/perfection, but in a separate dir, because we don’t want other builds (like ‘test’ or ‘release’) to include it.

Now, create the file src/figwheel/perfecton_figwheel.cljs, and put this code there:

1
2
3
4
(ns perfection-figwheel
  (:require [figwheel.client :as fw :include-macros true]))

(fw/watch-and-reload)

And require it from resources/public/index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
  <head>
    <title>Perfection - perfect dev environment for ClojureScript</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <script src="out/goog/base.js" type="text/javascript"></script>
    <script src="perfection.js" type="text/javascript"></script>
    <script type="text/javascript">goog.require("perfection.core");</script>
    <!-- Requiring figwheel -->
    <script type="text/javascript">goog.require("perfection_figwheel");</script>
  </body>
</html>

After that, let’s shut down our python server, but run

1
$ lein figwheel dev

instead, then open localhost:3449/index.html in the browser, and check the console. It should look like:

1
2
3
Hello world!
Figwheel: trying to open cljs reload socket
Figwheel: socket connection established

Now go to src/perfection/core.cljs, and change it to something else, e.g. change “Hello World!” to “Hello World 2!”. You should see in the console, that we reloaded our files, and the new “Hello World 2!” output:

1
2
3
4
Figwheel: loading files
Hello world 2!
Figwheel: loaded these files
("/out/perfection/core.js")

Figwheel will also track the changes in CSS and also reload them. Change the background in the CSS file from silver to blue (resources/public/css/style.css file):

1
2
3
body {
  background: blue;
}

and you’ll see in the browser console:

1
2
Figwheel: loaded CSS files
("/css/style.css")

Tests

I like unit tests. They give me confidence. But it really sucks to manually run them every time, and it especially sucks to run them in Clojure/ClojureScript, because you have to wait for the full JVM start, which takes several seconds. Not really perfect instant experience we are looking for. Thankfully, you can run it once, and then restart the tests on every change of the source or test files, via the same ‘lein clsbuild auto’ mechanism.

First thing, make sure you have PhantomJS installed. If not, install it with HomeBrew:

1
$ brew install phantomjs

Then, let’s add the ‘test’ build for cljsbuild, in project.clj:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2277"]
                 [figwheel "0.1.3-SNAPSHOT"]])

  :jvm-opts ["-Xmx1G"]

  :plugins [[lein-cljsbuild "1.0.3"]
            [lein-figwheel "0.1.3-SNAPSHOT"]
            [com.cemerick/clojurescript.test "0.3.1"]] ;; <- Adding lein plugin for tests

  :figwheel {
    :http-server-root "public"
    :port 3449
    :css-dirs ["resources/public/css"]}

  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/perfection" "src/figwheel"]
              :compiler {
                :output-to "resources/public/perfection.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}
             ;; Adding the new build 'test'. Note the :notify-command setting,
             ;; it will run the tests when the files change
             {:id "test"
              :source-paths ["src/perfection" "test"]
              :notify-command ["phantomjs" :cljs.test/runner "perfection_test.js"]
              :compiler {
                :output-to "perfection_test.js"
                :optimizations :whitespace}}]})

Now, let’s add some function to test (src/perfection/core.cljs):

1
2
3
4
5
6
7
(ns perfection.core)

(.log js/console "Hello world 2!")

;; Our new function
(defn add [a b]
  (+ a b))

Then, remove the currently existing test/perfection/core_test.clj, and create a new file test/perfection/core_test.cljs, which will contain our new first failing test:

1
2
3
4
5
6
7
(ns perfection.core-test
  (:require-macros [cemerick.cljs.test :refer (is deftest)])
  (:require [cemerick.cljs.test :as test]
            [perfection.core :as pn]))

(deftest ex1
  (is (= (pn/add 1 2) 4)))

Let’s try to run it. It should fail:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ lein cljsbuild auto test
Compiling ClojureScript.
Compiling "perfection_test.js" from ["src/perfection" "test"]...
SyntaxError: Parse error


Testing perfection.core-test

FAIL in (perfection.core-test/ex1) (:)
expected: (= (pn/add 1 2) 4)
  actual: (not (= 3 4))

Ran 1 tests containing 1 assertions.
Testing complete: 1 failures, 0 errors.

Ran 1 tests containing 1 assertions.
Testing complete: 1 failures, 0 errors.
Successfully compiled "perfection_test.js" in 4.203 seconds.

Change (is (= (pn/add 1 2) 4)) to (is (= (pn/add 1 2) 3)), then save the file, and the test should rerun automatically, and now it should pass:

1
2
3
4
5
6
7
8
9
10
11
12
Compiling "perfection_test.js" from ["src/perfection" "test"]...
SyntaxError: Parse error


Testing perfection.core-test

Ran 1 tests containing 1 assertions.
Testing complete: 0 failures, 0 errors.

Ran 1 tests containing 1 assertions.
Testing complete: 0 failures, 0 errors.
Successfully compiled "perfection_test.js" in 0.719 seconds.

W00t! Now we have an autorunning test in our app.

Some notes: PhantomJS is pretty old, it lacks a lot of features existing in modern browsers, like Function.bind or requestAnimationFrame. So, for tests you may need shims, you can write them in separate JS files, and then add to :notify-command in project.clj for the “test” build, e.g. like that:

1
2
3
4
5
:notify-command ["phantomjs"
                 :cljs.test/runner
                 "resources/public/js/function-bind-shim.js"
                 "resources/public/js/request-animation-frame-shim.js"
                 "perfection_test.js"]

Integration with Vim

We already have a lot, but we are not going to stop :). We would also want to quickly search docs for the functions we use from Vim, be able to jump to definitions of the functions, and even execute some code right in the browser’s context, right in the context of our running application, straight from Vim. Oh yes, that would be super cool. And that is totally possible, all thanks to famous Vim master Tim Pope and his beautiful ‘vim-fireplace’ plugin.

So, let’s first configure our app so it could accept connections from repl. We need to install brepl to it. As usual, we first need to add some changes to project.clj:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2277"]
                 [figwheel "0.1.3-SNAPSHOT"]])

  :jvm-opts ["-Xmx1G"]

  :plugins [[lein-cljsbuild "1.0.3"]
            [lein-figwheel "0.1.3-SNAPSHOT"]
            [com.cemerick/clojurescript.test "0.3.1"]
            [com.cemerick/austin "0.1.4"]] ;; <- Adds nice support for ClojureScript REPL.
                                           ;;    Also has Piggieback as a dependency, which
                                           ;;    we need for vim-fireplace

  :figwheel {
    :http-server-root "public"
    :port 3449
    :css-dirs ["resources/public/css"]}

  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/perfection" "src/figwheel" "src/brepl"]  ;; <- adding source path
                                                                           ;;    where brepl code
                                                                           ;;    will live
              :compiler {
                :output-to "resources/public/perfection.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}
             {:id "test"
              :source-paths ["src/perfection" "test"]
              :notify-command ["phantomjs" :cljs.test/runner "perfection_test.js"]
              :compiler {
                :output-to "perfection_test.js"
                :optimizations :whitespace}}]})

Same thing as for figwheel – we will place the client code for brepl not in src/perfection, but in a separate dir, because we don’t want other builds (like ‘test’ or ‘release’) to include it.

Now, create src/brepl/perfection_brepl.cljs:

1
2
3
4
(ns perfection-brepl
  (:require [clojure.browser.repl :as repl]))

(repl/connect "http://localhost:9000/repl")

And add its requiring to resources/public/index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
  <head>
    <title>Perfection - perfect dev environment for ClojureScript</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <script src="out/goog/base.js" type="text/javascript"></script>
    <script src="perfection.js" type="text/javascript"></script>
    <script type="text/javascript">goog.require("perfection.core");</script>
    <script type="text/javascript">goog.require("perfection_figwheel");</script>
    <!-- Requiring brepl -->
    <script type="text/javascript">goog.require("perfection_brepl");</script>
  </body>
</html>

After that, restart lein figwheel dev, then run lein repl.

Now, it’s time to install the vim-fireplace plugin for Vim. I use pathogen, so I just simply put the plugin to ~/.vim/bundle/vim-fireplace.

Next, restart Vim, open src/perfection/core.cljs, and in Vim run :Piggieback 9000. It will hang for several seconds.

Then reload localhost:3449/index.html in browser.

You may see the exception in the browser console, like:

1
2
3
Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement':
Blocked a frame with origin "http://localhost:3449" from accessing a frame with origin
"http://localhost:9000". Protocols, domains, and ports must match.

but that’s totally fine, and will work anyway.

Go back to src/perfection/core.cljs in Vim, and try to run something like :Eval (js/alert 123). The alert window should appear right in the browser. Cool, huh?

You can do a lot of things with vim-fireplace, just check the docs. It converts Vim to a pretty solid ClojureScript IDE, which now is quite close even to LightTable, but with all that well-known Vim stuff you and me love so much :)

Release

Now we have everything we need for comfortable development process, but I highly recommend that you also add another build, which will generate the “release” version of the app, which will eventually go to production. It is very important to do that right from the beginning, because sometimes there are issues with “advanced” optimizations of Google Closure (which ClojureScript uses under the hood for compilation). You should really always check that your app works with :advanced optimizations, because debugging may be difficult there.

So, add this to your project.clj:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
(defproject perfection "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [org.clojure/clojurescript "0.0-2277"]
                 [figwheel "0.1.3-SNAPSHOT"]])

  :jvm-opts ["-Xmx1G"]

  :plugins [[lein-cljsbuild "1.0.3"]
            [lein-figwheel "0.1.3-SNAPSHOT"]
            [com.cemerick/clojurescript.test "0.3.1"]
            [com.cemerick/austin "0.1.4"]]

  :figwheel {
    :http-server-root "public"
    :port 3449
    :css-dirs ["resources/public/css"]}

  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/perfection" "src/figwheel" "src/brepl"]
              :compiler {
                :output-to "resources/public/perfection.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}
             {:id "test"
              :source-paths ["src/perfection" "test"]
              :notify-command ["phantomjs" :cljs.test/runner "perfection_test.js"]
              :compiler {
                :output-to "perfection_test.js"
                :optimizations :whitespace}}
             ;; Our release build. The main difference - it uses :optimizations :advanced.
             ;; It also doesn't include src/figwheel or src/brepl - we don't need that in production
             {:id "release"
              :source-paths ["src/perfection"]
              :compiler {
                :output-to "resources/public/perfection_prod.js"
                :output-dir "resources/public/prod-out"
                :optimizations :advanced
                :pretty-print false
                :source-map "resources/public/perfection_prod.js.map"}}]})

Now create resources/public/index_prod.html, it’s going to be way shorter than our dev one:

1
2
3
4
5
6
7
8
9
<html>
  <head>
    <title>Perfection - perfect dev environment for ClojureScript</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <script src="perfection_prod.js" type="text/javascript"></script>
  </body>
</html>

And run:

1
$ lein cljsbuild auto release

Now you can open http://localhost:3449/index_prod.html, and make sure the site still works (by checking for “Hello World 2!” in the console.

BTW, now if you open resources/public/perfection_prod.js, all you will see there is:

1
2
3
4
5
;(function(){
console.log("Hello world 2!");
})();

//# sourceMappingURL=perfection_prod.js.map

That’s the power of :advanced optimizations. It got rid of everything which is not used (all the ClojureScript libs, Google Closure libs, thousands and thousands of lines of code), and left only what matters.

Summary

So, I usually have 4 things running simultaneously:

1
2
3
4
lein repl
lein figwheel dev
lein cljsbuild auto test
lein cljsbuild auto release

And whenever I make a change in the source code, I’ll get updated version of the app in the browser, without page refresh, the tests will run, and the release will be built. All automagically.

That’s what I call “the perfect dev environment”.

Tips & Tricks

Paredit

You should use it. Since Clojure is Lisp, and consists of s-expressions, it has a very clear structure. Paredit allows to efficiently edit your code by manipulating s-expressions. This is when all these parentheses, which initially scare Lisp newcomers, will bring power. There is a plugin for Vim, a bit buggy, especially when you copy/paste large structures (especially when they are unbalanced), but it’s still worth it.

p and b

I found it very useful for my development process to create the function p and the macro b for debugging.

  • p just prints what the value it receives and returns it.
  • b prints what time did the execution of the nested s-expression take, and also returns the value.

They look like this:

1
2
3
4
(defn p "Prints given arguments, and then returns the last one"
  [& values]
  (.log js/console (apply pr-str values))
  (last values))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(defn benchmark
  "Prints the execution time for the given function. Accepts optional string, which will
   be used as a description"
  ([f] (benchmark nil f))
  ([msg f]
  (let [start (.now js/Date)
        result (f)]
    (p (str (when msg (str msg ": ")) (- (.now js/Date) start) "ms"))
    result)))

(defmacro b
  ([f]
  `(tixi.utils/benchmark (fn [] ~f)))

  ([msg f]
  `(tixi.utils/benchmark ~msg (fn [] ~f))))

And then we can use them, like that. Imagine you have a function:

1
2
3
(let [foo 2
      bar (+ 3 foo)]
  (+ foo bar))

And you are curious about what’s the output of (+ 3 foo). So you can do:

1
2
3
(let [foo 2
      bar (p (+ 3 foo))]
  (+ foo bar))

And it will put ‘5’ in the browser console. Curious how long it takes to execute it?

1
2
3
(let [foo 2
      bar (b (+ 3 foo))]
  (+ foo bar))

‘0ms’ (obviously :)) will appear in the browser console.

It starts to really shine especially with Paredit, when you can wrap and unwrap parentheses by one keystroke.

Video

This is a little screencast I recorded how I develop Textik, using the dev environment like that:

Conclusion

I really think ClojureScript so far is the most interesting and productive way to build complex front-end projects on the web. Especially if you use it with React (and some React wrapper, like Om, Reagent or Quiescent). If you didn’t try it yet, you should definitely try, and then hopefully you’ll find this article useful :)