Introduction to ES6 Modules for Game Programmers

As an application (be it a game or anything else) grows, so does the need for some sort of organization. Also, all but the simplest programs will require the use of some sort of additional libraries. For both of these reasons, just about every programming language meant for serious use has a way to separate code into logical, self-contained blocks. Even lowly C has its header files, but more modern languages can do more.

JavaScript, however, used to be sorely lacking in this department. Sure, you can load additional scripts in a web page, and jQuery (to name one example) managed an entire plugin architecture using DOM onload events. But everyone recognized that this wasn’t enough, and thus modules were born.

The problem is, they weren’t a part of the JavaScript “core”, so there was no standard way of making or using a module. Node made its own module system based off the CommonJS spec (later used by Browserify), while Require.js championed a slightly different style called AMD. In general, server-side and other “app”-style JS used Node’s modules, since they were already using Node itself, while purely browser-based libraries went with AMD. As with any case where there are two competing standards, developers are left having to learn both of them.

Now, with ES6, all that can change. Modules are now a core part of the language. Once browsers fully support them, you can use them anywhere. (Babel can convert ES6 modules into AMD or CommonJS, though, if you want to use modules right now.) And this is a case where you’ll definitely want to, no matter what you’re writing.

Using Modules

Most of the discussions out there focus on writing modules, rather than using them. But game developers, in particular, are going to be more likely to use somebody else’s module first, so I’m starting there.

The import keyword is your gateway to modularity. If you’ve ever used require() in Node, you’re halfway there, but you’ve still got more to learn. The idea is pretty simple, though. Let’s say that we’re using some made-up game library that’s fully modularized, so that all its classes (because it’s all ES6, see?) are in their own module files. Well, we can do this:

import Vec2d from "lib/vector2d";

Now Vec2d is available for us to use in the rest of that script, and it will be whatever the vector2d module exported. Presumably, that would be a class or a function that created a 2D vector.

That’s the most basic import. For modules that export a single value (class, function, or even a constant), it’s all you have to do. In fact, the way the standard was made, it’s actually the preferred way of doing things. But it’s not the only way. What if we have a module that gives us multiple exports?

import {normalize, dotProduct} from "lib/vector2d";

That gives us names for two functions that might be defined (and exported) in our hypothetical vector2d module. Of course, we can combine these two:

import Vec2d, {normalize, dotProduct} from "lib/vector2d";

Here, the default export (see below) is assigned to the name Vec2d, while the braces indicate exports that we’re “picking” from the module. If we don’t like the names the module gives us, no problem:

import Vec2d, {normalize, dotProduct as dotP} from "lib/vector2d";

Finally, if we have a module that has a lot of exports (maybe something like jQuery), we can use a “wildcard” import that brings in the whole module as an object:

import * as $ from "lib/jquery";

We do have to name the object we’re importing into. (I used the traditional $ for jQuery.) After we use this import, anything the module exported with a name will be available as a property on the $ object.

Note that all importing is done statically, so you can’t “conditionally” import like this. (This is one downside to ES6 modules compared to earlier attempts like Node’s.) For that, you need to use the new Module Loader API, which would look something like AMD:

System.import("lib/jquery")
    .then(function($) {
        // Use jquery module as $, just like always
    });

Creating Modules

Eventually, you’ll want to make your own modules. For a game, you might be using a classical OO approach, where all your game entities (enemies, powerups, etc.) are ES6 classes, each in their own file. Like we did above, we’ll start with the simplest case, where your module only has one value, particularly a class.

/* enemy.js */
export default class {
    // Class definition...
};

That’s all there is to it. export default is the magic phrase that tells your JS interpreter that you’re defining a module and that you want it to export a single value. That value could be anything: class, function, constant, or even another module. And, when you want to use your enemy, all you have to do is import it like we did earlier.

If you want a module with more than one export (maybe a library of independent functions), then you can use a syntax that’s almost the reverse of that for importing multiple values:

/* utils.js */
export function log(msg) {
    console.log(msg);
}

export function foo(bar) {
    // Some other function
}

export const MAX_FPS = 60;  // TODO: Lower to 30 for console builds

All of these will be exported, and they can be imported using the “braces” or “wildcard” versions of import. (Since there’s no default export, these are, in fact, the only two ways to use the module. A simple import utils from "utils"; wouldn’t help us here.)

If you don’t like writing export everywhere in a module like this, you have an alternative. Instead, you can write the module as you normally would, then add an export declaration at the end. This declaration consists of export followed by the name of each expression you’re exporting, all of them put in braces, like an object literal. In other words, like this:

/* utils2.js */
function log(msg) {
    console.log(msg);
}

function foo(bar) {
    // Some other function
}

const MAX_FPS = 60; // TODO: Lower to 30 for console builds

export {log, foo, MAX_FPS};

The main advantage of this style is that you can export a function (or whatever) under a different name, using as like we can when importing. So, for example, we could instead write the last line as export {log as debug, foo, MAX_FPS};.

In Closing

So modules are good, and ES6 modules make JavaScript even better. Combined with classes, we now have a language that makes writing programs (including games) much easier. Once support for modules becomes widespread in browsers, all JS game developers will reap the benefits that “native” devs already enjoy.

Let’s make a language – Part 2a: Syllables and Stress (Intro)

The syllable is the next logical unit of speech after the phoneme. It’s one or more sounds that follow a pattern, usually (but not always) centered around a vowel. These syllables can then be strung together into words, which we’ll cover in the next part. For now, we’ll see what we can do with these intermediate building blocks.

The Syllable

Most linguistic discussions divide a syllable into two parts: the onset and the rhyme. That’s as good a place as any to start, so that’s what we’ll do. The rhyme part is further subdivided into a nucleus and a coda, again a useful distinction for us to work with. As the rhyme is often the more important, we’ll look at it first.

The nucleus is the center of the syllable, and it’s usually a vowel sound. Some languages, however, permit consonants here, too, and these are known as syllabic consonants. In English, these are the sounds at the ends of words like better, bottle, bottom, and button. A few languages (e.g., Bella Coola, some Berber languages) go even farther, to the point where the dividing line between syllables becomes so blurred as to be useless. By and large, though, vowels and the occasional syllabic consonant are the rule for the nucleus.

The coda is everything that follows the nucleus, and it’s a part that is, strictly speaking, optional. Languages like Hawaiian don’t have syllable codas at all, while Japanese only allows its “n” sound, as in onsen. A slightly more complex scheme allows most (if not all) of the consonants in the language to appear in the coda. Beyond that are languages that allow clusters of two, three, or even four consonants, with English a primary example of the last category, as in the words texts and strengths. (We’ll come back to that one later.) An important distinction we can draw is between open syllables without a coda and closed syllables with one. That will come into play later on, when we discuss stress.

Moving to the onset, we see another opportunity for consonants. This can range from nothing at all (though languages such as Arabic do require an onset) to a single consonant to a cluster of two or three. Again, English is ridiculously complex in this regard, at the far end of the scale in allowing three: split and (once again) strengths. Of course, this complexity is tempered by the fact that the first of those three must be /s/, which brings us to the topic of phonotactics.

Loosely speaking, phonotactics is a set of constraints on which sounds can appear in a syllable. It’s a different system for each language. They all have a few things in common, though. First, there’s a distinction between consonant and vowel. The simplest systems allow only syllables of CV, where C stands for any consonant, V for any vowel. An alternative is (C)V, where the parentheses around C mean that it’s optional.

The next step up in complexity comes with a coda or an onset cluster: CVC or CCV. (We’ll assume the parentheses indicated an optional consonant are implied.) These two are the most common, according to WALS Chapter 12, but they’re also where phonotactics becomes important. Which consonants can end a syllable? Which clusters are allowed? Although the first question has no universal answer, the second does have a trend that we can (or should) use.

Most languages that allow consonant clusters follow what’s called the sonority hierarchy. For consonants, it’s kind of a ranking of how “vowel-like” a sound is. Semivowels such as /w/ or /j/ are high on the list, usually followed by approximants like /l/ or /r/, then nasals, then fricatives, then stops. The rule, then, is that the allowed syllables have sonority that falls outward from the nucleus. In other words, it’s incredibly common for a language to allow a syllable onset like /kɾ/, but rare for /ɾk/ to be permitted. In the coda, that’s reversed, as the sounds with higher sonority come first. English bears this out: trust is the sequence stop – approximant – vowel – fricative – stop. /s/ (and /z/, for that matter) is special, though. Many languages allow either sound to appear in a place where the hierarchy says it shouldn’t go, like in stop or tops. That’s also how English gets three consonants in an onset: /s/ is always the first.

And, of course, there are the combinations that aren’t allowed by a language despite the sonority hierarchy saying they’re fine. In English, these are mostly combinations of stops and nasals. We don’t pronounce the k in knight or the p in pneumonia, but other languages do. Conversely, those other languages have their own rules about what’s forbidden.

For a conlang, there’s really no best option for syllable structure. CV is simple, true, but it’s also limiting, and it creates its own problems. Generally, less complex syllables mean longer words, since there aren’t that many permutations that fit the rules. On the other hand, something too complicated can devolve into a mess of rules about which phoneme is allowed where.

Auxiliary languages, then, should probably stick with something in the middle of the spectrum, like CVC or a very restricted form of CCVC. Conlang artisans can go with something a bit more bizarre, especially if they’re never intending their languages to be spoken by mere mortals. And, of course, an alien race might have a different sonority hierarchy altogether, and the idea of “syllable” might make as little sense as it does for the Nuxalk of British Columbia.

Stress and Accent

However we chose to make syllables, whether CV or CVC or CCCVCCCC, we can now put them together to form words. Some words need just one. (Like every word in that sentence!) Many will need more, though, and some people find joy in hunting down the longest possible words in different languages.

Once we have more than one syllable in a word, there can be a battle for supremacy. Stress is a way of marking a syllable so that it stands out from those around it. Stressed syllables are typically spoken louder or with more emphasis. (An alternative is pitch accent, where the emphasized syllable is spoken with a different tone. This can happen even in languages that don’t actually have phonemic tone, including Japanese and Swedish.)

There doesn’t have to be any special meaning attached to stress. Many languages fix the position of the stressed syllable, so it’s always the last, the next to last (penultimate), or the third to last (antepenultimate). Others go in the opposite direction, stressing the first (initial), second, or third syllable from the beginning. In any of these languages, the stress falls in a specified place that doesn’t change, no matter what the word is. Examples (according to [WALS Chapter 14])(http://wals.info/chapter/14) include:

  • Final stress: Persian, Modern Hebrew
  • Penultimate: Swahili, Tagalog
  • Antepenultimate: Modern Greek, Georgian
  • Initial: Finnish, Czech
  • Second syllable: mostly smaller languages such as Dakota and Paiute
  • Third syllable: almost no languages (the only example in WALS is Winnebago)

Conversely, a language can also have stress that doesn’t seem to follow any rules at all. This free stress occurs in languages like English, where (as usual) it is weirder than it looks. In fact, English stress is phonemic, as it can be used to tell words apart. The canonical example is permit, which is a noun if you stress the first syllable, but a verb when you stress the second. In languages with free stress, it must often be learned, and it can be indicated in the orthography by diacritics, as in Spanish or Italian. Free stress can even vary by dialect, as in English laboratory.

It’s rare that a language has completely unpredictable stress. Usually, it’s determined by the kind of syllables in a word. This is where the distinction between open and closed syllables comes into play. Closed syllables tend to be more likely to take stress (i.e., they’re “heavy”), while open (“light”) syllables are stressed only when they are the only option. (Some languages consider long vowels and diphthongs to be heavy, too, but this isn’t universal.) It’s entirely possible, for example, for a language to normally have penultimate stress, but force the stress to move “back” to the antepenultimate if the final two syllables are light.

Stress in conlangs might be entirely unpredictable. All types are represented, in similar proportions to the real world, although pitch accent is one of those things that conlangers find fascinating. Auxiliary languages tend to have stress that’s either fixed or easily predictable; Esperanto’s fixed penultimate is a good example. Artistic languages are more likely to have free stress, though some of this might be due to laziness on the part of their creators. Fixed is easier, of course, since it’s mechanical, but free stress has its advantages. (An interesting experiment would be to create a language with free, unmarked stress, then come back to it a few years later and try to read it.)

Rhythm and Timing

Rhythm is kind of a forgotten part of conlanging. (I’m guilty of it, too.) It’s most closely tied to poetry, obviously, but the same concept creeps into spoken language, as well. For this post, the main point of rhythm is secondary stress. This kind of stress is lighter than the main, primary stress we discussed above, and it mostly occurs in long words of at least four syllables. Now, some languages don’t need (or have) a rhythmic pattern, but it can make a conlang feel more natural.

Generally, a heavy syllable is going to be more likely to get secondary stress, especially if there is a single, light syllable between it and the main stress. (In which direction? Whichever one you use to find the primary stress.) Languages without heavy syllables (such as pure CV languages) will probably have a pattern of stressing alternate syllables; in a penultimate-stress language, this would be the second to last, fourth to last, and so on.

Somewhat related to rhythm is timing, another under-appreciated aspect of a language. In languages such as Spanish or Italian, unstressed syllables are treated essentially the same as those that are stressed, and each syllable sounds like it takes the same amount of time. In others, including English, an unstressed syllable is spoken more quickly, and its vowel is reduced; here, it seems to be the amount of time between stressed syllables that stays constant.

For the most part, conlangers don’t need to worry much about rhythm and timing. However, if you’re writing poetry (or song) in your language, it will certainly come into play. Any post I do about that is a long way off.

The Mora

Some languages don’t use the syllable as the basis for stress and rhythm. Instead, these languages (including Japanese and Ancient Greek, to name but two) use the mora (plural morae). This is, in essence, another way of looking at light and heavy syllables. Basically, a short vowel in a syllable nucleus counts for one mora, while long vowels or diphthongs are two. A coda consonant then adds another mora, giving a range of one to three. Thus, a syllable that has one mora is light, and two morae make a heavy syllable. Three morae can make a “superheavy” syllable, though some languages don’t have these, and four seems to be impossible.

In a moraic system, stress (or pitch, if using pitch accent) can then be assigned to heavier syllables. Rhythm, too, would be based on the mora, not the syllable. The distinction can even be shown in writing, as in the Japanese kana. The end result, though, can be explained in the same terms either way. It’s just another option you can look into.

Conclusion

That was a lot to cover, and I only scratched the surface of syllables. But we can now make words, and that was worth a long post. Next up is a combination post for both Isian and Ardari. Since the theory’s out of the way, the implementation won’t take much explanation, so I’ve decided to cover both languages at the same time. After that, we’ll actually start diving into grammar. See you next week!

Thoughts on ES6

ES6 is out, and the world will never be the same. Or something like that. JavaScript won’t be the same, at least once the browsers finish implementing the new standard. Of course, by that time, ES7 will be completed. It’s just like every other standardized language. Almost no compilers fully support C++14, even in 2015, and there will certainly be holes in their coverage two years from now, when C++17 (hopefully) arrives. C# and Java programmers are lucky, since their releases are dictated by the languages’ owners, who don’t have to worry about compatibility. The price of a standard, huh?

Anyway, ES6 does bring a lot to the table. It’s almost a total overhaul of JavaScript, in my opinion, and most of it looks to be for the better. New idioms and patterns will arise over the coming years. Eventually, we may even start talking about “Modern JavaScript” the way we do “Modern C++” or “Modern Perl”, indicating that the “old” way of thinking is antiquated, deprecated. The new JS coder in 2020 might wonder why we ever did half the things we did, the same way kids today wonder how anyone got by with only 4 MB of memory or a 250 MB hard drive (like my first PC).

Babel has an overview of the new features of ES6, so I won’t repeat them here. I will offer my opinions on them, though.

Classes and Object Literals

I already did a post on classes, with a focus on using them in games. But they’re useful everywhere, especially as so many JavaScript programmers come in from languages with a more “traditional” approach to objects. Even for veterans, they come in handy. We no longer have to reinvent the wheel or use an external library for simple inheritance. That’s a good thing.

The enhancements to object literals are nice, too. Mostly, I like the support for prototype objects, methods, and super. Those will give a big boost to the places where we don’t use classes. The shorthand assignments are pure sugar, but that’s really par for the course in ES6: lots of syntactic conveniences to help us do the things we were already doing.

Modules

I will do a post on modules for game development, I promise. For now, I’d like to say that I like the idea of modules, though I’m not totally sold on their implementation and syntax. I get why the standards people did it the way they did, but it feels odd, especially as someone who has been using CommonJS and AMD modules for a couple of years.

No matter what you think of them, modules will be one of the defining points of ES6. Once modules become widespread, Browserify becomes almost obsolete, RequireJS entirely so. The old pattern of adding a property to the global window goes…out the window. (Sorry.) ES6 modules are a little more restrictive than those of Node, but I’d start looking into them as soon as the browsers start supporting them.

Promises

Async programming is the hot thing right now, and promises are ES6’s answer. It’s not full on threading, and it’s distinct from Web Workers, but this could be another area to watch. Having a language-supplied async system will mean that everybody can use it, just like C++11’s threads and futures. Once people can use something, they will use it.

Promises, I think, will definitely come into their own for AJAX-type uses. If JavaScript ever gets truly big for desktop apps, then event-driven programming will become even more necessary, and that’s another place I see promises becoming important. Games will probably make use of them, too, if they don’t cause to much of a hit to speed.

Generators and Iterators

Both of these really help a lot of programming styles. (Comprehensions do, too, but they were pushed back to ES7.) Iterators finally give us an easy way of looping over an array’s values, something I’ve longed for. They also work for custom objects, so we can make our own collections and other nifty things.

You might recognize generators from Python. (That’s where I know them from.) When you use them, it will most likely be for making your own iterable objects. They’ll also be handy for async programming and coroutines, if they’re anything like their Python counterparts.

Syntactic Sugar

A lot of additions to ES6 are purely for aesthetic purposes, so I’ll lump them all together here, in the same order as Babel’s “Learn ES2015” page that I linked above.

  • Arrows: Every JavaScript clone (CoffeeScript, et al.) has a shortcut for function literals, so there’s no reason not to put one in the core language. ES6 uses the “fat” arrow =>, which stands out. I like that, and I’ll be using it as soon as possible, especially for lambda-like functions. The only gotcha here? Arrow functions don’t get their own this, so watch out for that.

  • Template Strings: String interpolation using ${}. Took long enough. This will save pinkies everywhere from over-stretching. Anyway, there’s nothing much to complain about here. It’s pretty much the same thing as PHP, and everybody likes that. Oh, wait…

  • Destructuring: One of those ideas where you go, “Why didn’t they think of it sooner?”

  • Function Parameters: All these seem to be meant to get rid of any use for arguments, which is probably a good thing. Default parameters were sorely needed, and “rest” parameters will mean one more way to prevent off-by-one errors. My advice? Start using these ASAP.

  • let & const: Everybody complains about JavaScript’s scoping rules. let is the answer to those complaints. It gives you block-scoped variables, just like you know from C, C++, Java, and C#. var is still there, though, as it should be. For newer JS coders coming from other languages, I’d use let everywhere to start. const gives you, well, constants. Those are nice, but module exports remove one reason for constants, so I don’t see const getting quite as much use.

  • Binary & Octal Literals: Uh, yeah, sure. I honestly don’t know how much use these are in any higher-level language nowadays. But they don’t hurt me just by being there, so I’m not complaining.

Library Additions

This is kind of an “everything else” category. ES6 adds quite a bit to the standard library. Everything that I don’t feel is big enough to warrant its own section goes here, again in the order shown on “Learn ES2015”.

  • Unicode: It’s about time. Not just the Unicode literal strings, but the String and RegExp support for higher characters. For anyone working with Unicode, ES6 is a godsend. Especially if you’re doing anything with emoji, like, say, making a language that uses them.

  • Maps and Sets: If these turn out to be more efficient than plain objects, then they’ll be perfect; otherwise, I don’t think they are terribly important. In fact, they’re not that hard to make yourself, and it’s a good programming exercise. WeakMap and WeakSet are more specialized; if you need them, then you know you need them, and you probably won’t care about raw performance.

  • Proxies: These are going to be bigger on the server side, I think. Testing will get a big boost, too, but I don’t see proxies being a must-have feature in the browser. I’d love to be proven wrong, though.

  • Symbols: Library makers might like symbols. With the exception of the builtins, though, some of us might not even notice they’re there. Still, they could be a performance boost if they’re faster than strings as property keys.

  • Subclassing: Builtin objects like Array and Date can be subclassed in ES6. I’m not sure how I feel on that. On the plus side, it’s good for consistency and for the times when you really do need a custom array that acts like the real thing. However, I can see this being overused at first.

  • New APIs: The new builtin methods are all welcome additions. The Array stuff, particularly, is going to be helpful. Math.imul() and friends will speed up low-level tasks, too. And the new methods for String (like startsWith()) should have already been there years ago. (Of all the ES6 features, these are the most widely implemented, so you might be able to use them now.)

  • Reflection: Reflection is always a cool feature, but it almost cries out to be overused and misused. Time will tell.

Conclusions

ES6 has a lot of new, exciting features, but it’ll take a while before we can use them everywhere. Still, I think there’s enough in there to get started learning right now. But there are going to be a lot of projects that will soon become needless. Well, that’s the future for you, and what a bright future it is. One thing’s for sure: JavaScript will never be the same.