integer - whole, signed numbers such as 2, 34234,
-1float - fractional signed numbers, e.g. 342.43
-0.342string - regular character strings, "hello world".boolean - true or falsedictionary - a reference to a data structure
that stores other values, keyed by strings or integers.function - functions are first-class values in
cake. void - an undefined value. most attempts to use
a void value will result in a runtime error.var a = 324; //creates the variable 'a' and puts an integer
into it
a = "hello" //puts a string into the variable 'a'
var a = "hello";
var f = function() {
var b = 123;
print(a + b); //we can also access 'a'
};
f();
In this example, the variables named a and f
exist in the top level scope, while b exists only in the scope of the function.
b is not accessible from outside the function. a,
f and b are all accessible from within the function.
remove keyword.var a = "bla";
print(a); //will print a
remove a; //removes a
print(a); //will cause a runtime error because 'a' is now gone
var a = "bla";
print(exists a); //will print TRUE
remove a;
print(exists a); //will print FALSE
if, while and
forconstructs, including break and continue
statements. case/switch statement or
a goto. var string = "a " + "series " + "of " + "strings
" + "joined together";
function function(arg1, arg2) {
var a = arg1+arg2;
}
Note that a function itself does not have a name, because it is simply
a value. var f = function(arg1, arg2) {
var a = arg1+arg2;
};
var g = f;
var h = f;
f(2, 3); //calls the function f
return keyword. var f = function(arg1, arg2) {
return arg1+arg2;
};
x = f(2,3);
void. void value for anything
they will get a runtime error. var a = 0;
var f = function() {
var b = 1; //can access 'a' and 'b'
var g = function() {
var c = 2; //can access 'a', 'b'
and 'c'
print(a);
};
};
f has access to the variable b that it defines,
as well as a, which is defined in the top level scope. g has access to a and b, as
well as c. print(a)" is called inside g, the
cake runtime must first search that function's scope. f. a doesn't exist there either, so the runtime has to search
f's parent, the top level scope.a is found there, and its value is what is printed.
a wasn't defined anywhere a runtime error would occur.
var' keyword in
a nested scope, it will block access, or shadow, any variable with
the same name that exists in a parent scope.var' keyword tells the runtime that we only
want to create a new binding in the current scope. a = "hello";
var f = function() {
var a = "bah!"; //shadow
'a' in parent scope with our own 'a'
print(a);
};
f();
print(a);
a is not changed by f.
Instead, a new variable is created with the same name in f's
scope. The first call to print(a) finds this and doesn't
bother looking for any other variables named a that might
exist in the parent scope. a = "hello";
var f = function() {
a = "bah!";
//bind 'a' in parent scope to string "bah!"
print(a);
};
f();
print(a);
var add_and_print = function(x,y)
{
print(x+y);
};
add_and_print(3,1); //prints '4'
f = function() {
var a = "hello";
g = function() {
print(a); //accesses
a, which lives in the parent scope
};
return g; //return the function
'g'
};
x = f(); //sets x to the function 'g'
defined inside the function 'f'
x(); //call it
x = f(); sets x to the return value
of the function f.f would be destroyed after the call returns. f() is actually another function,
which inside f, is called g. g uses the variable a, which lives in f's
scope. So x now contains a function which uses a variable
that lived in f when we called it to create this function.
f when it returns. var makePowerFunction = function(power) {
f = function(num) {
var result = num;
for (i = 1; i < power; i++) {
result = result * num;
}
print(num + " to the power of " + power +
" is " + result);
};
return f;
};
square = makePowerFunction(2);
cube = makePowerFunction(3);
square(3);
square(4);
square(5);
square(3);
cube(4);
cube(5);
newdict()var a = newdict();
a.greeting = "hello";
a, calls it greeting
and sets it to the string "hello".print(a.greeting);
greeting of the dictionary a.
foo = newdict();
foo.bar = newdict();
foo.bar.x = 303;
foo.bar.y = "hello";
foo.baz = true;
foo = newdict();
foo.bar = function(x) {
print("hello" + );
};
foo.bar("world");
The exists and remove operators work on dictionary slots too.
Interesting sidenote: in fact, scope in cake is implemented with a dictionary. Each scope has a "current" dictionary, that holds all the name to slot bindings for that scope.
Consider this code fragment:
a = 123;
hereais looked up in the scope dict of the top level scope.
With the following example,ais looked up in the scope dict of the function.
f = function() {
a = 123;
};
The scope dicts are chained to each other. Each scope dict is connected to its parent scope dict with a special slot named "__parent_context".
If the runtime system doesn't find a matching slot in the current scope dict, it searches the parent scope dict.
- this all goes on behind the scenes and you shouldn't need to access "__parent_context" yourself!
So when you say
you are first searching the current scope dict for the dict calleda.greet();a, then searching that dict for a slot calledgreet.
remove myDict.foo;
print(exists myDict.foo); //will print "false"
foo
= newdict()
foo.x = 23;
print(foo.x); //will
print 23
foo2 = foo; //not
creating a new dict, only pointing foo2 at the same dict as foo1
foo2.x = 10; //foo
and foo2 are the same dict, so this is identical to foo.x = 10;
print(foo.x); //will
print 10
print(foo2.x); //will
print 10
clonedict
function. foo
= newdict();
foo.x = 23;
foo2 = clonedict(foo);
foo2.x = 10;
print(foo.x); //will
print 23
print(foo2.x); //will
print 10 - we changed a different object
foreach statement d = newdict();
d.a = "one";
d.b = "two";
d.c = "three";
foreach val in d {
println(x);
}
val and iterates
through each element of d, setting val to be
the current element each time through the loop.foreach which allows this. foreach key, val in d {
println(key + " = " + val);
}
a = one
b = two
c = three
dictkeys()dictkeys function returns a new dictionary, containing
all the keys ofdictkeys
would return a dictionarya, b and c, with
the keys 0, 1 and 2.printskeys = dictkeys(d);
foreach key, val in d {
println(key + " = " + val);
}
makePerson
= function(name, lunch) {
var
person = newdict();
person.name
= name;
person.lunch
= lunch;
person.doLunch
= function() {
print(person.name
+ " eats " + person.lunch + " for lunch");
};
return
person;
};
joe = makePerson("joe",
"kebabs");
mary = makePerson("mary",
"lasagne");
joe.doLunch();
//prints "joe eats kebabs for lunch"
mary.doLunch();
//prints "mary eats lasagne for lunch"
makePerson function first creates a dictionary called
person. name and lunch
in the person dictionary and sets them to the passed in values.
doLunch().
doLunch() function note how we are able to
access the person dict from the parent scope.this"
or "self" keyword. makePerson() function we create two different
person objects. The last two lines demonstrate how although both joe and
mary have functions with the same name, they produce different results.
makeTeapot = function(capacity) {
teapot = newdict();
//these two variables are not added to 'teapot', so they
are only accessible
//to the member functions defined below
teabagCount = 0;
containsWater = false;
teapot.addTeaBags = function(numbags) {
teabagCount = teabagCount + numbags;
};
teapot.addWater = function() {
if (containsWater) {
println("the teapot overflows
with hot water, scalding you");
}
else {
containsWater = true;
}
};
teapot.empty = function() {
teabagCount = 0;
containsWater = false;
};
//the 3 functions below are also not added to teapot -
so they are effectively private
makeHotWater = function() {
println("seems lacking in flavour somehow...");
};
makeWeakTea = function() {
println("making insipid, weak tea.");
};
makeStrongTea = function() {
println("making a good strong brew.");
};
teapot.brew = function() {
if (!containsWater) {
println("can't make tea without
water!");
return;
}
if (teabagCount == 0) {
makeHotWater();
}
if (teabagCount < capacity) {
makeWeakTea();
}
else {
makeStrongTea();
}
};
return teapot;
};
teapot = makeTeapot(3);
teapot.addTeaBags(4);
teapot.addWater();
teapot.brew();
teapot.empty();
teabagCount, containsWater,
capacity, makeHotWater, makeWeakTea and makeStrongTea
slots.
makeEmployee
= function(name, lunch, salary) {
var
employee = makePerson(name, lunch);
employee.pay
= function() {
println(name
+ " gets paid " + salary);
};
};
makeEmployee have both the doLunch
and pay functions.
john
= makeEmployee("john", "pizza", 40000);
john.pay();
//prints 'john gets paid 40000'
john.doLunch();
//prints 'john eats pizza for lunch'
makeEmployee = function(name, lunch, salary, breaktime) { ;
var
employee = makePerson(name, lunch);
employee.pay
= function() {
println(name
+ " gets paid " + salary);
};
employee.doLunch
= function() {
println(name
+ " takes " + minutes + " for lunch");
};
};
doLunch slot is bound
to a function that printed the person's favourite lunch.
void doLunch() {
System.out.println(name + "takes" + minutes + " for lunch");
super.doLunch();
}
makeEmployee = function(name, lunch, salary, breaktime) {
var employee = makePerson(name, lunch);
employee.pay = function() {
println(name + " gets paid " + salary);
}
parentDoLunch = employee.doLunch; //local copy of
slot we are about to redefine
employee.doLunch() {
println(name + " takes " + minutes + " for lunch");
parentDoLunch();
};
};
john = makeEmployee("john", "pizza", 40000, 30);
john.doLunch(); //will print "john takes 30 minutes for lunch\n
john eats pizza for lunch"
john.doLunch();
john and the function doLunch
are specified at compile time. [ ] construct.
mostHungryPerson = function() {
return "john";
}
[mostHungryPerson()].doLunch();
doLunch on john.
employees[mostHungryPerson()].doLunch();
[get_func_name()](); //won't compile
func = [get_func_name()];
func();
//slot name that is just a number
employees.3.doLunch(); //invalid - can't have a slot named '3'
employees[3].doLunch(); //valid - ok, 3 is evaluated
first, then used as a key
//slotname that starts with a number
32a = "bus to malahide"; //invalid
["32a"] = "bus to malahide"; //valid because it is a string literal
wrapped in indirect access brackets.
Note to C programmers, don't be mislead into thinking []means the same in Cake as it does in C!
In C, square brackets denote an array index. In Cake, square brackets are used for an entirely different purpose - indirect slot access.
Of course, we can also use thefor (i = 0; i < 10; i++) {
employees[i].doLunch(); //'i' is evaluated and turned into a key
}
foreach statement for
this.foreach employee in employees {
employee.doLunch();
}