DEV Community

harry
harry

Posted on

7 1

Scalaの記号みたいな奴らなんなの

  • この記事は 2016年07月03日 にQiitaへ投稿した内容を転記したものです。
  • 本記事は執筆から1年以上が経過しています。

これなんなの?

「Scalaの記号はググラビリティ低すぎて調べようがないし全くわからん…せめて定義位置とか実装がわかれば…」という自分のためのメモ、いわゆるチラシの裏です。

とりあえずScalaの記号については、ソースコードをダウンロードして下記の正規表現でgrepしました。

^[^*/]* (?:def|class|object|type|val) ([^0-9A-Za-z" \(\[]+)(?:[ \(\[]|$)
Enter fullscreen mode Exit fullscreen mode

基本、わからない記号見つけたら定義元に飛んで ((IntelliJはCtrl+B、EclipseはF3))、ソースを読んでます。(読んで理解できるとは言っていない)

  • IDEで定義元に飛べない記号(_*など)は、言語仕様の可能性が高いので言語仕様を読みましょう。
  • 記号の使用例がわからない場合、そのライブラリのテストコードを読むと良いケースがあります。
  • 急に()が出てきた場合、カリー化された引数かapply()です。 apply()の場合、一時的に書き換えてショートカットキーで飛ぶか、検索してapply()の定義を確認します。

https://twitter.com/kmizu/status/724913410962558976

Language Specification

バッカス・ナウア記法

Scalaの言語仕様では、文法の定義にバッカス・ナウア記法(BNF: Backus-Naur form)が使用されています。使用されているBNFの記号と意味は下記の通りです。

記号 意味
::= 定義
‘…’ 文字列
`…' 文字列
| OR
( … ) グループ化
{ … } 0回以上の繰り返し
[ … ] オプション (0回 or 1回)
// コメント (注釈)
“…” コメント (注釈)

記号ではありませんが、nlsemiの意味は下記の通りです。

nl               ::=  “newlinecharacter”
semi             ::=  ‘;’ |  nl {nl}

Arrow

Identifiers

/

http://www.scala-lang.org/files/archive/spec/2.11/01-lexical-syntax.html#identifiers

The Unicode operators \u21D2 ‘⇒’ and \u2190 ‘←’, which have the ASCII equivalents => and <-, are also reserved.

なお、2.13.xから非推奨になりました。(実務で使ってる人は皆無だと思いますが…)

Deprecate unicode arrows , and by smarter · Pull Request #7540 · scala/scala
https://github.com/scala/scala/pull/7540

Import Clauses

http://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html#import-clauses

Import selectors work in the same way for type and term members. For instance, an import clause import p.{x => y} renames the term name p.x to the term name y and the type name p.x to the type name y. At least one of these two names must reference an importable member of p.

import p.{x => y}と書くと、p.xyという名前でimportすることができます。
SessionModule等のかぶりやすい名前のtypeを複数importする際に便利なことがあります。

  • java.sql.DateSQLDateとしてimportする例
import java.util.Date
import java.sql.{Date => SQLDate}
Enter fullscreen mode Exit fullscreen mode

If the target in an import selector is a wildcard, the import selector hides access to the source member. For instance, the import selector x => _ “renames” x to the wildcard symbol (which is unaccessible as a name in user programs), and thereby effectively prevents unqualified access to x. This is useful if there is a final wildcard in the same import selector list, which imports all members not mentioned in previous import selectors.

package内のすべてのtypeをimportする際はpackage._を使用しますが、特定のtypeだけ除外したい場合はpackage.{Type => _, _}と書くことで除外できます。
複数のtypeを除外することもできます。

  • java.util.Dateを除くjava.util._java.sql._をimportする例
import java.util.{Date => _, _}
import java.sql._
Enter fullscreen mode Exit fullscreen mode

Scalaスケーラブルプログラミング 第2版
13.3 インポート (P.238~239)

Self type

http://www.scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#templates

ClassTemplate   ::=  [EarlyDefs] ClassParents [TemplateBody]
TraitTemplate   ::=  [EarlyDefs] TraitParents [TemplateBody]
ClassParents    ::=  Constr {`with' AnnotType}
TraitParents    ::=  AnnotType {`with' AnnotType}
TemplateBody    ::=  [nl] `{' [SelfType] TemplateStat {semi TemplateStat} `}'
SelfType        ::=  id [`:' Type] `=>'
                 |   this `:' Type `=>'

The sequence of template statements may be prefixed with a formal parameter definition and an arrow, e.g. x =>, or x:T =>. If a formal parameter is given, it can be used as an alias for the reference this throughout the body of the template.

class, object, traitの定義部(TemplateBody)の最初にid : Type =>またはthis : Type =>と書くことができます。これはSelf Type(自分型)で、基本的に自分自身のインスタンスthisと同じものを指します。つまり、thisの別名(alias)を定義することができます。

id : Typeの型注釈は省略可能で、idは慣習的にselfが使用されます。

self =>の使用例

Optionでの使用例です。

Option.scala#L98-L99

sealed abstract class Option[+A] extends Product with Serializable {
  self =>
Enter fullscreen mode Exit fullscreen mode

Option.scala#L197-L211

  /** Necessary to keep $option from being implicitly converted to
   *  [[scala.collection.Iterable]] in `for` comprehensions.
   */
  @inline final def withFilter(p: A => Boolean): WithFilter = new WithFilter(p)

  /** We need a whole WithFilter class to honor the "doesn't create a new
   *  collection" contract even though it seems unlikely to matter much in a
   *  collection with max size 1.
   */
  class WithFilter(p: A => Boolean) {
    def map[B](f: A => B): Option[B] = self filter p map f
    def flatMap[B](f: A => Option[B]): Option[B] = self filter p flatMap f
    def foreach[U](f: A => U): Unit = self filter p foreach f
    def withFilter(q: A => Boolean): WithFilter = new WithFilter(x => p(x) && q(x))
  }
Enter fullscreen mode Exit fullscreen mode

abstract class Option内で定義したclass WithFilterからOptionのインスタンスを参照する際、thisを使用するとWithFilter自身のインスタンスを指してしまいますが、自分型のselfを使用することでOptionのインスタンスを参照することができます。

this: Type =>の使用例

例えば特定のtraitがmixinされていることが前提のクラスに対してmixinするためのtraitを定義するとします。
その際、定義したtrait内でクラスへmixinされているはずのtraitのメンバを使用したいことがあります。

class SomeClass extends Foo with Bar

trait Foo {
  // ...
}

// Bar は Foo をmixinしているtypeにmixinすることが前提のtrait
trait Bar {
  // Fooのメンバを使用したい…
}
Enter fullscreen mode Exit fullscreen mode

その場合は、自分型のTypeにそのtraitを指定することで、そのtraitのメンバが使用できます。
(thisがTypeに指定したtypeとして振舞います)

trait Bar {
  this: Foo =>

  // Fooのメンバが使用できる
}
Enter fullscreen mode Exit fullscreen mode

Play Frameworkでの使用例です。
Controllerをmixinしているクラスに対してmixinするtraitで、Controllerのメンバを使用したい場合です。

import play.api.mvc._

trait FooController {
  this: Controller =>

  // `Controller` のメンバが使用できる
}
Enter fullscreen mode Exit fullscreen mode

さらに、I18nSupportをmixinしていることが前提の場合は下記のようになります。

import play.api.i18n.I18nSupport
import play.api.mvc._

trait BarController {
  this: Controller with I18nSupport =>

  // `Controller` と `I18nSupport` のメンバが使用できる
}
Enter fullscreen mode Exit fullscreen mode

自分型でtypeを指定した場合、指定したtypeを継承しているtrait, class, objectに対してのみ継承やmixinができます。

import play.api.mvc._

// OK
class SomeContoller extends Controller with FooController

// Compile error
// illegal inheritance; self-type SomeClass does not conform to FooController's selftype Controller
class SomeClass extends FooController
Enter fullscreen mode Exit fullscreen mode

Atmark

Pattern Binders

http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#pattern-binders

A pattern binder x@p consists of a pattern variable x and a pattern p. The type of the variable x is the static type T of the pattern p. This pattern matches any value v matched by the pattern p, provided the run-time type of v is also an instance of T, and it binds the variable name to that value.

パターンマッチングでx@pと書くと、パターンpに一致した際の値を変数xとして参照(束縛)することができます。

List(1, 2, 3) match {
  // headが 1 にマッチするListを変数 x に束縛する
  case x @ 1 :: _ =>
    x // = x: List = List(1, 2, 3)
  case _ =>
    Nil
}
Enter fullscreen mode Exit fullscreen mode

[Scala]パターンマッチにおけるアットマーク(@)
http://qiita.com/petitviolet/items/89843d948b6fc06eba57

Colon

Infix Operations

http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#infix-operations

The associativity of an operator is determined by the operator's last character. Operators ending in a colon : are right-associative. All other operators are left-associative.

演算子(operator、メソッド)の最後の文字がコロン(:)の場合、その演算子は右被演算子から呼び出されます。(左被演算子は引数になります)

  • Seqの例
val a = Seq(1, 2, 3)

a :+ 4

a.:+(4) // = Seq(1, 2, 3, 4)

// メソッド `+:` は、右被演算子の a から呼び出される
0 +: a

a.+:(0) // = Seq(0, 1, 2, 3)
Enter fullscreen mode Exit fullscreen mode

Unary

Prefix Operations

http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#prefix-operations

A prefix operation op;e consists of a prefix operator op, which must be one of the identifiers ‘+’, ‘-’, ‘!’ or ‘~’. The expression op;e is equivalent to the postfix method application e.unary_op.

単項(unary)演算子のうち、+, -, !, ~は前置演算子として使用することができます。前置演算子として使用された場合、unary_opメソッドが呼び出されます。

  • Booleanの例
val b = true

// 前置演算子 `!` の場合、`unary_!` メソッドが呼び出される
!b

b.unary_! // = false
Enter fullscreen mode Exit fullscreen mode

つまり、unary_opメソッドを定義すればopを前置演算子として使用することができます。
メソッド定義のunary_op:の間にはスペースが必要です。

case object Black extends Things
case object White extends Things

sealed abstract class Things {
  def unary_! : Things = {
    this match {
      case Black => White
      case White => Black
    }
  }
}

!Black // = White
Enter fullscreen mode Exit fullscreen mode

Underscore

http://stackoverflow.com/questions/8000903/what-are-all-the-uses-of-an-underscore-in-scala

* / _*

Repeated Parameters

http://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html#repeated-parameters

http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#function-applications

  • メソッド、関数定義のType*は可変長引数
  • 可変長引数は最後の引数でのみ使用できます
def sum(args: Int*): Int = {
  var result = 0
  for (arg <- args) result += arg
  result
}
Enter fullscreen mode Exit fullscreen mode
val sum: (Int*) => Int = { args =>
  var result = 0
  for (arg <- args) result += arg
  result
}
Enter fullscreen mode Exit fullscreen mode

Seq型は_*型注釈をつけることで可変長引数に渡すことができます。

sum(Seq(1, 2, 3, 4): _*)
Enter fullscreen mode Exit fullscreen mode

Seq型であればよいので、Seq型に暗黙の型変換されるものでもよいです。

// Char の可変長引数を取るメソッド
def toHexString(args: Char*): String = {
  args.map(c => f"$c%#06x").mkString(",")
}

// String は WrappedString (Seq[Char]) へ暗黙の型変換されます
toHexString("str": _*)

// 0x0073,0x0074,0x0072
Enter fullscreen mode Exit fullscreen mode

Pattern Sequences

http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#pattern-sequences

Pattern matching中の_*はsequence wildcard

case List(1, 2, _*) => ... // will match all lists starting with 1, 2, ...
Enter fullscreen mode Exit fullscreen mode

[]

型パラメーター

[A <: B] [A >: B] [+T] [-T]

上限・下限境界と変位指定アノテーション

第21章:Scalaの型パラメータ
http://qiita.com/f81@github/items/7a664df8f4b87d86e5d8

共変・反変・上限境界・下限境界の関係性まとめ
http://qiita.com/mtoyoshi/items/bd0ad545935225419327

[_]

def f[M[_]]       // Higher kinded type parameter
def f(m: M[_])    // Existential type
Enter fullscreen mode Exit fullscreen mode

private[C] / protected[C]

http://www.scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#modifiers

A private modifier can be qualified with an identifier C (e.g. private[C]) that must denote a class or package enclosing the definition. Members labeled with such a modifier are accessible respectively only from code inside the package C or only from code inside the class C and its companion module.

A protected modifier can be qualified with an identifier C (e.g. protected[C]) that must denote a class or package enclosing the definition. Members labeled with such a modifier are also accessible respectively from all code inside the package C or from all code inside the class C and its companion module.

Scala Standard Library

Any

==

  final def ==(that: Any): Boolean = this equals that
Enter fullscreen mode Exit fullscreen mode

!=

  final def != (that: Any): Boolean = !(this == that)
Enter fullscreen mode Exit fullscreen mode

##

  /** Equivalent to `x.hashCode` except for boxed numeric types and `null`.
   *  For numerics, it returns a hash value which is consistent
   *  with value equality: if two value type instances compare
   *  as true, then ## will produce the same hash value for each
   *  of them.
   *  For `null` returns a hashcode where `null.hashCode` throws a
   *  `NullPointerException`.
   *
   *  @return   a hash value consistent with ==
   */
  final def ##(): Int = sys.error("##")
Enter fullscreen mode Exit fullscreen mode

null安全なhashCodeの取得ができます。

> scala -feature

scala> val s: String = null
s: String = null

scala> s.hashCode
java.lang.NullPointerException
  ... 33 elided

scala> s##
<console>:12: warning: postfix operator ## should be enabled
by making the implicit value scala.language.postfixOps visible.
This can be achieved by adding the import clause 'import scala.language.postfixOps'
or by setting the compiler option -language:postfixOps.
See the Scala docs for value scala.language.postfixOps for a discussion
why the feature should be explicitly enabled.
       s##
        ^
res0: Int = 0
Enter fullscreen mode Exit fullscreen mode

Scalaパズル 36の罠から学ぶベストプラクティス
PUZZLE28 好きな値を選んでください!――Pick a Value, AnyValue! (P.187-189)

AnyRef

==

  final def ==(that: Any): Boolean =
    if (this eq null) that.asInstanceOf[AnyRef] eq null
    else this equals that
Enter fullscreen mode Exit fullscreen mode

null安全な等価比較ができます。

library

package.scala

  type ::[A] = scala.collection.immutable.::[A]
  val :: = scala.collection.immutable.::

  val +: = scala.collection.+:
  val :+ = scala.collection.:+

  type Stream[+A] = scala.collection.immutable.Stream[A]
  val Stream = scala.collection.immutable.Stream
  val #:: = scala.collection.immutable.Stream.#::
Enter fullscreen mode Exit fullscreen mode

Numeric value types

http://www.scala-lang.org/files/archive/spec/2.11/12-the-scala-standard-library.html#numeric-value-types

Boolean

== != || && | & ^

Byte / Char / Short / Int / Long

<< >> <<< >>> == != < <= > >= | & ^ + - * / %

Float / Double

== != < <= > >= | & ^ + - * / %

Enumeration

Value

    /** Create a ValueSet which contains this value and another one */
    def + (v: Value) = ValueSet(this, v)
Enter fullscreen mode Exit fullscreen mode

ValueSet

    def + (value: Value) = new ValueSet(nnIds + (value.id - bottomId))
    def - (value: Value) = new ValueSet(nnIds - (value.id - bottomId))
Enter fullscreen mode Exit fullscreen mode

ValueSet.newBuilder

      def += (x: Value) = { b += (x.id - bottomId); this }
Enter fullscreen mode Exit fullscreen mode

Predef.scala

http://www.scala-lang.org/files/archive/spec/2.11/12-the-scala-standard-library.html#the-predef-object

???

  /** `???` can be used for marking methods that remain to be implemented.
   *  @throws NotImplementedError
   */
  def ??? : Nothing = throw new NotImplementedError
Enter fullscreen mode Exit fullscreen mode

ArrowAssoc

  implicit final class ArrowAssoc[A](private val self: A) extends AnyVal {
    @inline def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y)
    def [B](y: B): Tuple2[A, B] = ->(y)
  }
Enter fullscreen mode Exit fullscreen mode

a -> bと書くとTuple2(a, b)に変換されて不思議に思ったことありませんか?これです。

<:<

An instance of A <:< B witnesses that A is a subtype of B.

<:<を使ってメソッド引数の型を限定する (例: IntもしくはBooleanもしくはString
http://qiita.com/mtoyoshi/items/fbf2c744e3c7fd69a438

続<:<を使ってメソッド引数の型を限定する(厳密に)
http://qiita.com/mtoyoshi/items/13ac0500aa23fb4fa0d4

=:=

An instance of A =:= B witnesses that the types A and B are equal.

collections

Traversable

TraversableLike

++ コレクションを追加した新しいコレクションを返す
++: 右辺のコレクションと左辺のコレクションを追加した新しいコレクションを返す

Seq(1, 2, 3) ++ Seq(4, 5, 6)  // = Seq(1, 2, 3, 4, 5, 6)

Seq(1, 2, 3) ++: Seq(4, 5, 6) // = Seq(1, 2, 3, 4, 5, 6)
Enter fullscreen mode Exit fullscreen mode

TraversableOnce

  def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)

  def :\[B](z: B)(op: (A, B) => B): B = foldRight(z)(op)
Enter fullscreen mode Exit fullscreen mode

SeqLike

+: 要素にSeqを追加した新しいSeqを返す (Seqの先頭に要素を挿入した新しいSeqを返す)
:+ Seqに要素を追加した新しいSeqを返す

0 +: Seq(1, 2, 3) // = Seq(0, 1, 2, 3)

Seq(1, 2, 3) :+ 4 // = Seq(1, 2, 3, 4)
Enter fullscreen mode Exit fullscreen mode

generic

Growable

http://www.scala-lang.org/api/2.11.7/index.html#scala.collection.generic.Growable

mutableなコレクションなどにmixinされているtrait
新しいコレクションを作らず、コレクションに直接要素を追加する

+= コレクションへ1つ、または複数の要素を追加する
++= 引数のコレクションにある要素を追加する

Shrinkable

http://www.scala-lang.org/api/2.11.7/index.html#scala.collection.generic.Shrinkable

mutableなコレクションなどにmixinされているtrait
新しいコレクションを作らず、コレクションから直接要素を削除する

-= コレクションから1つ、または複数の要素を削除する
--= 引数のコレクションにある要素を削除する

Subtractable

http://www.scala-lang.org/api/2.11.7/index.html#scala.collection.generic.Subtractable

- コレクションから1つ、または複数の要素を削除した新しいコレクションを返す

override def -でソースコードを検索すると、コレクション毎の実装詳細を確認することができます。

--

  def --(xs: GenTraversableOnce[A]): Repr = (repr /: xs.seq) (_ - _)
Enter fullscreen mode Exit fullscreen mode

引数のコレクションにある要素を削除した新しいコレクションを返す

BitSetLike

| & &~ ^

BitSet同士のor, and, and-not, xorを取り、新しいBitSetを返す

mutable.BitSet

|= &= &~= ^=

BitSet同士のor, and, and-not, xorを取り、BitSetを更新する

GenSetLike

+ -

  def +(elem: A): Repr
  def -(elem: A): Repr
Enter fullscreen mode Exit fullscreen mode

Setへ要素を追加、削除する

&

  def &(that: GenSet[A]): Repr = this intersect that
Enter fullscreen mode Exit fullscreen mode

Set同士のandを取る

|

  def | (that: GenSet[A]): Repr = this union that
Enter fullscreen mode Exit fullscreen mode

Set同士のorを取る

&~

  def &~(that: GenSet[A]): Repr = this diff that
Enter fullscreen mode Exit fullscreen mode

Set同士のand-notを取る ((本来、要素の順序は保証されませんが、わかりやすさのためソートして書いています))

val a = Set(1, 2, 3, 4, 5, 6)
val b = Set(1, 2, 3)

a &~ b // = Set(4, 5, 6)

b &~ a // = Set()
Enter fullscreen mode Exit fullscreen mode

immutable.::

final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
  override def tail : List[B] = tl
  override def isEmpty: Boolean = false
}
Enter fullscreen mode Exit fullscreen mode

::(head, tail)となるようなListを返すクラスです。

package.scala::のtype aliasが定義されており、Listに対するパターンマッチ(case head :: tail)でも使用されます。

余談ですが、REPLでは:から始まる文字列がコマンドとして解釈されるため、バッククォートで囲みリテラル識別子(literal identifiers)にする必要があります。

scala> `::`(1, List(2, 3, 4))
res0: scala.collection.immutable.::[Int] = List(1, 2, 3, 4)

scala> res0.head
res1: Int = 1

scala> res0.tail
res2: List[Int] = List(2, 3, 4)
Enter fullscreen mode Exit fullscreen mode

immutable.List

::

  def ::[B >: A] (x: B): List[B] =
    new scala.collection.immutable.::(x, this)
Enter fullscreen mode Exit fullscreen mode

メソッドの最後の文字が:なので、right-associativeです。
Scaladocのexampleに書かれていますが、下記のようになります。

1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)

つまり、終端がListであればよいので、Nilも使えます。

1 :: 2 :: 3 :: Nil

1 :: 2 :: Nil.::(3)

1 :: 2 :: List(3)

1 :: List(3).::(2)

1 :: List(2, 3)

List(2, 3).::(1)

List(1, 2, 3)
Enter fullscreen mode Exit fullscreen mode

:::

  def :::[B >: A](prefix: List[B]): List[B] =
    if (isEmpty) prefix
    else if (prefix.isEmpty) this
    else (new ListBuffer[B] ++= prefix).prependToList(this)
Enter fullscreen mode Exit fullscreen mode

List同士を連結した新しいListを返す

List(1, 2, 3) ::: List(4, 5, 6) // = List(1, 2, 3, 4, 5, 6)
Enter fullscreen mode Exit fullscreen mode

Stream.#::

  object #:: {
    def unapply[A](xs: Stream[A]): Option[(A, Stream[A])] =
      if (xs.isEmpty) None
      else Some((xs.head, xs.tail))
  }
Enter fullscreen mode Exit fullscreen mode

Streamのパターンマッチ(case head #:: tail)のために定義されている記号(object)
package.scala#::のtype aliasが定義されています。

Stream.ConsWrapper

#::#:::が定義されていて、Streamからこれらのメソッドが呼ばれるとConsWrapperに暗黙の型変換が行われます。

  /** A wrapper method that adds `#::` for cons and `#:::` for concat as operations
   *  to streams.
   */
  implicit def consWrapper[A](stream: => Stream[A]): ConsWrapper[A] =
    new ConsWrapper[A](stream)
Enter fullscreen mode Exit fullscreen mode

#::

  def #::(hd: A): Stream[A] = cons(hd, tl)
Enter fullscreen mode Exit fullscreen mode

簡単にいうと、head #:: tailとなるようなStreamが返ります。

実際にはobject consapply()が実行され、final class Cons[+A](hd: A, tl: => Stream[A]) extends Stream[A]が返ります。

#:::

  def #:::(prefix: Stream[A]): Stream[A] = prefix append tl
Enter fullscreen mode Exit fullscreen mode

Stream同士を連結した新しいStreamを返す

mutable.BufferLike

++=:

  def ++=:(xs: TraversableOnce[A]): this.type = { insertAll(0, xs.toTraversable); this }
Enter fullscreen mode Exit fullscreen mode

コレクションxsを現在のBufferの前方に挿入する

val buffer = mutable.Buffer(4, 5, 6)

Seq(1, 2, 3) ++=: buffer

buffer // = mutable.Buffer(1, 2, 3, 4, 5, 6)
Enter fullscreen mode Exit fullscreen mode

StringLike

*

  def * (n: Int): String = {
    val buf = new StringBuilder
    for (i <- 0 until n) buf append toString
    buf.toString
  }
Enter fullscreen mode Exit fullscreen mode

現在の文字列を指定回数連結した文字列を返す

math

/%: 商と余りをTupleで返す
&~: and-not (this & ~that)

BigDecimal

<= >= < > + - * / /% %

BigInt

<= >= < > + - * / % /% << >> & | ^ &~

Fractional

/

Integral

/ % /%

Numeric

+ - *

Ordered

< > <= >=

Ordering

< <= > >=

PartiallyOrdered

< > <= >=

sys

SystemProperties

-= +=

package process

type =?>[-A, +B]     = PartialFunction[A, B]
Enter fullscreen mode Exit fullscreen mode

ProcessBuilder (extends Source with Sink)

http://www.scala-lang.org/api/2.11.x/#scala.sys.process.ProcessBuilder

記号 機能
!! blocks until all external commands exit, and returns a String with the output generated.
!!< stdin + !!
! blocks until all external commands exit, and returns the exit code of the last one in the chain of execution.
!< stdin + !
#| pipe (Shellの | )
### コマンドが終了後、後続のコマンドを実行。
#&& コマンドのexit codeが0の時、後続のコマンドを実行。 (Shellの && )
#|| コマンドのexit codeが0以外の時、後続のコマンドを実行 (Shellの || )

FileBuilder (extends Sink with Source)

#<<

Source

記号 意味
#> Redirect stdout (overwrite)
#>> Redirect stdout (append)

Sink

記号 意味
#< Redirect stdin

AbstractBuilder (extends ProcessBuilder with Sink with Source)

!! !!< ! !< #| ### #&& #||

FileImpl (extends FileBuilder with Sink with Source)

#<<

ex-Scala Standard Library

以下のライブラリは2.11.0で標準ライブラリから分離されました。

Scala 2.11.0で標準ライブラリから分離されたライブラリのjarファイル - kmizuの日記
http://kmizu.hatenablog.com/entry/2014/04/24/005958

scala-xml

Lexical Syntax - 1.5 XML mode
http://www.scala-lang.org/files/archive/spec/2.11/01-lexical-syntax.html#xml-mode

XML Expressions and Patterns
http://www.scala-lang.org/files/archive/spec/2.11/10-xml-expressions-and-patterns.html

XML expressions

ScalaはXML(XMLリテラル)を直接扱うことができます。ただし、前述のとおりscala.xmlが標準ライブラリから外されたため、クラスパスに追加しないとコンパイルできません。 ((REPLはscala.xmlパッケージが含まれているため、そのまま使用することができます))

val a = <a>text</a> // = a: scala.xml.Elem = <a>text</a>
Enter fullscreen mode Exit fullscreen mode

XML expressions may contain Scala expressions as attribute values or within nodes. In the latter case, these are embedded using a single opening brace { and ended by a closing brace }. To express a single opening braces within XML text as generated by CharData, it must be doubled. Thus, {{ represents the XML text { and does not introduce an embedded Scala expression.

XMLでScalaの変数や式を参照する場合は{}を使用します。
参照ではなく単純に波括弧を使用したい場合は{{}}を使用します。

<a>{brace}</a>   // error: not found: value brace
<a>{{brace}}</a> // <a>{brace}</a>

val text = "text value"

<a>{text}</a>         // = <a>text value</a>
<a>{{text</a>         // = <a>{text</a>
<a>text}}</a>         // = <a>text}</a>

<a>{{{text}}}</a>     // = <a>{text value}</a>
<a>{{{{text}}}}</a>   // = <a>{{text}}</a>
<a>{{{{{text}}}}}</a> // = <a>{{text value}}</a>
Enter fullscreen mode Exit fullscreen mode

単純に{{の組み合わせが、XML textの{に変換されています。

XML patterns

XMLリテラルはそのままパターンマッチングで使用することができます。
パターンマッチングの{}内ではScalaのパターンマッチが使用できます。

// 変数パターンによるマッチング
<a><b>text</b></a> match {
  case <a>{value}</a> => println(value)
  case _ => println("unmatched")
}

// <b>text</b>

<a><b>bold</b> text</a> match {
  case <a>{value}</a> => println(value)
  case _ => println("unmatched")
}

// unmatched
Enter fullscreen mode Exit fullscreen mode

任意のXMLノードシーケンスにマッチさせるためには、Pattern Sequences(_*)を使用する必要があります。
(2つ目の<a>には<b>textの2つのノードが含まれていました)

// Pattern Sequencesによるマッチング
val list = <ul type="circle">
             <li>a</li>
             <li>b</li>
             <li>c</li>
           </ul>

list match {
  case <ul>{items @ _*}</ul> =>
    for (item <- items) {
      println(s"item: $item")
    }
}

// item: 
//                
// item: <li>a</li>
// item: 
//                
// item: <li>b</li>
// item: 
//                
// item: <li>c</li>
// item: 
//              
Enter fullscreen mode Exit fullscreen mode

XMLでは改行や空白もTextノードなのでパターンに一致します。
for式のGeneratorでは

Pattern `<-' Expr

でパターンマッチングが使用できるので、これで必要なノードのみ抽出できます。

val list = <ul type="circle">
             <li>a</li>
             <li>b</li>
             <li>c</li>
           </ul>

list match {
  case <ul>{items @ _*}</ul> =>
    for (item @ <li>{_*}</li> <- items) {
      println(s"item: $item")
    }
}

// item: <li>a</li>
// item: <li>b</li>
// item: <li>c</li>
Enter fullscreen mode Exit fullscreen mode

Scalaスケーラブルプログラミング 第2版
28.8 XMLを対象とするパターンマッチ (P.566~569)

Atom

https://github.com/scala/scala-xml/blob/v1.0.5/src/main/scala/scala/xml/Atom.scala

記号とは直接関係ありませんが、この後new Atom()_.isAtomが出てくるため簡単に説明します。
Atomはlabelが#PCDATA ((PCDATA: Parsed Character Data)) であるTextノードです。

例えば、XML patternsのサンプルコードで一致した改行や空白のTextノードは、isAtomtrueを返します。

Elem

%

  final def %(updates: MetaData): Elem =
    copy(attributes = MetaData.update(attributes, scope, updates))
Enter fullscreen mode Exit fullscreen mode

指定されたattributes(MetaData)に更新した新しいElemを返す

NodeBuffer

&+

  def &+(o: Any): NodeBuffer = {
    o match {
      case null | _: Unit | Text("") => // ignore
      case it: Iterator[_]           => it foreach &+
      case n: Node                   => super.+=(n)
      case ns: Iterable[_]           => this &+ ns.iterator
      case ns: Array[_]              => this &+ ns.iterator
      case d                         => super.+=(new Atom(d))
    }
    this
  }
Enter fullscreen mode Exit fullscreen mode

下記のような処理になっています。

  • IterableArrayのようなコレクションはiterator.foreachで要素個別に処理
  • Nodeであればそのまま追加、それ以外はAtom(Textノード)として追加
  • nullUnit、空文字のTextは無視

NodeSeq

XPathに///がありますが、Scalaで//はコメント行として使用されるため、NodeSeqの記号ではバックスラッシュの\\\が使用されています。
(Scalaスケーラブルプログラミング 第2版 P.564より)

\

自身のノードを基準として条件に一致するノードを抽出します。
多機能なのでScaladocを引用すると下記のようになります。

  • this \ "foo" to get a list of all elements that are labelled with "foo";
  • \ "_" to get a list of all elements (wildcard);
  • ns \ "@foo" to get the unprefixed attribute "foo";
  • ns \ "@{uri}foo" to get the prefixed attribute "pre:foo" whose prefix "pre" is resolved to the namespace "uri".
  • this \ "foo"

直下の子ノードで要素名(label)に一致するノードを返します。
XPathのnode/fooに相当します。

  • this \ "_"

直下の子ノードでAtom以外のノードすべてを返します。(ワイルドカード)
XPathのnode/*に相当します。

  • ns \ "@foo"

自身のノードから一致する属性名の値を返します。
XPathのnode/@attributeに相当します。

ただし、自身が単一のノード(this.length == 1)である必要があります。
複数のノードの場合、空のNodeSeqを返します。

// 単一のノード
<a href="http://qiita.com/">Qiita</a> \ "@href" // = scala.xml.NodeSeq = http://qiita.com/

// 複数のノード
<a href="http://qiita.com/">Qiita</a>
<a href="https://teams.qiita.com/">Qiita:Team</a> \ "@href" // = scala.xml.NodeSeq = NodeSeq()
Enter fullscreen mode Exit fullscreen mode
  • ns \ "@{uri}foo"

名前空間プレフィックスのついた属性名(prefix:attribute)の値を返します。
Mavenのpom.xmlを例にすると下記のようになります。

val project =
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      http://maven.apache.org/xsd/maven-4.0.0.xsd"></project>

// "xsi:schemaLocation"の値を取得
project \ "@{http://www.w3.org/2001/XMLSchema-instance}schemaLocation"

// = scala.xml.NodeSeq =
// http://maven.apache.org/POM/4.0.0
//                       http://maven.apache.org/xsd/maven-4.0.0.xsd
Enter fullscreen mode Exit fullscreen mode

\\

XPathの//に相当し、すべての子孫ノードを対象に抽出を行います。

\@

  def \@(attributeName: String): String = (this \ ("@" + attributeName)).text
Enter fullscreen mode Exit fullscreen mode

Nodeの指定した属性名の値を取得する
(\ "@attributeName"と動作は等価ですが、戻り値はString型です)

CachedFileStorage

+=

  /** adds a node, setting this.dirty to true as a side effect */
  def +=(e: Node): Unit
Enter fullscreen mode Exit fullscreen mode

-=

  /** removes a tree, setting this.dirty to true as a side effect */
  def -=(e: Node): Unit
Enter fullscreen mode Exit fullscreen mode

SetStorage (extends CachedFileStorage)

+=

  def +=(e: Node): Unit = synchronized { this.dirty = true; theSet += e }
Enter fullscreen mode Exit fullscreen mode

-=

  def -=(e: Node): Unit = synchronized { this.dirty = true; theSet -= e }
Enter fullscreen mode Exit fullscreen mode

Top comments (0)

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay