Literal vs. Symbolic Values
When you say any of the following:
5 # literal
'a string' # literal
(1,2,3) # literal
{ key1 => 'val1', key2 => 'val2' } # literal
$var # symbolic
($a and $b) # symbolic
you're stating a value. The value may be literal, as the first four examples, or it may be symbolic, as the fifth and sixth examples.
Literal values are values that are fixed in meaning, at least within any particular grammar. Statements like 5, 'a string', (1,2,3), or { key1 => 'val1', key2 => 'val2' } represent values that will stay constant over the life of your program. By definition, literal values are constant.
Symbolic values are values that have symbols "standing in" for literal values that we don't yet know. Just as in algebra, in which you can represent unknown values using one-letter names like x or y, a variable is represented by a name. In Perl, variable names always begin with a special character called a sigil, which is a "signal" to both you and the interpreter that you will be using the variable in a certain predefined way. More on that later.
When you make statements like:
5 + 7 # literal
&do;_something(@list) # symbolic
($a and $b) # symbolic
you're describing expressions. An expression that contains no symbolic values, like the first example, is a literal expression, so called because it can be resolved entirely to a literal value. An expression that contains one or more symbolic values is a symbolic expression, so called because it can't be fully resolved until all the symbols inside it have first been resolved.
Assigning a Type to a Value
A type indicates what kind of "thing" the value is. Every value has a natural type: 5 is a num, 'my string' is a str, and $var is the type of whatever's contained in the variable named $var. Most of the time you don't have to worry about the type of a value: Perl can usually determine what type each value should be according to either what the value looks like, or the context in which the value is being used.
Any value may be forced, however, into being an explicit type: this is commonly known as casting or typecasting. Typecasting is the act of transforming a value of one type into a value of another type. The typecasting operator in Perl is as:
5 as int
5 as str
5 as MyNumber
These three code snippets treat the literal value 5 as an integer, a string, and as an object of type MyNumber. If the value can't be converted into the requested type, an exception is reported. You can typecast any value or expression, including symbolic values and expressions, so long as Perl can find a way to convert it into the requested type when the value is resolved:
$v as int
$myDog as Pet
($foo + $bar / 2) as Meters
No value can have more than one type at once: as soon as the value or expression is evaluated, whether at compile time (for literals) or runtime (for symbolics), the resulting value either has a single type, or no type at all. (But a container for a value might have multiple types, through superpositions: more on that later.)
Assigning Properties to a Value
(This section was very wrong; I need to re-edit. The words 'is' and 'but' mean two different things. -- mjl)
...
Variables
If values are just things, variables are simply containers for those things. You can put things in each container, and you can take things out of each container. Some of these containers are tailored to accept only certain types of values, while others are more liberal in what can be placed inside them. You can name each container anything you want, like "shoes" or "fish", in order to differentiate it from all the others you might have around.
If you use a variable in a statement or expression, as we saw in previous examples, the variable is a symbol standing in for whatever value it currently contains:
print $fish # print the value inside the container named "$fish"
$box # get the value inside the container named "$box"
$i + 1 # get the value inside the container named "$i", add 1 to it
@cats # get the values inside the container named "@cats"
Declaring Variables
In order to use a variable, you must declare it: that is, you must give it a name, and you must specify where and how you want to use it.
At minimum, declaring a variable in Perl consists of:
1) Choosing a scope for the variable: that is, where you will be using it.
2) Choosing a name for the variable, so you can refer to it later.
3) Choosing a sigil for the variable, specifying whether you will be using the variable as a single value ($), a list of values (@), or a hash of values (%).
In Perl, the sigil is considered part of the name (they're almost always used together). The scope is usually specified by the declarator, typically my for lexical or "private" variables, and our for global or "public" variables. (See the definitions of my and our, elsewhere, for more information.) Thus you can declare variables using statements like:
my $fish; # declare a lexical container named "$fish"
my $box; # declare a lexical container named "$box"
my $i; # declare a lexical container named "$i"
our @cats; # declare a global container named "@cats"
Note that we haven't told our variables what types they contain, which means they will happily accept whatever we place inside them. The variable $fish doesn't have to contain a fish; it can contain a cat, or a bicycle, or a reference to a list of hashes of AbstractEventHandlers.
Declaring the Type of a Variable
Declaring the type of a variable generally means to declare what type of things the container will accept. Only things of that type may then be placed in the container; attempting to put in anything else will generate an exception. In perl, you specify what type of value something is by placing the type name in front of the value:
my Fish $fish; # $fish may contain only a single Fish object
my int $i; # $i may contain only a single integer
our Cat @cats; # @cats may contain any number of Cat objects
Note that the last one doesn't scan particularly well, in that you might think it says "our variable is a Cat, and is named @cats". It in fact says "our variable is an array of Cats, and is named @cats."
There are actually two types associated with each variable: the type of the container, and the type that is contained within the container. To make this distinction more clear, you can use the alternate but sometimes more readable syntax:
our @cats is Array of Cat; # long, but clear
our @cats of Cat; # shorter, if you want it to be a normal Array
our Cat @cats; # shorter still, but sometimes more awkward
our %dogs is Hash of Dog; # long, but clear
our %dogs of Dog; # shorter, if you want it to be a normal Hash
our Dog %dogs; # shorter still, but sometimes more awkward
our $fish is Scalar of Fish; # more seldom used with scalars, but valid
our $fish of Fish; # shorter, if you want it to be a normal Scalar
our Fish $fish; # shorter still
There is an added bonus to the longer syntax, in that it allows you to declare that your variable will use a container implementation other than the standard Scalar, Array, and Hash. Suppose you had created a subclass of Hash named DogKennel that was especially suited to implementing a "dog kennel" container. You could then say:
our %kennel is DogKennel of Dog; # (1) use DogKennel as the container
our Dog %kennel is DogKennel; # (2) synonymous with (1)
our %kennel is DogKennel; # (3) if DogKennel defaults to values of
# type Dog, don't even have to say Dog.
our Dog %kennel; # (4) not the same: doesn't use DogKennel!
our DogKennel %kennel; # (5) WRONG, is a Hash of DogKennels!
Note that in order to use the DogKennel class as the implementor of our %kennel, you must always specify it using is, as shown in the first three examples. If you don't put the is, as in the last two examples, your %kennel will be implemented by the normal Hash class.
Hashes deserve special attention because you can declare not only what types of values they may contain, but what object type will be used for the "keys" of the hash. You do this by specifying a pair of types to use for the keys and values:
our %kennel is Hash of ( DogName => Dog )
our %kennel is DogKennel of ( DogName => Dog )
Declaring Properties of Variables
Properties assigned to variables are called compile-time properties, because they happen when the variable is declared -- at compilation time. You cannot assign a runtime property to a variable, but you can assign runtime properites to the value that may be contained within the variable.
Properties of variables are typically directives that give special behaviors to the variable they are used on. For example:
my Dog $dog is private; # allowed (but redundant)
my Dog %dogs is logged; # any changes to the hash will write to a log
Note that these properties are things that are always true of $dog or %dogs: you cannot change the compile-time properties of variables, you may only change the properties of the values they may contain.
Declaring Pre/Post Assertions for Variables
Assertions are blocks of code that are executed just before, or just after, an event happens. They are called assertions because they are typically used to "assert" that a given condition is true: if the code block returns a true value, the assertion has "passed" and the program continues. If the code block returns a false value, the assertion has "failed", and a runtime exception is generated.
Variables may be given two types of assertions, which are declared using on get and on set:
my Dog $dog
on get { ... }
on set { ... };
The on get assertion is executed whenever the variable is read, just before a value is returned from the container; you can use this method to check that the value you will be returning is actually valid, or to do more complex operations (like loading the value from a cached location).
The on set assertion is executed whenever the variable is set to contain a new value. It executes just before the assignment of the value takes place, so that you have access to both the previous value and the proposed new one. You can use this assertion to check the validity of the new value, among other things.
The on get and on set assertions are the only assertion types that may be attached to a variable. If you want to create an assertion that acts upon changes to the state of the value in your object (for example, upon adding a key to a hash, changing a runtime property, etc.), you should attach the assertion to the value, not the variable.
For more information about assertions, see later sections.
Assigning Values to Variables
So far we've declared values, and we've declared variables. As the final step, let's actually assign a value to a variable.
Assignment in Perl simply means to place a value inside a container: this is accomplished with the = operator. The following statements declare a variable named $i, then place the value 5 inside it.
my $i;
$i = 5;
Declaration and assignment can almost always happen in the same statement, so you can simplify the above to:
my $i = 5;
Many assignment operations in fact are just that simple, but they don't have to be. The following are all valid:
my int $i = $myString.chars + 1;
my %kennel is DogKennel of (DogName => Dog)
= ( 'fido' => $myDogFido, 'rex' => $myDogRex );
my @objs is BoundedArray( max => 100 ) of MyClass
= (File.read as Array of MyClass);
Suppose you want to declare a variable named $dog, which can contain objects only of type Dog. Then suppose you want to assign an actual Dog to it -- one that you will create by calling the Dog.new() method:
my Dog $dog; # Declare a var that can contain a Dog object
$dog = Dog.new(); # Place the value returned by Dog.new() inside var $dog.
Or simply:
my Dog $dog = Dog.new();
This particular construct -- declaring a typed variable, instantiating a new object via .new, and assigning the object to the variable -- is so common that Perl provides a special shortcut for it.
my new Dog $dog;