From 5853b2c0bf0c2511dcc81c276f9a4eb31b4bb7bb Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Sat, 29 Feb 2020 16:44:41 +0900 Subject: [PATCH] migrate to mdoc from tut --- README.md | 10 +- build.sbt | 8 +- project/GitBook.scala | 15 ++- project/NpmCliBase.scala | 2 +- project/plugins.sbt | 2 +- src/basic.md | 12 +-- src/case-class-and-pattern-matching.md | 53 ++++++----- src/class.md | 22 ++--- src/collection.md | 127 +++++++++++++------------ src/control-syntax.md | 60 ++++++------ src/error-handling.md | 62 ++++++------ src/function.md | 22 ++--- src/future-and-promise.md | 16 ++-- src/implicit.md | 67 +++++++------ src/introduction-to-typeclass.md | 24 ++--- src/java-interop.md | 26 ++--- src/main/scala/Testing.scala | 7 -- src/object.md | 4 +- src/sbt-compile-execute.md | 8 +- src/sbt-install.md | 4 +- src/test.md | 20 ++-- src/trait.md | 50 +++++----- src/type-parameter.md | 32 +++---- src/typeclass.md | 30 +++--- test/link.js | 8 +- 25 files changed, 347 insertions(+), 344 deletions(-) delete mode 100644 src/main/scala/Testing.scala diff --git a/README.md b/README.md index 3d1ac460f..355b52c09 100644 --- a/README.md +++ b/README.md @@ -51,19 +51,19 @@ sbt textLint src/introduction.md # リンク切れ確認 sbt textLinkTest -# tutキャッシュなしのビルド +# mdocのビルド sbt textBuildHtml # 全ての検査を実行した後にビルド sbt textBuildAllWithCheck ``` -### tut +### mdoc -[tut](https://github.com/tpolecat/tut)という、Scalaコードを書くと、そのコードのチェックや +[mdoc](https://scalameta.org/mdoc/))という、Scalaコードを書くと、そのコードのチェックや 元のソースとなるmarkdownファイルから、実行後の出力を付け加えたmarkdownに変換してくれるツールを使用している。 -Scalaのコード例をテキスト中に書く場合は、使用可能な箇所では出来る限りtutを使うこと。 -tut自体の具体的な使用方法は、tutのREADMEなどを参照すること。 +Scalaのコード例をテキスト中に書く場合は、使用可能な箇所では出来る限りmdocを使うこと。 +mdoc自体の具体的な使用方法は、mdocのREADMEなどを参照すること。 ### gitbookで特別視されるファイルについて diff --git a/build.sbt b/build.sbt index 32c2b6a24..eeab2869d 100644 --- a/build.sbt +++ b/build.sbt @@ -9,17 +9,17 @@ name := "textbook" scalaVersion := "2.12.8" -enablePlugins(TutPlugin) +enablePlugins(MdocPlugin) -tutSourceDirectory := srcDir +mdocIn := srcDir -tutTargetDirectory := compiledSrcDir +mdocOut := compiledSrcDir libraryDependencies ++= Seq( "org.scala-sbt" % "sbt" % sbtVersion.value, "org.mockito" % "mockito-core" % "3.3.3", "org.scalacheck" %% "scalacheck" % "1.14.3", - "org.scalatest" %% "scalatest" % "3.1.1" // tutで使うので、テストライブラリだが、わざとcompileスコープ + "org.scalatest" %% "scalatest" % "3.1.1" // mdocで使うので、テストライブラリだが、わざとcompileスコープ ) GitBook.settings diff --git a/project/GitBook.scala b/project/GitBook.scala index 94e55a88d..e07d85ad4 100644 --- a/project/GitBook.scala +++ b/project/GitBook.scala @@ -1,6 +1,6 @@ import sbt._ import sbt.io.IO -import tut.TutPlugin.autoImport._ +import mdoc.MdocPlugin.autoImport._ import scala.sys.process.Process object GitBook extends NpmCliBase { @@ -21,19 +21,16 @@ object GitBook extends NpmCliBase { lazy val textPluginInstall = taskKey[Unit]("install GitBook plugin") lazy val textHelpGitBook = taskKey[Unit]("help GitBook") - lazy val textBuildHtmlQuick = inputKey[Unit]("build GitBook to html with tut cache") + lazy val textBuildHtmlQuick = inputKey[Unit]("build GitBook to html with mdoc cache") lazy val textBuildHtml = inputKey[Unit]("build GitBook to html") lazy val textBuildEpub = inputKey[Unit]("build GitBook to epub") - lazy val textBuildPdf = inputKey[Unit]("build GitBook to pdf") + + private[this] val mdocTask = mdoc.toTask("") val settings = Seq( textPluginInstall := printRun(Process(s"$gitbookBin install")), textHelpGitBook := printRun(Process(s"$gitbookBin help")), - textBuildHtml := buildBook(Format.Html).dependsOn(tut).evaluated, - textBuildEpub := buildBook(Format.Epub).dependsOn(tut).evaluated, - textBuildPdf := sys.error("pdf-convertで利用するcalibreがcentOS6で上手く動かないので停止中"), - - // キャッシュ付きのtutを利用する - textBuildHtmlQuick := buildBook(Format.Html).dependsOn(tutQuick).evaluated + textBuildHtml := buildBook(Format.Html).dependsOn(mdocTask).evaluated, + textBuildEpub := buildBook(Format.Epub).dependsOn(mdocTask).evaluated ) } diff --git a/project/NpmCliBase.scala b/project/NpmCliBase.scala index 7eca809b7..2c6d6320f 100644 --- a/project/NpmCliBase.scala +++ b/project/NpmCliBase.scala @@ -18,7 +18,7 @@ trait NpmCliBase { // gitbookのビルドの起点/成果物が入るディレクトリ(gitbook/_book/index.html, gitbook/scala_text.epubが生成される) val bookDestDir = file("gitbook") - // tutで処理済みのmarkdownファイルが入るディレクトリ。これがgitbook buildされる + // mdocで処理済みのmarkdownファイルが入るディレクトリ。これがgitbook buildされる // book.jsonのrootにも指定されている。 val compiledSrcDir = bookDestDir diff --git a/project/plugins.sbt b/project/plugins.sbt index dcfb8863a..58508417c 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,3 @@ logLevel := Level.Warn -addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.6.13") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.1.1" ) diff --git a/src/basic.md b/src/basic.md index e4201dc84..f4f5775db 100644 --- a/src/basic.md +++ b/src/basic.md @@ -34,14 +34,14 @@ scala> まずは定番のHello, World!を表示させてみましょう。 -```tut +```scala mdoc:nest println("Hello, World!") ``` 無事、Hello, World!が表示されるはずです。次にちょっと入力を変えて、`println()`をはずしてみましょう。 さて、どうなるでしょうか。 -```tut +```scala mdoc:nest "Hello, World!" ``` @@ -87,7 +87,7 @@ res2: Int = 3 この機能はREPLで少し長いプログラムを入力するときに便利ですので、活用していきましょう。Int型には他にも`+`,`-`,`*`,`/`といった 演算子が用意されており、次のようにして使うことができます。 -```tut +```scala mdoc:nest 1 + 2 2 * 2 @@ -101,7 +101,7 @@ res2: Int = 3 見ればわかるように、`Double`という`Int`と異なる型が用意されています。`dbl.asInstanceOf[Int]`のようにキャストして型を変換することが できますが、その場合、浮動小数点数の小数の部分が切り捨てられることに注意してください。 -```tut +```scala mdoc:nest 1.0 + 2.0 2.2 * 2 @@ -153,7 +153,7 @@ Javaのfinalな変数と同じように考えれば良いでしょう。 それでは、変数を使ってみることにします。Scalaでは基本的に、`var`はあまり使わず`val`のみでプログラミングします。これは**Scalaでプログラミングをする上で大変重要なことなので**忘れない ようにしてください。 -```tut +```scala mdoc:nest val x = 3 * 2 ``` @@ -195,7 +195,7 @@ x: Int = 12 のようにします。 -```tut +```scala mdoc:nest val x: Int = 3 * 3 ``` diff --git a/src/case-class-and-pattern-matching.md b/src/case-class-and-pattern-matching.md index ec6037364..7d8738005 100644 --- a/src/case-class-and-pattern-matching.md +++ b/src/case-class-and-pattern-matching.md @@ -6,7 +6,7 @@ 簡単なケースクラスによるデータ型を定義してみます。 -```tut:silent +```scala mdoc:nest:silent sealed abstract class DayOfWeek case object Sunday extends DayOfWeek case object Monday extends DayOfWeek @@ -20,7 +20,7 @@ case object Saturday extends DayOfWeek これは、一週間の曜日を表すデータ型です。CやJavaの`enum`に似ていますね。実際、同じように使うことができます。 たとえば、以下のように`DayOfWeek`型の変数に`Sunday`を代入することができます。 -```tut:silent +```scala mdoc:nest:silent val x: DayOfWeek = Sunday ``` @@ -37,7 +37,7 @@ val x: DayOfWeek = Sunday のようになります。`DayOfWeek`の場合、次のようにして使うことができます。 -```tut +```scala mdoc:nest x match { case Sunday => 1 case Monday => 2 @@ -64,7 +64,7 @@ x match { 二項演算の結果として小数が現れた場合は小数部を切り捨てることとします。これを表すデータ型 をScalaで定義すると次のようになります。 -```tut:silent +```scala mdoc:nest:silent sealed abstract class Exp case class Add(lhs: Exp, rhs: Exp) extends Exp case class Sub(lhs: Exp, rhs: Exp) extends Exp @@ -76,14 +76,14 @@ case class Lit(value: Int) extends Exp 全てのデータ型に`case`修飾子がついているので、これらのデータ型はパターンマッチングのパターンとして使うことができます。 この定義から、`1 + ((2 * 3) / 2)`という式を表すノードを構築します。 -```tut +```scala mdoc:nest val example = Add(Lit(1), Div(Mul(Lit(2), Lit(3)), Lit(2))) ``` この`example`ノードを元に四則演算を定義する関数を定義してみます。関数の定義の 詳細は後ほど説明しますが、ここでは雰囲気だけをつかんでください。 -```tut +```scala mdoc:nest def eval(exp: Exp): Int = exp match { case Add(l, r) => eval(l) + eval(r) case Sub(l, r) => eval(l) - eval(r) @@ -95,7 +95,7 @@ def eval(exp: Exp): Int = exp match { この定義をREPLに読み込ませて、`eval(example)`として、 -```tut +```scala mdoc:nest eval(example) ``` @@ -124,13 +124,13 @@ It would fail on the following input: Lit(_) たとえば、次のようなケースクラス`Point`があったとします。 -```tut +```scala mdoc:nest case class Point(x: Int, y: Int) ``` このケースクラス`Point`に対して、 -```tut +```scala mdoc:nest val Point(x, y) = Point(10, 20) ``` @@ -171,7 +171,7 @@ res5: Boolean = false クラスに実装を足すことでも同等の振る舞いを持たせることもできます。 例えば、前節で定義した`Point`をケースクラスを使わずに定義するとしたら、次のようになるでしょう。 -```tut +```scala mdoc:nest class Point(val x: Int, val y: Int) { override def equals(that: Any): Boolean = that match { case thatPoint: Point => @@ -209,7 +209,7 @@ object Point { `DayOfWeek`型を使って、ある日の次の曜日を返すメソッド`nextDayOfWeek` -```tut:silent +```scala mdoc:nest:silent def nextDayOfWeek(d: DayOfWeek): DayOfWeek = ??? ``` @@ -217,7 +217,7 @@ def nextDayOfWeek(d: DayOfWeek): DayOfWeek = ??? -```tut:silent +```scala mdoc:nest:silent def nextDayOfWeek(d: DayOfWeek): DayOfWeek = d match { case Sunday => Monday case Monday => Tuesday @@ -229,7 +229,7 @@ def nextDayOfWeek(d: DayOfWeek): DayOfWeek = d match { } ``` -```tut +```scala mdoc:nest nextDayOfWeek(Sunday) nextDayOfWeek(Monday) nextDayOfWeek(Saturday) @@ -241,7 +241,7 @@ nextDayOfWeek(Saturday) 二分木(子の数が最大で2つであるような木構造)を表す型`Tree`と`Branch`, `Empty`を考えます: -```tut:silent +```scala mdoc:nest:silent sealed abstract class Tree case class Branch(value: Int, left: Tree, right: Tree) extends Tree case object Empty extends Tree @@ -249,7 +249,7 @@ case object Empty extends Tree 子が2つで左の子の値が`2`、右の子の値が`3`、自分自身の値が`1`の木構造はたとえば次のようにして定義することができます。 -```tut +```scala mdoc:nest val tree: Tree = Branch(1, Branch(2, Empty, Empty), Branch(3, Empty, Empty)) ``` @@ -259,7 +259,7 @@ val tree: Tree = Branch(1, Branch(2, Empty, Empty), Branch(3, Empty, Empty)) 2. 最小値を求める`min`メソッド: 3. 深さを求める`depth`メソッド: -```tut:silent +```scala mdoc:nest:silent def max(tree: Tree): Int = ??? def min(tree: Tree): Int = ??? def depth(tree: Tree): Int = ??? @@ -296,7 +296,7 @@ depth(Branch(10, Branch(20, となるような木構造に変換する`sort`メソッド: -```tut:silent +```scala mdoc:nest:silent def sort(tree: Tree): Tree = ??? ``` @@ -304,7 +304,7 @@ def sort(tree: Tree): Tree = ??? -```tut:silent +```scala mdoc:nest:silent object BinaryTree { sealed abstract class Tree case class Branch(value: Int, left: Tree, right: Tree) extends Tree @@ -382,9 +382,14 @@ object BinaryTree { } ``` -```tut:invisible +```scala mdoc:nest:invisible import org.scalacheck._, Arbitrary.arbitrary +def test[G] (g: Gen[G])(f: G => Boolean) = { + val result = Prop.forAll(g)(f).apply(Gen.Parameters.default) + assert(result.success, result) +} + val nonEmptyTreeGen: Gen[BinaryTree.Tree] = { lazy val branchGen: Gen[BinaryTree.Tree] = for{ @@ -402,11 +407,11 @@ val nonEmptyTreeGen: Gen[BinaryTree.Tree] = { branchGen } -Testing.test(nonEmptyTreeGen){ tree => +test(nonEmptyTreeGen){ tree => BinaryTree.max(tree) == BinaryTree.toList(tree).max } -Testing.test(nonEmptyTreeGen){ tree => +test(nonEmptyTreeGen){ tree => BinaryTree.min(tree) == BinaryTree.toList(tree).min } ``` @@ -419,7 +424,7 @@ Testing.test(nonEmptyTreeGen){ tree => これまでの説明の中で、無名関数とパターンマッチングについて説明してきましたが、この2つの機能を組み合わせた 部分関数(PartialFunction)がScalaには存在します。説明の前に、まず、具体的なユースケースを挙げます: -```tut +```scala mdoc:nest List(1, 2, 3, 4, 5).collect { case i if i % 2 == 1 => i * 2 } ``` @@ -455,7 +460,7 @@ i % 2 == 1 が生成されるのであって、通常の `FunctionN` 型が要求されたときには、違う意味を持つということです。たとえば、以下のような 定義があったとします。 -```tut +```scala mdoc:nest val even: Int => Boolean = { case i if i % 2 == 0 => true case _ => false @@ -464,7 +469,7 @@ val even: Int => Boolean = { このとき、この定義は、無名関数とパターンマッチを組み合わせたものと同じ意味になります。この点にだけ注意してください。 -```tut +```scala mdoc:nest val even: Int => Boolean = (x => x match { case i if i % 2 == 0 => true case _ => false diff --git a/src/class.md b/src/class.md index a3e9eefce..dbf426be8 100644 --- a/src/class.md +++ b/src/class.md @@ -15,7 +15,7 @@ class <クラス名> '(' (<引数名1> : <引数型1>, <引数名2>: <引数型2 たとえば、点を表すクラス`Point`を定義したいとします。`Point`はx座標を表すフィールド`x`(`Int`型)とフィールド`y`(`Int`型)からなるとします。このクラス`Point`をScalaで書くと次のようになります。 -```tut:silent +```scala mdoc:nest:silent class Point(_x: Int, _y: Int) { val x = _x val y = _y @@ -24,7 +24,7 @@ class Point(_x: Int, _y: Int) { コンストラクタの引数と同名のフィールドを定義し、それを公開する場合は、以下のように短く書くこともできます。 -```tut:silent +```scala mdoc:nest:silent class Point(val x: Int, val y: Int) ``` @@ -35,7 +35,7 @@ class Point(val x: Int, val y: Int) 2番目の点ですが、プライマリコンストラクタの引数にval/varをつけるとそのフィールドは公開され、外部からアクセスできるようになります。なお、プライマリコンストラクタの引数のスコープはクラス定義全体におよびます。そのため、以下のようにメソッド定義の中から直接コンストラクタ引数を参照できます。 -```tut:silent +```scala mdoc:nest:silent class Point(val x: Int, val y: Int) { def +(p: Point): Point = { new Point(x + p.x, y + p.y) @@ -71,7 +71,7 @@ class Point(val x: Int, val y: Int) { 先ほど定義した`Point`クラスをREPLから使ってみましょう。 -```tut +```scala mdoc:nest class Point(val x: Int, val y: Int) { def +(p: Point): Point = { new Point(x + p.x, y + p.y) @@ -100,7 +100,7 @@ p1 + p2 複数の引数リストを持つメソッドには、Scalaの糖衣構文と組み合わせて流暢なAPIを作ったり、後述するimplicit parameterのために必要になったり、型推論を補助するために使われたりといった用途があります。複数の引数リストを持つ加算メソッドを定義してみましょう。 -```tut +```scala mdoc:nest class Adder { def add(x: Int)(y: Int): Int = x + y } @@ -117,7 +117,7 @@ fun(3) 次のように、複数の引数リストを使わずに単に複数の引数を持つメソッドも作ることができます。 -```tut +```scala mdoc:nest class Adder { def add(x: Int, y: Int): Int = x + y } @@ -158,7 +158,7 @@ fun(3) メソッドやフィールドの中身がない以外は、通常のメソッドやフィールド定義と同じです。また、抽象メソッドを一個以上持つクラスは、抽象クラスとして宣言する必要があります。たとえば、`x`座標と`y`座標を持つ、抽象クラス`XY`は次のようにして定義します。クラスの前に`abstract` 修飾子をつける必要があるのがポイントです。 -```tut +```scala mdoc:nest abstract class XY { def x: Int def y: Int @@ -179,7 +179,7 @@ class <クラス名> <クラス引数> (extends <スーパークラス>)? (with トレイト名はここでは使われませんが、後で出てくるトレイトの節で説明を行います。継承のはたらきはJavaのクラスと同様ですが、既存のメソッドをオーバーライドするときは`override`キーワードを使わなければならない点が異なります。 たとえば、次のようにすることができます。 -```tut +```scala mdoc:nest class APrinter() { def print(): Unit = { println("A") @@ -200,7 +200,7 @@ new BPrinter().print ここで`override`キーワードをはずすと、次のようにメッセージを出力して、**コンパイルエラー**になります。 -```tut:fail +```scala class BPrinter() extends APrinter { def print(): Unit = { println("B") @@ -223,7 +223,7 @@ println(p.z) // 30 -```tut:silent +```scala mdoc:nest:silent class Point3D(val x: Int, val y: Int, val z: Int) ``` @@ -231,7 +231,7 @@ class Point3D(val x: Int, val y: Int, val z: Int) プライマリコンストラクタの引数として座標の値を渡し、それをそのまま取り出しているので、プライマリコンストラクタの引数に `val` を付けるのが最も簡単です。別解として、以下のように別途 `val` でフィールドを定義することも可能ですが、今回あえてそうする意味は少ないでしょう。 -```tut:silent +```scala mdoc:nest:silent class Point3D(x_ : Int, y_ : Int, z_ : Int) { val x: Int = x_ val y: Int = y_ diff --git a/src/collection.md b/src/collection.md index eef71c783..7e2dfea6c 100644 --- a/src/collection.md +++ b/src/collection.md @@ -26,20 +26,20 @@ mutableなコレクションを効果的に使えばプログラムの実行速 まずは大抵のプログラミング言語にある配列です。 -```tut +```scala mdoc:nest val arr = Array(1, 2, 3, 4, 5) ``` これで1から5までの要素を持った配列が`arr`に代入されました。Scalaの配列は、他の言語のそれと同じように要素の中身を入れ替えることができます。配列の添字は0から始まります。なお、配列の型を指定しなくて良いのは、`Array(1, 2, 3, 4, 5)`の部分で、要素型が`Int`であるに違いないと コンパイラが型推論してくれるからです。型を省略せずに書くと -```tut +```scala mdoc:nest val arr = Array[Int](1, 2, 3, 4, 5) ``` となります。ここで、`[Int]`の部分は型パラメータと呼びます。`Array`だけだとどの型かわからないので、`[Int]`を付けることでどの型の`Array`かを指定しているわけです。この型パラメータは型推論を補うために、色々な箇所で出てくるので覚えておいてください。しかし、この場面では、`Array`の要素型は`Int`だとわかっているので、冗長です。次に要素へのアクセスと代入です。 -```tut +```scala mdoc:nest arr(0) = 7 arr @@ -51,7 +51,7 @@ arr(0) 配列の長さは`arr.length`で取得することができます。 -```tut +```scala mdoc:nest arr.length ``` @@ -66,23 +66,23 @@ arr.length 配列の`i`番目の要素と`j`番目の要素を入れ替える`swapArray`メソッドを定義してみましょう。`swapArray`メソッドの宣言は -```tut:silent -def swapArray[T](arr: Array[T])(i: Int, j: Int): Unit = ??? +```scala mdoc:nest:silent +def swapArray[T] (arr: Array[T])(i: Int, j: Int): Unit = ??? ``` となります。`i`と`j`が配列の範囲外である場合は特に考慮しなくて良いです。 -```tut:silent -def swapArray[T](arr: Array[T])(i: Int, j: Int): Unit = { +```scala mdoc:nest:silent +def swapArray[T] (arr: Array[T])(i: Int, j: Int): Unit = { val tmp = arr(i) arr(i) = arr(j) arr(j) = tmp } ``` -```tut +```scala mdoc:nest val arr = Array(1, 2, 3, 4, 5) swapArray(arr)(0, 4) @@ -100,7 +100,7 @@ arr `Range`は範囲を表すオブジェクトです。`Range`は直接名前を指定して生成するより、`to`メソッドと`until`メソッドを用いて呼びだすことが多いです。また、`toList`メソッドを用いて、その範囲の数値の列を後述する`List`に変換することができます。では、早速REPLで`Range`を使ってみましょう。 -```tut +```scala mdoc:nest 1 to 5 (1 to 5).toList @@ -119,11 +119,11 @@ arr 変更できない(immutable)ということです。中身を変更できないデータ構造(永続データ構造とも呼びます)はScalaがサポートしている 関数型プログラミングにとって重要な要素です。それでは`List`を使ってみましょう。 -```tut +```scala mdoc:nest val lst = List(1, 2, 3, 4, 5) ``` -```tut:fail +```scala lst(0) = 7 ``` @@ -138,7 +138,7 @@ lst(0) = 7 `::`(コンスと読みます)は既にある`List`の先頭に要素をくっつけるメソッドです。これについては、REPLで結果をみた方が早いでしょう。 -```tut +```scala mdoc:nest val a1 = 1 :: Nil val a2 = 2 :: a1 val a3 = 3 :: a2 @@ -148,13 +148,13 @@ val a5 = 5 :: a3 付け足したい要素を`::`を挟んで`List`の前に書くことで`List`の先頭に要素がくっついていることがわかります。ここで、`::`はやや特別な呼び出し方をするメソッドであることを説明しなければなりません。まず、Scalaでは1引数のメソッドは中置記法で書くことができます。それで、`1 :: Nil` のように書くことができるわけです。次に、メソッド名の最後が`:`で終わる場合、被演算子の前と後ろをひっくり返して右結合で呼び出します。たとえば、 -```tut +```scala mdoc:nest 1 :: 2 :: 3 :: 4 :: Nil ``` は、実際には、 -```tut +```scala mdoc:nest Nil.::(4).::(3).::(2).::(1) ``` @@ -164,7 +164,7 @@ Nil.::(4).::(3).::(2).::(1) `++`はList同士を連結するメソッドです。これもREPLで見た方が早いでしょう。 -```tut +```scala mdoc:nest List(1, 2) ++ List(3, 4) List(1) ++ List(3, 4, 5) @@ -174,13 +174,13 @@ List(3, 4, 5) ++ List(1) `++`は1引数のメソッドなので、中置記法で書いています。また、末尾が`:`で終わっていないので、たとえば、 -```tut +```scala mdoc:nest List(1, 2) ++ List(3, 4) ``` は -```tut +```scala mdoc:nest List(1, 2).++(List(3, 4)) ``` @@ -196,14 +196,14 @@ List(1, 2).++(List(3, 4)) 引数なしバージョンです。このメソッドは、単に`List`の各要素を左から順に繋げた文字列を返します。 -```tut +```scala mdoc:nest List(1, 2, 3, 4, 5).mkString ``` 注意しなければならないのは、引数なしメソッドの`mkString`は`()`を付けて呼びだすことが**できない** という点です。たとえば、以下のコードは、若干分かりにくいエラーメッセージがでてコンパイルに失敗します。 -```tut:fail +```scala List(1, 2, 3, 4, 5).mkString() ``` @@ -217,7 +217,7 @@ Scalaの`0`引数メソッドは`()`なしと 引数にセパレータ文字列`sep`を取り、`List`の各要素を`sep`で区切って左から順に繋げた文字列を返します。 -```tut +```scala mdoc:nest List(1, 2, 3, 4, 5).mkString(",") ``` @@ -225,7 +225,7 @@ List(1, 2, 3, 4, 5).mkString(",") `mkString(sep)`とほとんど同じですが、`start`と`end`に囲まれた文字列を返すところが異なります。 -```tut +```scala mdoc:nest List(1, 2, 3, 4, 5).mkString("[", ",", "]") ``` @@ -245,7 +245,7 @@ start,(start+1),(start+2)...,end joinByComma(1,5) // 1,2,3,4,5 ``` -```tut:silent +```scala mdoc:nest:silent def joinByComma(start: Int, end: Int): String = { ??? } @@ -253,13 +253,13 @@ def joinByComma(start: Int, end: Int): String = { -```tut:silent +```scala mdoc:nest:silent def joinByComma(start: Int, end: Int): String = { (start to end).mkString(",") } ``` -```tut +```scala mdoc:nest joinByComma(1, 10) ``` @@ -300,13 +300,13 @@ def foldLeft[B](z: B)(f: (B, A) ⇒ B): B は英語で畳み込むという意味を持ちます)状態がイメージできるでしょうか。`foldLeft`は汎用性の高いメソッドで、 たとえば、`List`の要素の合計を求めたい場合は -```tut +```scala mdoc:nest List(1, 2, 3).foldLeft(0)((x, y) => x + y) ``` `List`の要素を全て掛けあわせた結果を求めたい場合は -```tut +```scala mdoc:nest List(1, 2, 3).foldLeft(1)((x, y) => x * y) ``` @@ -330,7 +330,7 @@ scala> List(List(1), List(2, 3), List(4)).foldLeft(Nil)(_ ++ _) エラーメッセージの意味としては、今回の`Nil`は`List[Int]`型と見なされてほしいわけですが、期待したように型推論できていないようです。 `Nil`に明示的に型注釈を付けることで、コンパイルできるようになります。 -```tut +```scala mdoc:nest List(List(1), List(2, 3), List(4)).foldLeft(Nil: List[Int])(_ ++ _) ``` @@ -341,27 +341,32 @@ List(List(1), List(2, 3), List(4)).foldLeft(Nil: List[Int])(_ ++ _) `foldLeft`を用いて、`List`の要素を反転させる次のシグニチャを持ったメソッド`reverse`を実装してみましょう: -```tut:silent +```scala mdoc:nest:silent def reverse[T](list: List[T]): List[T] = ??? ``` -```tut:silent +```scala mdoc:nest:silent def reverse[T](list: List[T]): List[T] = list.foldLeft(Nil: List[T])((a, b) => b :: a) ``` -```tut +```scala mdoc:nest reverse(List(1, 2, 3, 4, 5)) ``` `foldLeft` の初期値に `Nil` を与えて、そこから後ろにたどる毎に、「前に」要素を追加していくことで、 逆順のリストを作ることができています。 -```tut:invisible +```scala mdoc:nest:invisible import org.scalacheck._, Arbitrary.arbitrary -Testing.test(arbitrary[List[Int]]){ list => +def test[G] (g: Gen[G])(f: G => Boolean) = { + val result = Prop.forAll(g)(f).apply(Gen.Parameters.default) + assert(result.success, result) +} + +test(arbitrary[List[Int]]){ list => reverse(list) == list.reverse } ``` @@ -398,17 +403,17 @@ def foldRight[B](z: B)(op: (A, B) ⇒ B): B `List`の全ての要素を足し合わせるメソッド`sum`を`foldRight`を用いて実装してみましょう。`sum`の宣言は次のようになります。 なお、`List`が空のときは0を返してみましょう。 -```tut:silent +```scala mdoc:nest:silent def sum(list: List[Int]): Int = ??? ``` -```tut:silent +```scala mdoc:nest:silent def sum(list: List[Int]): Int = list.foldRight(0){(x, y) => x + y} ``` -```tut +```scala mdoc:nest sum(List(1, 2, 3, 4, 5)) ``` @@ -419,17 +424,17 @@ sum(List(1, 2, 3, 4, 5)) `List`の全ての要素を掛け合わせるメソッド`mul`を`foldRight`を用いて実装してみましょう。`mul`の宣言は次のようになります。 なお、`List`が空のときは1を返してみましょう。 -```tut +```scala mdoc:nest def mul(list: List[Int]): Int = ??? ``` -```tut:silent +```scala mdoc:nest:silent def mul(list: List[Int]): Int = list.foldRight(1){(x, y) => x * y} ``` -```tut +```scala mdoc:nest mul(List(1, 2, 3, 4, 5)) ``` @@ -441,7 +446,7 @@ mul(List(1, 2, 3, 4, 5)) 他のメソッドは自由に使って構いません。[ListのAPIリファレンス](https://www.scala-lang.org/api/current/scala/collection/immutable/List.html) を読めば必要なメソッドが載っています。実装する`mkString`の宣言は -```tut:silent +```scala mdoc:nest:silent def mkString[T](list: List[T])(sep: String): String = ??? ``` @@ -449,7 +454,7 @@ def mkString[T](list: List[T])(sep: String): String = ??? -```tut:silent +```scala mdoc:nest:silent def mkString[T](list: List[T])(sep: String): String = list match { case Nil => "" case x::xs => xs.foldLeft(x.toString){(x, y) => x + sep + y} @@ -463,7 +468,7 @@ def mkString[T](list: List[T])(sep: String): String = list match { `map`メソッドは、1引数の関数を引数に取り、各要素に関数を適用した結果できた要素からなる新たな`List`を返します。 ためしに`List(1, 2, 3, 4, 5)`の各要素を2倍してみましょう。 -```tut +```scala mdoc:nest List(1, 2, 3, 4, 5).map(x => x * 2) ``` `x => x * 2`の部分は既に述べたように、無名関数を定義するための構文です。メソッドの引数に与える短い関数を定義するときは、 @@ -474,13 +479,13 @@ Scalaのコレクションのメソッドの中でも非常によく使われる 次のシグニチャを持つ`map`メソッドを`foldLeft`と`reverse`を使って実装してみましょう: -```tut:silent +```scala mdoc:nest:silent def map[T, U](list: List[T])(f: T => U): List[U] = ??? ``` -```tut:silent +```scala mdoc:nest:silent def map[T, U](list: List[T])(f: T => U): List[U] = { list.foldLeft(Nil:List[U]){(x, y) => f(y) :: x}.reverse } @@ -493,7 +498,7 @@ def map[T, U](list: List[T])(f: T => U): List[U] = { `filter`メソッドは、`Boolean`型を返す1引数の関数を引数に取り、各要素に関数を適用し、`true`になった要素のみを抽出した 新たな`List`を返します。`List(1, 2, 3, 4, 5)`から奇数だけを抽出してみましょう。 -```tut +```scala mdoc:nest List(1, 2, 3, 4, 5).filter(x => x % 2 == 1) ``` @@ -501,13 +506,13 @@ List(1, 2, 3, 4, 5).filter(x => x % 2 == 1) 次のシグニチャを持つ`filter`メソッドを`foldLeft`と`reverse`を使って実装してみましょう: -```tut:silent +```scala mdoc:nest:silent def filter[T](list: List[T])(f: T => Boolean): List[T] = ??? ``` -```tut:silent +```scala mdoc:nest:silent def filter[T](list: List[T])(f: T => Boolean): List[T] = { list.foldLeft(Nil:List[T]){(x, y) => if(f(y)) y::x else x}.reverse } @@ -521,7 +526,7 @@ def filter[T](list: List[T])(f: T => Boolean): List[T] = { `Some`でくるんだ値を`Option`型として返します。1つの要素もマッチしなかった場合`None`を`Option`型として返します。 `List(1, 2, 3, 4, 5)`から最初の奇数だけを抽出してみましょう -```tut +```scala mdoc:nest List(1, 2, 3, 4, 5).find(x => x % 2 == 1) ``` @@ -531,7 +536,7 @@ List(1, 2, 3, 4, 5).find(x => x % 2 == 1) `takeWhile`メソッドは、`Boolean`型を返す1引数の関数を引数に取り、前から順番に関数を適用し、結果が`true`の間のみからなる`List`を返します。`List(1, 2, 3, 4, 5)`の5より前の4要素を抽出してみます。 -```tut +```scala mdoc:nest List(1, 2, 3, 4, 5).takeWhile(x => x != 5) ``` @@ -539,7 +544,7 @@ List(1, 2, 3, 4, 5).takeWhile(x => x != 5) `count`メソッドは、`Boolean`型を返す1引数の関数を引数に取り、全ての要素に関数を適用して、`true`が返ってきた要素の数を計算します。例として`List(1, 2, 3, 4, 5)`の中から偶数の数(2になるはず)を計算してみます。 -```tut +```scala mdoc:nest List(1, 2, 3, 4, 5).count(x => x % 2 == 0) ``` @@ -547,13 +552,13 @@ List(1, 2, 3, 4, 5).count(x => x % 2 == 0) 次のシグニチャを持つ`count`メソッドを`foldLeft`を使って実装してみましょう: -```tut:silent +```scala mdoc:nest:silent def count[T](list: List[T])(f: T => Boolean): Int = ??? ``` -```tut:silent +```scala mdoc:nest:silent def count[T](list: List[T])(f: T => Boolean): Int = { list.foldLeft(0){(x, y) => if(f(y)) x + 1 else x} } @@ -574,13 +579,13 @@ final def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): List[B] できる型程度に考えてください。さて、flatMapの引数fの型は`(A) => GenTraversableOnce[B]`です。`flatMap`はこれを使って、各 要素にfを適用して、結果の要素からなるコレクションを分解してListの要素にします。これについては、実際に見た方が早いでしょう。 -```tut +```scala mdoc:nest List(List(1, 2, 3), List(4, 5)).flatMap{e => e.map{g => g + 1}} ``` ネストした`List`の各要素に`flatMap`の中で`map`を適用して、`List`の各要素に1を足したものをたいらにしています。これだけだとありがたみがわかりにくいですが、ちょっと形を変えてみると非常に面白い使い方ができます: -```tut +```scala mdoc:nest List(1, 2, 3).flatMap{e => List(4, 5).map(g => e * g)} ``` @@ -608,7 +613,7 @@ for構文の中で使うことができるのです。 特に他の言語のプログラマはうっかり`List`の末尾に要素を追加するような遅いプログラムを書いてしまうことがあるので注意する必要 があります。 -```tut +```scala mdoc:nest List(1, 2, 3, 4) 5 :: List(1, 2, 3, 4) // Listの先頭のセルに新しいをくっつける @@ -626,7 +631,7 @@ List(1, 2, 3, 4) :+ 5 // 注意!末尾への追加は、Listの要素数分か です。要素へのランダムアクセスや長さの取得、データの挿入や削除、いずれの操作も十分に高速にできる比較的 万能なデータ構造です。immutableなデータ構造を使う場合は、まず`Vector`を検討すると良いでしょう。 -```tut +```scala mdoc:nest Vector(1, 2, 3, 4, 5) //どの操作も「ほぼ」一定の時間で終わる 6 +: Vector(1, 2, 3, 4, 5) @@ -647,7 +652,7 @@ Scalaで何も設定せずにただ`Map`と書いた場合、`scala.collection.i 作成すると変更することはできません。内部の実装としては主に[`scala.collection.immutable.HashMap`](https://github.com/scala/scala/blob/v2.12.8/src/library/scala/collection/immutable/HashMap.scala)と [`scala.collection.immutable.TreeMap`](https://github.com/scala/scala/blob/v2.12.8/src/library/scala/collection/immutable/TreeMap.scala)の2種類がありますが、通常は`HashMap`が使われます。 -```tut +```scala mdoc:nest val m = Map("A" -> 1, "B" -> 2, "C" -> 3) m.updated("B", 4) //一見元のMapを変更したように見えても @@ -661,7 +666,7 @@ Scalaの変更可能な`Map`は`scala.collection.mutable.Map`にあります。 `scala.collection.mutable.LinkedHashMap`、リストをベースにした`scala.collection.mutable.ListMap`がありますが、通常は `HashMap`が使われます。 -```tut +```scala mdoc:nest import scala.collection.mutable val m = mutable.Map("A" -> 1, "B" -> 2, "C" -> 3) @@ -675,7 +680,7 @@ m // 変更が反映されている `Set`は値の集合を提供するデータ構造です。`Set`の中では同じ値が2つ以上存在しません。たとえば、`Int`の`Set`の中には1が2つ以上含まれていてはいけません。REPLで`Set`を作成するための式を入力すると、 -```tut +```scala mdoc:nest Set(1, 1, 2, 3, 4) ``` @@ -688,7 +693,7 @@ Scalaで何も設定せずにただ`Set`と書いた場合、`scala.collection.i 同じく、一度作成すると変更することはできません。内部の実装としては、主に [`scala.collection.immutable.HashSet`](https://github.com/scala/scala/blob/v2.12.8/src/library/scala/collection/immutable/HashSet.scala) と [`scala.collection.immutable.TreeSet`](https://github.com/scala/scala/blob/v2.12.8/src/library/scala/collection/immutable/TreeSet.scala) の2種類がありますが、通常は`HashSet`が使われます。 -```tut +```scala mdoc:nest val s = Set(1, 2, 3, 4, 5) s - 5 // 5を削除した後も @@ -701,7 +706,7 @@ s // 元のSetはそのまま Scalaの変更可能な`Set`は`scala.collection.mutable.Set`にあります。主な実装としては、[`scala.collection.mutable.HashSet`](https://github.com/scala/scala/blob/v2.12.8/src/library/scala/collection/mutable/HashSet.scala) 、 [`scala.collection.mutable.TreeSet`](https://github.com/scala/scala/blob/v2.12.8/src/library/scala/collection/mutable/TreeSet.scala)がありますが、通常は`HashSet`が使われます。 -```tut +```scala mdoc:nest import scala.collection.mutable val s = mutable.Set(1, 2, 3, 4, 5) diff --git a/src/control-syntax.md b/src/control-syntax.md index ae14a156b..e79ca1981 100644 --- a/src/control-syntax.md +++ b/src/control-syntax.md @@ -40,7 +40,7 @@ Scalaでは `{}` で複数の式の並びを囲むと、それ全体が式にな 次の式では -```tut +```scala mdoc:nest { println("A"); println("B"); 1 + 2; } ``` @@ -48,7 +48,7 @@ AとBが出力され、最後の式である`1 + 2`の結果である`3`が`{}` このことは、後ほど記述するメソッド定義などにおいて重要になってきます。Scalaでは、 -```tut:silent +```scala mdoc:nest:silent def foo(): String = { "foo" + "foo" } @@ -69,7 +69,7 @@ if '('<条件式>')' (else )? 早速`if`式を使ってみましょう。 -```tut +```scala mdoc:nest var age = 17 if(age < 18) { @@ -107,7 +107,7 @@ if '(' <条件式> ')' else () -```tut +```scala mdoc:nest var age: Int = 5 var isSchoolStarted: Boolean = false if(1 <= age && age <= 6 && !isSchoolStarted) { @@ -131,7 +131,7 @@ while '(' <条件式> ')' 本体式 さて、 `while` 式を使って1から10までの値を出力してみましょう。 -```tut +```scala mdoc:nest var i = 1 while(i <= 10) { @@ -146,7 +146,7 @@ Javaで `while` 文を使った場合と同様です。 `do while` 式もあり `do while`を利用して、0から数え上げて9まで出力して10になったらループを終了するメソッド`loopFrom0To9`を書いてみましょう。`loopFrom0To9`は次のような形になります。`???`の部分を埋めてください。 -```tut:silent +```scala mdoc:nest:silent def loopFrom0To9(): Unit = { var i = ??? do { @@ -157,7 +157,7 @@ def loopFrom0To9(): Unit = { -```tut:silent +```scala mdoc:nest:silent def loopFrom0To9(): Unit = { var i = 0 do { @@ -167,7 +167,7 @@ def loopFrom0To9(): Unit = { } ``` -```tut +```scala mdoc:nest loopFrom0To9() ``` @@ -181,7 +181,7 @@ Scalaでは、メソッド定義の `=` の右は式であり、それを評価 一方で、特に手続き的にコードを書くときに `return` 式が便利なこともあります。以下は配列から、指定された要素を見つけてその添字を返すメソッドです。 -```tut +```scala mdoc:nest def indexOf(array: Array[String], target: String): Int = { var index = -1 var found = false @@ -201,7 +201,7 @@ def indexOf(array: Array[String], target: String): Int = { -```tut +```scala mdoc:nest def indexOf(array: Array[String], target: String): Int = { var i = 0 while(i < array.length) { @@ -232,7 +232,7 @@ for '(' (<ジェネレータ>;)+ ')' '<本体>' それでは、早速 `for` 式を使ってみましょう。 -```tut +```scala mdoc:nest for(x <- 1 to 5; y <- 1 until 5){ println("x = " + x + " y = " + y) } @@ -242,7 +242,7 @@ for(x <- 1 to 5; y <- 1 until 5){ `for`式の力はこれだけではありません。ループ変数の中から条件にあったものだけを絞り込むこともできます。`until`の後で`if x != y`と書いていますが、これは、`x`と`y`が異なる値の場合のみを抽出したものです。 -```tut +```scala mdoc:nest for(x <- 1 to 5; y <- 1 until 5 if x != y){ println("x = " + x + " y = " + y) } @@ -251,14 +251,14 @@ for(x <- 1 to 5; y <- 1 until 5 if x != y){ `for`式はコレクションの要素を1つ1つたどって何かの処理を行うことにも利用することができます。`"A"`, `"B"`, `"C"`, `"D"`, `"E"`の5つの要素からなるリストをたどって全てを出力する処理を書いてみましょう。 -```tut +```scala mdoc:nest for(e <- List("A", "B", "C", "D", "E")) println(e) ``` さらに、`for`式はたどった要素を加工して新しいコレクションを作ることもできます。先ほどのリストの要素全てに`Pre`という 文字列を付加してみましょう。 -```tut +```scala mdoc:nest for(e <- List("A", "B", "C", "D", "E")) yield { "Pre" + e } @@ -301,7 +301,7 @@ for(a <- 1 to 1000; b <- 1 to 1000; c <- 1 to 1000 if a * a == b * b + c * c) { のようになりますが、この「パターン」に書ける内容が非常に多岐に渡るためです。 まず、Javaのswitch-caseのような使い方をしてみます。たとえば、 -```tut +```scala mdoc:nest val taro = "Taro" taro match { @@ -315,7 +315,7 @@ taro match { パターンは文字列だけでなく数値など多様な値を扱うことができます。 -```tut +```scala mdoc:nest val one = 1 one match { @@ -331,7 +331,7 @@ one match { JavaやCなどの言語でswitch-case文を学んだ方には、Scalaのパターンマッチがいわゆるフォールスルー(fall through)の動作をしないことに違和感があるかもしれません。 -```tut:silent +```scala mdoc:nest:silent "abc" match { case "abc" => println("first") // ここで処理が終了 case "def" => println("second") // こっちは表示されない @@ -342,7 +342,7 @@ C言語のswitch-case文のフォールスルー動作は利点よりバグを JavaがC言語のフォールスルー動作を引き継いだことはしばしば非難されます。 それでScalaのパターンマッチにはフォールスルー動作がないわけですが、複数のパターンをまとめたいときのために`|`があります -```tut:silent +```scala mdoc:nest:silent "abc" match { case "abc" | "def" => println("first") @@ -354,7 +354,7 @@ JavaがC言語のフォールスルー動作を引き継いだことはしばし switch-case以外の使い方としては、コレクションの要素の一部にマッチさせる使い方があります。次のプログラムを見てみましょう。 -```tut +```scala mdoc:nest val lst = List("A", "B", "C") lst match { @@ -370,7 +370,7 @@ lst match { パターンマッチではガード式を用いて、パターンにマッチして、かつ、ガード式(`Boolean`型でなければならない)にもマッチしなければ右辺の式が評価されないような使い方もできます。 -```tut +```scala mdoc:nest val lst = List("A", "B", "C") lst match { @@ -386,7 +386,7 @@ lst match { また、パターンマッチのパターンはネストが可能です。先ほどのプログラムを少し改変して、先頭が`List("A")`であるような`List`にマッチさせてみましょう。 -```tut +```scala mdoc:nest val lst = List(List("A"), List("B", "C")) lst match { @@ -399,7 +399,7 @@ lst match { `lst`は`List("A")`と`List("B", "C")`の2要素からなる`List`です。ここで、match式を使うことで、先頭が`List("A")`であるというネストしたパターンを記述できていることがわかります。また、パターンの前に`@`がついているのはasパターンと呼ばれるもので、`@`の後に続くパターンにマッチする式を `@` の前の変数(ここでは`a`)に束縛します。 `as` パターンはパターンが複雑なときにパターンの一部だけを切り取りたい時に便利です。ただし `|` を使ったパターンマッチの場合は値を取り出すことができない点に注意してください。下記のように`|`のパターンマッチで変数を使った場合はコンパイルエラーになります。 -```tut:fail +```scala (List("a"): Any) match { case List(a) | Some(a) => println(a) @@ -408,7 +408,7 @@ lst match { 値を取り出さないパターンマッチは可能です。 -```tut:silent +```scala mdoc:nest:silent (List("a"): Any) match { case List(_) | Some(_) => println("ok") @@ -419,7 +419,7 @@ lst match { 先の節で書いたようなパターンマッチを別の記法で書くことができます。たとえば、 -```tut +```scala mdoc:nest val lst = List("A", "B", "C") lst match { @@ -433,7 +433,7 @@ lst match { というコードは、以下のように書き換えることができます。 -```tut +```scala mdoc:nest val lst = List("A", "B", "C") lst match { @@ -451,7 +451,7 @@ lst match { パターンとしては値が特定の型に所属する場合にのみマッチするパターンも使うことができます。値が特定の型に所属する場合にのみマッチするパターンは、`名前:マッチする型`の形で使います。たとえば、以下のようにして使うことができます。なお、`AnyRef`型は、Javaの`Object`型に相当する型で、あらゆる参照型の値を`AnyRef`型の変数に格納することができます。 -```tut +```scala mdoc:nest import java.util.Locale val obj: AnyRef = "String Literal" @@ -474,7 +474,7 @@ obj match { たとえば、以下の様なパターンマッチをREPLで実行しようとすると、警告が出てしまいます。 -```tut +```scala mdoc:nest val obj: Any = List("a") obj match { case v: List[Int] => println("List[Int]") @@ -490,7 +490,7 @@ obj match { 型変数を含む型のパターンマッチは、以下のようにワイルドカードパターンを使うと良いでしょう。 -```tut:silent +```scala mdoc:nest:silent obj match { case v: List[_] => println("List[_]") } @@ -498,7 +498,7 @@ obj match { ### 練習問題 {#control_syntax_ex4} -```tut:silent +```scala mdoc:nest:silent new scala.util.Random(new java.security.SecureRandom()).alphanumeric.take(5).toList ``` @@ -506,7 +506,7 @@ new scala.util.Random(new java.security.SecureRandom()).alphanumeric.take(5).toL -```tut:silent +```scala mdoc:nest:silent for(i <- 1 to 1000) { val s = new scala.util.Random(new java.security.SecureRandom()).alphanumeric.take(5).toList match { case List(a,b,c,d,_) => List(a,b,c,d,a).mkString diff --git a/src/error-handling.md b/src/error-handling.md index f2ce3b923..0f76b2c5a 100644 --- a/src/error-handling.md +++ b/src/error-handling.md @@ -177,7 +177,7 @@ Option型には具体的には 具体的な動きを見てみましょう。`Option`に具体的な値が入った場合は以下の様な動きをします。 -```tut +```scala mdoc:nest val o: Option[String] = Option("hoge") o.get @@ -189,7 +189,7 @@ o.isDefined 今度は`null`を`Option`に入れるとどうなるでしょうか。 -```tut +```scala mdoc:nest val o: Option[String] = Option(null) o.isEmpty @@ -197,7 +197,7 @@ o.isEmpty o.isDefined ``` -```tut:fail +```scala o.get ``` @@ -207,14 +207,14 @@ Optionのコンパニオンオブジェクトのapplyには引数がnullであ これがNPEと同じだと思うかもしれません。 しかしOptionには以下の様な便利メソッドがあり、それらを回避することができます。 -```tut +```scala mdoc:nest o.getOrElse("") ``` 以上は`Option[String]`の中身が`None`だった場合に、空文字を返すというコードになります。 値以外にも処理を書くこともできます。 -```tut:fail +```scala o.getOrElse(throw new RuntimeException("nullは受け入れられません")) ``` @@ -226,7 +226,7 @@ o.getOrElse(throw new RuntimeException("nullは受け入れられません")) 上記では、手続き的にOptionを処理しましたが、型を持っているため パターンマッチを使って処理することもできます。 -```tut +```scala mdoc:nest val s: Option[String] = Some("hoge") val result = s match { @@ -246,13 +246,13 @@ val result = s match { Optionには、コレクションの性質があると言いましたが、関数を内容の要素に適用できるという 性質もそのまま持ち合わせています。 -```tut +```scala mdoc:nest Some(3).map(_ * 3) ``` このように、`map`で関数を適用する事もできます。なお、値が`None`の場合にはどうなるでしょうか。 -```tut +```scala mdoc:nest val n: Option[Int] = None n.map(_ * 3) @@ -264,7 +264,7 @@ Noneのままだと型情報を持たないので一度、変数にしていま Java風に書くならば、 -```tut:fail +```scala if (n.isDefined) { n.get * 3 } else { @@ -286,14 +286,14 @@ fold[B](ifEmpty: ⇒ B)(f: (A) ⇒ B): B そして関数を適用した値を最終的に取得できます。 -```tut:fail +```scala n.fold(throw new RuntimeException)(_ * 3) ``` 上記のように書くことで、Noneの際に実行する処理を定義し、かつ、関数を適用した中身の値を 取得することができます。 -```tut +```scala mdoc:nest Some(3).fold(throw new RuntimeException)(_ * 3) ``` @@ -311,7 +311,7 @@ Some(3).fold(throw new RuntimeException)(_ * 3) 例えば、1つ目と2つ目の整数の値がOptionで返ってきてそれをかけた値をもとめるような場合です。 -```tut +```scala mdoc:nest val v1: Option[Int] = Some(3) val v2: Option[Int] = Some(5) @@ -324,14 +324,14 @@ mapだけを使ってシンプルに実装するとこんな風になってし このような入れ子のoptionを解消するために用意されているのが、`flatten`です。 -```tut +```scala mdoc:nest v1.map(i1 => v2.map(i2 => i1 * i2)).flatten ``` 最後に`flatten`を実行することで、Optionの入れ子を解消することができます。 なお、v2がNoneである場合にも`flatten`は成立します。 -```tut +```scala mdoc:nest val v1: Option[Int] = Some(3) val v2: Option[Int] = None @@ -348,7 +348,7 @@ v1.map(i1 => v2.map(i2 => i1 * i2)).flatten -```tut:silent +```scala mdoc:nest:silent val v1: Option[Int] = Some(2) val v2: Option[Int] = Some(3) val v3: Option[Int] = Some(5) @@ -376,7 +376,7 @@ v1.map { i1 => 実際に先ほどの、`Some(3)`と`Some(5)`をかける例で利用してみると以下のようになります。 -```tut +```scala mdoc:nest val v1: Option[Int] = Some(3) val v2: Option[Int] = Some(5) @@ -388,7 +388,7 @@ v1.flatMap(i1 => v2.map(i2 => i1 * i2)) `Some(3)`と`Some(5)`と`Some(7)`をかける場合はどうなるでしょうか。 -```tut +```scala mdoc:nest val v1: Option[Int] = Some(3) val v2: Option[Int] = Some(5) @@ -401,7 +401,7 @@ v1.flatMap(i1 => v2.flatMap(i2 => v3.map(i3 => i1 * i2 * i3))) 無論これは、 `v1`, `v2`, `v3` のいずれが `None` であった場合にも成立します。 その場合には `flatten` の時と同様に `None` が最終的な答えになります。 -```tut +```scala mdoc:nest val v3: Option[Int] = None v1.flatMap(i1 => v2.flatMap(i2 => v3.map(i3 => i1 * i2 * i3))) @@ -417,7 +417,7 @@ v1.flatMap(i1 => v2.flatMap(i2 => v3.map(i3 => i1 * i2 * i3))) -```tut:silent +```scala mdoc:nest:silent val v1: Option[Int] = Some(2) val v2: Option[Int] = Some(3) val v3: Option[Int] = Some(5) @@ -447,7 +447,7 @@ for式は実際には`flatMap`と`map`展開されて実行されるのです。 何をいっているのかわかりにくいと思いますので、先ほどの `Some(3)`と`Some(5)`と`Some(7)`をflatMapでかけるという処理をforで書いてみましょう。 -```tut +```scala mdoc:nest val v1: Option[Int] = Some(3) val v2: Option[Int] = Some(5) @@ -470,7 +470,7 @@ for { i1 <- v1 -```tut:silent +```scala mdoc:nest:silent val v1: Option[Int] = Some(2) val v2: Option[Int] = Some(3) val v3: Option[Int] = Some(5) @@ -494,7 +494,7 @@ Noneの場合、値が取得できなかったことはわかりますが、エ Optionが正常な値と何もない値のどちらかを表現するデータ型だったのに対して、Eitherは2つの値のどちらかを表現するデータ型です。 具体的には、Optionでは`Some`と`None`の2つの値を持ちましたが、Eitherは`Right`と`Left`の2つの値を持ちます。 -```tut +```scala mdoc:nest val v1: Either[String, Int] = Right(123) val v2: Either[String, Int] = Left("abc") @@ -502,7 +502,7 @@ val v2: Either[String, Int] = Left("abc") パターンマッチで値を取得できるのもOptionと同じです。 -```tut +```scala mdoc:nest v1 match { case Right(i) => println(i) case Left(s) => println(s) @@ -578,7 +578,7 @@ EitherにはRight, Leftの2つの値がありますが、ScalaのEitherではRig ためしに`Either`の`map`メソッドを使ってみましょう -```tut +```scala mdoc:nest val v: Either[String, Int] = Right(123) v.map(_ * 2) @@ -600,7 +600,7 @@ EitherがLeftの場合は何の処理もおこなわれません。 Scalaにおいては、メソッド実行前にはまず引数が評価され、次いでメソッド本体のコードが実行されます。 次の例からも分かります。 -```tut +```scala mdoc:nest def f(x: Any): Unit = println("f") def g(): Unit = println("g") f(g()) @@ -613,7 +613,7 @@ f(g()) **変数が実際に使用される箇所まで評価を遅延させる** ことができます。 メソッド本体のそれが使われる箇所で引数の式が計算されるということです。次のようなコードを見ると分かりやすいと思います。 -```tut +```scala mdoc:nest def g(): Unit = println("g") def f(g: => Unit): Unit = { println("prologue f") @@ -646,7 +646,7 @@ Eitherとの違いは、2つの型が平等ではなく、エラー値がThrowab ここでSuccessは型変数を取り、任意の値を入れることができますが、FailureはThrowableしか入れることができません。 そしてTryには、コンパニオンオブジェクトのapplyで生成する際に、例外をcatchし、Failureにする機能があります。 -```tut +```scala mdoc:nest import scala.util.Try val v: Try[Int] = Try(throw new RuntimeException("to be caught")) @@ -654,7 +654,7 @@ val v: Try[Int] = Try(throw new RuntimeException("to be caught")) この機能を使って、例外が起こりそうな箇所を`Try`で包み、Failureにして値として扱えるようにするのがTryの特徴です。 -```tut +```scala mdoc:nest val v1 = Try(3) val v2 = Try(5) @@ -677,7 +677,7 @@ NonFatalではないエラーはアプリケーション中で復旧が困難な Try以外でも、たとえば扱うことができる全ての例外をまとめて処理したい場合などに、 -```tut:silent +```scala mdoc:nest:silent import scala.util.control.NonFatal try { @@ -729,7 +729,7 @@ Noneを盲目的に処理するのであれば、flatMapやfor式をつかえば 以下のようなコードになります。 -```tut:silent +```scala mdoc:nest:silent object MainBefore { case class Address(id: Int, name: String, postalCode: Option[String]) @@ -800,7 +800,7 @@ findの各段階でFailureオブジェクトに引き換えるという動きを リファクタリングした結果は以下のようになります。 -```tut:silent +```scala mdoc:nest:silent object MainRefactored { case class Address(id: Int, name: String, postalCode: Option[String]) diff --git a/src/function.md b/src/function.md index 983a98ad4..87a2ba472 100644 --- a/src/function.md +++ b/src/function.md @@ -5,7 +5,7 @@ Scalaの関数は、他の言語の関数と扱いが異なります。Scalaの たとえば、2つの整数を取って加算した値を返す`add`関数は次のようにして定義することができます: -```tut +```scala mdoc:nest val add = new Function2[Int, Int, Int]{ def apply(x: Int, y: Int): Int = x + y } @@ -28,7 +28,7 @@ add(100, 200) Scalaでは`Function0`〜`Function22`までのトレイトのインスタンスを生成するためのシンタックスシュガー[^scala212]が 用意されています。たとえば、先ほどの`add`関数は -```tut +```scala mdoc:nest val add = (x: Int, y: Int) => x + y ``` と書くことができます。ここで、`add`には単に関数オブジェクトが入っているだけであって、関数本体 @@ -71,7 +71,7 @@ n1からnnまでが仮引数の定義でN1からNNまでが仮引数の型です カリー化とは、たとえば `(Int, Int) => Int` 型の関数のように複数の引数を取る関数があったとき、 これを `Int => Int => Int` 型の関数のように、1つの引数を取り、残りの引数を取る関数を返す関数のチェインで表現するというものです。試しに上記の`add`をカリー化してみましょう。 -```tut +```scala mdoc:nest val add = (x: Int, y: Int) => x + y val addCurried = (x: Int) => ((y: Int) => x + y) @@ -121,7 +121,7 @@ res1: Int => (Int => Int) = 早速高階関数の例についてみてみましょう。 -```tut +```scala mdoc:nest def double(n: Int, f: Int => Int): Int = { f(f(n)) } @@ -131,7 +131,7 @@ def double(n: Int, f: Int => Int): Int = { ことも多く、その場合は`f`や`g`などの1文字の名前をよく使います。他の関数型プログラミング言語でも同様の慣習があります。 呼び出しは次のようになります。 -```tut +```scala mdoc:nest double(1, m => m * 2) double(2, m => m * 3) @@ -151,7 +151,7 @@ double(3, m => m * 4) というパターンは頻出します。これをメソッドにした高階関数`around`を定義します。 -```tut +```scala mdoc:nest def around(init: () => Unit, body: () => Any, fin: () => Unit): Any = { init() try { @@ -165,7 +165,7 @@ def around(init: () => Unit, body: () => Any, fin: () => Unit): Any = { `try-finally` 構文は、後の例外処理の節でも出てきますが、大体Javaのそれと同じだと思ってください。この`around`関数は 次のようにして使うことができます。 -```tut +```scala mdoc:nest around( () => println("ファイルを開く"), () => println("ファイルに対する処理"), @@ -175,7 +175,7 @@ around( `around`に渡した関数が順番に呼ばれていることがわかります。ここで、`body`の部分で例外を発生させてみます。`throw`はJavaのそれと同じで例外を投げるための構文です。 -```tut:fail +```scala around( () => println("ファイルを開く"), () => throw new Exception("例外発生!"), @@ -195,7 +195,7 @@ Java 7では後始末処理を自動化するtry-with-resources文が言語と なお`around`のように高階関数を利用してリソースの後始末を行うパターンはローンパターンと呼ばれています。 ローンパターンは例えば以下の`withFile`のような形で利用されることが多いです。 -```tut +```scala mdoc:nest import scala.io.Source def withFile[A](filename: String)(f: Source => A): A = { val s = Source.fromFile(filename) @@ -213,13 +213,13 @@ def withFile[A](filename: String)(f: Source => A): A = { `withFile`メソッドを使って、次のようなシグネチャを持つテキストファイルの中身を一行ずつ表示する関数`printFile`を実装してみましょう。 -```tut:silent +```scala mdoc:nest:silent def printFile(filename: String): Unit = ??? ``` -```tut:silent +```scala mdoc:nest:silent def printFile(filename: String): Unit = { withFile(filename) { file => file.getLines.foreach(println) diff --git a/src/future-and-promise.md b/src/future-and-promise.md index 7fc0ccb2d..03acd6fe4 100644 --- a/src/future-and-promise.md +++ b/src/future-and-promise.md @@ -43,7 +43,7 @@ mapやflatMapやfilter、for式の適用といったようなOptionやListでも 実際のコード例を見てみましょう。 -```tut:silent +```scala mdoc:nest:silent import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global @@ -108,7 +108,7 @@ futureの結果取得を5000ミリ秒間待つという処理を行った後、 Futureが終わるまで最大5000ミリ秒を待つという書き方となります。 ただし、この書き方をする前に、 -```tut:silent +```scala mdoc:nest:silent import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps @@ -117,7 +117,7 @@ import scala.language.postfixOps 以上をimport文に追加する必要があります。さらにこれらがどのように動いているのかを、スレッドの観点から見てみましょう。 以下のようにコードを書いてみます。 -```tut:silent +```scala mdoc:nest:silent import scala.concurrent.{Await, Future} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ @@ -172,7 +172,7 @@ ForkJoinPoolに関しては、Javaの並行プログラミングをサポート Futureについての動きがわかった所で、FutureがOptionのように扱えることも説明します。 -```tut:silent +```scala mdoc:nest:silent import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.util.{Failure, Random, Success} @@ -232,7 +232,7 @@ Futureに適用する関数の中でさらにFutureが取得できるような 実際に実装例を見てみましょう。 -```tut:silent +```scala mdoc:nest:silent import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.language.postfixOps @@ -285,7 +285,7 @@ object CompositeFutureSample extends App { 成功あるいは失敗を表す値を設定することによってFutureに変換することのできるクラスです。 実際にサンプルコードを示します。 -```tut:silent +```scala mdoc:nest:silent import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.{Await, Promise, Future} import scala.concurrent.duration._ @@ -318,7 +318,7 @@ object PromiseSample extends App { 下記の例では、CallBackSomethingをラップしたFutureSomethingを定義しています。 `doSomething` の中でPromiseが使われていることに注目してください。 -```tut:silent +```scala mdoc:nest:silent import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.{Future, Promise} import scala.util.{Failure, Random, Success} @@ -375,7 +375,7 @@ FutureやPromiseの便利な特性を利用して、0〜1000ミリ秒間のラ -```tut:silent +```scala mdoc:nest:silent import java.util.concurrent.atomic.AtomicInteger import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.{Promise, Future} diff --git a/src/implicit.md b/src/implicit.md index 641bd2dd7..1452920fd 100644 --- a/src/implicit.md +++ b/src/implicit.md @@ -22,7 +22,7 @@ implicit conversionは 定義したimplicit conversionは大きく分けて二通りの使われ方をします。1つは、新しく定義したユーザ定義の型などを 既存の型に当てはめたい場合です。たとえば、 -```tut +```scala mdoc:nest implicit def intToBoolean(arg: Int): Boolean = arg != 0 if(1) { @@ -44,7 +44,7 @@ implicit conversionを利用することができます。 試しに、`String`の末尾に`":-)"`という文字列を追加して返すimplicit conversionを定義してみましょう。 -```tut +```scala mdoc:nest class RichString(val src: String) { def smile: String = src + ":-)" } @@ -61,7 +61,7 @@ implicit def enrichString(arg: String): RichString = new RichString(arg) 上の定義は、Scala 2.10以降では、 -```tut:reset +```scala mdoc:reset implicit class RichString(val src: String) { def smile: String = src + ":-)" } @@ -86,7 +86,7 @@ pimp my libraryパターンで、既存のクラスの利用を便利にする -```tut:silent +```scala mdoc:nest:silent object Taps { implicit class Tap[T](self: T) { def tap[U](block: T => U): T = { @@ -101,7 +101,7 @@ object Taps { } ``` -```tut +```scala mdoc:nest import Taps._ Taps.main(Array()) ``` @@ -161,7 +161,7 @@ implicit parameterのもう1つの使い方は、少々変わっています。 まず、2つの同じ型を足す(0の場合はそれに相当する値を返す)方法を知っている型を定義します。ここではその型を`Additive`とします。 `Additive`の定義は次のようになります: -```tut:silent +```scala mdoc:nest:silent trait Additive[A] { def plus(a: A, b: A): A def zero: A @@ -177,13 +177,13 @@ trait Additive[A] { 次に、この`Additive`型を使って、`List`の全ての要素を合計するメソッドを定義します: -```tut:silent +```scala mdoc:nest:silent def sum[A](lst: List[A])(m: Additive[A]) = lst.foldLeft(m.zero)((x, y) => m.plus(x, y)) ``` 後は、それぞれの型に応じた加算と0の定義を持ったobjectを定義します。ここでは`String`と`Int`について定義をします。 -```tut:silent +```scala mdoc:nest:silent object StringAdditive extends Additive[String] { def plus(a: String, b: String): String = a + b def zero: String = "" @@ -197,7 +197,7 @@ object IntAdditive extends Additive[Int] { まとめると次のようになります。 -```tut:silent +```scala mdoc:nest:silent trait Additive[A] { def plus(a: A, b: A): A def zero: A @@ -218,7 +218,7 @@ def sum[A](lst: List[A])(m: Additive[A]) = lst.foldLeft(m.zero)((x, y) => m.plus これで、`Int`型の`List`も`String`型の`List`のどちらの要素の合計も計算できる汎用的な`sum`メソッドができました。 実際に呼び出したいときには、 -```tut +```scala mdoc:nest sum(List(1, 2, 3))(IntAdditive) sum(List("A", "B", "C"))(StringAdditive) ``` @@ -227,7 +227,7 @@ sum(List("A", "B", "C"))(StringAdditive) いるのだからいちいち`IntAdditive`, `StringAdditive`を明示的に渡さずとも賢く推論してほしいものです。そして、まさにそれをimplicit parameterで実現することができます。方法は簡単で、`StringAdditive`と`IntAdditive`の定義の前にimplicitと付けることと、`sum`の最後の引数リストの`m`にimplicitを付けるだけです。implicit parameterを使った最終形は次のようになります。 -```tut +```scala mdoc:nest trait Additive[A] { def plus(a: A, b: A): A def zero: A @@ -256,7 +256,7 @@ sum(List("A", "B", "C")) このimplicit parameterの用法は標準ライブラリにもあって、たとえば、 -```tut +```scala mdoc:nest List[Int]().sum List(1, 2, 3, 4).sum @@ -282,37 +282,34 @@ m.plus(t1, m.plus(t2, t3)) == m.plus(m.plus(t1, t2), t3) // 結合則 -```tut:silent +```scala mdoc:silent -object Additives { - trait Additive[A] { - def plus(a: A, b: A): A - def zero: A - } - - implicit object StringAdditive extends Additive[String] { - def plus(a: String, b: String): String = a + b - def zero: String = "" - } +trait Additive[A] { + def plus(a: A, b: A): A + def zero: A +} - implicit object IntAdditive extends Additive[Int] { - def plus(a: Int, b: Int): Int = a + b - def zero: Int = 0 - } +implicit object StringAdditive extends Additive[String] { + def plus(a: String, b: String): String = a + b + def zero: String = "" +} - case class Point(x: Int, y: Int) +implicit object IntAdditive extends Additive[Int] { + def plus(a: Int, b: Int): Int = a + b + def zero: Int = 0 +} - implicit object PointAdditive extends Additive[Point] { - def plus(a: Point, b: Point): Point = Point(a.x + b.x, a.y + b.y) - def zero: Point = Point(0, 0) - } +case class Point(x: Int, y: Int) - def sum[A](lst: List[A])(implicit m: Additive[A]) = lst.foldLeft(m.zero)((x, y) => m.plus(x, y)) +implicit object PointAdditive extends Additive[Point] { + def plus(a: Point, b: Point): Point = Point(a.x + b.x, a.y + b.y) + def zero: Point = Point(0, 0) } + +def sum[A](lst: List[A])(implicit m: Additive[A]) = lst.foldLeft(m.zero)((x, y) => m.plus(x, y)) ``` -```tut -import Additives._ +```scala mdoc:nest println(sum(List(Point(1, 1), Point(2, 2), Point(3, 3)))) // Point(6, 6) println(sum(List(Point(1, 2), Point(3, 4), Point(5, 6)))) // Point(9, 12) ``` diff --git a/src/introduction-to-typeclass.md b/src/introduction-to-typeclass.md index 1ded261e9..5a3595c4b 100644 --- a/src/introduction-to-typeclass.md +++ b/src/introduction-to-typeclass.md @@ -11,13 +11,13 @@ Implicitの章では、`Additive`という型クラスを定義することで まず、`sum`に続いて、要素の平均値を計算するための`average`メソッドを作成することを考えます。`average`メソッドの 素朴な実装は次のようになるでしょう。 [^list-size] -```tut +```scala mdoc:nest def average(list: List[Int]): Int = list.foldLeft(0)(_ + _) / list.size ``` これを、前章のように `Additive`を使ってみます。 -```tut +```scala mdoc:nest trait Additive[A] { def plus(a: A, b: A): A def zero: A @@ -47,7 +47,7 @@ def average[A](lst: List[A])(implicit m: Additive[A]): A = { メソッドを `multiply` 、割り算のメソッドを `divide` とすると、 `Num` は次のようになるでしょう。 ここで、 `Nums` は、対話環境でコンパニオンクラス/オブジェクトを扱うために便宜的に作った名前空間であり、通常のScalaプログラムでは、コンパニオンクラス/オブジェクトを定義するときの作法に従えばよいです[^repl-companion]。 -```tut +```scala mdoc:nest object Nums { trait Num[A] { def plus(a: A, b: A): A @@ -78,7 +78,7 @@ object Nums { また、 `average` メソッドは、リストの長さ、つまり整数で割る必要があるので、整数を `A` 型に変換するための型 `FromInt` も用意します。 `FromInt` は次のようになります。 `to` は `Int` 型を対象の型に変換するメソッドです。 -```tut +```scala mdoc:nest object FromInts { trait FromInt[A] { def to(from: Int): A @@ -96,7 +96,7 @@ object FromInts { `Num` と `FromInt` を使うと、 `average` 関数は次のように書くことができます。 -```tut +```scala mdoc:nest import Nums._ import FromInts._ def average[A](lst: List[A])(implicit a: Num[A], b: FromInt[A]): A = { @@ -108,7 +108,7 @@ def average[A](lst: List[A])(implicit a: Num[A], b: FromInt[A]): A = { この `average` 関数は次のようにして使うことができます。 -```tut +```scala mdoc:nest average(List(1, 3, 5)) average(List(1.5, 2.5, 3.5)) ``` @@ -119,7 +119,7 @@ average(List(1.5, 2.5, 3.5)) 上記のコードは、context boundsというシンタックスシュガーを使うことで、次のように書き換えることもできます。 -```tut +```scala mdoc:nest import Nums._ import FromInts._ def average[A:Num:FromInt](lst: List[A]): A = { @@ -139,7 +139,7 @@ implicit parameterの名前 `a` と `b` が引数から見えなくなりまし 別のアルゴリズムをライブラリ化した例を、Scalaの標準ライブラリから紹介します。コレクションから 最大値を取得する `max` と最小値を取得する `min` です。これらは、次のようにして使うことができます。 -```tut +```scala mdoc:nest List(1, 3, 4, 2).max List(1, 3, 2, 4).min ``` @@ -171,7 +171,7 @@ def min[B >: A](implicit cmp: Ordering[B]): A この3つを使って、 `median` メソッドを定義してみます。先程出てきたcontext boundsを使って、シグネチャ が見やすいようにしています。 -```tut +```scala mdoc:nest import Nums._ import FromInts._ def median[A:Num:Ordering:FromInt](lst: List[A]): A = { @@ -193,7 +193,7 @@ def median[A:Num:Ordering:FromInt](lst: List[A]): A = { このメソッドは次のようにして使うことができます。 -```tut +```scala mdoc:nest assert(2 == median(List(1, 3, 2))) assert(2.5 == median(List(1.5, 2.5, 3.5))) assert(3 == median(List(1, 3, 4, 5))) @@ -250,7 +250,7 @@ def string[A:Serializer](obj: A): String = ??? `Serializer` の `serialize` メソッドを呼びだせばいいだけなので、 次のようになります。 -```tut +```scala mdoc:nest object Serializers { trait Serializer[A] { def serialize(obj: A): String @@ -310,7 +310,7 @@ implicit def ListSerializer[A](implicit serializer: Serializer[A]): Serializer[L ここまでで、一通りの実装ができたので、定義を一箇所にまとめて実行結果を確認してみましょう。この節の最初の 方の入力例を使って動作確認をします。 -```tut +```scala mdoc:nest object Serializers { trait Serializer[A] { def serialize(obj: A): String diff --git a/src/java-interop.md b/src/java-interop.md index 341675952..c6e604b50 100644 --- a/src/java-interop.md +++ b/src/java-interop.md @@ -15,7 +15,7 @@ import java.util.ArrayList; ``` は -```tut:silent +```scala mdoc:nest:silent import java.util._ import java.util.ArrayList ``` @@ -32,7 +32,7 @@ ArrayList list = new ArrayList<>(); というコードはScalaでは -```tut +```scala mdoc:nest val list = new ArrayList[String]() ``` @@ -44,7 +44,7 @@ val list = new ArrayList[String]() -```tut +```scala mdoc:nest import java.util.HashSet val set = new HashSet[String] ``` @@ -62,7 +62,7 @@ list.add("World"); は -```tut +```scala mdoc:nest list.add("Hello") list.add("World") ``` @@ -75,7 +75,7 @@ list.add("World") -```tut +```scala mdoc:nest System.out.println("Hello, World!") ``` @@ -216,7 +216,7 @@ Scalaの世界ではnullを使うことはなく、代わりにOption型を使 java.util.Mapを使って確かめてみましょう。 -```tut +```scala mdoc:nest val map = new java.util.HashMap[String, Int]() map.put("A", 1) @@ -243,14 +243,14 @@ JavaのコレクションとScalaのコレクションはインタフェース に渡したり、逆に返ってきたJavaのコレクションをScalaのコレクションに変換したい場合に不便です。そのような場合に便利なのがJavaConverters です。使い方はいたって簡単で、 -```tut:silent +```scala mdoc:nest:silent import scala.collection.JavaConverters._ ``` とするだけです。これで、JavaとScalaのコレクションのそれぞれにasJava()やasScala()といったメソッドが追加されるのでそのメソッドを以下のように 呼び出せば良いです。 -```tut +```scala mdoc:nest import scala.collection.JavaConverters._ import java.util.ArrayList @@ -272,7 +272,7 @@ BufferはScalaの変更可能なリストのスーパークラスですが、と -```tut +```scala mdoc:nest import scala.collection.mutable.ArrayBuffer import scala.collection.JavaConverters._ val buffer = new ArrayBuffer[String] @@ -312,13 +312,13 @@ Comparator cmp = new Comparator() { この機能に対応するものとして、Scalaには存在型があります。上記のJavaコードは、Scalaでは次のコードで表現することができます。 -```tut +```scala mdoc:nest import java.util.{List => JList, ArrayList => JArrayList} val objects: JList[_ <: Object] = new JArrayList[String]() ``` -```tut +```scala mdoc:nest import java.util.{Comparator => JComparator} val cmp: JComparator[_ >: String] = new JComparator[Any] { @@ -361,7 +361,7 @@ int factorial10 = IntStream.rangeClosed(1, 10).reduce(1, しかし、以前のScalaでは`FunctionN`型が期待される箇所に限定されており、Javaにおいてラムダ式が期待される箇所の大半において使用することができませんでした。 例えば、10の階乗の例は`IntBinaryOperator`型が期待されているので以下のように無名クラスを使う必要がありました。 -```tut +```scala mdoc:nest import java.util.stream.IntStream; import java.util.function.IntBinaryOperator; val factorial10 = IntStream.rangeClosed(1, 10).reduce(1, @@ -372,7 +372,7 @@ val factorial10 = IntStream.rangeClosed(1, 10).reduce(1, SAM変換を利用すると以下のようにここにも無名関数を利用できるようになります。 -```tut +```scala mdoc:nest import java.util.stream.IntStream; val factorial10 = IntStream.rangeClosed(1, 10).reduce(1, _ * _); ``` diff --git a/src/main/scala/Testing.scala b/src/main/scala/Testing.scala deleted file mode 100644 index aaaed86b3..000000000 --- a/src/main/scala/Testing.scala +++ /dev/null @@ -1,7 +0,0 @@ -import org.scalacheck._, Arbitrary.arbitrary -object Testing { - def test[G](g: Gen[G])(f: G => Boolean) = { - val result = Prop.forAll(g)(f).apply(Gen.Parameters.default) - assert(result.success, result) - } -} diff --git a/src/object.md b/src/object.md index 8892825e7..2aad3220d 100644 --- a/src/object.md +++ b/src/object.md @@ -34,7 +34,7 @@ object <オブジェクト名> extends <クラス名> (with <トレイト名>)* `apply`メソッドでオブジェクトを生成するようにすることで、`Point(3, 5)`のような記述でオブジェクトを 生成できるようになります。 -```tut +```scala mdoc:nest class Point(val x:Int, val y:Int) object Point { @@ -49,7 +49,7 @@ object Point { といったメリットがあります。なお、上記の記述はケースクラスを用いてもっと簡単に -```tut +```scala mdoc:nest case class Point(x: Int, y: Int) ``` diff --git a/src/sbt-compile-execute.md b/src/sbt-compile-execute.md index ac3af711e..b5e860b68 100644 --- a/src/sbt-compile-execute.md +++ b/src/sbt-compile-execute.md @@ -1,6 +1,6 @@ # sbtでプログラムをコンパイル・実行する -```tut:invisible +```scala mdoc:nest:invisible import sbt._, Keys._ ``` @@ -15,7 +15,7 @@ import sbt._, Keys._ Scala 2.10までは`exit`、Scala 2.11以降は`sys.exit`で終了することができますが、これらはREPL専用のコマンドではなく、今のプロセス自体を 終了させる汎用的なメソッドなのでREPLを終了させる時には使用しないようにしましょう。 -```tut:silent +```scala mdoc:nest:silent object HelloWorld { def main(args: Array[String]): Unit = { println("Hello, World!") @@ -38,7 +38,7 @@ sandbox 今回の_build.sbt_にはScalaのバージョンと一緒に`scalac`の警告オプションも有効にしてみましょう。 -```tut:silent +```scala mdoc:nest:silent // build.sbt scalaVersion := "2.12.8" @@ -78,7 +78,7 @@ HelloWorldプログラムがコンパイルされ、さらに実行されて`Hel またsbtの管理下のScalaプログラムは`console`コマンドでREPLから呼び出せるようになります。 _HelloWorld.scala_と同じ場所に_User.scala_というファイルを作ってみましょう -```tut:silent +```scala mdoc:nest:silent // User.scala class User(val name: String, val age: Int) diff --git a/src/sbt-install.md b/src/sbt-install.md index 241732f0d..c0afd2c64 100644 --- a/src/sbt-install.md +++ b/src/sbt-install.md @@ -1,6 +1,6 @@ # sbtをインストールする -```tut:invisible +```scala mdoc:nest:invisible import sbt._, Keys._ ``` @@ -87,7 +87,7 @@ scala> :quit になってしまうので、こちらが指定したバージョンのScalaでREPLを起動したい場合は、同じディレクトリに _build.sbt_というファイルを作成し、 -```tut:silent +```scala mdoc:nest:silent scalaVersion := "2.12.8" ``` diff --git a/src/test.md b/src/test.md index 290e0579c..62b6551cd 100644 --- a/src/test.md +++ b/src/test.md @@ -124,11 +124,11 @@ BDDでは、テスト内にそのプログラムに与えられた機能的な `build.sbt`を用意して、以下を記述しておきます。 -```tut:invisible +```scala mdoc:nest:invisible import sbt._, Keys._ ``` -```tut:silent +```scala mdoc:nest:silent name := "scalatest_study" version := "1.0" @@ -164,7 +164,7 @@ libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test" これを実装した場合、`src/main/scala/Calc.scala`は以下のようになります。 -```tut:silent +```scala mdoc:nest:silent class Calc { /** 整数の配列を取得し、それらを出し合わせた整数を返す @@ -222,7 +222,7 @@ XP(エクストリームプログラミング)のプラクティスに、不 最小のテストを書いてみます。`src/test/scala/CalcSpec.scala`を以下のように記述します。 -```tut:silent +```scala mdoc:nest:silent import org.scalatest._ class CalcSpec extends FlatSpec with DiagrammedAssertions { @@ -298,7 +298,7 @@ class CalcSpec extends FlatSpec with DiagrammedAssertions { 次に、例外が発生することをテストする場合について記述してみましょう。 div関数までテストの実装を進めます。 -```tut:silent +```scala mdoc:nest:silent import org.scalatest._ class CalcSpec extends FlatSpec with DiagrammedAssertions { @@ -328,7 +328,7 @@ class CalcSpec extends FlatSpec with DiagrammedAssertions { なお、本来ユニットテストは時間がかかるテストを書くべきではありませんが、 できるだけ短い時間でそれを判定できるように実装します。 -```tut:silent +```scala mdoc:nest:silent import org.scalatest._ import org.scalatest.concurrent.TimeLimits import org.scalatest.time.SpanSugar._ @@ -398,13 +398,13 @@ BDDでテストを書くことによってテストによってどのような ここでは、よく使われているMockitoを利用してみましょう。 `build.sbt`に以下を追記することで利用可能になります。 -```tut:silent +```scala mdoc:nest:silent libraryDependencies += "org.mockito" % "mockito-core" % "3.3.3" % "test" ``` せっかくなので、先ほど用意したCalcクラスのモックを用意して、モックにsumの振る舞いを仕込んで見ましょう。 -```tut:silent +```scala mdoc:nest:silent import org.scalatest.time.SpanSugar._ import org.scalatest.{FlatSpec, DiagrammedAssertions} import org.scalatest.concurrent.TimeLimits @@ -442,7 +442,7 @@ class CalcSpec extends FlatSpec with DiagrammedAssertions with TimeLimits { `project/plugins.sbt` に以下のコードを記述します。 -```tut:silent +```scala mdoc:nest:silent resolvers += Classpaths.sbtPluginReleases addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1") @@ -476,7 +476,7 @@ addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1") 使い方は、`project/plugins.sbt` に以下のコードを記述します。 -```tut:silent +```scala mdoc:nest:silent addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") ``` diff --git a/src/trait.md b/src/trait.md index e27bfc472..50753b61e 100644 --- a/src/trait.md +++ b/src/trait.md @@ -37,7 +37,7 @@ Scalaのトレイトはクラスに比べて以下のような特徴がありま Scalaのトレイトはクラスとは違い、複数のトレイトを1つのクラスやトレイトにミックスインすることができます。 -```tut:silent +```scala mdoc:nest:silent trait TraitA trait TraitB @@ -50,7 +50,7 @@ class ClassB class ClassC extends ClassA with TraitA with TraitB ``` -```tut:fail +```scala // コンパイルエラー! class ClassD extends ClassA with ClassB ``` @@ -62,11 +62,11 @@ class ClassD extends ClassA with ClassB Scalaのトレイトはクラスと違い、直接インスタンス化できません。 -```tut +```scala mdoc:nest trait TraitA ``` -```tut:fail +```scala object ObjectA { // コンパイルエラー! val a = new TraitA @@ -76,7 +76,7 @@ object ObjectA { これは、トレイトが単体で使われることをそもそも想定していないための制限です。トレイトを使うときは、通常、それを継承した クラスを作ります。 -```tut:silent +```scala mdoc:nest:silent trait TraitA class ClassA extends TraitA @@ -96,14 +96,14 @@ object ObjectA { Scalaのトレイトはクラスと違いパラメータ(コンストラクタの引数)を取ることができないという制限があります[^trait-param-dotty]。 -```tut:silent +```scala mdoc:nest:silent // 正しいプログラム class ClassA(name: String) { def printName() = println(name) } ``` -```tut:fail +```scala // コンパイルエラー! trait TraitA(name: String) ``` @@ -112,7 +112,7 @@ trait TraitA(name: String) インスタンス化できない問題のときと同じようにクラスに継承させたり、 インスタンス化のときに抽象メンバーを実装をすることでトレイトに値を渡すことができます。 -```tut:silent +```scala mdoc:nest:silent trait TraitA { val name: String def printName(): Unit = println(name) @@ -151,7 +151,7 @@ object ObjectA { 以下のような継承関係を考えてみましょう。 `greet`メソッドを定義した`TraitA`と、`greet`を実装した`TraitB`と`TraitC`、そして`TraitB`と`TraitC`のどちらも継承した`ClassA`です。 -```tut:silent +```scala mdoc:nest:silent trait TraitA { def greet(): Unit } @@ -165,7 +165,7 @@ trait TraitC extends TraitA { } ``` -```tut:fail:silent +```scala class ClassA extends TraitB with TraitC ``` @@ -175,7 +175,7 @@ class ClassA extends TraitB with TraitC ちなみに、上記の例をScalaでコンパイルすると以下のようなエラーが出ます。 -```tut:fail +```scala class ClassA extends TraitB with TraitC ``` @@ -183,7 +183,7 @@ Scalaではoverride指定なしの場合メソッド定義の衝突はエラー この場合の1つの解法は、コンパイルエラーに「Note: this can be resolved by declaring an override in class ClassA.」とあるように`ClassA`で`greet`をoverrideすることです。 -```tut:silent +```scala mdoc:nest:silent class ClassA extends TraitB with TraitC { override def greet(): Unit = println("How are you?") } @@ -191,7 +191,7 @@ class ClassA extends TraitB with TraitC { このとき`ClassA`で`super`に型を指定してメソッドを呼びだすことで、`TraitB`や`TraitC`のメソッドを指定して使うこともできます。 -```tut:silent +```scala mdoc:nest:silent class ClassB extends TraitB with TraitC { override def greet(): Unit = super[TraitB].greet() } @@ -199,7 +199,7 @@ class ClassB extends TraitB with TraitC { 実行結果は以下にようになります。 -```tut +```scala mdoc:nest (new ClassA).greet() (new ClassB).greet() @@ -208,7 +208,7 @@ class ClassB extends TraitB with TraitC { では、`TraitB`と`TraitC`の両方のメソッドを呼び出したい場合はどうでしょうか? 1つの方法は上記と同じように`TraitB`と`TraitC`の両方のクラスを明示して呼びだすことです。 -```tut:silent +```scala mdoc:nest:silent class ClassA extends TraitB with TraitC { override def greet(): Unit = { super[TraitB].greet() @@ -228,7 +228,7 @@ Scalaのトレイトの線形化機能とは、トレイトがミックスイン 次に以下の例を考えてみます。先程の例との違いは`TraitB`と`TraitC`の`greet`メソッド定義に`override`修飾子が付いていることです。 -```tut:silent +```scala mdoc:nest:silent trait TraitA { def greet(): Unit } @@ -247,7 +247,7 @@ class ClassA extends TraitB with TraitC この場合はコンパイルエラーにはなりません。では`ClassA`の`greet`メソッドを呼び出した場合、いったい何が表示されるのでしょうか? 実際に実行してみましょう。 -```tut +```scala mdoc:nest (new ClassA).greet() ``` @@ -256,7 +256,7 @@ class ClassA extends TraitB with TraitC つまりトレイトのミックスインの順番を逆にすると`TraitB`が優先されるようになります。 以下のようにミックスインの順番を変えてみます。 -```tut:silent +```scala mdoc:nest:silent class ClassB extends TraitC with TraitB ``` @@ -269,7 +269,7 @@ Good morning! `super`を使うことで線形化された親トレイトを使うこともできます -```tut:silent +```scala mdoc:nest:silent trait TraitA { def greet(): Unit = println("Hello!") } @@ -294,7 +294,7 @@ class ClassB extends TraitC with TraitB この`greet`メソッドの結果もまた継承された順番で変わります。 -```tut +```scala mdoc:nest (new ClassA).greet() (new ClassB).greet() @@ -310,7 +310,7 @@ class ClassB extends TraitC with TraitB Scalaのトレイトの`val`の初期化順序はトレイトを使う上で大きな落とし穴になります。 以下のような例を考えてみましょう。トレイト`A`で変数`foo`を宣言し、トレイト`B`が`foo`を使って変数`bar`を作成し、クラス`C`で`foo`に値を代入してから`bar`を使っています。 -```tut:silent +```scala mdoc:nest:silent trait A { val foo: String } @@ -328,7 +328,7 @@ class C extends B { REPLでクラス`C`の`printBar`メソッドを呼び出してみましょう。 -```tut +```scala mdoc:nest (new C).printBar() ``` @@ -346,7 +346,7 @@ REPLでクラス`C`の`printBar`メソッドを呼び出してみましょう。 具体的なコードを見てみましょう。 -```tut:silent +```scala mdoc:nest:silent trait A { val foo: String } @@ -368,7 +368,7 @@ class C extends B { 今度はクラス`C`の`printBar`メソッドを呼び出してもちゃんと`HelloWorld`と表示されます。 -```tut +```scala mdoc:nest (new C).printBar() ``` @@ -379,7 +379,7 @@ class C extends B { トレイトの`val`の初期化順序を回避するもう1つの方法としては事前定義(Early Definitions)を使う方法もあります。 事前定義というのはフィールドの初期化をスーパークラスより先におこなう方法です。 -```tut:silent +```scala mdoc:nest:silent trait A { val foo: String } diff --git a/src/type-parameter.md b/src/type-parameter.md index eb3240a8b..eb90b5873 100644 --- a/src/type-parameter.md +++ b/src/type-parameter.md @@ -12,7 +12,7 @@ class <クラス名>[<型パラメータ1>, <型パラメータ2>, ...](<クラ 簡単な例として、1個の要素を保持して、要素を入れる(`put`する)か取りだす(`get`する)操作ができるクラス`Cell`を定義してみます。`Cell`の定義は次のようになります。 -```tut:silent +```scala mdoc:nest:silent class Cell[A](var value: A) { def put(newValue: A): Unit = { value = newValue @@ -52,7 +52,7 @@ scala> cell.put("something") 上記コードの -```tut +```scala mdoc:nest val cell = new Cell[Int](1) ``` @@ -65,7 +65,7 @@ val cell = new Cell[Int](1) という選択肢しかありませんでした。しかし、前者は引数を返り値に使うという点で邪道ですし、後者の方法は多数の引数を返したい、あるいは解く問題上で意味のある名前の付けられるクラスであれば良いですが、ただ2つの値を返したいといった場合には小回りが効かず不便です。こういう場合、型パラメータを2つ取る`Pair`クラスを作ってしまいます。```Pair```クラスの定義は次のようになります。`toString`メソッドの定義は後で表示のために使うだけなので気にしないでください。 -```tut:silent +```scala mdoc:nest:silent class Pair[A, B](val a: A, val b: B) { override def toString(): String = "(" + a + "," + b + ")" } @@ -73,13 +73,13 @@ class Pair[A, B](val a: A, val b: B) { このクラス```Pair```の利用法としては、たとえば割り算の商と余りの両方を返すメソッド`divide`が挙げられます。`divide`の定義は次のようになります。 -```tut:silent +```scala mdoc:nest:silent def divide(m: Int, n: Int): Pair[Int, Int] = new Pair[Int, Int](m / n, m % n) ``` これらをREPLにまとめて流し込むと次のようになります。 -```tut +```scala mdoc:nest class Pair[A, B](val a: A, val b: B) { override def toString(): String = "(" + a + "," + b + ")" } @@ -93,7 +93,7 @@ divide(7, 3) ちなみに、この`Pair`のようなクラスはScalaではよく使われるため、`Tuple1`から`Tuple22`(`Tuple`の後の数字は要素数)があらかじめ用意されています。また、インスタンス化する際も、 -```tut +```scala mdoc:nest val m = 7 val n = 3 new Tuple2(m / n, m % n) @@ -101,7 +101,7 @@ new Tuple2(m / n, m % n) などとしなくても、 -```tut +```scala mdoc:nest val m = 7 val n = 3 (m / n, m % n) @@ -131,7 +131,7 @@ val : G[B] = G[A] というような代入が許される性質を表します。Scalaでは、クラス定義時に -```tut:silent +```scala mdoc:nest:silent class G[+A] ``` のように型パラメータの前に`+`を付けるとその型パラメータは(あるいはそのクラスは)共変になります。 @@ -157,7 +157,7 @@ scala> val arr: Array[Any] = new Array[String](1) さて、Scalaでは型パラメータを共変にした時点で、安全ではない操作はコンパイラがエラーを出してくれるので安心ですが、共変をどのような場合に使えるかを知っておくのは意味があります。たとえば、先ほど作成したクラス`Pair[A, B]`について考えてみましょう。`Pair[A, B]`は一度インスタンス化したら、変更する操作ができませんから、`ArrayStoreException`のような例外は起こり得ません。実際、`Pair[A, B]`は安全に共変にできるクラスで、`class Pair[+A, +B]`のようにしても問題が起きません。 -```tut +```scala mdoc:nest class Pair[+A, +B](val a: A, val b: B) { override def toString(): String = "(" + a + "," + b + ")" } @@ -171,7 +171,7 @@ val pair: Pair[AnyRef, AnyRef] = new Pair[String, String]("foo", "bar") 次の*immutable*な*Stack*型の定義(途中)があります。`???`の箇所を埋めて、*Stack*の定義を完成させなさい。なお、`E >: A`は、`E`は`A`の継承元である、という制約を表しています。 -```tut:silent +```scala mdoc:nest:silent trait Stack[+A] { def push[E >: A](e: E): Stack[E] def top: A @@ -201,14 +201,14 @@ object Stack { また、`Nothing`は全ての型のサブクラスであるような型を表現します。`Stack[A]`は共変なので、`Stack[Nothing]`はどんな型の`Stack`変数にでも格納することができます。 例えば`Stack[Nothing]`型である`EmptyStack`は、`Stack[Int]`型の変数と`Stack[String]`型の変数の両方に代入することができます。 -```tut +```scala mdoc:nest val intStack: Stack[Int] = Stack() val stringStack: Stack[String] = Stack() ``` -```tut:silent +```scala mdoc:nest:silent class NonEmptyStack[+A](private val first: A, private val rest: Stack[A]) extends Stack[A] { def push[E >: A](e: E): Stack[E] = new NonEmptyStack[E](e, this) def top: A = first @@ -229,7 +229,7 @@ val : G[A] = G[B] というような代入が許される性質を表します。Scalaでは、クラス定義時に -```tut:silent +```scala mdoc:nest:silent class G[-A] ``` のように型パラメータの前に`-`を付けるとその型パラメータは(あるいはそのクラスは)反変になります。 @@ -280,7 +280,7 @@ x1: String => AnyRef = の後に、`<:`を記述し、それに続いて制約となる型を記述します。以下では、`show`によって文字列化できるクラス`Show`を定義した うえで、`Show`であるような型のみを要素として持つ`ShowablePair`を定義しています。 -```tut:silent +```scala mdoc:nest:silent abstract class Show { def show: String } @@ -299,7 +299,7 @@ class ShowablePair[A <: Show, B <: Show](val a: A, val b: B) extends Show { まず、共変の練習問題であったような、イミュータブルな`Stack`クラスを定義します。この`Stack`は共変にしたいとします。 -```tut:fail:silent +```scala abstract class Stack[+A]{ def push(element: A): Stack[A] def top: A @@ -322,7 +322,7 @@ error: covariant type A occurs in contravariant position in type A of value elem 対処するために型パラメータの下限境界を使うことができます。型パラメータ`E`を`push`に追加し、その下限境界として、`Stack` の型パラメータ`A`を指定します。 -```tut:silent +```scala mdoc:nest:silent abstract class Stack[+A]{ def push[E >: A](element: E): Stack[E] def top: A diff --git a/src/typeclass.md b/src/typeclass.md index 5c70ba62e..7ff4837a2 100644 --- a/src/typeclass.md +++ b/src/typeclass.md @@ -12,7 +12,7 @@ 前章に登場した`List`や`Option`には、`map`という関数が共通して定義されていました。 この`map`関数がある規則を満たす場合はFunctor型クラスとして抽象化できます[^hkind]。 -```tut:silent +```scala mdoc:nest:silent trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] } @@ -20,7 +20,7 @@ trait Functor[F[_]] { この型クラスが満たすべき規則は2つです。 -```tut:silent +```scala mdoc:nest:silent def identityLaw[F[_], A](fa: F[A])(implicit F: Functor[F]): Boolean = F.map(fa)(identity) == fa @@ -30,13 +30,13 @@ def compositeLaw[F[_], A, B, C](fa: F[A], f1: A => B, f2: B => C)(implicit F: Fu なお、`identity`は次のように定義されます。 -```tut:silent +```scala mdoc:nest:silent def identity[A](a: A): A = a ``` 例として、Option型でFunctor型クラスのインスタンスを定義し、前述の規則を満たすかどうか調べてみましょう。 -```tut +```scala mdoc:nest import scala.language.higherKinds trait Functor[F[_]] { @@ -63,7 +63,7 @@ compositeLaw(n, (i: Int) => i * i, (i: Int) => i.toString) 複数の値が登場する場合にはFunctorでは力不足です。 そこで、複数の引数を持つ関数と値を組み合わせて1つの値を作りだせる機能を提供するApplicative Functorが登場します。 -```tut:silent +```scala mdoc:nest:silent trait Applicative[F[_]] { def point[A](a: A): F[A] def ap[A, B](fa: F[A])(f: F[A => B]): F[B] @@ -72,14 +72,14 @@ trait Applicative[F[_]] { Applicative FunctorはFunctorを特殊化したものなので、Applicative Functorが持つ関数から`map`関数を定義できます。 -```tut:silent +```scala mdoc:nest:silent def map[F[_], A, B](fa: F[A])(f: A => B)(implicit F: Applicative[F]): F[B] = F.ap(fa)(F.point(f)) ``` Applicative Functorが満たすべき規則は以下の通りです。 -```tut:silent +```scala mdoc:nest:silent def identityLaw[F[_], A](fa: F[A])(implicit F: Applicative[F]): Boolean = F.ap(fa)(F.point((a: A) => a)) == fa @@ -94,7 +94,7 @@ def interchangeLaw[F[_], A, B](f: F[A => B], a: A)(implicit F: Applicative[F]): 例として、Option型でApplicative Functorを定義してみましょう。 -```tut +```scala mdoc:nest trait Applicative[F[_]] { def point[A](a: A): F[A] def ap[A, B](fa: F[A])(f: F[A => B]): F[B] @@ -135,7 +135,7 @@ OptionApplicative.map(a)(_ + 1) == OptionFunctor.map(a)(_ + 1) ある値を受け取りその値を包んだ型を返す関数をApplicative Functorで扱おうとすると、型がネストしてしまい平坦化できません。 このネストする問題を解決するためにMonadと呼ばれる型クラスを用います。 -```tut:silent +```scala mdoc:nest:silent trait Monad[F[_]] { def point[A](a: A): F[A] def bind[A, B](fa: F[A])(f: A => F[B]): F[B] @@ -146,7 +146,7 @@ trait Monad[F[_]] { Monadは以下の規則を満たす必要があります。 -```tut:silent +```scala mdoc:nest:silent def rightIdentityLaw[F[_], A](a: F[A])(implicit F: Monad[F]): Boolean = F.bind(a)(F.point(_)) == a @@ -160,14 +160,14 @@ def associativeLaw[F[_], A, B, C](fa: F[A], f: A => F[B], g: B => F[C])(implicit MonadはApplicative Functorを特殊化したものなので、Monadが持つ関数から`point`関数と`ap`関数を定義できます。 `point`に関しては同じシグネチャなので自明でしょう。 -```tut:silent +```scala mdoc:nest:silent def ap[F[_], A, B](fa: F[A])(f: F[A => B])(implicit F: Monad[F]): F[B] = F.bind(f)((g: A => B) => F.bind(fa)((a: A) => F.point(g(a)))) ``` それでは、Option型が前述の規則をみたすかどうか確認してみましょう。 -```tut +```scala mdoc:nest trait Monad[F[_]] { def point[A](a: A): F[A] def bind[A, B](fa: F[A])(f: A => F[B]): F[B] @@ -202,7 +202,7 @@ associativeLaw(fa, f, g) 2つの同じ型を結合する機能を持ち、更にゼロ値を知る型クラスはMonoidと呼ばれています。 -```tut:silent +```scala mdoc:nest:silent trait Monoid[F] { def append(a: F, b: F): F def zero: F @@ -211,7 +211,7 @@ trait Monoid[F] { 前章で定義したAdditive型とよく似ていますが、Monoidは次の規則を満たす必要があります。 -```tut:silent +```scala mdoc:nest:silent def leftIdentityLaw[F](a: F)(implicit F: Monoid[F]): Boolean = a == F.append(F.zero, a) def rightIdentityLaw[F](a: F)(implicit F: Monoid[F]): Boolean = a == F.append(a, F.zero) def associativeLaw[F](a: F, b: F, c: F)(implicit F: Monoid[F]): Boolean = { @@ -221,7 +221,7 @@ def associativeLaw[F](a: F, b: F, c: F)(implicit F: Monoid[F]): Boolean = { Option[Int]型でMonoidインスタンスを定義してみましょう。 -```tut +```scala mdoc:nest trait Monoid[F] { def append(a: F, b: F): F def zero: F diff --git a/test/link.js b/test/link.js index 1900dbcbf..078f45d01 100644 --- a/test/link.js +++ b/test/link.js @@ -229,7 +229,13 @@ describe("Check links", () => { let [urls, files] = partition(links, isUrl); // #から始まるリンクはページ内リンク(脚注)なので除外 - let testFiles = files.filter((s) => !s.startsWith("#")).map(modifyLinkForTest); + // + // Scalaコードの一部をリンクとして検出してしまうので、特定の拡張子以外は除外 + let testFiles = files.filter((s) => + !s.startsWith("#") && ( + s.endsWith(".md") || s.endsWith(".scala") || s.endsWith(".sbt") + ) + ).map(modifyLinkForTest); // src/../foo/barのようなリンクはビルド後は参照できなくなるのでそういったリンクがないことを確認する。 it("should local files are in src directory", function() {