Poignant Guide To Learn You a Roy, The Hard Way¶
Contents:
Introduction¶
Roy is a programming language that targets JavaScript. It has a few main features:
- Damas-Hindley-Milner type inference
- Whitespace significant syntax
- Simple tagged unions
- Pattern matching
- Structural typing
- Monad syntax
- Not-horrible JS output
Most of these features are common in statically-typed, functional languages, such as:
Why JavaScript?¶
JavaScript is a necessity for web applications. It is the only feasible language you can natively run in your web browser.
A lot of developers are now using JavaScript outside of the browser; you can use node.js and Rhino to write server-side JavaScript.
What not just write JavaScript?¶
As universal as JavaScript has become, the language itself has problems:
Boilerplate¶
Creating functions is something that functional programmers want to do but JavaScript makes this a bit too verbose:
var x = function(a, b, c){
return a + b + c;
};
One solution would be to come up with a shorthand syntax for functions and have implicit returns. We might also be able to get rid of those braces.
Tolerance¶
JavaScript tries to guess what the developer is trying to do:
var two = 1 + true;
console.log(two == "2"); // true
It doesn’t really make sense to add the number 1 to the Boolean true value. It doesn’t really make sense to compare the number 2 to the string "2".
For correctness, we should show an error instead of trying to guess what the programmer wants.
Complexity¶
JavaScript allows multiple ways to do the same thing. Which is the correct way to construct a Person?
var p1 = Person();
var p2 = new Person();
It depends on the library and can be a source of confusion.
Dangerous¶
It can be easy to do things with unintentional side-effects. JavaScript has the var keyword to create local variables but unqualified assignments write to the global object:
var x = 10;
function getX() {
x = 100; // forgot 'var'
return x;
}
console.log(getX()); // 100
console.log(x); // 100
What can we do?¶
We’ve identified some problems with JavaScript, what can we do to fix it?
- Try to replace JavaScript in the browser with another language
- Try to replace JavaScript in the browser with a general purpose bytecode
- Change the JavaScript standard
- Compile from another language to JavaScript
The last option is the path of least resistance. In fact, there’s already quite a few languages that compile to JavaScript, the most popular being CoffeeScript, haXe and Objective-J.
There also ways to compile Haskell, OCaml and Scala to JavaScript. These can help with writing statically-typed, functional code for the browser but they usually have a few downsides:
- Hard/impossible to interoperate with JavaScript libraries
- Generate a lot of code
- Generate code that requires a hefty runtime
- Must be compiled on the server-side (not in the browser)
The Roy solution¶
After trying to write correct programs in JavaScript and languages that compile to JavaScript, Roy was created. Roy tries to keep close to JavaScript semantics for ease of interoperability and code generation. It’s also written in JavaScript so that it can compile code from the browser.
One of the biggest ideas when coming from JavaScript is the use of compile-time type-checking to remove type errors. We’ll cover that in the next chapter.
Types¶
The Curry-Howard isomorphism states that types are theorems and programs are proofs. Roy takes the stance of not generating programs (proofs) if the types (theorems) don’t make sense.
This is called static type-checking and is a useful tool for writing correct programs.
Primitives¶
Roy implements the same primitive types as JavaScript:
- Number
- Boolean
- String
- Array
- Object
In Roy, Arrays and Objects have special semantics and are composed of multiple types. We’ll separately cover typing of Arrays and typing of Objects.
The Read Evaluate Print Loop¶
Roy has an interactive mode which allows compilation and execution of code.
The simplest example is a value. Putting in the number 1 will respond back with that same value and its type:
roy> 1
1 : Number
We could also give a string of characters:
roy> "Hello world!"
Hello world! : String
Or a Boolean:
roy> true
true : Boolean
Evaluating expressions will also tell you the type of the result:
roy> 1 + 1
2 : Number
Let’s make the type-checking fail:
roy> 1 + "Test"
Error: Type error: Number is not String
Notice that it doesn’t give you an answer to the expression. JavaScript at this point would instead guess what you meant and give you an answer of "1Test".
Strings¶
These behave similar to a Javascript String, but they won’t have methods attached to them. String concatentation is done with the ‘++’ operator.
Arrays¶
Arrays are homogeneous, meaning that they can only hold elements of the same type. For example, we can make an Array of Numbers:
roy> [1, 2, 3]
1,2,3 : [Number]
Trying to treat it as a heterogeneous collection will result in a type error:
roy> [1, true, 3]
Error: Type error: Number is not Boolean
Access array elements with the @ operator:
roy> [1,2,3] @ 1
2 : Number
Objects¶
Objects use structural subtyping. An object is a “subtype” of another object if it satisfies all of the properties that the other object has.
An empty object is the supertype of all objects:
roy> {}
[object Object] : {}
An object containing a single property is a subtype of only the empty object:
roy> {a: 100}
[object Object] : {a: Number}
This property can be used to write well-typed code that works on object properties:
roy> let a = {a:100}
roy> let b = {a:5, b:5}
roy> let f o = o.a + 6
roy> f a
106 : Number
roy> f b
11 : Number
roy> let d = {b:100}
roy> f d
Error: Type error: {b: Number} is not {a: Number}
Interoperating with JavaScript¶
Referring to unknown identifier will assume that the identifier refers to a native JavaScript global.
For example, you can refer to console.log, something not known natively to Roy:
roy> console.log "Hello!"
Hello!
Using Native types¶
Given Roy’s current limitations, you may want to use a Native type sometimes:
roy> "abc".length
Error: Parse error on line 2: Unexpected '.'
roy> (String "abc")
abc : Native
roy> (String "abc").length
3 : Native
Regular Expressions¶
Roy does not have direct support for regular expressions, including literals like /exp/
To use a regular expression in Roy you need one of the following approaches:
- Have an existing RegExp
- Create a native RegExp using the RegExp constructor
- Invoke match on a Native String, which converts the matching String to a RegExp
roy> (String "abcd").match "a.c"
["abc"] : Native
roy> (RegExp("a.c")).exec 'abcd'
["abc"] : Native
If you want, you can try and shorten up RegExp construction:
roy> let r s = RegExp s
roy> r "a.c"
/a.c/ : Native
roy> r"a.c"
/a.c/ : Native
roy> (r"a.c").exec "abcd"
["abc"] : Native
Access array elements with the @ operator