Don’t learn a syntax, learn to change the way you think

A lan­guage that doesn’t affect the way you think about pro­gram­ming, is not worth knowing.”

- Alan Perlis

In my last post, I out­lined the tech­niques to learn­ing that I picked up from Josh Kaufman’s TEDx talk. I briefly men­tioned that one should learn a new par­a­digm rather than a lan­guage with a slightly dif­fer­ent syn­tax (e.g. C# <=> Java, Action­Script <=> Haxe) but didn’t stress nearly enough the impor­tance of doing so.

As the math­e­mati­cian Richard Ham­ming once said, that it’s pos­si­ble for there to be thoughts that we can­not have:

Just as there are odours that dogs can smell and we can­not, as well as sounds that dogs can hear and we can­not, so too there are wave­lengths of light we can­not see and flavours we can­not taste. Why then, given our brains wired the way they are, does the remark “Per­haps there are thoughts we can­not think,” sur­prise you? Evo­lu­tion, so far, may pos­si­bly have blocked us from being able to think in some direc­tions; there could be unthink­able thoughts.”

- Richard Hamming

but what does this have to do with pro­gram­ming lan­guages, or any lan­guages for that matter?

What we can­not express, we can­not think

BBC went to a remote, uncon­nected tribe called the Himba, whose colour vocab­u­lary is very dif­fer­ent from us in that they only have 5 words for colour.


What they found was intriguing.

The Himba is able to eas­ily iden­tify the square with a dif­fer­ent shade of green in this picture:


because they have dif­fer­ent words for the two shades of green. For us, this feat is dif­fi­cult because to us, they’re all green.

On the other hand, because we have two words for blue and green, it is easy for us to spot the blue square from the rest:


For the Himba, who has only one word that describes both blue and green, they strug­gle to iden­tify the square with a dif­fer­ent colour.

The impli­ca­tion is pro­found, that not only do lan­guages shape our think­ing, they might pro­hibit it.

The lim­its of my lan­guage mean the lim­its of my world.”

- Lud­wig Wittgenstein

What if the same is true for pro­gram­ming languages?

Pro­gram­ming lan­guages have a devi­ous influ­ence: they shape our think­ing habits.”

- Dijk­stra

What if our cre­ative pow­ers are lim­ited by the ideas that can be expressed by the pro­gram­ming lan­guage we use?

What if some solu­tions are out of our reach because they can­not be clearly expressed in those lan­guages? How would we ever dis­cover these unknown unknowns?

With­out being aware of the out­side pos­si­bil­i­ties, would we ever become aware of our uncon­scious incom­pe­tence?

I don’t know of any research into the impact pro­gram­ming par­a­digms have on cre­ativ­ity. From per­sonal expe­ri­ence, my most cre­ative solu­tions have come from my ven­tures into non-mainstream pro­gram­ming and data­base par­a­digms. These ven­tures have opened doors in my mind and allowed me to see solu­tions I couldn’t before.

By con­trast, my other learn­ing efforts such as Dart and Haxe has taught me a slightly dif­fer­ent syn­tax for express­ing the same ideas that I’m already famil­iar with. Whilst they can prove to be use­ful tools for spe­cific tasks, they failed to have the same mind-expanding effect that is far more valu­able to my long term career.

On Diver­sity

We can diver­sify our ways of think­ing and approaches to prob­lem solv­ing by learn­ing new pro­gram­ming par­a­digms, but that’s not the only way. In fact, some of the most notable peo­ple in the field of com­puter sci­ence has come from other disciplines.

For exam­ple, Alan Kay was a biol­o­gist, and it was the study of biol­ogy that inspired him to coin the term objects.

I thought of objects being like bio­log­i­cal cells and/or indi­vid­ual com­put­ers on a net­work, only able to com­mu­ni­cate with messages.”

- Alan Kay

(the fact that we have come to teach and think about OOP in a very dif­fer­ent sense to what Alan Kay envi­sioned is another matter..)

Sim­i­larly, Joe Arm­strong started out as a physicist.

And Adam Torn­hill, a speaker whose work I thor­oughly enjoy, has learnt much about pro­gram­ming from his stud­ies in psy­chol­ogy.


We also need diver­sity in our communities.

There’s a famous Chi­nese proverb that goes like this,

three hum­ble shoe­mak­ers brain­storm­ing make a great statesman.”

How­ever, this only holds true if each of the shoe­mak­ers is able to offer a dif­fer­ent per­spec­tive and ideas.


In real­ity, given that the shoe­mak­ers have the same pro­fes­sion and gen­der they prob­a­bly have a much greater over­lap in mindscape.


The result? The shoe­mak­ers’ numer­i­cal advan­tage amounts to lit­tle when it comes to find­ing cre­ative solu­tions. Which is why, in a cre­ative indus­try like ours,  we need to guard against mono­cul­ture.


To sum­marise, learn­ing a new lan­guage within the same par­a­digm gives you imple­men­ta­tion options and trade-offs for the set of approaches that is best afforded by the paradigm.

Learn­ing a new par­a­digm gives you entire new approaches that you didn’t have access to before, and has a far longer-lasting ben­e­fit. Which is why, given the choice, you should learn a new par­a­digm rather than a new language.





Learn to Learn

Being a respon­si­ble speaker, I have started prepar­ing my talk — A tour of the lan­guage land­scape — for NDC Oslo months ahead of time! When I first came up with the idea for this talk, I asked on Twit­ter if any­one else thought it was a good idea. Phil made a great point about includ­ing some infor­ma­tion on how I go about learn­ing a new language.


I came across this TEDx talk by Josh Kauf­man a while back and found it use­ful in help­ing me for­mu­late a learn­ing strat­egy that works for me.

Truth about the 10,000 hours rule

In the talk, Josh debunked the often mis­quoted 10,000 hours rule. When the study was first pub­lished, the find­ing was that it takes 10,000 hours of delib­er­ate prac­tice to reach the top of an ultra-competitive field. But through a col­lec­tive Chi­nese whis­per the mes­sage was warped into ‘it takes 10,000 hours of delib­er­ate prac­tice to be good at something’.

Instead, Josh found that researches sug­gest 20 hours is all it takes for you to be rea­son­ably good at some­thing so long you make those hours count.


This is impor­tant, because for us busy pro­gram­mers — who, by the way, have a ten­dency to work long hours — the time to learn new skills is both lim­ited and nec­es­sary given how fast our indus­try moves.

4 steps to learn

Josh pro­posed these 4 steps to learn­ing anything.


Decon­struct the skill

Most things we con­sider as skills are actu­ally bun­dles of skills. The more we are able to break them up into smaller skills the bet­ter we can decide which of them actu­ally helps us achieve what we want out of our learn­ing. We can then pri­ori­tise the skills that are most use­ful to us and improve our abil­ity in the least time possible.

For learn­ing a pro­gram­ming lan­guage, you can decon­struct most lan­guages into smaller chunks:

  • vari­able assignment
  • com­mon data types
  • con­trol flows (if-else, loops, recur­sions, etc.)
  • work­ing with col­lec­tion types
  • work­ing with strings
  • error han­dling
  • con­cur­rency

Most intro­duc­tory books and tuto­ri­als fol­low this struc­ture already.

Learn enough to self-correct

You should first focus on get­ting to the point where you can self-correct and self-edit as you learn. For learn­ing a pro­gram­ming lan­guage, I inter­pret this point as:

  • know how to com­pile and run your code
  • able to put sim­ple pro­grams together, and tweak it to start get­ting a ‘feel’ of it

Again, most intro­duc­tory books and tuto­ri­als fol­low this pat­tern already and have you build a Hello World exam­ple very early on.

Remove prac­tice barriers

Remove dis­trac­tions — TV, inter­net, twit­ter, etc. — so that you can focus on learn­ing. This can be hard when dis­trac­tions are all around us and so read­ily available!

I once heard a story about John Car­mack that, before a new project, he’d check in to a hotel with a bunch of good books and lit­er­ally cut him­self off from the out­side world for days so he can soak up the ideas and inspi­ra­tions before start­ing any work on the project.

I’m not say­ing that you should do the same, obvi­ously dif­fer­ent approaches work for dif­fer­ent peo­ple. Per­son­ally I’m most effec­tive between the hours of 10PM and 2AM because my wife goes to bed early and I’m able to just zone out.

I’m not a heavy Twit­ter user, or any other social net­work for that mat­ter, so they’re not a prob­lem for me.

On the other hand, comic-based TV shows is my poi­son — The Flash, Gotham, Arrow, Agents of SHIELD, etc. To limit the amount of dis­rup­tion these bring, I binge watch them in one night so I can have the rest of the nights that week for more con­struc­tive uses.

Prac­tice at least 20 hours

Josh raised a good point that, for most things you learn, there is a frus­tra­tion bar­rier — the moment when we become con­sciously incom­pe­tent and realise how lit­tle we know and how much more we need to learn.


It’s not a great feel­ing as no one likes to feel stu­pid, and this is often the point where we lose our momen­tum and derail our hard-earned progress.

Which is why it’s impor­tant that we pre-commit at least 20 hours of our time, so that if and when we hit this frus­tra­tion point we have a good rea­son to push on since we already bud­geted 20 hours anyway.

Set your goal

Before you start invest­ing a min­i­mum of 20 hours into learn­ing a new lan­guage, it helps if you could decide what you want to get out of the process. Depend­ing on your sit­u­a­tion and needs this could be quite dif­fer­ent, e.g.

  • are you look­ing to move to a dif­fer­ent lan­guage stack and try­ing to make your­self employable?
  • are you try­ing to under­stand the hype around a new lan­guage and see what it’s all about?

Per­son­ally, most of my learn­ing is aimed at expand­ing my hori­zon and allow­ing me to see beyond the pos­si­bil­i­ties and options I have at my dis­posal with the stack that I work with day-to-day.

Other times I might have spe­cific goals of what I want to be able to do in that new lan­guage, for instance:

  • I learnt Dart as a replace­ment to JavaScript for my web devel­op­ment needs
  • I learnt Elm to be bet­ter acquainted with functional-reactive pro­gram­ming (FRP) and with the aim of being able to make games using FRP

Pri­ori­tise learn­ing a new paradigm

One mis­take that I see many peo­ple make is to choose to learn a new lan­guage over a new par­a­digm. For exam­ple, mak­ing the jump from C# to Java is a rel­a­tive easy one, but at the end of day you have learnt a new syn­tax with­out nec­es­sar­ily taught your­self a new way to solve problems.

Learn­ing a new par­a­digm on the other hand, fun­da­men­tally change the way you see pro­gram­ming and allow you to see new ways to solve prob­lems. From per­sonal expe­ri­ence, each time I ven­tured into a new par­a­digm — Func­tional Pro­gram­ming, Aspect-Oriented Pro­gram­ming, Func­tional Reac­tive Pro­gram­ming, etc. — has allowed me to see pro­gram­ming in a new light.

If you’re inter­ested in explor­ing some less trav­elled roads, check out these three par­a­digms rec­om­mended by John Croisant.

These two books by Bruce Tate are also a great source for exploratory learning:

7-langs-7-weeks  7-more-langs-7-weeks

And finally, I leave you with a great quote from none other than Alan Perlis.

A lan­guage that doesn’t affect the way you think about pro­gram­ming, is not worth knowing.

- Alan Perlis

Happy learn­ing!


Interview with Rik Van Bruggen @ QCon London 2015

Modelling game economy with Neo4j


In Here Be Mon­sters, we have a MMORPG that is content-heavy with over:

  • 5000 items
  • 800 recipes
  • 500 loca­tions
  • 1500 quests

and since the con­tents are highly con­nected, it makes bal­anc­ing the game a rather inter­est­ing and chal­leng­ing prob­lem for our small team behind the project.

The Chal­lenge

Con­sider a sim­ple exam­ple involv­ing the Cam­ou­flage Trap, which is one of the very first traps you’ll make. It’s made from a num­ber of ingre­di­ents, each can be:

  • found in the cer­tain parts of the world
  • pur­chased at shops
  • awarded for com­plet­ing quests/achievements
  • crafted using other ingredients

Now, sup­pose you want to raise the price of basic ingre­di­ents such as Water, that increase needs to prop­a­gate all the way through the chain.

Fur­ther­more, when you con­sider how many items are made from Water, and how many more items are made from those items.

There’s a huge knock-on effect here. Fail­ing to address these knock-on effects will cre­ate arbi­trage oppor­tu­ni­ties in your econ­omy for play­ers to exploit. They will be able to mine coins by buy­ing and sell­ing items and will there­fore have less need to make real-money pur­chases with us.

As a game designer, when­ever you want to make a change you are faced with this huge uncer­tainty because of the hid­den knock-on effects that you don’t see.

It’s a com­plex­ity that we have to man­age, but man­ag­ing this com­plex­ity by hand is:

  • labo­ri­ous — involv­ing many iter­a­tions of trial and error, and likely repeated each time new con­tents are added
  • slow
  • error prone
  • sub­jec­tive — what ‘feels’ right can vary greatly from per­son to person

Instead, we opted for a more auto­mated process whereby every item’s intrin­sic value in the game can be eval­u­ated based on the val­ues of its inputs (e.g. baits for mon­sters, ingre­di­ents for crafted items, etc.). The amount of time involved, and your chance of suc­cess is also taken into account. We can then use the intrin­sic value of an item to drive or quan­tify other less tan­gi­ble aspects of the game’s model.

This is where Neo4j comes in.

Hello, Neo

One of the main chal­lenges that came up in our effort to auto­mate the eco­nomic bal­anc­ing was to under­stand the com­plex rela­tion­ships between items, quests, achieve­ments, as well as loca­tions and activ­i­ties that can be per­formed against/with spe­cific items at spe­cific locations.

Take Big­foot as an exam­ple, from his almanac page in the game:

We can see that to catch the Big­foot you need to consider:

  • loca­tion — he’s only avail­able in cer­tain parts of the world
  • bait — to lure him out you need the Allur­ing Goat which gives you a roughly 4 in 7 chance of see­ing the monster
  • trap — you need a trap strong enough to hold him after you’ve man­aged to lure him out, the Musket-teer Trap has a 5 in 7 chance of success
  • loot — upon a suc­cess­ful catch, Big­foot occa­sion­ally drops Big­foot Toe­nail Clip­pings as loot, which you might need in future quests or as ingre­di­ent to make other items

We can model all the infor­ma­tion we see on this screen as a graph in Neo4j:

Addi­tion­ally, each node and edge can have an arbi­trary set of prop­er­ties.

For instance, Big­foot and the Musket-teer Trap will have their stats. The lootsrela­tion­ship that exists between Big­foot and his loot will also spec­ify the chance of him drop­ping the loot.

Look­ing beyond the imme­di­ate con­nec­tions to Big­foot, the Allur­ing Goat and Musket-teer Trap each have numer­ous con­nec­tions of their own.

To make the Allur­ing Goat, you need to gather:

  • Honey, which you need to build a Bee Hive in your home­stead and har­vest from it;
  • Goat, which you can buy from ani­mal traders in cities around the world such asLon­don and Nan­Jing;
  • Golden Hair, which is a loot dropped by Blonde Mer­maid, Dan­de­lion Pixie and Blonde Selkie;

This dia­gram illus­trates the highly con­nected and com­plex nature of the data we’re deal­ing with — the result of us mak­ing a game where every­thing you can do and every item you find has a pur­pose and can be used for some­thing else.

It is also by no means an uncon­nected sub­set of the over­all graph. For sim­plic­ity sake I have omit­ted many types of rela­tion­ships and con­nected nodes.

Visu­al­iz­ing the 8,000+ nodes and around 40,000+ edges in Gephi, where the colour and size of the nodes rep­re­sent the num­ber of con­nec­tions they have, this is what the inter­nal data model for Here Be Mon­sters look like:

As you can see, the degree of con­nect­ed­ness varies greatly. For com­mon low-level mon­sters such as Sylph, Sprig­gan, Sprite and Sala­man­der, they are each con­nected to no less than 300 loca­tions, traps and items.

Sim­i­larly, for com­mon ingre­di­ents such as Salt, it can be found through many items (e.g. most fish drop Sea­weed and Salt as loot when you catch them) and is used in many recipes — Pas­try, Pizza Base, Ketchup to name a few.

With the inter­nal data model cap­tured as a graph in Neo4j, we were able to ask some inter­est­ing ques­tions that would have been dif­fi­cult or impos­si­ble to answer otherwise.

To give you a few examples.

Impact Analy­sis

In the ear­lier pric­ing exam­ple with Water and Cam­ou­flage Trap, the key chal­lenge is to be able to under­stand the impact of change. This is a very sim­i­lar prob­lem to the ones you face in deriv­a­tive pric­ing in Finance.

If you take White Bread as an exam­ple, to work out the blast radius of a price change, let’s look at the rela­tion­ships that exist between an item and a recipe.

To find all the items that are made from White Bread, directly or indi­rectly, we can run the fol­low­ing Cypher (which is the built-in query lan­guage for Neo4j) query against our Neo4j database:

(wb:BaseItem { Name:“White Bread”})
–[rel:CRAFTS | IS_USED_IN*1..] ->(i:BaseItem)
RETURN i, rel, wb

Cou­ple of things to note from this query.

  • notice how we are basi­cally pat­tern match­ing against the graph using the pat­tern node-[relationship]->node?
  • we cap­ture the nodes and rela­tion­ships into vari­ables wb, i and rel so we can return them from the query;
  • we can option­ally fil­ter the nodes by type, e.g. (i:BaseItem) will only match against nodes that are of type BaseItem;
  • to iden­tify White Bread, we also fil­ter one of the nodes by the value of its prop­er­ties, in this case the node wb must have a Name prop­erty with the value of White Bread;
  • we can use OR seman­tic when fil­ter­ing on rela­tion­ship types, here we’re look­ing for rela­tion­ships of type CRAFTS or IS_USED_IN;
  • for the pat­tern to work, there must exist 1 or more instance of such rela­tion­ships between any two nodes, hence the car­di­nal­ity of *1..

Run­ning this query yields the fol­low­ing result, where the pur­ple nodes are items and red nodes are recipes.

From this graph we can see that White Bread is used directly in 10 recipes, and then indi­rectly (virtue of being an ingre­di­ent for mak­ing Sausage) in a fur­ther 5. If we were to change the price of White Bread, all 15 of these items will need to have their prices adjusted based on the num­ber of White Bread required to make them.

For exam­ple, if it takes 2 pieces of White Bread to make 1 Sausage and 2 Sausage to make a Full Eng­lish Break­fast, then the change to White Bread’s price would need to be mul­ti­plied by 4 when applied to Full Eng­lish Break­fast.

Scarcity Analy­sis

Not all items can be priced as deriv­a­tives of oth­ers. Some need to be priced based on their scarcity in the world, such as the fruits that you can for­age from fruit trees you find on your travels.

To find out how scarcely avail­able Durian and Drag­on­fruit is, we can use the fol­low­ing Cypher query:

fruit.Name=‘Durian’ OR fruit.Name=‘Dragonfruit’
RETURN fruit, tree, spot

Again, we’re sim­ply pat­tern match­ing against our graph using the pat­tern node<-[relationship1]-node-[relationship2]->node. The expres­sive power of Cypher lies in the abil­ity to take our rela­tion­ship dia­gram above and trans­late it like-for-like into an exe­cutable query.

You might also noticed that I’m not fil­ter­ing any of the nodes by type here. This is because I know those rela­tion­ships exist only between the spec­i­fied types of nodes, hence it’s safe for me to omit them.

Imme­di­ately, you can see that Drag­on­fruit Tree is much more read­ily avail­able in the world com­pared to Durian Tree. How­ever, you still need to consider:

  • the num­ber of trees at each loca­tion, which you can find out from the EXISTS_IN rela­tion­ship
  • the num­ber of fruits you get by for­ag­ing the tree, which you can find out from the FORAGES relationship

Tak­ing all these fac­tors into account, we can set prices for Durian and Drag­on­fruit which reflects their scarcity in the world.

Quest Pro­gres­sion

Some quests require spe­cific items to com­plete. For instance, an NPC might ask you to fetch an item from Bob in Cam­bridge, or find some fea­ture under a rock some­where, or catch a Grif­fin and get a Grif­fin Egg as loot.

On the other hand, com­plet­ing a quest can some­times award you items as well. If the quest is part of a quest line then you will also unlock follow-up quests too, so there is a self-recursive rela­tion­ship there.

To answer ques­tions such as

What comes after the Year of the Horse quest?

you can use a sim­ple Cypher query like the one below.

(q1:Quest { Name: “Year of the Horse” })
–[:UNLOCKS] ->(q2:Quest)
RETURN q1, q2

From the result­ing graph, you can quickly see the quests that are unlocked by com­plet­ing the Year of the Horse quest.

In fact, if you con­nect all the quests in the game then you’ll end up with the following.

No won­der our game design­ers need a hand work­ing with the data!

But, just being able to work out how quests are con­nected to each other and visu­al­ize them is not all that excit­ing or use­ful. We can do much more.

With our price model in full swing, we are able to:

  1. price base­line items based on fac­tors such as scarcity
  2. price items that are derived from the base­line items

Since the price of an item reflects the dif­fi­culty in obtain­ing it, we can make use of the rela­tion­ships between quests and items to “price” quests the same way — i.e. the more expen­sive the items a quest require, the more dif­fi­cult that quest is to complete.

From there, you can estab­lish sim­ple rules such as:

  • cheaper quests should come before more expen­sive ones, to ensure a sense of pro­gres­sion for the players
  • a quest should not reward items whose total price exceeds the quest’s price

Mon­ster Hierarchy

Finally, mon­ster trap­ping is a big part of the game as its name sug­gests. As men­tioned ear­lier, to catch a mon­ster you need the right com­bi­na­tion of bait and trap.

To catch a mon­ster, some­times you have to first catch a lower level mon­ster; get its loot; and use the loot to make the bait for the mon­ster you want to catch. Using the fol­low­ing rela­tion­ships you can place the mon­sters into a hierarchy.

Which can be trans­lated into the fol­low­ing Cypher query:

–[r:IS_USED_IN | CRAFTS*0..] ->(bait)-[:CAN_ATTRACT]->(monster2)
RETURN monster1,  monster2

Again, see how it mir­rors our diagram?

Sup­pose we are on a quest to catch Big­foot, we can use this query to iden­tify the mon­sters we have to catch first in order to get the ingre­di­ents to make the bait for Big­foot. The query yields the fol­low­ing result where the pur­ple nodes are mon­sters and the red node in the mid­dle is the recipe for craft­ing the Allur­ing Goat.

This places Big­foot at the peak of its hierarchy.

If you repeat the same exer­cise for every mon­ster in the game and com­pose their hier­ar­chies together, then you’ll end up with a more com­plete mon­ster hier­ar­chy cov­er­ing most of the mon­sters that exist in the game.

Once we have both the quest hier­ar­chy and mon­ster hier­ar­chy we can do some inter­est­ing analysis.

For instance, if com­plet­ing Quest 1 unlocks Quest 2, and catch­ing Mon­ster 2 gives you the loot you need to make the bait for Mon­ster 1:

then Quest 1 can­not ask the player to catch Mon­ster 1 if Quest 2 asks the player to catch Mon­ster 2.

This is to ensure that we do not break the sense of pro­gres­sion as the player pro­gresses through the quests.

Oth­er­wise, as a player, you have to catch Mon­ster 2 mul­ti­ple times to get the loot to make the bait for Mon­ster 1, and then take sev­eral attempts to suc­cess­fully catch Mon­ster 1. Just as you had fin­ished with that cycle, the very next quest (or shortly after) you are asked to catch the same mon­ster again, which doesn’t make for a very sat­is­fy­ing play­ing experience.

Remem­ber, the sit­u­a­tion might be even worse if you have to first catch other mon­sters in order to make the bait for Mon­ster 2.

Even more Impact Analysis

We looked at how impact analy­sis applies to item pric­ing ear­lier, but we have another inter­est­ing use for impact analy­sis with regards to the mon­ster hierarchy.

When you suc­cess­fully catch a mon­ster, you receive a gold reward from the Min­istry of Mon­sters and a chance to get the monster’s loot.

Suc­cess­ful Catch = Gold + (maybe)Loot

There­fore, for mon­ster catch­ing, there’s an equa­tion that needs to be balanced:

When­ever you change the price of an item that is either a bait or a loot, it can have a pro­found impact on the mon­ster hierarchy:

  • change to one side of the equa­tion (item price, drop/attraction rate, gold reward) requires change to the other side to keep things in balance
  • change to the input side of the equa­tion requires changes to all pre­ced­ing mon­sters in the hierarchy
  • change to the out­put side requires changes to all sub­se­quent mon­sters in the hierarchy

Thank­fully, Neo4j makes this really easy, which is impor­tant because when­ever we intro­duce a new mon­ster (and it hap­pens pretty reg­u­larly) it has an impact on all other mon­sters in the same region as there is a new com­peti­tor for food!


I hope I have given you a flavour of our use case with Neo4j. In gen­eral I find graph data­bases to be the most pow­er­ful and nat­ural way to model a domain, espe­cially for domains with com­plex and/or highly con­nected datasets.

Slides and Recording

My adventure with Elm @ Lambda Days 2015

Lambda Days 2015 - Yan Cui - My adventure with Elm from Erlang Solutions on Vimeo.