Built-In Data Structures
Sleep provides two data structures built into the language: the
hashtable (usually referred to as just a "hash") and the
ever-versatile array. Normal Sleep arrays are versatile in the fact
that they can act as lists, stacks, or arrays.
To use an array and start playing with it:
@array = array("Raphael",
"Serge",
"Andreas",
"Fuzzy Puppy");
println("The last element is: " . pop(@array));
push(@array, "Mr. Anderson");
foreach $element (@array)
{
println("An element: $element");
}
In Sleep multi-dimensional arrays are easy to create. Just start
indexing new dimensions:
for ($x = 1; $x <= 10; $x++)
{
for ($y = 1; $y <= 10; $y++)
{
@multiplication[$x - 1][$y - 1] = $x * $y;
}
}
# print out our multiplication table
foreach $row (@multiplication)
{
foreach $column ($row)
{
print(" $[3]column |");
}
println();
}
As a side note, the string " $[3]column |" is called
a parsed literal in Sleep. A parsed literal is a double-quoted string. Scalar variable names are evaluated inside of parsed
literals. Within parsed literals, some formatting is available. For
example, $[n]var means "append spaces to $var
until the string length is n characters". A negative value
indicates that spaces should be prepended instead. Single-quoted
strings in Sleep are simple no-frills string literals.
Hashes are another Sleep data type. The Hash interface in Sleep
is backed by nothing more than a java.util.HashMap.
All scalar keys are converted to strings prior to storage in a
hash:
%dictionary[1] = "The number one";
%dictionary["1"] = "The string one";
In terms of multi-dimensional data structures, hashes and arrays
can be mixed and matched. This is because [] is a
special operator in Sleep. It attempts to index data from whatever
expression to which it is applied. If it is applied to an expression
returning an array, it will index array data. If it is applied to a
hash, it will index hash data. This means that technically, any
expression that returns array or hash data can be indexed. For
example:
$temp = array("a", "b", "c");
println("Second element is: " . $temp[1]);
or:
println("Second element is: " . array("a", "b", "c")[1]);
Arrays in Sleep are always prefixed with an @; hashes, a %, and
scalars are always prefixed with a $. Sleep uses the symbol at the
beginning of the variable name to determine which type of data
structure to create when referencing a variable that does not
exist:
# do we want a hash or an array in this case?
$data[0] = "Hello World";
In the example above, Sleep will silently ignore the attempt to
assign a value to a $scalar that doesn't reference a
hash or an array.
The symbols also apply in multi-dimensional data structures. If
the symbol at the beginning of the variable name is a %, then any
time an index is applied to a nonexistent dimension, a new hash
will be created.
The nice thing about this system is that hashes and arrays are
just like any other variables, with no need for special treatment.
For example, to pass an array to a subroutine:
sub multiplyAll
{
foreach $temp ($1)
{
# assigning to $temp is the same as
# assigning to the individual element
$temp = $temp * $2;
}
return $1;
}
@data = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
printAll(multiplyAll(@data, 3));
Above, @data was passed to
&multiplyAll with no special handling. Perl would
normally "flatten" @data and pass each element as a
separate parameter unless \ were used to turn
@data into a reference. In Sleep @data is
passed as a reference automatically.
Instantiating and Talking to Objects
Another fun thing in Sleep is the ability to instantiate and
talk to Java objects. This ability was added to the language to
allow access to APIs that I was too lazy to create.
The following is a simple web browser created in Sleep:
#
# Simple Sleep Based Graphical Web Browser
# Java's HTML renderer isn't very good, therefore
# this browser isn't either
#
import java.awt.*;
import javax.swing.*;
import java.net.*;
$window = [new JFrame:"Sleep Based Web Browser"];
[$window setDefaultCloseOperation:
[JFrame EXIT_ON_CLOSE]];
[$window setSize:480, 320];
sub go_to_site
{
[$display setPage: [$address getText] ];
if (checkError($check))
{
println("Error: $check");
}
}
sub link_clicked
{
if ([$1 getEventType] eq "ACTIVATED")
{
[$display setPage: [$1 getURL]];
[$address setText: [$1 getURL]];
}
}
$address = [new JTextField:20];
[$address addActionListener:&go_to_site];
$button = [new JButton:"Go!"];
[$button addActionListener:&go_to_site];
$panel = [new JPanel];
[$panel add: $address, [FlowLayout CENTER]];
[$panel add: $button, [FlowLayout RIGHT]];
[[$window getContentPane] setLayout:
[new BorderLayout]];
[[$window getContentPane] add: $panel,
[BorderLayout NORTH]];
$display = [new JEditorPane: "text/html", ""];
[$display addHyperlinkListener:&link_clicked];
[$display setEditable:0];
[[$window getContentPane] add:
[new JScrollPane: $display],
[BorderLayout CENTER]];
[$window show];
One will easily notice the calls to the Java API pretty quickly.
They are all surrounded in square brackets. Sleep's syntax for
using Java objects is similar to that of Objective-C:
[reference message: argument, argument, ...]
Each call has a reference, a message, and then a colon, followed
by a comma-separated list of arguments.
The web browser example demonstrates that the Sleep object
syntax allows one to get stuff done with the Java API. However,
working with swing this way is a little cumbersome. A Sleep/Swing
module is currently in the works to help make UI scripting in Sleep
more practical.
In Sleep, you can't create new Java classes. However, interfaces
can be faked by passing a subroutine or a closure to an argument
expecting a specific interface. Closures and subroutines are
actually one and the same. The topic of closures is covered
next.
Closures
In Sleep, functions are considered "first class" types. This
means that a scripter can define a new function, assign it to a
variable, pass it as a value, invoke a function referenced by a
variable, and so on.
To define a named closure:
sub foo
{
println("bar");
}
The named closure can be invoked as follows:
foo();
OK, that wasn't too exciting. To get technical, a named closure
can also be invoked with:
[&foo];
That was confusing. It will make sense in just a minute. To
assign a named closure to a variable and invoke the closure from
the variable:
$var = &foo;
[$var];
Consequently, this could have been written as:
$var = { println("bar"); };
[$var];
or:
[{ println("bar"); }];
Sleep closures are called with the same syntax used with
objects. Arguments in closures are available starting at $1
on up to $n for the nth argument. The message
parameter (defined before the semicolon) is passed to closures as
$0. This allows you to create some cool interfaces in
Sleep. For example:
sub BuildStack
{
return {
this('@stack');
if ($0 eq "push")
{
push(@stack, $1);
}
if ($0 eq "pop")
{
return pop(@stack);
}
};
}
# construct a new stack closure...
$mystack = BuildStack();
# push the string "test" onto the stack
[$mystack push: "test"];
# pop the top value off of the stack and print it
println("Top value is: " . [$mystack pop]);
The example above defines a new subroutine called
&BuildStack. The subroutine returns a new closure.
Inside of the closure, the variable @stack is put into
the this scope. Inside of the this scope,
@stack is visible only inside of the owning closure
instance. A second call to &BuildStack() would
return a new closure instance with its own @stack
variable.
Closures can also be passed to Java objects expecting an
interface. Any Java method call against the closure interface will
result in the entire closure being executed. The message parameter
($0) will contain the name of the method Java is trying to
invoke. Closures are the closest thing to objects Sleep has.
What Sleep Brings to the Java Platform
Sleep is a language for Perl hackers who also live in the world
of Java. Sleep brings the power of Perl to the Java platform. Not
only can Sleep extract data, parse it, rework it, and spit it back
out, but Sleep can extract data from, and send it back to, Java objects.
Sleep is also highly extensible, allowing new functions, operators,
and constructs to be added to the language. Sleep's extensibility
allows it to fit into new problem domains or be embedded into Java
applications. Combine the extensibility to fit into new problem
areas with powerful language features, and the possibilities are
endless.
Sleep Resources
Real Perl on Java Resources