以下はあくまで暫定的要約であり、不適切な訳語・用語や誤解的間違いがある可能性が高いことを承認の上で、非公式な参考資料程度なものと受け取り利用されるべきものである。
::= > 定義
letters > 終端記号(そのままタイプされる文字(列)や記号)
| > いずれか選択(最後の余分な|は、その定義自体が省略可能であることを示す)
{ } > 省略可能な繰り返し
[ ] > 省略可能
··· > 前後の要素から推定可能な集合の任意の要素
( ) > グループ
コメント > コメント("~"は任意の数字、"&"は任意の文字列。 "任意匿名"は処理系が使用する、 ユーザからは見えない内部識別子)
ScalaはUnicodeで記述されるものとする
改行区切り ::= 終了可能字句の直後でかつ開始可能字句の直前の、複文可能領域に在る改行 該当しない改行は改行区切りとは見なされない
終了可能字句 ::= リテラル | 識別子 | XMLリテラル開始部 | this | null | true | false | return | type | _ | ) | ] | }
開始不可能字句 ::= case | catch | else | extends | finally | forSome | match | requires | with | yield | , | . | ; | : | _ | = | => | <- | <: | <% | >: | # | [ | ) | ] | }
開始可能字句 ::= case (class | object) | 開始不可能字句を除く字句
複文不可能領域 ::= (と呼応する)で直接挟まれた領域 | [と呼応する]で直接挟まれた領域 | caseと呼応する=>で直接挟まれた領域 | XMLモード内
複文可能領域 ::= グローバル領域 | XMLモードと文字列内を除く、{と呼応する}で直接挟まれた領域
複文区切り ::= ; | 改行区切り {改行区切り} 後者は複数の連続する改行区切りは単一の複文区切りと見なされる
一般に、次の箇所には複数の改行が許される: ifまたはwhileの条件式と次の式の間、 forの逐次子リストと次の式の間、 typeとその型定義または型宣言の間
また、次の箇所には改行が1つだけ許される: 文または式の必須項としての{の前、 間置演算子と直後の開始可能字句の間、型引数節を除く引数節の前、 注釈の後ろ
コメント ::= /* 文字の連なり */ ネスト可 | // 行末までの文字の連なり 空白とコメントは字句区切りとして機能し得る
Unicodeエスケープ ::= \ {\ \} u {u} 16進数字 16進数字 16進数字 16進数字 先頭は、奇数個の連続するバックスラッシュはバックスラッシュ1つに等しい、ということ
Unicodeエスケープは解析時にそれの指す字に変換される
16進数字 ::= 0 | ··· | 9 | A | ··· | F | a | ··· | f
空白文字 ::= \u0020 半角空白 | \u0009 タブ | \u000d CR | \u000a LF
括弧 ::= ( | ) 丸括弧 | [ | ] 角括弧 | { | } 波括弧
分離子 ::= ` \u0060、バッククォート | ' \u0027, シングルクォート | " | . | ; | ,
予約語 ::= abstract | case | catch | class | def | do | else | extends | false | final | finally | for | forSome | if | implicit | import | lazy | match | new | null | object | override | package | private | protected | requires かつて使われていたもの | return | sealed | super | this | throw | trait | try | true | type | val | var | while | with | yield | _ | : | = | => | <- | <: | <% | >: | # | @
=>で代替され得る\u21d2も予約語と見なされる
大文字 ::= A | ··· | Z | $ | _ | Unicode category Lu
小文字 ::= a | ··· | z | Unicode category Ll
文字 ::= 大文字 | 小文字 | Unicode category Lo 大小の区別の無い文字 | Unicode category Lt タイトルケース文字、2文字で1字な特殊字 | Unicode category Nl 数字以外の数を示す文字
Unicodeの方は英字以外の文字も含む、つまり、日本語字もScala文字の一部である
数字 ::= 0 | ゼロ以外の数字
ゼロ以外の数字 ::= 1 | ··· | 9
記号字 ::= 以上を除く印字可能なアスキー記号 \u0020-\u007fのうちの、$と_(これらは記号字では無く大文字)を除く印字可能なもの
記号 ::= 記号字 {記号字}
変数識別子 ::= 小文字 識別子文字列 パターンの束縛用変数はこれのみ可
一般識別子 ::= 大文字 識別子文字列 | 変数識別子 | 記号
識別子 ::= 一般識別子 | ` 文字列 ` 後者は予約語を識別子化する場合を想定
識別子文字列 ::= {文字 | 数字} [_ 記号] 文字は_も含むことに注意 最後の部分は、最後の_の後ろには記号も可、ということ $はコンパイラによる自動生成識別子に使われるのでユーザは使わないこと
a+bとa + bの同義であることを保証するために、Scalaでは逆に空白の有無は必ずしも区切りを意味しない
すなわち、a+bとa + bはいずれもa.+(b)として解析される
文字数字系に記号を続ける識別子(逆は不可)で_が間に必要なのも同じ理由; 直接続けた場合、文字部と記号部は別の識別子として解析される
パス ::= 終端識別子 | [識別子 .] this thisはそれを内包するテンプレートか複合型を指す
終端識別子 ::= 識別子 | パス . 識別子 | [識別子 .] super [親クラス明示子] . 識別子
親クラス明示子 ::= [ 識別子 ]
thisとsuperで、"識別子."の省略はその識別子が指すクラス定義内でのみ可
リテラル ::= [- | +] 整数リテラル | [- | +] 浮動小数点数リテラル | ブーリアンリテラル | 文字リテラル | 文字列リテラル | シンボルリテラル | null scala.Null型 | ( ) 空の丸括弧。scala.Unit型(Javaのvoidに相当)
-に前置される数字リテラルはそうでないものとは別の値である
整数リテラル ::= (10進リテラル | 16進リテラル | 8進リテラル) [L | l] 普通Intで指定時のみLong。 要請型がByte、Short、Charで値が範囲内ならその型になる(各々の範囲はJavaに準じる)
10進リテラル ::= 0 | ゼロ以外の数字 {数字}
16進リテラル ::= 0 (X | x) 16進数字 {16進数字}
8進リテラル ::= 0 8進数字 {8進数字}
8進数字 ::= 0 | ··· | 7
浮動小数点数リテラル ::= 数字 {数字} . {数字} [指数部] [浮動型] | . 数字 {数字} [指数部] [浮動型] | 数字 {数字} 指数部 [浮動型] | 数字 {数字} [指数部] 浮動型 普通Doubleで指定時のみFloat(各々の範囲はJavaに準じる) 浮動小数点リテラルと続く文字で始まる字句の間には必ず空白が必要
指数部 ::= (E | e) [+ | -] 数字 {数字}
浮動型 ::= F | f | D | d
数値型はビューにより演算過程で変換され得る; Byte、Short、Charは必ずInt化、それ以外はより値範囲が大きい方の型へ変換される
その理由により、"1 / 2 == 0"(共にInt>Int)、"1.0 / 2 == 0.5"(DoubleとInt>Double)となることに注意
数値型が必ず持つメソッド(演算子の意味はJavaに準じる): "=="、"!="、"<"、">"、"<="、">="、 "+"、"-"、"*"、"/"、"%"、 前置の"+"、前置の"-"、 "toByte"、"toShort"、"toChar"、"toInt"、"toFloat"、"toDouble"
Byte、Short、Char、Int、Longが持つ演算子(意味はJavaに準じる): "&"、"|"、"^"、 "~"、 "<<"、">>"、">>>"
Javaの"++"と"--"に等しい演算子は無い; 前者は構造型の結合、 後者も構造型の差分を意味する間置演算子としてもっぱら使われていることに注意
ブーリアンリテラル ::= true | false Boolean型
Booleanが持つ演算子(意味はJavaに準じる): "&&"、"||"、 "&"、"|"、"^"、 "=="、"!="、 "!"
文字リテラル ::= ' 印字可能文字 印字可能文字を結果するUnicodeエスケープを含む ' | ' エスケープ文字 ' Char型
エスケープ文字 ::= \b | \t | \n | \f | \r | \" | \' | \\ | \ 000から377までの8進数字 各々の意味はJavaに準じる 単独の\は不可
文字列リテラル ::= " {文字列要素} " | " " " 複数行文字 " " " java.lang.String型
文字列要素 ::= ダブルクォートを除く印字可能文字 | エスケープ文字
複数行文字 ::= {["] ["] ダブルクォートを除く文字} 改行やタブ等の非印字可能文字自体も可。エスケープ文字は変換されない
Stringが持つ演算子(意味はJavaに準じる); "+"
ScalaではJavaとは異なり、"=="は(すべての型で必ず)値比較で、インスタンス比較には"eq"を用いる
Anyな比較は"equals"を使う('val s = "abc"'で"1 == s"は警告、"1 eq s"はエラー)
シンボルリテラル ::= ' 一般識別子 scala.Symbol("一般識別子").internに等しい; すなわち、同じ一般識別子のシンボルリテラル同士は、 必ずその一般識別子を内容とする文字列の同一インスタンスを指す
XMLリテラル開始部 ::= (空白文字 | ( | {) < (XML名前開始 | ! | ?)
以上が満たされる場合、XML解析が完了するまで字句解析はXMLモードで為される
パターン ::= パターン1 {| パターン1} |による列挙はいずれかにマッチ; この時、束縛は_へのみ可
変数識別子を引数として、マッチ対象の該当部分にそれを束縛する
この変数識別子は宣言不要
scala.Seq型へのマッチで変数識別子の方が少ない場合、最後の変数識別子に残りすべてが束縛される
上は、"最後の変数識別子@ _*"でも可
パターン1 ::= 変数識別子 : 型パターン 型でマッチ | _ : 型パターン 型のみマッチ | パターン2
型パターン ::= 型 scala.Nothingとscala.Nullは不可
変数識別子な型名も可能だが、デフォルトで定義されているJava互換型名及びunitはそれには属さない
パターン2 ::= 変数識別子 [@ パターン3] パターン3へのマッチ全体を変数識別子に束縛 | パターン3
パターン3 ::= 単純パターン | 単純パターン {識別子 [改行区切り] 単純パターン} 後者は"識別子(前の単純パターン, 後の単純パターン)"パターンに等しい
単純パターン ::= _ 何でも匿名束縛; 要するに不要要素へのマッチ
| 変数識別子
| リテラル このリテラルに等しいものにマッチ
| 終端識別子 この終端識別子の値に等しいものにマッチ; 変数識別子形態で無いか、`で囲むこと
| 終端識別子 ( [パターンリスト [,]] ) ケースクラスまたはケースシングルトンマッチ
| 終端識別子 ( [パターンリスト ,] _ * ) 上で可変長実引数の不要な残りを無視
| ( [パターンリスト [,]] ) タプルマッチ(scala.Seq型では無いことに注意)
| XMLパターン XMLリテラルマッチ
XMLの属性はパターン指定できないことに注意; scala.xml.Nodeクラスのattributeメソッドでガードから指定可
例:'case link @ <a>{ _* }</a> if link.attribute("href").isEmpty => "href missing"'
パターンリスト ::= パターン [, パターンリスト] | _ * 最後のはscala.Seq型の不要な残りを無視
正規表現マッチは既に廃止; ユーザの希望が強ければ改良の上再実装もあり得るが、 作者サイドとしては以上のパターンマッチとXPathで十分ではないか、とのこと
正規表現パッケージ自体は一応在る(scala.util.matching); Javaのそれへのラッパー
ScalaのXML式及びXMLパターンは、 Scala式及びScalaパターンリスト埋め込み機能を除き、 W3Cのそれのサブセットである
以下に欠けるものはscala.xmlパッケージ(特にそのNodeクラス)で補われることが想定されている
XML式 ::= XML内容 {要素}
XML内容 ::= 要素 | CDATAセクション | 処理命令 | XMLコメント CDATAセクション、処理命令の指示は無視
要素 ::= 空要素タグ | 開始タグ 内容 終了タグ
空要素タグ ::= < 名前 {XML空白文字 属性} [XML空白文字] />
開始タグ ::= < 名前 {XML空白文字 属性} [XML空白文字] >
終了タグ ::= </ 名前 [XML空白文字] >
詳細はW3CのXML定義に準じる
内容 ::= [文字データ] {内容1 [文字データ]}
文字データ ::= { 文字実体 } ]]>と単一の{は除く
内容1 ::= XML内容 | 参照 | Scala式 参照の指示は無視
名前 ::= XML名前開始 {名前使用可能文字}
XML名前開始 ::= _ | :を除くXML基底文字 | XML象形文字
属性 ::= 名前 = 属性値
属性値 ::= " {"を除くXML可能文字1 | 文字参照} " | ' {'を除くXML可能文字1 | 文字参照} ' | Scala式
XML可能文字1 ::= <と&を除くXML可能文字
Scala式 ::= { 式 } Scalaでの式 XML文字としての{は{{で表す
基底文字, XML可能文字, XMLコメント, 結合文字, 象形文字, 名前使用可能文字, XML空白文字, 参照, 文字参照 ::= W3CのXML定義に準じる
XMLパターン ::= 要素パターン
要素パターン ::= 空要素タグパターン | 開始タグパターン 内容パターン 終了タグパターン
空要素タグパターン ::= < 名前 [XML空白文字] />
開始タグパターン ::= < 名前 [XML空白文字] >
終了タグパターン ::= </ 名前 [XML空白文字]>
内容パターン ::= [文字データ] {内容パターン1 [文字データ]}
内容パターン1 ::= 要素パターン | 参照 | CDATAセクション | 処理命令 | XMLコメント | Scalaパターンリスト
Scalaパターンリスト ::= { パターンリスト } Scalaでのパターンのリスト
名前空間は型名空間と一般名空間及び暗黙名空間の3つ
型名空間のスコープ単位は角括弧内及びその角括弧を文法部分とする式内; 静的なので間接的なものも含め循環参照は不可
一般名空間のスコープ単位は波括弧内及びその波括弧を文法部分とする式内
暗黙名空間のスコープ単位は一般名空間のそれに従い、 さらにその名前の属する型と関連するすべてのシングルトンが持つ暗黙名空間で包括する
暗黙名空間はまたビュー機能をも提供する; すなわち、必要に応じて"A => B"または"( => A) => B"である関数を暗黙名空間から探し出して型変換する
同一スコープ内の名前束縛の強さは、明示定義 > 明示インポート > ワイルドカードインポート > パッケージの順
あるスコープにおいて、そのスコープ外側のものはそのスコープ内のものより名前束縛性は弱い
同一スコープ内での前方参照は可
"文"はその副作用のみを目的とするものであり、従ってその"文"自体の結果は返さない; すなわち、"式"要素として使用不可である
"文"は宣言(val、var、def、type)、 定義(宣言+class、object、traitとcaseリスト)、 インポート(import)、 パッケージ(package)で、 他はすべてScalaでは式である
宣言 ::= val 定数宣言 | var 変数宣言 | def 関数宣言 | type {改行区切り} 型宣言 宣言はクラスか細別の中でのみ可; 抽象メンバを示す
Scalaでは、本質的には変数は動的参照定数で関数は遅延評価定数(定義式自体への参照)である
パターン変数定義 ::= [lazy] val 定義パターン | var 変数定義
lazy指定は定数の定義式を遅延評価とする; 参照毎に定義式が評価されるdefと異なり、評価は初めての参照時のみ
定義 ::= パターン変数定義 | def 関数定義 | type {改行区切り} 型定義 | テンプレート定義
定義はシングルトン、クラスまたはブロックにローカルでのみ可
識別子リスト ::= 識別子 {, 識別子}
定義パターン ::= 定義パターン2 {, 定義パターン2} [: 型] = 式
定義パターン2 ::= 識別子 | パターン2
"val a, b = (1,2)"は"val a = (1, 2); val b = (1, 2)"に等しい
一方、"val (a, b) = (1, 2)"ではaは1、bは2となる
定数宣言 ::= 識別子リスト : 型
変数宣言 ::= 識別子リスト : 型
変数定義 ::= 定義パターン | 識別子リスト : 型 = _ 後者はテンプレート内のみ可; 値をデフォルト値(0、false、Unit、nullのうちの適合するもの)とする
型宣言 ::= 識別子 [型引数節] 型限定 抽象型
型限定 ::= [>: 型] [<: 型] 前者は最低型限定(無しはscala.Nothing)、後者は最大型限定(無しはscala.Any)
型定義 ::= 識別子 [型引数節] = 型 この識別子は元の型の完全な代替として使用可
型引数節 ::= [ 変形型引数 {, 変形型引数} ]
変形型引数 ::= [+ | -] 型引数 "class B extends A"の時、"class S[T]"で、"S[B]"と"S[A]"とは別型と見なされる。 この時、"class S[+T]"なら"S[B]"は"S[A]"の子型となる(すなわち、"S[+T]"は総称型である)。 また、"S[-T]"なら"S[B]"は"S[A]"の祖型となる("型"の関数型説明を参照のこと) 静的に決定不可能な型引数の変形指定は不可; その動的さがprivateにのみ係る場合は可(動的変換を配慮する必要が無いため) ">:型引数"も結局変換は行われないのでこの制限回避に利用できる
型引数 ::= (識別子 | _) [型引数節] [>: 型] [<: 型] [<% 型] 最後のものは"(implicit 任意匿名: 識別子 => 型)"に等しい
関数宣言 ::= 関数シグネチャ [: 型] 型無し(Unit型が仮定される)は手続き宣言
関数はscala.Function~(~は引数の数)型(関数実体はそのapplyメソッドになる)。 Function1はまたFunctionの別名も持つ
関数定義 ::= 関数シグネチャ [: 型] = 式 再帰関数では型指定は必要 | 関数シグネチャ [改行区切り] { ブロック } 手続き定義; "関数シグネチャ: Unit = {ブロック}"の省略形 | this 引数節 引数節リスト (= コンストラクタ式 | [改行区切り] コンストラクタブロック) 追加コンストラクタ
定義式の末尾で再帰する関数は新たなスタックフレームを必要としない
オーバーライドする返値型無しの再帰関数は、 再帰途中はオーバーライドされる再帰関数の返値型が適用され、 再帰完了後は本体の値の返値型となる
関数シグネチャ ::= 識別子 [関数型引数節] 引数節リスト 型引数と引数の同名は不可
関数型引数節 ::= [ 型引数 {, 型引数} ] その関数にローカルな型変数名宣言
引数節リスト ::= {引数節} [[改行区切り] ( implicit 引数リスト )]
"def f(args1)(args2) = e"は"def f = (args1) => (args2) => e"に等しい(カリー化)
カリー化を使えば、引数1つの関数型引数に、最初の引数だけ実引数を与えた引数2つの関数を渡せる
implicitは引数リストを暗黙名空間にコピーする; 実引数が省略された場合、その暗黙名空間の適切なもので補われる
引数節 ::= [改行区切り] ( [引数リスト] )
引数リスト ::= 引数 {, 引数}
引数 ::= {注釈} 識別子 [: 引数型]
引数型 ::= 型 事前評価渡し | => 型 遅延評価渡し; 直前に:を持つことで無名関数と区別される | 型 * 可変長引数(scala.Seq[型]); 最後の引数であること
式 ::= (名前束縛リスト | 識別子) => 式 識別子を引数とする無名関数(scala.Function~(~は引数の数)) | 式1
式の評価順は、型推定 > リテラル > null > 識別子 > thisとsuper > 関数呼び出し > メソッドを返す無名関数 > 型引数 > タプル > インスタンス化 > ブロック式 > 前置演算子 > 二項演算子 > 後置演算子 > 代入演算子の演算部分 > 帰属型 > 帰属注釈 > 代入 > if式 > while式 > do式 > for式 > return式 > throw式 > try式
名前束縛リスト ::= ( 名前束縛 {, 名前束縛} )
名前束縛 ::= 識別子 [: 型]
式1 ::= if ( 式 ) {改行区切り} 式 [[複文区切り] else 式]
最初の式はBooleanと互換であること
残る2つの式のより祖型な方が結果の型になる
else部省略時の結果は必ずUnit型
| while ( 式 ) {改行区切り} 式
最初の式はBooleanと互換であること
結果は必ずUnit型
| do 式 [複文区切り] while ( 式 )
最後の式はBooleanと互換であること
結果は必ずUnit型
| for (( 逐次子リスト ) | { 逐次子リスト }) {改行区切り} [yield] 式
yield有りなら結果は逐次されるものと同じ型(mapメソッド適用に等しい)、 無しならUnit型(foreachメソッド適用に等しい)
"{逐次子リスト}"ならそのリスト要素間を改行だけでも区切れる
"for (x <- e1) yield e2"は"e1.map(x => e2)"に等しい
"for (x <- e1; y <- e2) yield e3"は"e1.flatmap(x => for (y <- e2) yield e3)"に等しい
"for (x <- e1) e2"は"e1.foreach(x => e2)"に等しい
"for (x <- e1; y <- e2) e3"は"e1.foreach(x => for (y <- e2) e3)"に等しい
| return [式] 関数またはメソッド定義の最後の式を返値とする場合はreturn指定は不要
名前を持つ関数またはメソッド内部でのみ使用可
return式自体の型はscala.Nothing
| throw 式
式はjava.lang.Throwableに適合するものであること
throw式自体の型はscala.Nothing
| try { ブロック } [catch { ケース節リスト }] [finally 式]
tryとcatch式の型はifとelse式のそれと同様
finally式の型はUnitに適合するものであること
| [単純式 .] 識別子 = 式 代入式。式自体の結果はscala.Unit
| 単純式1 引数式 = 式 "単純式1.update(引数式, 式)"に等しい
| 後置式 無引数関数呼び出しも含む 副作用のある無引数関数呼び出しは一般に普通の関数呼び出しの形の方が好ましい
| 後置式 帰属 後置式の参照が曖昧な時に特定させるためのヒントを与える
| 後置式 match { ケース節リスト } 後置式に対しケースマッチを行う
ケースのパターンは後置式に可能な型でなければならない
後置式の該当するものがケースパターン内変数識別子に束縛される
match式の型はすべてのケース節のブロックの型のうちの最も祖型なもの
マッチが皆無ならscala.MatchError例外を投げる
後置式 ::= 間置式 [識別子 [改行区切り]] "間置式.識別子"に等しい この識別子は無引数メソッドである
後置識別子の優先順位は常に間置演算子より低い
間置式 ::= 前置式 | 間置式 識別子 [改行区切り] 間置式 "前の間置式.識別子(後の間置式)"に等しい(右から評価の場合間置式の前後は逆になる)
この識別子の優先順位は最初の字で定まる; 低い順に、 文字 < | < ^ < & < < > < = ! < : < + - < * / % < その他の記号 この識別子が:で終わるなら右から、以外は左から評価される; 右から評価される識別子opから成る間置式"e1 op e2"は"e2.op(e1)"に等しい 同一優先順位の間置演算子の連続から成る式部分では、すべての間置演算子の方向は同じでなければならない
=で終わる代入演算子は、特にメソッド定義されていなければ単純な演算と代入とに解析される; 代入が不可能な場合は識別子と見なされる(普通未定義エラー)
前置式 ::= [- | + | ~ | !] 単純式 この4つの記号だけが前置可能
単純式 ::= new (クラステンプレート ただのコンストラクタ呼び出し以外はそれらを親とする無名クラス | テンプレート 無名クラス) | ブロック式 | 単純式1 [_] 単純式1(メソッドまたは遅延評価引数)を結果とする無名関数。下の_の説明を見よ
単純式1 ::= リテラル
| パス
| 単純式 . 識別子
| _ それをユーザからは見えない新たな内部識別子"x"として、"x => それを含む式"に等しい
| ( [式リスト [,]] ) 無名(疑似リテラル)タプル。要素無しはscala.Unitに等しい
| 単純式 型引数 型引数を持つ関数またはコンストラクタ呼び出し 型引数の内容が明白である場合は省略可
| 単純式1 引数式 関数呼び出し 単純式の結果がapplyメソッドを持つ場合は"単純式.apply 引数式"に等しい 引数式が2つあるものは"(単純式1(引数式1))(引数式2)"に等しい
| XML式
式リスト ::= 式 {, 式} この順に評価される 遅延評価渡しで式自体を関数へ渡すことも可能
引数式 ::= ( [式リスト [,]] ) | ( [式リスト ,] 後置式 : _ * ) 後置式の結果をscala.Seqに変換して渡す | [改行区切り] ブロック式 遅延評価渡しでブロック式自体を関数へ渡すことも可能
ブロック式 ::= { ケース節リスト } "_ match {ケース節リスト}"に等しい | { ブロック }
ブロック ::= {ブロック文 複文区切り} [結果式] 結果式無しは()を返す 最後の"結果式"は、結果式と}との間に複文区切りは不可である、ということ; }の直前が;ならそのブロック式の値は必ずscala.Unitになる ブロック内での前方参照は、間に変数または定数定義が無い時のみ可
ブロック文 ::= インポート | [implicit] 定義 この項に対する説明は言語仕様には無い | {ローカル修飾子} テンプレート定義 | 式1 |
ブロック内でも関数やテンプレートを定義できることに注意
ブロックそのものに帰属するものとしての宣言は不可であることに注意
結果式 ::= 式1 | (名前束縛リスト | 識別子 : 複合型) => ブロック 後者は識別子を引数とする無名関数
帰属 ::= : 複合型 | : 注釈 {注釈} | : _ * 任意の型のscala.Seq
逐次子リスト ::= 生成子 {複文区切り 逐次子}
逐次子 ::= 生成子 | ガード それまでの各生成子の結果へのガード; 複数の生成子を一括ガードできる | val パターン1 = 式 ループ毎に評価される定義
生成子 ::= 生成子パターン <- 式 [ガード] ループ毎に式の(ガードがtrueとなる)次の要素を生成子パターンに投入; filterメソッド適用に等しい 従って、式はfilter(とmap、flatmap、foreach)メソッドを持つ型を結果するものでなければならない; リテラルのそれに関してはscala.runtime.Rich&(&は型名)を参照 例えばIntなら、 "1 to 5"または"1 until 6"で"1, 2, 3, 4, 5"を生成するscala.Range型を返す(ゆえに増分を"by"で指定可; "1 to 10 by 2"なら結果は"1, 3, 5, 7, 9"、また"5 to 1 by -1"なら"5, 4, 3, 2, 1")
そのようにfor式は完全な糖衣構文であることから、filter、map、flatmap、foreachの実装次第では言語仕様本来のfor式とは異なる動きをさせられることに注意(標準ライブラリではResponderクラスがこれである)
生成子パターン ::= 識別子 | パターン1 この識別子は暗黙に定数宣言である(ループ毎に評価)
ケース節リスト ::= ケース節 {ケース節} パターンが最初に一致する(かつガードがtrueとなる)ケース節のブロックのみ評価
ケース節同士の間の複文区切りは不可であることに注意
ケース節 ::= case パターン [ガード] => ブロック
ブロック「式」で無い、つまりこのブロックは波括弧で囲まれないことに注意
ガード ::= if 後置式 式はBooleanと互換であること
テンプレート定義 ::= [case] class クラス定義 | [case] object シングルトン定義 | trait トレイト定義 テンプレートはクラス、トレイト、シングルトンに共通する定義構造のこと
case指定なら、 自己複製するシングルトン生成、 引数定数メンバ化その他、 マッチングに必要な諸事が自動で為される
同じケースクラスによって自己複製されたインスタンス同士は、初期化実引数が同じ場合のみ等しい
ケースクラスのtoStringメソッドは"クラス名(初期化実引数値)"を返す
クラステンプレート ::= [事前定義リスト] 親クラスリスト [テンプレート]
トレイトテンプレート ::= [事前定義リスト] 親トレイトリスト [テンプレート]
親クラスリスト ::= コンストラクタ参照 {with 注釈型} Javaのインターフェースも親トレイトとして利用可 scala.ScalaObjectがテンプレート定義の後ろに暗黙の最終親トレイトとして常に追加される 評価は事前定義 > 親クラス > 親トレイト > 定義本体の順(暗黙のコンストラクタによって為される); 親トレイトが親クラスより後に評価されることに注意
親トレイトリスト ::= 注釈型 {with 注釈型}
テンプレート ::= [改行区切り] { [自己型] テンプレート文 {複文区切り テンプレート文} }
自己型 ::= 識別子 [: 型] => thisの別名を定義; 型は子型のみ可 | this : 型 => 前者の型指定のみの形
テンプレート文 ::= インポート | {注釈 [改行区切り]} {修飾子} 定義 | {注釈 [改行区切り]} {修飾子} 宣言 | 式 | テンプレート内でもテンプレート定義が可能であることに注意
コンストラクタ参照 ::= 注釈型 {引数式}
事前定義リスト ::= { [事前定義 {複文区切り 事前定義}] } with with以降が必要とする定義を設定する
事前定義 ::= {注釈 [改行区切り]} {修飾子} パターン変数定義
修飾子 ::= ローカル修飾子 | アクセス修飾子 | override 非抽象親メンバの上書きにはoverride指定が必要(型は+と-に沿って変更可)
各々の修飾子は一度だけ指定可。finalとprivateの同時指定は不可
トレイトの抽象定数メンバはabstract overrideとすればsuper.で参照可能になる
abstract sealedでユーザが継承もインスタンス化もできないクラスを設定できる
ローカル修飾子 ::= abstract 抽象クラス
| final オーバーライド不可メンバ又は継承不可クラス
| sealed 同一ソースファイル内のみ継承可なクラス; sealedで無い子クラスはこの制約を受けない
| implicit 暗黙名空間にコピー 型名空間に属するもの及びトップレベルのものは不可
アクセス修飾子 ::= (private ブロック名ローカル | protected 子クラスローカルメンバ) [アクセス限定子]
アクセス修飾子指定無しはJavaのpublicに等しい
デフォルトコンストラクタへの修飾は後置になることに注意
アクセス限定子 ::= [ (識別子 パッケージ又はクラス限定 | this インスタンス限定) ]
クラス定義 ::= 識別子 [型引数節] {注釈} [アクセス修飾子] クラス引数節リスト クラス継承 この識別子は型名空間に属する 型引数節はローカルな型変数名宣言; +付きなら大抵はクラス内での型宣言でも可 識別子と同名のコンストラクタが暗黙に生成される
クラス引数節リスト ::= {クラス引数節} [[改行区切り] ( implicit クラス引数リスト )] implicitはクラス引数リストを暗黙名空間にコピーする; 実引数が省略された場合、その暗黙名空間の適切なもので補われる
クラス引数節 ::= [改行区切り] ( [クラス引数リスト] )
クラス引数リスト ::= クラス引数 {, クラス引数}
クラス引数 ::= {注釈} [{修飾子} (val | var)] 識別子 [: 引数型] val又はvar指定ならメンバ、さもなければローカル識別子と見なされる
クラス継承 ::= 継承 クラステンプレート | [[継承] テンプレート] "継承"省略は"extends scala.AnyRef"に等しい Javaのクラスも親クラスとして利用可
継承 ::= extends | <:
コンストラクタ式 ::= 自己参照 | コンストラクタブロック
コンストラクタブロック ::= { 自己参照 {複文区切り ブロック文} }
自己参照 ::= this 引数式 {引数式}
追加コンストラクタは始めに暗黙のコンストラクタを呼び出さねばならない
トレイト定義 ::= 識別子 [型引数節] トレイト継承 ミックスイン用クラス この識別子は型名空間に属する 型引数節はローカルな型変数名宣言; +付きなら大抵はトレイト内での型宣言でも可
Javaのインターフェースと異なり、トレイトは非抽象定義も保持可
トレイト継承 ::= 継承 トレイトテンプレート | [[継承] テンプレート] この"継承"はトレイト定義時の親クラス。 トレイト評価時は、"class A extends B with C with D"の時"trait D extends B with C"に等しいものと見なされる
また、"trait A extends B"で"class C extends A"は"class C extends B with A"に等しい
シングルトン定義 ::= 識別子 クラス継承 シングルトン(唯一インスタンス)はまたモジュールとも呼ばれる シングルトンの生成は遅延評価される この識別子は一般名空間に属し、ゆえに同名のクラス定義と両立する privateで後置されるクラス名は同名シングルトン内でのみインスタンス化可能
シングルトン自体の参照は"シングルトン.apply()"に等しい(applyメソッドはユーザ定義)
シングルトンパターンは"シングルトン.unapply()"に等しい(同上、可変長引数ならunapplySeq)
unapplyはBooleanかOption型(要素はタプル型も可)を返す
unapplySeqの返値の最後の要素はscala.Seq型でなければならない
言語仕様には(まだ)ケースシングルトンの説明は無いが、オンラインで得たところでは、同じクラスを継承するケースクラスのインスタンスとマッチし得る別名シングルトンを定義したい時に使うようだ
Javaのクラスはそのstaticメンバから成るシングルトンと以外から成る同名クラスとしてScala化
逆に、Scalaの同名なクラスとシングルトンはJava化の際に上のようなJavaクラスに統合される
Scalaでは、型は本質的には静的参照定数である
実際の制約上、そのゆえにJavaに無い型参照(総称型)は型チェック完了後に削除される
Scalaの型とは、要するにクラス間の継承以外の関係を(無関係、も含めて)記述したものである
すべての祖型はscala.Any。 数値型の祖型はscala.AnyVal。 オブジェクトの祖型はscala.AnyRef(他言語からのものも含む)。 (少なくともユーザ定義の)Scalaオブジェクトはさらにscala.ScalaObjectトレイトを持つ
すべての子型はscala.Nothing。 すべてのオブジェクトの子型はscala.Null
言うまでも無く、 一般に子型は祖型の派生であってその祖型としても振る舞えるが、 祖型は子型としては不完全なので子型として振る舞うことはできない
つまり、子型を祖型として参照できるが、祖型を子型として参照することは不可である
値型 ::= 値型 型引数 構造型(要素を持つ型)
| 値型 # 識別子 型メンバ
クラスまたはトレイト内の型メンバ; "値型.識別子"だとその値型はシングルトンを指す
| 終端識別子 (パスで限定された)テンプレート名
| パス . type シングルトンの型
| ( 型リスト [,] ) タプル型(scala.Tuple~(~は要素数)の別名); 最後の,は型1つだけのタプル(scala.Tuple1)用
各要素は"_~"(~は1から始まる要素番号)で参照可(scala.Product~トレイトから継承)
要素が2つのタプルはPair、3つはTripleの別名も持つ
Scalaのタプルは、要素型の混在は可だが変更は不可(操作は常に新しいタプルを返す)
型引数 ::= [ 型リスト ]
型リスト ::= 型 {, 型}
注釈型 ::= 値型 {注釈}
複合型 ::= 注釈型 {with 注釈型} [細別] それらを祖型に持つ型 | 細別 後者はAnyRef{細別}に等しい
細別 ::= [改行区切り] { 細別文 {複文区切り 細別文} } それらをメンバに持つ型
細別によるオーバーライドでない追加メンバへの参照は明白に実行速度を遅くすることに注意
また、追加宣言されるメソッドの引数の型は細分内か自身の関数型引数節で宣言されたもののみ可
細別文 ::= 宣言 | type 型定義 |
型 ::= 二項演算型 => 型 前者を引数型とし後者を返値型とする関数型(scala.Function~(~は引数型の数)); 前者が複数なら括弧で囲むこと(右から評価されるため)
デフォルトでは引数型は-、返値型は+前置と見なされる; つまり、関数型では、引数が祖型で返値が子型のものと互換である
もし引数型が+なら、 "AnyRef => String"に"f(x: String): String"を渡せるが、 この場合評価時にその関数の実引数としてAnyRef型を渡すことになるのだから、 当然にエラーとなる
引数型が-なら、 "String => String"に"f(x: AnyRef): String"を渡せ、 その関数は実引数のString型をAnyRef型として扱うのだからまったく問題無い
| ( [=> 型] ) => 型 引数が遅延評価渡し(scala.ByNameFunction)
| 二項演算型 [子孫節]
二項演算型 ::= (複合型 | ワイルドカード型) {識別子 [改行区切り] (複合型 | ワイルドカード型)} 識別子に*は不可; この識別子は末尾の:による以外の優先順位は持たない
子孫節 ::= forSome { 子孫宣言 {複文区切り 子孫宣言} } 要素の型特定のためのヒント
子孫宣言 ::= type 型宣言 | val 定数宣言 後者はシングルトンが含む祖型指定; "T1[x.T2] forSome {val x: T3}"で、T3を祖型に持つシングルトンのT2型、の意味になる
ワイルドカード型 ::= _ 型限定 "型[_ >: L <: U]"は"型[要素型] forSome {type 要素型 >: L <: U}"に等しい
"class T1[+T2]"で"T1[T2] forSome {T2 <: T3]"は"T1[T3]"に等しい
"class T1[-T2]"で"T1[T2] forSome {T2 >: T3]"は"T1[T3]"に等しい
注釈 ::= @ 注釈式
"注釈"(アノテーションとも; コンパイラや環境への指示のこと)の使い方等はJavaに準じる
注釈式 ::= コンストラクタ参照 [[改行区切り] { [名前値ペア {, 名前値ペア}] }] このコンストラクタのクラスはscala.Annotationに適合するものでなければならない
名前値ペア ::= val 識別子 = 前置式
定義済み注釈は、 @transient(シリアライズせず)、 @volatile(値の同期保証)、 @serializable(シリアライズ可)、 @SerialVersionUID(Long型リテラル)(その値でこの名のメンバを追加)、 @throws(クラス名リテラル)(JavaでそのScalaクラスの例外をキャッチできるようにする)、 @deprecated(使用に非推奨警告を出す)、 @scala.reflect.BeanProperty(JavaBean式のゲッタ・セッタを生成させる)、 @unchecked(不完全なmatchに警告を出さない)、 @cloneable(cloneメソッド使用可)、 @inline(インライン化指示)、 @native(型チェックのみで本体生成せず)、 @remote(ネットから呼び出し可能)
インポート ::= import インポート式 {, インポート式}
java.langとscalaパッケージ及びscala.PreDefシングルトンはこの順で暗黙にインポート済み
より遅いインポートによる要素名はより早いインポートによる同名要素を隠す
インポート式 ::= 終端識別子 . (識別子 | _ ワイルドカード | インポート選択子リスト) 終端識別子参照は相対パス、すなわちスコープによって決定される; なお、パッケージp内でのインポートでは、"p."はあっても無くても結果は同じである
全スコープの外側は_root_として参照可能であり、すなわちここから始まるパスは絶対パスである
インポート選択子リスト ::= { {インポート選択子 ,} (インポート選択子 | _ 残り全部) }
インポート選択子 ::= 識別子 [=> 識別子 違う名前でインポート | => _ 匿名でインポート; その識別子に単純名でアクセスできないようにする]
ソースファイル ::= [package 限定識別子 複文区切り] トップレベル文並び
フルパスのパッケージ名は同様なクラス名やシングルトン名と同じであってはならない
同一パッケージは複数ファイルに分割可
パッケージ外部(パッケージ指定省略時)は、 インポート不可能な唯一の無名パッケージに属すると見なされる
トップレベル文並び ::= トップレベル文 {複文区切り トップレベル文}
トップレベル文 ::= {注釈 [改行区切り]} {修飾子} テンプレート定義 | インポート | パッケージ |
Scalaのパッケージやクラスはソースファイル名と無関係 > 単一ソースファイルに複数のパッケージやクラス可; ただし、大規模なプログラムではJava式に従う方が親切 (内部クラスで無いクラスごとに1ファイルとしそのクラス名をファイル名とするやり方)
パッケージ ::= package 限定識別子 [改行区切り] { トップレベル文並び } 同一ファイルに複数のパッケージがある場合(ファイル先頭での波括弧無しの指定はこの場合不可)
限定識別子 ::= 識別子 {. 識別子}
コンパイルされた実行可能なプログラムは、 main(識別子: Array[String]) メソッドを含むまたは継承(scala.Application)する、 トップレベルのシングルトンでなければならない
後者の継承の場合、それを継承するクラス定義が丸ごと暗黙のmainメソッドに繰り込まれる
scala.Applicationまたはシェルのみによる起動法ではコマンドライン引数を参照不可
スクリプトファイルは、 ユーザからは見えないシングルトンのmainメソッドにそのファイル全部を繰り込む形で実行される
ゆえに、上のコンパイル用の措置はスクリプトモードではエラーになる; トップレベルの最後に"そのシングルトン名.main(args)"と記述すれば実は実行可能である
コンパイル用でない記述のままで、 "-Xscript 希望シングルトン名"オプションでコンパイルする手もある; そのシングルトンの暗黙の"main(args: Array[String])"メソッドで記述全体をラップしてくれる
スクリプトファイル実行の場合は、コマンドライン引数は"args"(Array[String])で参照可
スクリプトファイル冒頭の"#!"から"!#"の間、または"::#!"から"::!#"の間は無視される; その間の行に'exec scala "%0" "%@"'(Linux等)、 または"@echo off" "call scala %0 %*" "goto :eof"(Windows)と記述することでスクリプト名だけで実行可能にできる
対話シェルでは、 有効行が入力されるごとにコンパイルして一時的classファイルを作成、 Javaのクラスローダーとリフレクションでそれをロードし実行、 その都度生成されるリポーターインスタンスがインポートして結果を表示、 またそのclassファイルのメンバは次の入力行で作成されるclassファイルにインポートされ、 以上を繰り返す、という仕組みになっている
つまり、Scalaではスクリプトファイルはもちろん、 対話シェルでもコンパイル時とほぼ同等の速度でコードが実行される
その代わり、対話シェルではトップレベルの"this"は有効行ごとに参照先が変わることになる
また、以上の理由により、対話シェルに限り、同じ識別子が何度でも再定義可能となる; 以前のはインポートされたものであって、同一ブロック内での再定義とはならないからである
Scalaでは配列はオブジェクトとしてのみ実装されているのでJavaのそれとは異なる
親子な要素型の配列同士は親子では無い > asInstanceOfメソッドによるキャストは可
同じ理由で、文字列の配列はObjectの配列の子では無い > 上と同じくキャストは可
要素型が動的に定まる配列の型保証は不完全 > isInstanceOfとasInstanceOfメソッドでチェックすること
java.lang.System.arraycopyメソッドはScalaのArrayインスタンスには使えない (Javaの配列とは内部表現がまったく異なる) > scala.Array.copyメソッドを使うこと
以下、"(要素リスト)"はそれら要素を内容とするタプルのこと
リテラルは"Array(要素リスト)"(Arrayシングルトンを経由して新たなArrayインスタンスを生成)
arrをArrayインスタンスとして、 要素参照は"arr(インデクス)"(applyメソッドによる)、 要素書き換えは"arr(インデクス) = 式"(updateメソッドによる)、 要素数参照は"arr.length"
多重配列の作成は"val a = Array(Array(1, 2), Array(3, 4, 5))"(配列への参照の配列)、参照は"a(1)(1)"
タプル以外のScalaの主要なデータ型は次の通り
文字列
java.lang.Stringそれ自体(変更不可文字列)
scala.runtime.RichString経由で次のメソッドが使用可: *('"ab" * 2 == "abab"')、 ++("+"と同じ)、 capitalize(1文字目を大文字にする)、 compare(-1、0、1を返す比較)、 containsSlice(指定文字列を含むならtrue)、 drop('"abc" drop 1 == "bc"')、 endsWith、 indexOf、 length、 lines(改行文字で分けられた各行から成るIteratorを返す)、 linesWithSeparators(左で改行文字自体も1行として含む)、 mkString(コレクション型を文字列へ変換)、 r(Regex型へ変換)、 reverse、 slice、 split、 startsWith、 stripLineEnd、 stripMargin、 take('"abc" take 2 == "ab"')、 toArray、 toBoolean、 toByte、 toDouble、 toFloat、 toInt、 toLong、 toShort
StringBuilder(Javaのそれへのラッパー)で可変文字列も可だが、同期は保証されない
生成は'new StringBuilder("abc")'
可変なので、sbをそのインスタンスとして"sb(1) = 'a'"で書き換え可能(インデクスは配列のと同じ)
scala.runtime.RichStringBuilder経由で次のメソッドが使用可: ++(追加)、 ++=(同左)、 +:(前に追加)、 +=(1文字(Char)追加)、 clear(空文字列にする)、 ensureSize、 insertAll(挿入)、 length、 mkString、 remove(1文字削除)
RichString、RichStringBuilderは様々なクラスの子孫なので他にも色々なメソッドが使える
Array
長さ固定、要素型は一種のみ、内容は変更可
List
内容も変更不可なArray>操作は常に新たなListインスタンスを返す
リテラルは"List(要素リスト)"または"要素1::要素2::Nil"
"::"はリストの前に追加(ゆえに上2番目でラストに"Nil"("List()"に等しい)が必要)
":::"は結合リストを返す
ともに、":"で終わる演算識別子は右から評価されることに注意
参照はlistをListインスタンスとして"list(インデクス)"、書き換えは不可
Set
scala.Predefで導入定義
変更不可(可はscala.collection.mutable.Setシングルトンで生成)、要素の重複が無い集合
リテラルは"Set(要素リスト)"
Map
scala.Predefで導入定義
変更不可(可はscala.collection.mutable.Mapシングルトンで生成)、連想配列
要素はPairのみで"._1"をキー(全キーの型は同一)、"._2"を内容(全内容の型は同一)とする
"キー -> 内容"でPair生成可
リテラルは"Map(要素リスト)"
他にも色々
Java互換用型名エイリアス: boolean、 char、 byte、 short、 int、 long、 float、 double、 String、 Class、 Runnable、 Throwable、 Exception(scala.runtime.RichException経由でgetStackTraceStringメソッドが使用可)、 Error
Java互換用例外エイリアス: ArrayIndexOutOfBoundsException、 ClassCastException、 IllegalArgumentException、 IndexOutOfBoundsException、 NoSuchElementException、 NullPointerException、 NumberFormatException、 RuntimeException、 StringIndexOutOfBoundsException、 UnsupportedOperationException
以前のScalaとの互換用(今後の使用は非推奨): Character(java.lang.Characterを推奨)、 Integer(java.lang.Integerを推奨)
Scala型名エイリアス: unit(scala.Unit)、 Function(scala.Function1(引数1つの関数型))、 Map(scala.collection.immutable.Mapシングルトン(同パッケージのHashMapが実体))、 Set(scala.collection.immutable.Setシングルトン(同パッケージのHashSetが実体))
内部使用シングルトン: Pair(要素2つのタプル)、 Triple(要素3つのタプル)
型汎用メソッド: ->、 ensure(間置演算子型assert)、 +(文字列との結合)
関数: assert、 assume(弱めのassert)、 classOf(実行時のJavaでのクラス)、 currentThread(現在実行中のスレッド)、 error(RuntimeExceptionを投げる)、 exit、 require(必須引数用assert)
Consoleシングルトンの読み書き関数へのショートカット(readf~は、 java.text.messageFormatのパターンから該当するものをその要素数のタプルで返す)
内部使用: ビュー用型変換関数; javaの型及びscala.runtime.Rich&(&は数値・文字・文字列・ブーリアン・例外型名)へのものも含む、 タプルリテラル用関数