I have few experience with SML. Now I want to learn LiveScript, but I'm stuck at data types. So, is possible to create type using data constructors like in SML / Haskell / OCaml? If not, what is the preferred way to create data types?
The main differences between Haskell/SML and JavaScript/LiveScript/CoffeeScript are that:
In these functional languages, data
type definitions are used for compile-time type checking - JS's dynamic type system does its type checking at runtime, and so does not need to know the exact structure of objects. As a result, there is no direct translation of a data
type definition.
If you just want to define a one-use data structure in your program, just instantiate a new object and give it some properties:
// Create a new object with properties foo and bar
var MyThing = {
foo: 'a',
bar: 'b'
};
// Set the baz property of our new object
MyThing.baz = 'c';
// Remove the foo property from our object
delete MyThing.foo;
This is almost identical in LiveScript, except less syntax-heavy:
MyThing = { foo: \a, bar: \b }
MyThing.baz = \c
delete MyThing.foo
If you are dealing with many instances of an object, or anything more simple than just defining a one-use object, you will probably want to use object prototypes. In JavaScript, all objects have a prototype object from which they are based. When you invoke the new
operator on a constructor function, you get a copy of the function's prototype back, which is used as the this
context for the constructor. For example:
// Define our constructor
var Thing = function(foo, bar) {
this.foo = foo;
this.bar = bar;
};
// Set a 'default' baz property for all Things
Thing.prototype.baz = 'c';
// Create a Thing
var myThing = new Thing('a', 'b');
// Inspect our properties
console.log(myThing.foo, myThing.bar, myThing.baz) // => 'a', 'b', 'c'
This can be represented directly in LiveScript:
# Define a constructor
Thing = (foo, bar) ->
@foo = foo
@bar = bar
# Set a 'default' baz property for all Things
Thing::baz = \c
# Create a new Thing
my-thing = new Thing \a \b
# Inspect our properties
console.log myThing.foo, myThing.bar, myThing.baz
Or, more concisely, using the class
syntax which represents (almost exactly) the same thing:
class Thing
(@foo, @bar) ->
baz: \c
my-thing = new Thing \a \b
If you're coming from Haskell or SML, you'll be familiar with the idea of immutability, and writing functions which cannot perform side effects.
In the original example, we declared our object, myThing
, which we then mutated. In JS, where objects are passed into functions by reference, side-effects can make it difficult to reason about where something is going wrong without using a debugger.
To circumvent that, we can use the Immutable.js library, which provides immutable data structures such as Maps (essentially objects) and Lists (essentially arrays).
Here is the original example, rewritten using an Immutable.js Map.
// Create a new Map (like a standard JS object, but immutable)
var myThing = Immutable.Map({ foo: 'a', bar: 'b' });
/*
* Set the baz property of our new Map
* Note that the original `myThing` object remains unchanged,
* because `myThing.set` returns a new Map with the changes supplied to set
*/
var myThingWithBaz = myThing.set('baz', 'c');
var myThingWithBazButNotFoo = myThingWithBaz.delete('foo');