mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-27 09:15:50 +03:00
Perl 6: Make information about Hash tables correct. Rewrite the example for dynamically scoped variables.. (#2475)
* Explain that you cannot pass immutable values like integers to subs even if you use $n is rw. * Add myself as a contributor * Remove contributor since I am not a major contributor * Add many more smartmatch examples. Make the ternary operatory clearer and add a code example for it. Make the section for && and || have a working code example and show the output it gives * Fix assigning $a $b and $c values in the && operator section * Rename a few things so they don't conflict with variables in other parts of the code * Remove extra dashes * Move description of smartmatch type checks toward the end of that section * Better names for scoping examples * Redo the example for dynamically scoped variables so the example is better and functions properly when ran (old example did not work properly when running it by itself) * Rename these classes so they aren't named the same as the ones above * Add information about the different twigils in Perl 6. This makes understanding what $. $! and $* are and how they relate. Also complete the renaming in the previous commit * Fix capitalization and indenting here * Using the word interpolate to mean accessing an element of an array is not the proper terminology. Using the word interpolate is usually associated with things such as this: say "@names = { @names.perl }" * Make the wording a little clearer for arrays * Remove incorrect information about hashes. Hashes are not arrays of pairs although Perl 6 makes it easy to use them as such * Make the subroutine text read better * Clean up the Object Model introduction section * Fix the section on interpolating all elements of an array * Use the word attribute instead of field since this is the wording that the perl 6 documentation uses for these objects * Fix column width in a few places * More clearly mark which twigils are used for "normal" variables and which ones are used on "objects". This makes the flow from the # Scoping => # Twigil => # Object Model section smoother * Semi-rewrite the example for object inheritance and use more natural names. Also rename class A to class Attrib-Class * Clean up the scoping introduction a little * Remove a leftover line from rewriting the inheritance example * More gradually introduce exceptions and fix a typo, as well as adding a few examples * Reorder introduction of packages to be more natural. Show how to "use" a package before we show how to create and declare our own modules * Avoid using the word 'use' because we're not refering to the perl keyword 'use'
This commit is contained in:
parent
8905c0c5bc
commit
e336c909b9
@ -37,11 +37,11 @@ my $str = 'String';
|
||||
# double quotes allow for interpolation (which we'll see later):
|
||||
my $str2 = "String";
|
||||
|
||||
# variable names can contain but not end with simple quotes and dashes,
|
||||
# and can contain (and end with) underscores :
|
||||
# Variable names can contain but not end with simple quotes and dashes,
|
||||
# and can contain (and end with) underscores :
|
||||
# my $weird'variable-name_ = 5; # works !
|
||||
|
||||
my $bool = True; # `True` and `False` are Perl 6's boolean
|
||||
my $bool = True; # `True` and `False` are Perl 6's boolean values.
|
||||
my $inverse = !$bool; # You can invert a bool with the prefix `!` operator
|
||||
my $forced-bool = so $str; # And you can use the prefix `so` operator
|
||||
# which turns its operand into a Bool
|
||||
@ -56,28 +56,32 @@ my @array = 1, 2, 3;
|
||||
|
||||
say @array[2]; # Array indices start at 0 -- This is the third element
|
||||
|
||||
say "Interpolate an array using [] : @array[]";
|
||||
#=> Interpolate an array using [] : 1 2 3
|
||||
say "Interpolate all elements of an array using [] : @array[]";
|
||||
#=> Interpolate all elements of an array using [] : 1 2 3
|
||||
|
||||
@array[0] = -1; # Assign a new value to an array index
|
||||
@array[0, 1] = 5, 6; # Assign multiple values
|
||||
|
||||
my @keys = 0, 2;
|
||||
@array[@keys] = @letters; # Assign using an array
|
||||
@array[@keys] = @letters; # Assignment using an array containing index values
|
||||
say @array; #=> a 6 b
|
||||
|
||||
## * Hashes, or key-value Pairs.
|
||||
# Hashes are actually arrays of Pairs
|
||||
# (you can construct a Pair object using the syntax `Key => Value`),
|
||||
# except they get "flattened" (hash context), removing duplicated keys.
|
||||
# Hashes are pairs of keys and values.
|
||||
# You can construct a Pair object using the syntax `Key => Value`.
|
||||
# Hash tables are very fast for lookup, and are stored unordered.
|
||||
# Keep in mind that keys get "flattened" in hash context, and any duplicated
|
||||
# keys are deduplicated.
|
||||
my %hash = 1 => 2,
|
||||
3 => 4;
|
||||
my %hash = foo => "bar", # keys get auto-quoted
|
||||
"some other" => "value", # trailing commas are okay
|
||||
;
|
||||
my %hash = <key1 value1 key2 value2>; # you can also create a hash
|
||||
# from an even-numbered array
|
||||
my %hash = key1 => 'value1', key2 => 'value2'; # same as this
|
||||
# Even though hashes are internally stored differently than arrays,
|
||||
# Perl 6 allows you to easily create a hash from an even numbered array:
|
||||
my %hash = <key1 value1 key2 value2>;
|
||||
|
||||
my %hash = key1 => 'value1', key2 => 'value2'; # same result as above
|
||||
|
||||
# You can also use the "colon pair" syntax:
|
||||
# (especially handy for named parameters that you'll see later)
|
||||
@ -92,7 +96,8 @@ say %hash{'key1'}; # You can use {} to get the value from a key
|
||||
say %hash<key2>; # If it's a string, you can actually use <>
|
||||
# (`{key1}` doesn't work, as Perl6 doesn't have barewords)
|
||||
|
||||
## * Subs (subroutines, or functions in most other languages).
|
||||
## * Subs: subroutines or functions as most other languages call them are
|
||||
# created with the `sub` keyword.
|
||||
sub say-hello { say "Hello, world" }
|
||||
|
||||
sub say-hello-to(Str $name) { # You can provide the type of an argument
|
||||
@ -619,66 +624,95 @@ multi with-or-without-you {
|
||||
|
||||
|
||||
### Scoping
|
||||
# In Perl 6, contrarily to many scripting languages (like Python, Ruby, PHP),
|
||||
# you are to declare your variables before using them. You know `my`.
|
||||
# (there are other declarators, `our`, `state`, ..., which we'll see later).
|
||||
# In Perl 6, unlike many scripting languages, (such as Python, Ruby, PHP),
|
||||
# you must declare your variables before using them. The `my` declarator
|
||||
# you have learned uses "lexical scoping". There are a few other declarators,
|
||||
# (`our`, `state`, ..., ) which we'll see later.
|
||||
# This is called "lexical scoping", where in inner blocks,
|
||||
# you can access variables from outer blocks.
|
||||
my $foo = 'Foo';
|
||||
sub foo {
|
||||
my $bar = 'Bar';
|
||||
sub bar {
|
||||
say "$foo $bar";
|
||||
my $file_scoped = 'Foo';
|
||||
sub outer {
|
||||
my $outer_scoped = 'Bar';
|
||||
sub inner {
|
||||
say "$file_scoped $outer_scoped";
|
||||
}
|
||||
&bar; # return the function
|
||||
&inner; # return the function
|
||||
}
|
||||
foo()(); #=> 'Foo Bar'
|
||||
outer()(); #=> 'Foo Bar'
|
||||
|
||||
# As you can see, `$foo` and `$bar` were captured.
|
||||
# As you can see, `$file_scoped` and `$outer_scoped` were captured.
|
||||
# But if we were to try and use `$bar` outside of `foo`,
|
||||
# the variable would be undefined (and you'd get a compile time error).
|
||||
|
||||
# Perl 6 has another kind of scope : dynamic scope.
|
||||
# They use the twigil (composed sigil) `*` to mark dynamically-scoped variables:
|
||||
my $*a = 1;
|
||||
# Dyamically-scoped variables depend on the current call stack,
|
||||
# instead of the current block depth.
|
||||
sub foo {
|
||||
my $*foo = 1;
|
||||
bar(); # call `bar` in-place
|
||||
### Twigils
|
||||
|
||||
# There are many special `twigils` (composed sigil's) in Perl 6.
|
||||
# Twigils define the variables' scope.
|
||||
# The * and ? twigils work on standard variables:
|
||||
# * Dynamic variable
|
||||
# ? Compile-time variable
|
||||
# The ! and the . twigils are used with Perl 6's objects:
|
||||
# ! Attribute (class member)
|
||||
# . Method (not really a variable)
|
||||
|
||||
# `*` Twigil: Dynamic Scope
|
||||
# These variables use the`*` twigil to mark dynamically-scoped variables.
|
||||
# Dynamically-scoped variables are looked up through the caller, not through
|
||||
# the outer scope
|
||||
|
||||
my $*dyn_scoped_1 = 1;
|
||||
my $*dyn_scoped_2 = 10;
|
||||
|
||||
sub say_dyn {
|
||||
say "$*dyn_scoped_1 $*dyn_scoped_2";
|
||||
}
|
||||
sub bar {
|
||||
say $*foo; # `$*foo` will be looked in the call stack, and find `foo`'s,
|
||||
# even though the blocks aren't nested (they're call-nested).
|
||||
#=> 1
|
||||
|
||||
sub call_say_dyn {
|
||||
my $*dyn_scoped_1 = 25; # Defines $*dyn_scoped_1 only for this sub.
|
||||
$*dyn_scoped_2 = 100; # Will change the value of the file scoped variable.
|
||||
say_dyn(); #=> 25 100 $*dyn_scoped 1 and 2 will be looked for in the call.
|
||||
# It uses he value of $*dyn_scoped_1 from inside this sub's lexical
|
||||
# scope even though the blocks aren't nested (they're call-nested).
|
||||
}
|
||||
say_dyn(); #=> 1 10
|
||||
call_say_dyn(); #=> 25 100
|
||||
# Uses $*dyn_scoped_1 as defined in call_say_dyn even though
|
||||
# we are calling it from outside.
|
||||
say_dyn(); #=> 1 100 We changed the value of $*dyn_scoped_2 in call_say_dyn
|
||||
# so now its value has changed.
|
||||
|
||||
### Object Model
|
||||
|
||||
# You declare a class with the keyword `class`, fields with `has`,
|
||||
# methods with `method`. Every attribute that is private is named `$!attr`.
|
||||
# Immutable public attributes are named `$.attr`
|
||||
# To call a method on an object, add a dot followed by the method name:
|
||||
# => $object.method
|
||||
# Classes are declared with the `class` keyword. Attributes are declared
|
||||
# with the `has` keyword, and methods declared with `method`.
|
||||
# Every attribute that is private uses the ! twigil for example: `$!attr`.
|
||||
# Immutable public attributes use the `.` twigil.
|
||||
# (you can make them mutable with `is rw`)
|
||||
# The easiest way to remember the `$.` twigil is comparing it to how methods
|
||||
# are called.
|
||||
|
||||
# Perl 6's object model ("SixModel") is very flexible,
|
||||
# and allows you to dynamically add methods, change semantics, etc ...
|
||||
# (this will not be covered here, and you should refer to the Synopsis).
|
||||
# (these will not all be covered here, and you should refer to:
|
||||
# https://docs.perl6.org/language/objects.html.
|
||||
|
||||
class A {
|
||||
has $.field; # `$.field` is immutable.
|
||||
# From inside the class, use `$!field` to modify it.
|
||||
has $.other-field is rw; # You can mark a public attribute `rw`.
|
||||
has Int $!private-field = 10;
|
||||
class Attrib-Class {
|
||||
has $.attrib; # `$.attrib` is immutable.
|
||||
# From inside the class, use `$!attrib` to modify it.
|
||||
has $.other-attrib is rw; # You can mark a public attribute `rw`.
|
||||
has Int $!private-attrib = 10;
|
||||
|
||||
method get-value {
|
||||
$.field + $!private-field;
|
||||
$.attrib + $!private-attrib;
|
||||
}
|
||||
|
||||
method set-value($n) {
|
||||
# $.field = $n; # As stated before, you can't use the `$.` immutable version.
|
||||
$!field = $n; # This works, because `$!` is always mutable.
|
||||
method set-value($param) { # Methods can take parameters
|
||||
$!attrib = $param; # This works, because `$!` is always mutable.
|
||||
# $.attrib = $param; # Wrong: You can't use the `$.` immutable version.
|
||||
|
||||
$.other-field = 5; # This works, because `$.other-field` is `rw`.
|
||||
$.other-attrib = 5; # This works, because `$.other-attrib` is `rw`.
|
||||
}
|
||||
|
||||
method !private-method {
|
||||
@ -686,33 +720,44 @@ class A {
|
||||
}
|
||||
};
|
||||
|
||||
# Create a new instance of A with $.field set to 5 :
|
||||
# Note: you can't set private-field from here (more later on).
|
||||
my $a = A.new(field => 5);
|
||||
$a.get-value; #=> 15
|
||||
#$a.field = 5; # This fails, because the `has $.field` is immutable
|
||||
$a.other-field = 10; # This, however, works, because the public field
|
||||
# is mutable (`rw`).
|
||||
# Create a new instance of Attrib-Class with $.attrib set to 5 :
|
||||
# Note: you can't set private-attribute from here (more later on).
|
||||
my $class-obj = Attrib-Class.new(attrib => 5);
|
||||
say $class-obj.get-value; #=> 15
|
||||
#$class-obj.attrib = 5; # This fails, because the `has $.attrib` is immutable
|
||||
$class-obj.other-attrib = 10; # This, however, works, because the public
|
||||
# attribute is mutable (`rw`).
|
||||
|
||||
## Perl 6 also has inheritance (along with multiple inheritance)
|
||||
## Object Inheritance
|
||||
# Perl 6 also has inheritance (along with multiple inheritance)
|
||||
# While `method`'s are inherited, `submethod`'s are not.
|
||||
# Submethods are useful for object construction and destruction tasks,
|
||||
# such as BUILD, or methods that must be overriden by subtypes.
|
||||
# We will learn about BUILD later on.
|
||||
|
||||
class A {
|
||||
has $.val;
|
||||
|
||||
submethod not-inherited {
|
||||
say "This method won't be available on B.";
|
||||
say "This is most useful for BUILD, which we'll see later";
|
||||
class Parent {
|
||||
has $.age;
|
||||
has $.name;
|
||||
# This submethod won't be inherited by Child.
|
||||
submethod favorite-color {
|
||||
say "My favorite color is Blue";
|
||||
}
|
||||
|
||||
method bar { $.val * 5 }
|
||||
# This method is inherited
|
||||
method talk { say "Hi, my name is $!name" }
|
||||
}
|
||||
class B is A { # inheritance uses `is`
|
||||
method foo {
|
||||
say $.val;
|
||||
}
|
||||
|
||||
method bar { $.val * 10 } # this shadows A's `bar`
|
||||
# Inheritance uses the `is` keyword
|
||||
class Child is Parent {
|
||||
method talk { say "Goo goo ga ga" }
|
||||
# This shadows Parent's `talk` method, This child hasn't learned to speak yet!
|
||||
}
|
||||
my Parent $Richard .= new(age => 40, name => 'Richard');
|
||||
$Richard.favorite-color; #=> "My favorite color is Blue"
|
||||
$Richard.talk; #=> "Hi, my name is Richard"
|
||||
# # $Richard is able to access the submethod, he knows how to say his name.
|
||||
|
||||
my Child $Madison .= new(age => 1, name => 'Madison');
|
||||
$Madison.talk; # prints "Goo goo ga ga" due to the overrided method.
|
||||
# $Madison.favorite-color does not work since it is not inherited
|
||||
|
||||
# When you use `my T $var`, `$var` starts off with `T` itself in it,
|
||||
# so you can call `new` on it.
|
||||
@ -720,11 +765,7 @@ class B is A { # inheritance uses `is`
|
||||
# `$a .= b` is the same as `$a = $a.b`)
|
||||
# Also note that `BUILD` (the method called inside `new`)
|
||||
# will set parent properties too, so you can pass `val => 5`.
|
||||
my B $b .= new(val => 5);
|
||||
|
||||
# $b.not-inherited; # This won't work, for reasons explained above
|
||||
$b.foo; # prints 5
|
||||
$b.bar; #=> 50, since it calls B's `bar`
|
||||
|
||||
## Roles are supported too (also called Mixins in other languages)
|
||||
role PrintableVal {
|
||||
@ -739,8 +780,8 @@ class Item does PrintableVal {
|
||||
has $.val;
|
||||
|
||||
# When `does`-ed, a `role` literally "mixes in" the class:
|
||||
# the methods and fields are put together, which means a class can access
|
||||
# the private fields/methods of its roles (but not the inverse !):
|
||||
# the methods and attributes are put together, which means a class can access
|
||||
# the private attributes/methods of its roles (but not the inverse !):
|
||||
method access {
|
||||
say $!counter++;
|
||||
}
|
||||
@ -757,34 +798,48 @@ class Item does PrintableVal {
|
||||
|
||||
### Exceptions
|
||||
# Exceptions are built on top of classes, in the package `X` (like `X::IO`).
|
||||
# Unlike many other languages, in Perl 6, you put the `CATCH` block *within* the
|
||||
# block to `try`. By default, a `try` has a `CATCH` block that catches
|
||||
# any exception (`CATCH { default {} }`).
|
||||
# You can access the last exception with the special variable `$!`
|
||||
# (use `$_` in a `CATCH` block) Note: This has no relation to $!variables.
|
||||
|
||||
# You can throw an exception using `die`:
|
||||
open 'foo' or die 'Error!'; #=> Error!
|
||||
# Or more explicitly:
|
||||
die X::AdHoc.new(payload => 'Error!');
|
||||
|
||||
## Using `try` and `CATCH`
|
||||
# By using `try` and `CATCH` you can contain and handle exceptions without
|
||||
# disrupting the rest of the program.
|
||||
# Unlike many other languages, in Perl 6, you put the `CATCH` block *within*
|
||||
# the block to `try`. By default, a `try` has a `CATCH` block that catches
|
||||
# any exception (`CATCH { default {} }`).
|
||||
|
||||
try { my $a = (0 %% 0); CATCH { say "Something happened: $_" } }
|
||||
#=> Something happened: Attempt to divide by zero using infix:<%%>
|
||||
|
||||
# You can redefine it using `when`s (and `default`)
|
||||
# to handle the exceptions you want:
|
||||
# to handle the exceptions you want:
|
||||
try {
|
||||
open 'foo';
|
||||
CATCH {
|
||||
when X::AdHoc { say "unable to open file !" }
|
||||
CATCH { # In the `CATCH` block, the exception is set to $_
|
||||
when X::AdHoc { say "Error: $_" }
|
||||
#=>Error: Failed to open file /dir/foo: no such file or directory
|
||||
|
||||
# Any other exception will be re-raised, since we don't have a `default`
|
||||
# Basically, if a `when` matches (or there's a `default`) marks the exception as
|
||||
# "handled" so that it doesn't get re-thrown from the `CATCH`.
|
||||
# Basically, if a `when` matches (or there's a `default`) marks the
|
||||
# exception as
|
||||
# "handled" so that it doesn't get re-thrown from the `CATCH`.
|
||||
# You still can re-throw the exception (see below) by hand.
|
||||
}
|
||||
}
|
||||
|
||||
# You can throw an exception using `die`:
|
||||
die X::AdHoc.new(payload => 'Error !');
|
||||
|
||||
# You can access the last exception with `$!` (use `$_` in a `CATCH` block)
|
||||
|
||||
# There are also some subtelties to exceptions. Some Perl 6 subs return a `Failure`,
|
||||
# which is a kind of "unthrown exception". They're not thrown until you tried to look
|
||||
# at their content, unless you call `.Bool`/`.defined` on them - then they're handled.
|
||||
# (the `.handled` method is `rw`, so you can mark it as `False` back yourself)
|
||||
# There are also some subtleties to exceptions. Some Perl 6 subs return a
|
||||
# `Failure`, which is a kind of "unthrown exception". They're not thrown until
|
||||
# you tried to look at their content, unless you call `.Bool`/`.defined` on
|
||||
# them - then they're handled.
|
||||
# (the `.handled` method is `rw`, so you can mark it as `False` back yourself)
|
||||
#
|
||||
# You can throw a `Failure` using `fail`. Note that if the pragma `use fatal` is on,
|
||||
# `fail` will throw an exception (like `die`).
|
||||
# You can throw a `Failure` using `fail`. Note that if the pragma `use fatal`
|
||||
# is on, `fail` will throw an exception (like `die`).
|
||||
fail "foo"; # We're not trying to access the value, so no problem.
|
||||
try {
|
||||
fail "foo";
|
||||
@ -804,22 +859,26 @@ try {
|
||||
# and `enum`) are actually packages. (Packages are the lowest common denominator)
|
||||
# Packages are important - especially as Perl is well-known for CPAN,
|
||||
# the Comprehensive Perl Archive Network.
|
||||
# You're not supposed to use the package keyword, usually:
|
||||
# you use `class Package::Name::Here;` to declare a class,
|
||||
# or if you only want to export variables/subs, you can use `module`:
|
||||
|
||||
# You can use a module (bring its declarations into scope) with `use`
|
||||
use JSON::Tiny; # if you installed Rakudo* or Panda, you'll have this module
|
||||
say from-json('[1]').perl; #=> [1]
|
||||
|
||||
# Declare your own packages like this:
|
||||
# `class Package::Name::Here;` to declare a class, or if you only want to
|
||||
# export variables/subs, you can use `module`. If you're coming from Perl 5
|
||||
# please note you're not usually supposed to use the `package` keyword.
|
||||
|
||||
module Hello::World { # Bracketed form
|
||||
# If `Hello` doesn't exist yet, it'll just be a "stub",
|
||||
# that can be redeclared as something else later.
|
||||
# ... declarations here ...
|
||||
}
|
||||
unit module Parse::Text; # file-scoped form
|
||||
|
||||
grammar Parse::Text::Grammar { # A grammar is a package, which you could `use`
|
||||
}
|
||||
|
||||
# You can use a module (bring its declarations into scope) with `use`
|
||||
use JSON::Tiny; # if you installed Rakudo* or Panda, you'll have this module
|
||||
say from-json('[1]').perl; #=> [1]
|
||||
|
||||
# As said before, any part of the six model is also a package.
|
||||
# Since `JSON::Tiny` uses (its own) `JSON::Tiny::Actions` class, you can use it:
|
||||
my $actions = JSON::Tiny::Actions.new;
|
||||
@ -1128,10 +1187,11 @@ sub add($a, $b) { $a + $b }
|
||||
say [[&add]] 1, 2, 3; #=> 6
|
||||
|
||||
## * Zip meta-operator
|
||||
# This one is an infix meta-operator than also can be used as a "normal" operator.
|
||||
# It takes an optional binary function (by default, it just creates a pair),
|
||||
# and will pop one value off of each array and call its binary function on these
|
||||
# until it runs out of elements. It returns an array with all of these new elements.
|
||||
# This one is an infix meta-operator than also can be used as a "normal"
|
||||
# operator. It takes an optional binary function (by default, it just creates
|
||||
# a pair), and will pop one value off of each array and call its binary function
|
||||
# on these until it runs out of elements. It returns an array with all of these
|
||||
# new elements.
|
||||
(1, 2) Z (3, 4); # ((1, 3), (2, 4)), since by default, the function makes an array
|
||||
1..3 Z+ 4..6; # (5, 7, 9), using the custom infix:<+> function
|
||||
|
||||
@ -1205,7 +1265,8 @@ say so 'a' ~~ / a /; # More readable with some spaces!
|
||||
# returning a `Match` object. They know how to respond to list indexing,
|
||||
# hash indexing, and return the matched string.
|
||||
# The results of the match are available as `$/` (implicitly lexically-scoped).
|
||||
# You can also use the capture variables (`$0`, `$1`, ... starting at 0, not 1 !).
|
||||
# You can also use the capture variables which start at 0:
|
||||
# `$0`, `$1', `$2`...
|
||||
#
|
||||
# You can also note that `~~` does not perform start/end checking
|
||||
# (meaning the regexp can be matched with just one char of the string),
|
||||
|
Loading…
Reference in New Issue
Block a user