There are very few professions where the skills of the profession are the same skills to building tools for that profession
...the main reason we take the trouble to develop high-level languages is to get leverage, so that we can say (and more importantly, think) in 10 lines of a high-level language what would require 1000 lines of machine language.
-- Paul Graham, Succinctness is Power
http://www.paulgraham.com/power.html
{{for r in row}}
<tr class="{{r.name}}">
{{for c in column}}
{{if c.isNumeric}}
<td class="numeric">{{c}}</td>
{{else}}
<td>{{c}}</td>
{{end}}
{{end}}
</tr>
{{end}}
How do we know which {{end}}
closes which {{for}}
?
/* the "bar-button" module */
.m-bar-button {
...
}
If we make modules a first-class citizen, we create a language that lets us think in terms of modular components instead of an upchuck of CSS smeared all over various files.
@module title >> heading {
this {
font-size: 12px;
}
h1 {
font-size: @this.font-size * 2;
}
h2 {
font-size: @this.h1.font-size - 4;
}
}
.heading-title {
font-size: 12px;
}
.heading-title h1 {
font-size: 24px;
}
.heading-title {
font-size: 20px;
}
bomb location = alphabetic character, number ;
number = "0" | "1" ... | "10" ;
alphabetic character = "A" | "B" | "C" ... | "J" ;
,
is a sequence -- a follows b
|
is alternation -- a or b could be here
Real World Example: Scala compiler
@module breadcrumb {
div {
font-size: 12px;
}
ul {
list-style-type: none;
}
}
"foo" match {
case "foo" => "bar"
case _ => "nada"
}
switch("foo") {
case "foo":
return "bar";
break;
default:
return "nada";
break;
}
val x: Any = 1 // x is actually an Int
x match {
case s: String => s
case a => a.toString
}
Variables declared in the case are scoped within the block that's executed for that case.
val age = 16
age match {
case a if a > 65 => relax(p)
case 65 => retire(p)
case a if a <= 2 => sleepAndPoop(p)
case a => work(p)
}
Types can be represented as case class
'es
case class Person(name: String, age: Int)
Case classes can be pattern matched against their constructor
Person("Titus", 31) match {
case p: Person("Titus", _) => titusSlideshow(p.name)
case Person(name: String, _) => genericSlideshow(name)
}
Person("Titus", 31) match {
// meh
case p: Person if p.name == "Titus" => titusSlideshow(p.name)
// preferred
case p: Person("Titus", _) => titusSlideshow(p.name)
}
Imagine a class named ~
case class Sequence(left: Parser, right: Parser)
case class ~(left: Parser, right: Parser)
It could be pattern matched just like any other class
val tilde = new ~(x, y)
tilde match {
case ~(a, b) => ...
case Sequence(a, b) => ...
}
"foo" ~ "bar"
?// equivalent
"foo" ~ "bar"
"foo".~("bar")
...but String
doesn't have a ~
method
And even if it did how would it know to return Parser[String]
?
implicit def stringToRichString(s: String) = new RichString(s)
class RichString(left: String) {
def ~(right: String) =
new Parser[String](left) + new Parser[String](right)
}
"foo" ~ "bar"
, it first looks to see if String
has a ~
method.~
methodRichString
but "foo"
isn't of that type.String => RichString
and finds one."foo"
through stringToRichString
, making it a RichString
then calls the ~
function, resutling in a new Parser[String]
* = this whole process would be horribly unperformant in an interpreted language like Python or Ruby