Perl 6 - From Wikipedia

Perl 6 是 Perl 編程語言家族中的一員. 它仍舊在開發中, 幾個解釋器和編譯器在同時進行. 它引入了很多現代和歷史語言的元素. Perl 6 打算有很多實現. 和 Perl 5 兼容不是它的目標, 盡管兼容模式是它的計劃書的一部分. Perl 6 的設計程序是從 2000 年開始的. 2015 年 9 月 預期會發布候選版, 并在 12 月 發布最終版.

Perl 6 第一個完成度最高的實現是 Pugs , 開始于 2005 年, 今天已經有多個 Perl 6 實現的工程了. Rakudo Perl 是基于 NQP (Not Quite Perl) 并使用 MoarVMJava 虛擬機 作為運行時環境的一個 Perl 6 實現, 并且每個月會發布一個新版本; 在 2010 年 七月, 該項目發布了第一次 Rakudo Star 分發, 一個有用和?可用的 Perl 6 實現和相關東西的集合. Larry Wall 維護了一個眾所周知的叫做 STD.pm6 的引用文法, 它是用 Perl 6 寫的, 并使用 Perl 5 驅動 (bootstrapped)

Niecza, 另一個主要的 Perl 6 實現, 專注于優化和效率研究. 它的目標是 Common Language Infrastructure

歷史


在 Perl 6 中, 我們決定修復語言而非修正使用者 — Larry Wall

Perl 6 的設計程序首先是在 2000.7.19 , 在 Perl 大會的第四天, 由 Larry Wall 在它的"洋蔥頭的狀態 2000" 演講上宣布的. 那時候, 主要的目標是移除語言中的"歷史毒瘤”; 容易的事情應該保持容易, 困難的事情應該變得更簡單, 不可能的事情應該變得可能.開始整理內部設計和 APIs. 程序開始于一系列請求評論 或者 ‘RFCs’. 這個過程對所有貢獻者公開, 讓語言的各個方面都保持可變狀態.

一旦 RFC 完成 , Larry Wall 評估并歸類了每個請求(收到了 361個請求)。 他開始寫了幾個”啟示”,他本來打算再寫一部 Programming Perl 的。

有 3 個主要的場所來學習 Perl 6: #perl6 IRC 頻道郵件列表Git 源代碼倉庫

目標


解決 Perl 5 的歷史毒瘤。過去那些年, Perl 6 在方向上經歷了幾個抉擇。早期受 Python 和 Ruby 的影響引入了一些概念, 但是因為 Pugs 解釋器是用 Haskell 寫的, Perl 6 設計團隊吸收了很多函數式編程的思想。

吉祥物


Perl 6 的吉祥物是一只蝴蝶,代表著 Perl 6 的蟲子。 蝴蝶的翅膀上嵌入了類似字符 “P6”的螺旋形設計,那是 Perl 6 的昵稱。

實現


到 2015 年, 只有 Rakudo Perl 還在活躍開發中。沒有實現會成為 Perl 6 的官方實現;當然啦,"只要通過官方測試套件的實現就是 Perl 6”。

Rakudo Perl 是在幾種虛擬機之上的 Perl 6 實現, 例如 MoarVM, Java 虛擬機,和 Javascript。 MoarVM 是為漸進類型編程語言設計的虛擬機,主要用于 Perl 6. 在 Perl 6 和 虛擬機之間有一個叫做 Not Quite Perl 或 NQP 的層, 它實現了解析 Perl 6 的 Perl 6 規則, 還有一個抽象語法樹 和 特定后端的代碼生成. Perl 6 的大部分都是用 Perl 6 自身寫成的, 或用它的子集 NQP 寫成。 Rakudo 既不是一個完全自舉的實現, 此時也不會有具體的計劃讓 Rakudo 成為一個 bootstrapping 編譯器。

歷史上的實現


那時候最早出現的 Perl 6 實現是 Pugs,它是用 Haskell 寫的。Pugs 過去是完成度最高的 Perl 6 實現,但是自從 2007 年年中,它就基本上蟄伏了。 到 2014 年 11 月, Pugs 就不再被積極維護了。

2007 年,v6-MiniPerl6 (“mp6”) 和它的重新實現, v6-KindaPerl6 ("kp6”) 被寫出來, 它使用Perl 5來驅動 Perl-6.0.0 STD。 STD 對于 Perl 6 是完整的 grammar, 并用 Perl 6 寫成。 理論上, 任何能解析 STD并生成可執行代碼的東西對于Perl 6 都是合適的驅動系統。 kp6 目前由 mp6 編譯,并且能工作在多個后端上。 mp6 和 kp6 不是完全的 Perl 6 實現, 它們被設計為實現用于驅動一個完整 Perl 6 編譯器的最少功能集合。

Yapsi 是一個 Perl 6 編譯器, 并且運行時是用 Perl 6 自身寫的。 作為結果, 為了運行,它要求先存在一個 Perl 6 解釋器, 例如 Rakudo Star 的發布之一。

模塊系統


Perl 6 計劃書要求模塊由名字,版本和作者標識。 只加載指定版本的模塊, 或兩個同名但版本或作者不同的版本。

相對 Perl 5來說主要的變化


Perl 5 和 Perl 6從根本上就是不同的兩種語言,主要的變化是使新手和專家更容易掌握該語言, 使容易的事情更容易, 困難的事情變得可能。

計劃書


Perl 6 是從計劃書開始的。這意味著 Perl 6可以在需要時被重新實現。

類型系統


在 Perl 6 中, Perl 5 的動態類型系統通過額外的靜態類型被增強了。 例如:

 my Int $i = 0;
 my Rat $r = 3.142;
 my Str $s = "Hello, world";

然而, 靜態類型仍舊是可選的, 所以編程人員可以不用任何顯式的類型:

 my $i = "25" + 10; # $i is 35

Perl 6 提供了混合類型系統, 靠這點兒, 程序員可以選擇使用靜態類型、使用動態類型、或混合使用這兩種類型。

形式的子例程參數列表


Perl 5 定義子例程時一點兒也沒使用形式參數。傳遞進來的子例程參數被存儲到數組 @_ 中,數組中的每個參數都是對應形式參數的別名,如果 @_ 中的元素被修改了, 變化會被反映到原數據上。

Perl 6 引入了真正的形式參數。 在Perl 6 中, 子例程像這樣定義:

sub do_something(Str $thing, Int $other) {
     . . .
 }

就像在 Perl 5中那樣, 形參(i.e. 參數列表中的變量)是實參(傳遞進來的值)的別名, 但是默認的, 這種別名是常量的,所以不能被修改。 它們可以被顯式地聲明為可讀-可寫,作為原值或原值的副本, 使用 is rwis copy 指令。

參數傳遞模式


Perl 6 提供 3 種基本的參數傳遞模式

  • 位置的

  • 具名的

  • Slurpy

    ?

位置參數是大多數編程語言中都使用的典型的有序參數列表。所有的參數也可以一種無序的方式, 通過使用它們的名字傳遞。 具名參數只能通過指定它們的名字被傳遞(即,它從來不會捕獲位置參數),具名參數用一個前置的 : 標示。吞噬參數(由參數名字前面的 * 標示) 是 Perl 6 中創建可變函數的工具。吞噬散列會捕獲剩下的按名字傳遞的參數, 而吞噬數組會捕獲剩下的按位置傳遞的參數。

下面是一個使用 3 種參數傳遞模式的例子:

 sub somefunction($a, $b, :$c, :$d, *@e) {
   . . .
 }

 somefunction(1, 2, :d(3), 4, 5, 6); # $a=1, $b=2, $d=3, @e=(4,5,6)
 somefunction(:b<2>, :a<1>);         # $a="1", $b="2"

諸如上面使用的位置參數,總是必須的, 除非它后面跟著一個 ? 以標示它們是可選的。 具名參數默認是可選的, 但是能夠通過在變量名的后面添加一個 ! 把它標記為必選參數。吞噬參數總是可選的。

Blocks 和 閉包


參數能被傳遞給任意的塊兒, 這些塊兒充當著閉包。 即, 舉個例子, forwhile 的循環變量可以被命名。在下面的例子中, 遍歷一個列表, 每次 3 個元素,$a,$b,$c 作為變量被傳遞給循環的塊兒。

for @list -> $a, $b, $c {
     . . .
 }

這通常被引用為 pointy subpointy block, 箭頭表現的幾乎和 sub 關鍵字相同,它引入了一個匿名的閉包(或者用 Perl 5 的專業術語來說,引入了一個匿名子例程)。

符號不變性


在 Perl 5 中,變量名字前面的標點符號字符, 根據變量的使用方式而變化:

# Perl 5 code
my @array = ('a', 'b', 'c');
my $element = $array[1];    # $element equals 'b',
my @extract = @array[1, 2]; # @extract equals ('b', 'c')
my $element = @array[1];    # 'b' comes with a warning (5.10 option)

在 Perl 6 中, 符號是不變的,這意味著,它不會根據它是一個數組還是一個數組元素而改變變量名前的符號:

# Perl 6 code
my @array = 'a', 'b', 'c';
my $element = @array[1];    # $element equals 'b'
my @extract = @array[1];    # @extract equals ('b')
my @extract = @array[1, 2]; # @extract equals ('b', 'c')

Perl 5 中的變體是受英語中的數字契約和其它很多自然語言影響的:

"This apple."                    # $a        CORRECT
"These apples."                  # @a        CORRECT
"This third apple."              # $a[3]     CORRECT
"These third apple."             # @a[3]     WRONG

然而, 當有了引用之后, 這種概念的映射就不起作用了, 因為它們可能引用的數據結構, 盡管它們是標量。 因此, 在單個 term 中,處理嵌套的數據結構可能既需要單數, 也需要復數形式的表達式:

# Perl 5 code: retrieve a list from the leaf of a hash containing hashes that contain arrays
my @trans_verbs = @{ $dictionary{ 'verb' }{ 'transitive' } };

相比來說, 在 Perl 6 中:

# Perl 6 code: retrieve a list from the leaf of a hash containing hashes that contain arrays
my @trans_verbs = %dictionary{ 'verb' }{ 'transitive' }.list;

面向對象編程


Perl 5 通過 blessing 機制支持面向對象的編程。 任何引用都可以被 blessed 到特定類的對象中。

例如, 一個封裝了笛卡爾點的類可以這樣寫:

class Point is rw {
   has $.x;
   has $.y;
 }

 my $point = Point.new( x => 1.2, y => -3.7 );

 # Now change x (note method "x" used as lvalue):
 $point.x = 2;
 say "Point is at X location: ", $point.x;

在 Perl 6 的專業術語中, $.x 叫做"屬性”。有些語言叫這個 字段或成員。用于訪問屬性的方法叫做 “存取器”。自動存取器是自動創建的方法, 就像上面的 x 方法一樣。 這些存取器函數返回屬性的值。 當使用 is rw 修飾符聲明一個類或單獨的屬性時,能傳遞一個新值給自動存取器來設置給屬性, 或者它能作為一個左值被直接賦值(就像例子中的一樣)。 自動存取器能被用戶自定義的方法替換,程序員應該想要接口更豐富的屬性。 屬性只能在類的定義中被直接訪問。 所有其它的訪問方式必須經過存取器方法。

Roles


Perl 6 中的 roles 具有 Java 中的接口、 Ruby 中的 mixins 和 Smalltalk 中 traits 的功能。 這些都很像類, 但是它們是完全抽象的。 這些 Roles 在和類一塊兒使用時, 用于執行混合而不是添加到類的繼承鏈中。 Roles 定義名義上的類型, 它們為行為和狀態集合提供了語義上的名字。role 和 類的最重要的區別就是, 類是可繼承的, 而 roles 不是。

本質上, role 是一束不使用繼承,添加到類中的方法和屬性。 role 甚至可以被添加到單獨的對象中。這時, Perl 6 會創建一個匿名的子類, 并把 role 添加到子類中, 并把對象的類改為匿名的子類。

例如, 狗(Dog)是一種哺乳動物(Mammal)。Dogs 從 Mammals 那兒繼承某些特征, 諸如乳腺和脊柱(通過 Mammals的父類, 脊椎動物 Vetebrate)。 Dogs 可能有幾個不同類型的行為; 例如, 一個 Dog 可能是 Pet, Stray, 或 導盲犬。 然而, 這些只是一組可以添加給一個 Dog 的額外的行為; Cat 能等同于 一個 Pet 或 Stray, 舉個例子, 因此, Dog 和 Mammal 是類, 而 Pet, Stray, 和 導盲犬是 roles。

class Mammal is Vertebrate {
     . . .
 }
 class Dog is Mammal {
     . . .
 }
 role Pet {
     . . .
 }
 role Stray {
     . . .
 }
 role Guide {
     . . .
 }

通過使用 does 關鍵字把 Roles 添加到類中, 與繼承的關鍵字 is 相對。關鍵字反應了兩種功能的不同意義: role 混合給類一個行為的 role, 但是不繼承表明它和 role 是同樣的東西:


 class GuideDog is Dog does Guide {
     . . .
 }   # Subclass composes role

 my $dog = new Dog;
 $dog does Guide;                         # Individual object composes role

盡管 role 和 類不同, 但它們都是類型, 所以 role 能出現在變量聲明的地方, 這個地方通常會放置一個類。例如, 一個用于 Human 的 導盲犬 role 可以包含 Guide 類型的屬性; 這個屬性可以包含 一個 Guide Dog, 一個 Guide Horse, 一個 Guide Human, 或者甚至一個 Guide Machine。

 class Human {
     has Dog $dog;                        # Can contain any kind of Dog, whether it does the
     ...                                  # Guide role or not
 }
 role Blind {
     has Guide $guide;                    # Can contain any object that does the Guide role,
     ...                                  # whether it is a Dog or something else
 }

正則表達式


主要文章 : Perl 6 rules

鏈式比較


Perl 6 允許鏈式比較, 即諸如下面這樣的比較序列是被允許的:

if 20 <= $temperature <= 25 {
     say "Room temperature is between 20 and 25!"
 }

惰性求值


Perl 6 使用列表的惰性列表求值技術,惰性求值已經是諸如 Haskell 等函數式編程語言的功能亮點了:

@integers = 0..Inf; # integers from 0 to infinity

上面的代碼在嘗試把一個無限列表賦值給數組 @integers 時既不會崩潰, 也不會在嘗試搜索某個限定的點以擴展列表時無限掛起。

這簡化了很多普通的 Perl 6 任務, 包括輸入/輸出操作, 列表轉換 和參數傳遞。

Gather


跟惰性求值相關的就是使用 gathertake 構建惰性列表, 這跟 Python 中的生成器有點像。

my $squares = gather for 0..Inf {
     take $_ * $_;
 };

$squares 會是一個平方數字的無限列表, 但是 gather 的惰性求值確保了值只在被訪問時才被計算。

Junctions


Perl 6 引入了 junctions 概念:由其它值混合而成的值。在 Perl 6 早期的設計中, 它們被叫做 “疊加”, 與量子物理中的量子疊加概念類似 — 波形能同時占據幾種狀態直到觀測使它們坍縮。

在它們的最簡單的形式中, 通過使用 junctive 操作符組合一組值來創建 junctions :

my $any_even_digit = 0 | 2 | 4 | 6 | 8; # any(0, 2, 4, 6, 8)
my $all_odd_digits = 1 & 3 & 5 & 7 & 9; # all(1, 3, 5, 7, 9)

| 表明一個要么等于它左邊的參數, 要么等于它右邊的參數的值。 & 表明一個即等于它左邊的參數又等于它右邊參數的值。這些值能像普通值那樣用在任何代碼中。 對 Junction 執行的操作會同等地作用在 junction 的每一個成員身上, 并根據 junctive 操作符進行組合。 所以, ("apple"|"banana") ~ "s" 會產生 "apples"|"bananas". 在比較操作中, junction 會返回單個 True 或 False。 any junction 會返回 true, 如果junction的任一元素的比較為真的話。 all junctions 會返回真, 如果 junction 的所有元素的比較為真的話。

Junctions 通過引入用于約束 junctions 類型的一般編程風格,也能用于極大地增強類型系統:

sub get_tint(RGB_Color | CMYK_Color $color, Num $opacity) {
     . . .
 }
 sub store_record(Record & Storable $rec) {
     . . .
 }

Autothreading


Junctions 是無序的, 1|2|33|2|1 代表相同的值。沒有順序意味著 Perl 6 編譯器能并行地選擇計算 junctive 表達式。例如, 代碼:

 if $string ~~ all(@list_of_regexes) {
     . . .
 }

將會對編譯器表明, 字符串和一組正則表達式的所有匹配能被并發地被執行, 可能在單獨的線程中。這個功能被授予 autothreading. autothreading 的并行化尚未在任一編譯器中實現。


Perl 6 會利用 Lisp-like 的宏概念。 這種宏的強大源自這樣的事實, 它像高級數據結構一樣操作程序, 而非像普通的文本那樣。

Perl 6 中宏的定義很像子例程或方法定義, 能作用在未解析的字符串上、AST 那樣預解析的代碼、或兩者的組合。 一個宏定義就像這樣:

 macro hello($what) {
   quasi { say "Hello { {{{$what}}} }" };
 }

Examples


Hello World

say 'Hello, world';

快速排序


一個使用函數式編程范式的有效實現能使用 Perl 6 簡潔地寫出:

 # Empty list sorts to the empty list
 multi quicksort([]) { () }

 # Otherwise, extract first item as pivot...
 multi quicksort([$pivot, *@rest]) {
     # Partition.
     my @before = @rest.grep(* < $pivot);
     my @after  = @rest.grep(* >= $pivot);

     # Sort the partitions.
     (quicksort(@before), $pivot, quicksort(@after))
 }

漢諾塔


在計算機科學上漢諾塔通常用于介紹遞歸編程。這個實現使用 Perl 6 的多重分派機制和參數限制:

multi sub hanoi(0, $, $, $) { }                         # No disk, so do not do anything
multi sub hanoi($n, $a = 'A', $b = 'B', $c = 'C') {     # Start with $n disks and three pegs A, B, C
     hanoi $n - 1, $a, $c, $b;                          # firstly move top $n - 1 disks from A to B
     say "Move disk $n from peg $a to peg $c";          # then move last disk from A to C
     hanoi $n - 1, $b, $a, $c;                          # lastly move $n - 1 disks from B to C
 }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容