diff --git a/.github/workflows/mdbook.yml b/.github/workflows/mdbook.yml
index de22af42..bbf452a3 100644
--- a/.github/workflows/mdbook.yml
+++ b/.github/workflows/mdbook.yml
@@ -4,7 +4,7 @@ on:
push:
branches: [ja]
# branches: [master]
- pull_request:
+ # pull_request:
jobs:
deploy:
diff --git a/.po4a-version b/.po4a-version
index 710dfe7d..a5f31293 100644
--- a/.po4a-version
+++ b/.po4a-version
@@ -1,4 +1,4 @@
-po4a version 0.68.
+po4a version 0.69.
Written by Martin Quinson and Denis Barbier.
Copyright © 2002-2022 Software in the Public Interest, Inc.
diff --git a/.textlintrc.json b/.textlintrc.json
index 62a53392..f71270f5 100644
--- a/.textlintrc.json
+++ b/.textlintrc.json
@@ -5,10 +5,15 @@
},
"preset-ja-technical-writing": {
"ja-no-mixed-period": {
- "allowPeriodMarks": ["、", "…"]
+ "allowPeriodMarks": [
+ "、",
+ "…"
+ ]
},
"ja-no-successive-word": {
- "allow": ["…"]
+ "allow": [
+ "…"
+ ]
},
"ja-no-weak-phrase": false,
"max-kanji-continuous-len": {
@@ -34,7 +39,8 @@
"配列内包表記",
"配列型構築子",
"関数合成演算子",
- "領域特化言語"
+ "領域特化言語",
+ "可変参照領域"
]
},
"no-doubled-joshi": false,
@@ -42,7 +48,17 @@
"allowHalfWidthExclamation": true,
"allowFullWidthExclamation": true
},
- "sentence-length": false
+ "sentence-length": false,
+ "ja-no-redundant-expression": {
+ "dictOptions": {
+ "dict6": {
+ "allows": [
+ "動作を実行",
+ "プログラムを実行"
+ ]
+ }
+ }
+ }
}
}
}
diff --git a/CONTRIBUTING.ja.md b/CONTRIBUTING.ja.md
new file mode 100644
index 00000000..1f1b55b1
--- /dev/null
+++ b/CONTRIBUTING.ja.md
@@ -0,0 +1,15 @@
+# 貢献
+
+本書で何かしら改善になるものを見付けたらPRやイシューを開いてください。
+
+フィードバックに感謝します。
+
+## 章のテキストを編集する
+
+テキストの変更をローカルで見てみるには、[mdbook](https://github.com/rust-lang/mdBook)をインストールし、以下のコマンドをリポジトリのルートで走らせます。
+
+```sh
+mdbook serve -o
+```
+
+readmeファイルへの変更毎に描画されたwebページが自動的に再読み込みされます。
diff --git a/manifest.scm b/manifest.scm
index 27fd07e1..adbbe998 100644
--- a/manifest.scm
+++ b/manifest.scm
@@ -1,5 +1,4 @@
(use-modules (gnu packages node)
- (gnu packages gettext)
- (gemmaro packages mdbook))
+ (gnu packages gettext))
-(packages->manifest (list node-lts po4a mdbook))
+(packages->manifest (list node-lts po4a))
diff --git a/po4a.cfg b/po4a.cfg
index 2e602bc2..44f1b5bd 100644
--- a/po4a.cfg
+++ b/po4a.cfg
@@ -56,3 +56,6 @@
[type:text] text/chapter9.md \
$lang:text-ja/chapter9.md
+
+[type:text] CONTRIBUTING.md \
+ $lang:CONTRIBUTING.ja.md
diff --git a/text-ja/chapter1.md b/text-ja/chapter1.md
index d1c29a4a..632284a6 100644
--- a/text-ja/chapter1.md
+++ b/text-ja/chapter1.md
@@ -35,29 +35,33 @@
- [React](https://reactjs.org)や[virtual-dom](https://github.com/Matt-Esch/virtual-dom)などのライブラリは、アプリケーションの状態についての純粋な関数としてその外観をモデル化しています
-関数は単純な抽象化を可能にし、優れた生産性をもたらしてくれます。
+関数は大幅な生産性の向上を齎しうる単純な抽象化を可能にします。
しかし、JavaScriptでの関数型プログラミングには欠点があります。
-JavaScriptは冗長で、型付けされず、強力な抽象化を欠いているのです。
-また、無秩序に書かれたJavaScriptコードでは、等式推論がとても困難です。
+JavaScriptは冗長で、型付けされず、強力な抽象化の形式を欠いているのです。
+また、野放図なJavaScriptコードは等式推論がとても困難です。
-PureScriptはこのような問題を解決すべく作られたプログラミング言語です。
-PureScriptは、とても表現力豊かでありながらわかりやすく読みやすいコードを書けるようにする、軽量な構文を備えています。
-強力な抽象化を提供する豊かな型システムも採用しています。
+PureScriptはこうした課題への対処を目指すプログラミング言語です。
+PureScriptは軽量な構文を備えていますが、この構文によりとても表現力豊かでありながら分かりやすく読みやすいコードが書けるのです。
+強力な抽象化を支援する豊かな型システムも採用しています。
また、JavaScriptやJavaScriptへとコンパイルされる他の言語と相互運用するときに重要な、高速で理解しやすいコードを生成します。
-PureScriptを一言で言えば、純粋関数型プログラミングの理論的な強力さと、JavaScriptのお手軽で緩いプログラミングスタイルとの、とても現実的なバランスを狙った言語だということを理解して頂けたらと思います。
+概してPureScriptとは、純粋関数型プログラミングの理論的な強力さと、JavaScriptのお手軽で緩いプログラミングスタイルとの、とても現実的なバランスを狙った言語だということを理解して頂けたらと思います。
+
+> なお、PureScriptはJavaScriptのみならず他のバックエンドを対象にできますが、本書ではwebブラウザとnode環境に焦点を絞ります。
## 型と型推論
-動的型付けの言語と静的型付けの言語をめぐる議論についてはよく知られています。PureScriptは*静的型付け*の言語、つまり正しいプログラムはコンパイラによってその動作を示すような*型*を与えられる言語です。逆にいえば、型を与えることができないプログラムは
-_誤ったプログラム_
-であり、コンパイラによって拒否されます。動的型付けの言語とは異なり、PureScriptでは型は*コンパイル時*のみに存在し、実行時には型の表現はありません。
+動的型付けの言語と静的型付けの言語をめぐる議論については充分に文書化されています。
+PureScriptは*静的型付け*の言語、つまり正しいプログラムはコンパイラによって*型*を与えられる言語です。
+またこの型は、その動作を示すものです。
+逆に言えば、型を与えることができないプログラムは*誤ったプログラム*であり、コンパイラによって拒否されます。
+動的型付けの言語とは異なり、PureScriptでは型は*コンパイル時*にのみ存在し、実行時には一切その表現がありません。
-PureScriptの型は、これまでJavaやC#のような他の言語で見たような型とは、いろいろな意味で異なっていることにも注意することが大切です。
-おおまかに言えばPureScriptの型はJavaやC#と同じ目的を持っているものの、PureScriptの型はMLやHaskellのような言語に影響を受けています。
-開発者がプログラムについての強い主張を表明できるので、PureScriptの型は表現力豊かなのです。
-最も重要なのは、PureScriptの型システムは*型推論*に対応していることです。
-型推論があれば他の言語より遥かに少ない型注釈で済み、型システムを厄介者ではなく*道具*にしてくれます。
-簡単な例を示すと、次のコードは*数*を定義していますが、それが `Number`型だという注釈はコードのどこにもありません。
+多くの点で、PureScriptの型とこれまでJavaやC#のような他の言語で見てきたであろう型が異なっていることにも、注意することが大切です。
+大まかに言えばPureScriptの型はJavaやC#と同じ目的を持っているものの、PureScriptの型はMLやHaskellのような言語に影響を受けています。
+PureScriptの型は表現力豊かであり、開発者はプログラムについての強い主張を表明できます。
+最も重要なのはPureScriptの型システムが*型推論*に対応していることです。
+型推論があれば他の言語より明示的な型注釈が遥かに少なく済み、型システムを厄介者ではなく*道具*にしてくれます。
+単純な一例として、次のコードは*数*を定義していますが、`Number`型への言及はコードのどこにもありません。
```haskell
iAmANumber =
@@ -65,8 +69,8 @@ iAmANumber =
in square 42.0
```
-次のもっと複雑な例では、 _コンパイラにとって未知_
-の型が存在しているときでさえも、型注釈なしで型の正しさを確かめることができるということが示されています。
+より込み入った次の例では、*コンパイラにとって未知*の型が存在します。
+それでも、型注釈なく型の正しさを確かめられていることを示しています。
```haskell
iterate f 0 x = x
@@ -78,20 +82,21 @@ iterate f n x = iterate f (n - 1) (f x)
本書で納得していただきたい(または既にお持ちの信条に寄り添って改めて断言したい)ことは、静的型が単にプログラムの正しさに自信を持つためだけのものではなく、それ自体の正しさによって開発の手助けになるものでもあるということです。JavaScriptではごく単純な抽象化を施すのでも大規模なコードのリファクタリングをすることは難しいですが、型検証器のある表現力豊かな型システムは、リファクタリングさえ楽しく対話的な体験にしてくれます。
-加えて、型システムによって提供されるこの安全網は、より高度な抽象化をも可能にします。
-実際に、関数型プログラミング言語Haskellによって知られるようになった、型駆動の強力な抽象化の形式である『型クラス』をPureScriptは備えています。
+加えて、型システムによって提供されるこの安全網は、より高度な抽象化を可能にします。
+実際に、根本的に型駆動な抽象化の強力な形式である型クラスをPureScriptは提供しています。
+この型クラスとは、関数型プログラミング言語Haskellによって有名になりました。
-## 多言語Webプログラミング
+## 多言語webプログラミング
-関数型プログラミングは既に多くの成功を収めています。
-枚挙に暇がありませんが、特に成功している応用例を幾つか挙げると、データ解析、構文解析、コンパイラの実装、ジェネリックプログラミング、並列処理などがあります。
+関数型プログラミングは成功を収めてきました。
+特に成功している応用例を挙げると、データ解析、構文解析、コンパイラの実装、ジェネリックプログラミング、並列処理といった具合に、枚挙に暇がありません。
PureScriptのような関数型言語でアプリケーション開発の最初から最後までを実施できるでしょう。
値や関数の型を提供することで既存のJavaScriptコードをインポートし、通常のPureScriptコードからこれらの関数を使用する機能をPureScriptは提供しています。
この手法については本書の後半で見ていくことになります。
しかし、PureScriptの強みの1つは、JavaScriptを対象とする他の言語との相互運用性にあります。
-アプリケーションの開発の一部にだけPureScriptを使用し、JavaScriptの残りの部分を記述するのに他の言語を使用するという方法もあります。
+アプリケーションの開発の一部にだけPureScriptを使用し、JavaScriptの残りの部分を記述するのに1つ以上の他の言語を使用するという方法もあります。
幾つかの例を示します。
@@ -99,17 +104,18 @@ PureScriptのような関数型言語でアプリケーション開発の最初
- JavaScriptや、他のJavaScriptにコンパイルする言語でアプリケーションを書き、PureScriptでそのテストを書く
- 既存のアプリケーションのユーザインターフェースのテストを自動化するためにPureScriptを使用する
-この本では小規模な課題をPureScriptで解決することに焦点を当てます。
+本書では小規模な課題をPureScriptで解決することに焦点を当てます。
ここで学ぶ手法は大規模なアプリケーションに組み込むこともできますが、JavaScriptからPureScriptコードを呼び出す方法、及びその逆についても見ていきます。
## ソフトウェア要件
-この本でのソフトウェア要件は最小限です。
+本書のソフトウェア要件は最小限です。
第1章では開発環境の構築を一から案内します。
これから使用するツールは、ほとんどの現代のオペレーティングシステムの標準リポジトリで使用できるものです。
-PureScriptコンパイラ自体はバイナリ形式でもダウンロードできますし、最新のHaskellコンパイラが動くシステム上でソースからもビルドできます。
-次の章ではこの手順を説明していきます。
+PureScriptコンパイラ自体はバイナリの配布物としてもダウンロードできますし、最新のGHC
+Haskellコンパイラが動く任意のシステム上でソースからのビルドもできます。
+次の章ではこの手順を進めていきます。
本書のこのバージョンのコードは`0.15.*`バージョンのPureScriptコンパイラと互換性があります。
@@ -119,23 +125,26 @@ PureScriptコンパイラ自体はバイナリ形式でもダウンロードで
既にNPMやBowerのようなJavaScriptのエコシステムでの経験があれば、自身の好みに応じて標準設定をカスタマイズしたい場合などに役に立ちます。
ですがそのような知識は必要ありません。
-関数型プログラミングの予備知識は必要ありませんが、あっても害にはならないでしょう。
-新しい考えかたは実例とともに登場するので、これから使う関数型プログラミングからこうした概念に対する直感的な理解を得ることができるはずです。
+関数型プログラミングの事前知識は必要ありませんが、あっても決して害にはならないでしょう。
+新しい考えかたは実例と共に登場するため、これから使っていく関数型プログラミングからこうした概念に対する直感が形成されることでしょう。
-PureScriptはプログラミング言語Haskellに強く影響を受けているため、Haskellに通じている読者はこの本の中で提示された概念や構文の多くに見覚えがあるでしょう。
-しかし、読者はPureScriptとHaskellの間には幾らか重要な違いがあることも理解しておかなければなりません。
+PureScriptはプログラミング言語Haskellに強く影響を受けているため、Haskellに通じている読者は本書で提示された概念や構文の多くに見覚えがあるでしょう。
+しかし、PureScriptとHaskellの間には数多くの重要な違いがあることも理解しておくと良いでしょう。
ここで紹介する概念の多くはHaskellでも同じように解釈できるとはいえ、どちらかの言語での考え方を他方の言語でそのまま応用しようとすることは、必ずしも適切ではありません。
## 本書の読み進めかた
-本書の各章は、概ね章ごとに完結しています。
-しかし、多少の関数型プログラミングの経験がある初心者でも、まずは各章を順番に進めていくことをお勧めします。
-最初の数章では、本書の後半の内容を理解するために必要な基礎知識を養います。
+本書のほとんどの章が各章毎に完結しています。
+しかし、関数型プログラミングの経験がほとんどない初心者の方は、各章を順番に進めていくのが賢明です。
+最初の数章は本書の後半の内容を理解するのに必要な下地作りです。
関数型プログラミングの考え方に充分通じた読者(特にMLやHaskellのような強く型付けされた言語での経験を持つ読者)なら、本書の前半の章を読まなくても、後半の章のコードの大まかな理解を得ることが恐らく可能でしょう。
-各章ではそれぞれ1つの実用的な例に焦点をあて、新しい考え方を導入するための動機付けとして用います。各章のコードは本書の[GitHubのリポジトリ](https://github.com/purescript-contrib/purescript-book)から入手できます。ソースコードから抜粋したコード片が掲載されている章もありますが、完全に理解するためには本書に掲載されたコードと平行してリポジトリのソースコードを読む必要があります。対話式環境PSCiで実行し理解を確かめられるように、長めの節には短いコード片が掲載されていることがあります。
+各章では1つの実用的な例に焦点を当て、新しい考え方を導入するための動機を与えます。
+各章のコードは本書の[GitHubのリポジトリ](https://github.com/purescript-contrib/purescript-book)から入手できます。
+該当の章のソースコードから抜粋したコード片が含まれる章もありますが、本書の内容に沿ってリポジトリのソースコードを読まれると良いでしょう。
+長めの節には、理解を確かめられるように対話式モードのPSCiで実行できる短めのコード片が含まれます。
-コード例は次のように等幅フォントで示されています。
+コード例は次のように等幅フォントで示されます。
```haskell
module Example where
@@ -151,8 +160,9 @@ main = log "Hello, World!"
$ spago build
```
-通常、これらのコマンドはLinuxやMac
-OSの利用者ならそのまま適用できますが、Windowsの利用者はファイル区切り文字を変更する、シェルの組み込み機能をWindowsの相当するものに置き換えるなどの小さな変更を加える必要があるかもしれません。
+通常、これらのコマンドはLinuxやMac OSの利用者に合わせたものになっています。
+そのためWindowsの利用者は小さな変更を加える必要があるかもしれません。
+ファイル区切り文字を変更したり、シェルの組み込み機能をWindowsの相当するものに置き換えるなどです。
PSCi対話式モードプロンプトに入力するコマンドは、行の先頭に山括弧が付けられています。
@@ -161,54 +171,61 @@ PSCi対話式モードプロンプトに入力するコマンドは、行の先
3
```
-各章には演習が付いており、それぞれ難易度も示されています。各章の内容を完全に理解するために、演習に取り組むことを強くお勧めします。
+各章には演習が含まれており、難易度も示されています。
+内容を完全に理解するために、各章の演習に取り組むことを強くお勧めします。
-この本は初心者にPureScriptへの導入を提供することを目的としており、問題についてのお決まりの解決策の一覧を提供するような種類の本ではありません。初心者にとってこの本を読むのは楽しい挑戦になるはずですし、本書の内容を読み演習に挑戦すればだいたいの利益を得られるでしょうが、なにより重要なのは、あなたが自分自身のコードを書いてみることです。
+本書は初心者にPureScriptへの導入を提供することを目的としており、課題に対するお決まりの解決策の一覧を提供するような類の本ではありません。
+初心者にとっては楽しい挑戦になるはずです。
+内容を読んで演習に挑戦すれば得るものがあることでしょう。
+そして何よりも大切なのは、自分自身でコードを書いてみることです。
## 困ったときには
もしどこかでつまずいたときには、PureScriptを学べるオンラインで利用可能な資料が沢山あります。
- [PureScriptのDiscordサーバ](https://discord.gg/vKn9up84bp)は抱えている問題についてチャットするのに良い場所です。
- サーバはPureScriptについてのチャット専用です。
-- [PurescriptのDiscourseフォーラム](https://discourse.purescript.org/)もよくある問題への解決策を探すのに良い場所です。メッセージ履歴が約2週間しか保たないSlackとは違い、ここで質問した内容は将来の読者の助けとして使えるでしょう。
+ こちらのサーバはPureScriptについてのチャット専用です。
+- [PurescriptのDiscourseフォーラム](https://discourse.purescript.org/)もよくある問題への解決策を探すのに良い場所です。
- [PureScript: Jordan's
Reference](https://github.com/jordanmartinez/purescript-jordans-reference)は別のかなり深く踏み込んだ学習資料です。
- この本の中のある概念が理解しにくかったら、そちらの参考書の対応する節を読むとよいでしょう。
+ 本書中の概念で理解しにくいものがあったら、そちらの参考書の対応する節を読むとよいでしょう。
- [Pursuit](https://pursuit.purescript.org)はPureScriptの型と関数を検索できるデータベースです。
Pursuitのヘルプページを読むと[どのような種類の検索ができるのかがわかります](https://pursuit.purescript.org/help/users)。
- 非公式の[PureScript
Cookbook](https://github.com/JordanMartinez/purescript-cookbook)は「Xするにはどうするの」といった類の質問にコードを混じえて答えを提供します。
- [PureScriptドキュメントリポジトリ](https://github.com/purescript/documentation)には、PureScriptの開発者や利用者が書いた幅広い話題の記事と例が集まっています。
-- [PureScriptのWebサイト](https://www.purescript.org)には、コード例、映像、他の初心者向け資料を含む幾つかの学習資料へのリンクがあります。
+- [PureScriptのwebサイト](https://www.purescript.org)には幾つかの学習資料へのリンクがあります。
+ コード例、映像、他の初心者向け資料などです。
- [Try
- PureScript!](https://try.purescript.org)は利用者がWebブラウザでPureScriptのコードをコンパイルできるWebサイトです。
- 幾つかの簡単なコードの例があります。
+ PureScript!](https://try.purescript.org)は利用者がwebブラウザでPureScriptのコードをコンパイルできるwebサイトです。
+ 幾つかの簡単なコードの例もあります。
-もし例を読んで学ぶ方が好きでしたら、GitHubの
-`purescript`組織、`purescript-node`組織及び`purescript-contrib`組織にはPureScriptコードの例が沢山あります。
+もし例を読んで学ぶ方が好きでしたら、GitHubの[purescript](https://github.com/purescript)、[purescript-node](https://github.com/purescript-node)、[purescript-contrib](https://github.com/purescript-contrib)組織にはPureScriptコードの例が沢山あります。
## 著者について
-私はPureScriptコンパイラの最初の開発者です。私はカリフォルニア州ロサンゼルスを拠点にしており、8ビットパーソナルコンピュータ、Amstrad
+私はPureScriptコンパイラの最初の開発者です。
+カリフォルニア州ロサンゼルスを拠点にしており、8ビットパーソナルコンピュータであるAmstrad
CPC上のBASICでまだ幼い時にプログラミングを始めました。
-それ以来、私は幾つものプログラミング言語(JavaやScala、C#、F#、Haskell、そしてPureScript)で業務に携わってきました。
+それ以来、私は幾つものプログラミング言語(JavaやScala、C#、F#、Haskell、そしてPureScript)で専門的に業務に携わってきました。
プロとしての経歴が始まって間もなく、私は関数型プログラミングと数学の関係を理解するようになり、そしてプログラミング言語Haskellを使って関数型の概念の学習を楽しみました。
JavaScriptでの経験をもとに、私はPureScriptコンパイラの開発を始めることにしました。
気が付くとHaskellのような言語から取り上げた関数型プログラミングの手法を使っていましたが、それを応用するためのもっと理に適った環境を求めていました。
そのとき検討した案のなかには、Haskellからその意味論を維持しながらJavaScriptへとコンパイルするいろいろな試み(Fay、Haste、GHCJS)もありました。
-しかし私が興味を持っていたのはこの問題への別の切り口からのアプローチ、すなわちHaskellのような言語の構文と型システムを楽しみながらJavaScriptの意味論も維持するということが、どのようにすれば可能になるのかでした。
+しかし私が興味を持っていたのは、この問題へ別の切り口からアプローチすると、どの程度うまくいくのかということでした。
+そのアプローチとは、JavaScriptの意味論を維持しつつ、Haskellのような言語の構文と型システムを楽しむことなのです。
私は[ブログ](http://blog.functorial.com)を運営しており、[Twitterで連絡をとる](http://twitter.com/paf31)こともできます。
## 謝辞
-現在の状態に到達するまでPureScriptを手伝ってくれた多くの協力者に感謝したいと思います。コンパイラやツール、ライブラリ、ドキュメント、テストでの組織的で弛まぬ努力がなかったら、プロジェクトは間違いなく失敗していたことでしょう。
+現在に至るまでPureScriptに手を貸してくださった多くの協力者に感謝したいと思います。
+コンパイラ、ツール、ライブラリ、ドキュメント、テストでの、巨大で組織的な尽力なくしては、プロジェクトは間違いなく失敗していたことでしょう。
-この本の表紙に表示されたPureScriptのロゴはGareth Hughesによって作成されたもので、[Creative Commons
+本書の表紙に示されたPureScriptのロゴはGareth Hughesによって作成されたもので、[Creative Commons
Attribution 4.0
license](https://creativecommons.org/licenses/by/4.0/)の条件の下で再利用させて頂いています 。
-最後に、この本の内容に関する反応や訂正をくださった全ての方に、心より感謝したいと思います。
+最後に、本書の内容に関する反応や訂正をくださった全ての方に、心より感謝したいと思います。
diff --git a/text-ja/chapter10.md b/text-ja/chapter10.md
index 78f319ff..c4dc8cb1 100644
--- a/text-ja/chapter10.md
+++ b/text-ja/chapter10.md
@@ -2,9 +2,9 @@
## この章の目標
-この章でPureScriptの _外部関数インターフェース_ (foreign function interface; _FFI_) を紹介します。
+本章ではPureScriptの*外部関数インターフェース* (foreign function interface; *FFI*) を紹介します。
これによりPureScriptコードからJavaScriptコードへの呼び出し、及びその逆が可能になります。
-これから扱うのは次のようなものです。
+以下の方法を押さえていきます。
- 純粋で、作用のある、非同期なJavaScript関数をPureScriptから呼び出す。
- 型付けされていないデータを扱う。
@@ -16,15 +16,16 @@
- 利用者にポップアップ通知で警告する。
- フォームのデータを直列化してブラウザのローカルストレージに保存し、アプリケーションが再起動したときにそれを再読み込みする
-さらに一般にはそこまで重用されない幾つかの話題を押さえた補遺もあります。
-ご自由にこれらの節を読んで構いませんが、学習目標にあまり関係しなければ、本の残りを読み進める妨げにならないようにしてください。
+さらに一般にはそこまで重用されない幾つかの追加の話題を押さえた補遺もあります。
+ご自由にこれらの節を読んで構いませんが、学習目標にあまり関係しなければ、本書の残りを読み進める妨げにならないようにしてください。
- 実行時のPureScriptの値の表現を理解する。
- JavaScriptからPureScriptを呼び出す。
## プロジェクトの準備
-このモジュールのソースコードは、第3章、第7章及び第8章の続きになります。そうしたわけでソースツリーにはこれらの章からの適切なソースファイルが含まれています。
+このモジュールのソースコードは、第3章、第7章及び第8章の続きになります。
+そうしたわけでソースツリーにはこれらの章からの適切なソースファイルが含まれています。
この章は`argonaut`ライブラリを依存関係として導入しています。
このライブラリはJSONにエンコードしたりJSONをデコードしたりするために使います。
@@ -37,14 +38,14 @@ test`を走らせることによって`test/Main.purs`中の単体試験につ
## 免責事項
-JavaScriptを扱う作業をできる限り簡単にするため、PureScriptは直感的な外部関数インターフェースを提供しています。
-しかし、FFIはPureScriptの*応用的な*機能であることには留意していただきたいと思います。
-FFIを安全かつ効率的に使用するには、扱うつもりであるデータの実行時の表現についてよく理解していなければなりません。
+JavaScriptの扱いをできる限り単純にするため、PureScriptは直感的な外部関数インターフェースを提供しています。
+しかし、FFIはこの言語の*応用的な*機能であることには心に留めておかれると良いでしょう。
+安全かつ効率的に使用するには、扱うつもりであるデータの実行時の表現について理解していなければなりません。
この章では、PureScriptの標準ライブラリのコードに付いて回るそのような理解を伝授することを目指します。
PureScriptのFFIはとても柔軟に設計されています。
-実際には、外部関数に最低限の型だけを与えるか、それとも型システムを利用して外部のコードの誤った使い方を防ぐようにするか、開発者が選べるということを意味しています。
-標準ライブラリのコードは、後者の手法を好む傾向にあります。
+実際には、外部関数にとても単純な型を与えるか、型システムを利用して外部のコードの誤った使い方を防ぐようにするか、開発者が選べるようになっています。
+標準ライブラリのコードは、後者の手法を採る傾向にあります。
簡単な例としては、JavaScriptの関数で戻り値が `null`にならないことは保証できません。
実のところ、JavaScriptらしさのあるコードはかなり頻繁に `null`を返します。
@@ -68,7 +69,8 @@ node> encodeURIComponent('Hello World')
'Hello%20World'
```
-`null`でない文字列から `null`でない文字列への関数であり、副作用を持っていないので、この関数は型 `String -> String`について適切な実行時表現を持っています。
+この関数は関数の型`String -> String`について適切な実行時表現を持っています。
+`null`でない文字列を取って`null`でない文字列にするもので、副作用を持たないからです。
次のような外部インポート宣言を使うと、この関数に型を割り当てることができます。
@@ -135,8 +137,8 @@ $ spago repl
{{#include ../exercises/chapter10/test/Examples.purs:diagonal}}
```
-PureScriptの関数は _カリー化_ されていることを思い出してください。
-`diagonal`は`Number`を取って _関数_ を返す関数です。
+PureScriptの関数は*カリー化*されていることを思い出してください。
+`diagonal`は`Number`を取って*関数*を返す関数です。
そして返された関数は`Number`を取って`Number`を返します。
```js
@@ -189,10 +191,11 @@ Type -> Type -> Type -> Type
```
`Fn2`は3つの型引数を取ります。
-`Fn2 a b c`は、型 `a`と `b`の2つの引数、返り値の型 `c`をもつカリー化されていない関数の型を表現しています。
+`Fn2 a b c`は、型`a`と`b`の2つの引数、返り値の型`c`を持つカリー化されていない関数の型を表現しています。
これを使って外部モジュールから`diagonalUncurried`をインポートしました。
-カリー化されていない関数と引数を取る`runFn2`で呼び出すことができます。
+そうして`runFn2`を使って呼び出せます。
+これはカリー化されていない関数と引数を取るものです。
```text
$ spago repl
@@ -207,7 +210,10 @@ $ spago repl
## カリー化されていない関数についての補足
-PureScriptのカリー化された関数にはもちろん利点があります。部分的に関数を適用でき、関数型に型クラスインスタンスを与えられます。しかし効率上の代償も付いてくるのです。効率性が決定的に重要なコードでは多変数を受け付けるカリー化されていないJavaScript関数を定義する必要が時々あります。
+PureScriptのカリー化された関数には勿論利点があります。
+部分的に関数を適用でき、関数型に型クラスインスタンスを与えられるのです。
+しかし効率上の代償も付いてきます。
+効率性が決定的に重要なコードでは時々、多変数を受け付けるカリー化されていないJavaScript関数を定義する必要があります。
PureScriptでカリー化されていない関数を作ることもできます。
2引数の関数については`mkFn2`関数が使えます。
@@ -255,12 +261,12 @@ var curriedSum = curriedAdd(3)(10);
## 現代的なJavaScriptの構文についての補足
前に見た矢印関数構文はES6の機能であり、そのため幾つかの古いブラウザ(名指しすればIE11)と互換性がありません。
-執筆時点でWebブラウザをまだ更新していない[6%の利用者が矢印関数を使うことができないと推計](https://caniuse.com/#feat=arrow-functions)されています。
+執筆時点でwebブラウザをまだ更新していない[6%の利用者が矢印関数を使うことができないと推計](https://caniuse.com/#feat=arrow-functions)されています。
ほとんどの利用者にとって互換性があるようにするため、PureScriptコンパイラによって生成されるJavaScriptコードは矢印関数を使っていません。
また、同じ理由で**公開するライブラリでも矢印関数を避ける**ことが推奨されます。
-それでも自分のFFIコードで矢印関数を使うこともできますが、デプロイの作業工程でES5に互換性のある関数へ変換するために[Babel](https://github.com/babel/babel#intro)などのツールを含めるべきです。
+それでも自分のFFIコードで矢印関数を使うこともできますが、デプロイの作業工程でES5に互換性のある関数へ変換するために[Babel](https://github.com/babel/babel#intro)などのツールを含めると良いでしょう。
ES6の矢印関数がより読みやすく感じたら[Lebab](https://github.com/lebab/lebab)のようなツールを使ってコンパイラの`output`ディレクトリにJavaScriptのコードを変換できます。
@@ -299,7 +305,8 @@ Record | Object
`String`と`Number`という原始型の例は既に見てきました。
ここから`Array`や`Record`(JavaScriptでは`Object`)といった構造的な型を眺めていきます。
-`Array`の受け渡しを実演するために、以下に`Int`の`Array`を取って別の配列として累計の和を返すJavaScriptの関数の呼び出し方を示します。前にありましたが、JavaScriptは`Int`のための分離した型を持たないため、PureScriptでの`Int`と`Number`はJavaScriptでの`Number`に翻訳されます。
+`Array`を渡すところを実演するために、以下に`Int`の`Array`を取って別の配列として累計の和を返すJavaScriptの関数の呼び出し方を示します。
+前にありましたが、JavaScriptは`Int`のための分離した型を持たないため、PureScriptでの`Int`と`Number`は両方共JavaScriptでの`Number`に翻訳されます。
```hs
foreign import cumulativeSums :: Array Int -> Array Int
@@ -325,7 +332,7 @@ $ spago repl
[1,3,6]
```
-`Record`の受け渡しを実演するために、以下に2つの`Complex`な数をレコードとして取り、和を別のレコードとして返すJavaScriptの呼び出し方を示します。
+`Record`を渡すところを実演するために、以下に2つの`Complex`な数をレコードとして取り、和を別のレコードとして返すJavaScriptの呼び出し方を示します。
PureScriptでの`Record`がJavaScriptでは`Object`として表現されることに注意してください。
```hs
@@ -354,7 +361,10 @@ $ spago repl
{ imag: 6.0, real: 4.0 }
```
-なお、上の手法にはJavaScriptが期待通りの型を返すことを信用する必要があります。PureScriptはJavaScriptのコードに型検査を適用できないからです。この型安全性の配慮について後のJSONの節でより詳しく記述していきます。型の不整合から身を守る手法についても押さえます。
+なお、上の手法にはJavaScriptが期待通りの型を返すことを信用する必要があります。
+PureScriptはJavaScriptのコードに型検査を適用できないからです。
+この型安全性の配慮について後のJSONの節でより詳しく解説していきます。
+型の不整合から身を守る手法についても押さえます。
## 演習
@@ -430,23 +440,26 @@ forall a. (forall x. x -> Maybe x) -> (forall x. Maybe x) -> Array a -> Maybe a
以下ではないことに注意です。
```hs
-forall a. ( a -> Maybe a) -> Maybe a -> Array a -> Maybe a
+forall a. (a -> Maybe a) -> Maybe a -> Array a -> Maybe a
```
-どちらの形式でも動きますが、後者は`Just`と`Nothing`の場所での招かれざる入力に侵されやすくなります。例えばより脆弱な場合では以下のようにして呼ぶことができます。
+どちらの形式でも動きますが、後者は`Just`と`Nothing`の場所での招かれざる入力に対してより脆弱です。
+
+例えば、比較的脆い方では、以下のように呼び出せるでしょう。
```hs
maybeHeadImpl (\_ -> Just 1000) (Just 1000) [1,2,3]
```
-これはいかなる配列についても`Just 1000`を返します。
-この脆弱性は`a`が`Int`のときに(これは入力の配列に基づきます)`(\_ -> Just 1000)`と`Just 1000`がシグネチャ`(a -> Maybe a)`と`Maybe a`にそれぞれ合致しているために許されているのです。
+これは如何なる配列の入力に対しても`Just 1000`を返します。
+
+この脆弱性では、`a`が`Int`のときに(これは入力の配列に基づきます)`(\_ -> Just 1000)`と`Just 1000`がシグネチャ`(a -> Maybe a)`と`Maybe a`にそれぞれ照合するために許容されてしまっています。
より安全な型シグネチャでは、入力の配列に基づいて`a`が`Int`に決定されたとしても、`forall x`に絡むシグネチャに合致する妥当な関数を提供する必要があります。`(forall x. Maybe x)`の *唯一* の選択肢は`Nothing`ですが、それは`Just`値が`x`の型を前提にしてしまうと、もはや全ての`x`については妥当でなくなってしまうからです。`(forall x. x -> Maybe x)`の唯一の選択肢は`Just`(望まれている引数)と`(\_ -> Nothing)`であり、後者は唯一残っている脆弱性になるのです。
## 外部型の定義
-`Maybe a`を返す代わりに実は`arr[0]`を返したいのだとしましょう。
+`Maybe a`を返す代わりに`arr[0]`を返したいのだとしましょう。
型`a`ないし`undefined`値(ただし`null`ではありません)の何れかの値を表現する型がほしいです。
この型を`Undefined a`と呼びましょう。
@@ -462,7 +475,7 @@ foreign import data Undefined :: Type -> Type
この場合は`Undefined`の種が `Type -> Type`であると宣言しています。
言い換えれば`Undefined`は型構築子です。
-これで元の`head`の定義を単に再利用できます。
+これで元の`head`の定義を再利用できます。
```javascript
export const undefinedHead = arr =>
@@ -478,7 +491,7 @@ foreign import undefinedHead :: forall a. Array a -> Undefined a
`undefinedHead`関数の本体は`undefined`かもしれない`arr[0]`を返します。
そしてこの型シグネチャはその事実を正しく反映しています。
-この関数はその型の適切な実行時表現を持っていますが、型 `Undefined a`の値を使用する方法がないので、全く役に立ちません。
+この関数はその型の適切な実行時表現を持っていますが、型`Undefined a`の値を使用する方法がないので、全く役に立ちません。
いや、言い過ぎました。
別のFFIでこの型を使えますからね。
@@ -502,7 +515,7 @@ isEmpty :: forall a. Array a -> Boolean
isEmpty = isUndefined <<< undefinedHead
```
-このように、定義したこの外部関数はとても簡単です。
+このように、定義したこの外部関数はとても単純です。
つまりPureScriptの型検査器を使うことによる利益が最大限得られるのです。
一般に、外部関数は可能な限り小さく保ち、できるだけアプリケーションの処理はPureScriptコードへ移動しておくことをお勧めします。
@@ -583,7 +596,7 @@ bold :: forall a. Show a => a -> String
bold x = boldImpl show x
```
-代わりにポイントフリー形式だとこうです。
+代えてポイントフリー形式だとこうです。
```hs
bold :: forall a. Show a => a -> String
@@ -744,7 +757,7 @@ done waiting
```
REPLでの非同期ログ出力はブロック全体が実行を終了するまで印字を待機する点に注意しましょう。
-このコードを`spago test`で走らせた場合、印字の _合間に_ 僅かな遅延があり、より予測に近い挙動をします。
+このコードを`spago test`で走らせた場合、印字の*合間に*僅かな遅延があり、より予測に近い挙動をします。
他にプロミスから値を返す例を見てみましょう。
この関数は`async`と`await`を使って書かれていますが、これはプロミスの糖衣構文に過ぎません。
@@ -792,9 +805,9 @@ unit
## JSON
アプリケーションでJSONを使うことには多くの理由があります。
-例えばWebのAPIと疎通するよくある手段であるためです。
+例えばwebのAPIと疎通するよくある手段であるためです。
この節では他の用例についてもお話ししましょう。
-構造的なデータをFFI越しに渡すことで型安全性を向上させる手法から始めます。
+構造的なデータをFFI越しに渡す場合に型安全性を向上させる手法から始めます。
少し前のFFI関数`cumulativeSums`と`addComplex`を再訪し、それぞれに1つバグを混入させてみましょう。
@@ -864,7 +877,7 @@ PureScriptのコードにバグ一匹通さないようにするため、JavaScr
`argonaut`ライブラリにはこのために必要なJSONのデコードとエンコードの機能が備わっています。
このライブラリには素晴らしい[ドキュメント](https://github.com/purescript-contrib/purescript-argonaut#documentation)があるので、本書では基本的な用法だけを押さえます。
-返る型を`Json`として定義するようにして、代わりとなる外部インポートをつくるとこうなります。
+返る型を`Json`として定義するようにして、代わりとなる外部インポートを作るとこうなります。
```hs
foreign import cumulativeSumsJson :: Array Int -> Json
@@ -981,11 +994,11 @@ Map String Int
1. (簡単)より広い種類のマップに関して動作するよう、前のJavaScriptの関数の新しい梱包を書いてください。
シグネチャは`valuesOfMapGeneric :: forall k v. Map k v -> Either
JsonDecodeError (Set v)`です。
- なお`k`と`v`に幾つかの型クラス制約を加える必要があるでしょう。
+ なお、`k`と`v`に幾つかの型クラス制約を加える必要があるでしょう。
コンパイラが導いてくれます。
-1. (普通)少し前の`quadraticRoots`を書き換えて`quadraticRootSet`としてください。
+1. (普通)少し前の`quadraticRoots`関数を書き換えて`quadraticRootSet`としてください。
この関数は`Complex`の根をJSONを介して(`Pair`の代わりに)`Set`として返します。
-1. (難しい)少し前の`quadraticRoots`を書き換えて`quadraticRootsSafe`としてください。
+1. (難しい)少し前の`quadraticRoots`関数を書き換えて`quadraticRootsSafe`としてください。
この関数はJSONを使って`Complex`の根の`Pair`をFFI越しに渡します。
JavaScriptでは`Pair`構築子を使わないでください。
その代わり、デコーダーに互換性のある形式で対を返すだけにしてください。
@@ -1029,7 +1042,7 @@ Map String Int
フォームのフィールドにはこの文書の内容を入れます。
- フォームの状態を保存したり読み込んだりするのに問題があればポップアップの警告を出します。
-`Effect.Storage`モジュールに以下のWebストレージAPIのためのFFIの梱包をつくることから始めていきます。
+`Effect.Storage`モジュールに以下のwebストレージAPIのためのFFIの梱包を作ることから始めていきます。
- `setItem`はキーと値(両方とも文字列)を受け取り、指定されたキーでローカルストレージに値を格納する計算を返します。
- `getItem`はキーを取り、ローカルストレージから関連付けられたバリューの取得を試みます。
@@ -1081,7 +1094,7 @@ validateAndSave = do
log "Saved"
```
-なおこの段階でコンパイルしようとすると以下のエラーに遭遇します。
+なお、この段階でコンパイルしようとすると以下のエラーに遭遇します。
```text
No type class instance was found for
@@ -1089,8 +1102,8 @@ validateAndSave = do
```
これはなぜかというと`Person`レコード中の`PhoneType`が`EncodeJson`インスタンスを必要としているからです。
-単純に汎用のエンコードインスタンスとデコードインスタンスを導出すれば完了です。
-この仕組みについて、より詳しくはargonautのドキュメントで見られます。
+また、ついでに汎用のエンコードインスタンスとデコードインスタンスを導出していきます。
+この仕組みについての詳細情報はargonautのドキュメントにあります。
```hs
{{#include ../exercises/chapter10/src/Data/AddressBook.purs:import}}
@@ -1108,10 +1121,10 @@ validateAndSave = do
item <- getItem "person"
```
-そうしてローカルストレージから、文字列から`Person`レコードへの変換を扱う補助関数をつくります。
-なおこのストレージ中の文字列は`null`かもしれないので、うまく`String`としてデコードされるまでは外部の`Json`として表現します。
+そうしてローカルストレージ由来の文字列から`Person`レコードへ変換する補助関数を作ります。
+なお、このストレージ中の文字列は`null`かもしれないので、正常に`String`としてデコードされるまでは外部の`Json`として表現します。
道中には他にも多くの変換工程があり、それぞれで`Either`の値を返します。
-そのためこれらを`do`ブロックの中に纏めるのは理に適っています。
+そのためこれらをまとめて`do`ブロックの中に纏めるのは理に適っています。
```hs
processItem :: Json -> Either String Person
@@ -1149,7 +1162,7 @@ mkAddressBookApp =
Tuple person setPerson <- useState props.initialPerson
```
-仕上げとして、それぞれの`Left`値の`String`に`lmap`を使って前置し、エラー文言の質を向上させます。
+仕上げとして、各`Left`値の`String`に`lmap`を使って前置し、エラー文言の質を向上させます。
```hs
processItem :: Json -> Either String Person
@@ -1160,16 +1173,16 @@ processItem item = do
```
最初のエラーのみがこのアプリの通常の操作内で起こります。
-他のエラーはWebブラウザの開発ツールを開いてローカルストレージ中に保存された「person」文字列を編集し、そのページを参照することで引き起こせます。
-どのようにJSON文字列を変更したかが、どのエラーの引き金になるかを決定します。
-それぞれのエラーを引き起こせるかどうかやってみてください。
+他のエラーはwebブラウザの開発ツールを開いてローカルストレージ中に保存された「person」文字列を編集し、そのページを参照することで引き起こせます。
+どのようにJSON文字列を変更したかが、どのエラーを引き起こすかを決定します。
+各エラーを引き起こせるかご確認ください。
これでローカルストレージについては押さえました。
-次に`alert`アクションを実装していきます。
-このアクションは`Effect.Console`モジュールの`log`アクションによく似ています。
-唯一の相違点は`alert`アクションが`window.alert`メソッドを使うことで、対して`log`アクションは`console.log`メソッドを使っています。
+次に`alert`動作を実装していきます。
+この動作は`Effect.Console`モジュールの`log`動作に似ています。
+唯一の相違点は`alert`動作が`window.alert`メソッドを使うことで、対して`log`動作は`console.log`メソッドを使っています。
そういうわけで`alert`は`window.alert`が定義された環境でのみ使うことができます。
-例えばWebブラウザなどです。
+webブラウザなどです。
```hs
foreign import alert :: String -> Effect Unit
@@ -1205,14 +1218,15 @@ alert $ "Error: " <> err <> ". Loading examplePerson"
## まとめ
-この章では、PureScriptから外部のJavaScriptコードを扱う方法を学びました。また、FFIを使用して信頼できるコードを書く時に生じる問題について見てきました。
+この章では、PureScriptから外部のJavaScriptコードを扱う方法を学びました。
+また、FFIを使用して信頼できるコードを書く時に生じる問題について見てきました。
- 外部関数が正しい表現を持っていることを確かめる重要性を見てきました。
- 外部型や`Json`データ型を使用することによって、null値やJavaScriptの他の型のデータのような特殊な場合に対処する方法を学びました。
- 安全にJSONデータを直列化・直列化復元する方法を見ました。
-より多くの例については、Githubの `purescript`組織、`purescript-contrib`組織および
-`purescript-node`組織が、FFIを使用するライブラリの例を多数提供しています。残りの章では、型安全な方法で現実世界の問題を解決するために使うライブラリを幾つか見ていきます。
+より多くの例については、GitHubの`purescript`組織、`purescript-contrib`組織、及び`purescript-node`組織が、FFIを使用するライブラリの例を多数提供しています。
+残りの章では、型安全な方法で現実世界の問題を解決するために使うライブラリを幾つか見ていきます。
## 補遺
@@ -1230,7 +1244,7 @@ gcd 0 m = m
gcd n 0 = n
gcd n m
| n > m = gcd (n - m) m
- | otherwise = gcd (m - n) n
+ | otherwise = gcd (m – n) n
```
この関数は、減算を繰り返すことによって2つの数の最大公約数を見つけます。
@@ -1244,9 +1258,9 @@ import Test from 'Test.js';
Test.gcd(15)(20);
```
-ここでは、コードがPureScriptモジュールをESモジュールにコンパイルする `spago
-build`でコンパイルされていると仮定しています。そのため、 `import`を使って `Test`モジュールをインポートした後、
-`Test`オブジェクトの `gcd`関数を参照できました。
+ここでは`spago build`でコンパイルされていることを前提としています。
+SpagoはPureScriptモジュールをESモジュールにコンパイルするものです。
+そのため、`import`を使って`Test`モジュールをインポートした後、`Test`オブジェクトの`gcd`関数を参照できました。
`spago bundle-app`や`spago
bundle-module`コマンドを使って生成されたJavaScriptを単一のファイルにまとめることもできます。
@@ -1254,7 +1268,9 @@ bundle-module`コマンドを使って生成されたJavaScriptを単一のフ
### 名前の生成を理解する
-PureScriptはコード生成時にできるだけ名前を保存することを目的としています。具体的には、少なくともトップレベルで宣言される名前については、PureScriptやJavaScriptのキーワードでなければほとんどの識別子が保存されます。
+PureScriptはコード生成時にできるだけ名前を保持することを目指します。
+とりわけ、PureScriptやJavaScriptのキーワードでなければほとんどの識別子が保存されることが期待できます。
+少なくとも最上位で宣言される名前についてはそうです。
識別子としてJavaScriptのキーワードを使う場合は、名前は2重のドル記号でエスケープされます。
例えば次のPureScriptコードを考えてみます。
@@ -1282,15 +1298,15 @@ example' = 100
var example$prime = 100;
```
-コンパイルされたPureScriptコードがJavaScriptから呼び出されることを意図している場合、識別子は英数字のみを使用し、JavaScriptの予約語を避けることをお勧めします。
-ユーザ定義演算子がPureScriptコードでの使用のために提供される場合でも、JavaScriptから使うための英数字の名前を持つ代替関数を提供しておくことをお勧めします。
+コンパイルされたPureScriptコードがJavaScriptから呼び出されることを意図している場合、識別子は英数字のみを使用し、JavaScriptのキーワードを避けることをお勧めします。
+ユーザ定義演算子がPureScriptコードでの使用のために提供される場合、JavaScriptから使うための英数字の名前を持つ代替関数を提供しておくことをお勧めします。
### 実行時のデータ表現
-型はプログラムがある意味で「正しい」ことをコンパイル時に判断できるようにします。
+型はプログラムがある意味で「正しい」ことをコンパイル時に論証できるようにします。
つまり、その点については壊れることがありません。
しかし、これは何を意味するのでしょうか。
-PureScriptでは式の型は実行時の表現と互換性がなければならないことを意味します。
+PureScriptでは、式の型は実行時の表現と互換性があることを意味します。
そのため、PureScriptとJavaScriptコードを一緒に効率的に使用できるように、実行時のデータ表現について理解することが重要です。
これはつまり、与えられた任意のPureScriptの式について、その値が実行時にどのように評価されるかという挙動を理解できるべきだということです。
@@ -1303,38 +1319,38 @@ PureScriptでは式の型は実行時の表現と互換性がなければなら
つまり、型 `Boolean`の式は `true`もしくは `false`のどちらか一方の(JavaScriptの)値へと評価されます。
特に`null`や `undefined`に評価される型`Boolean`なPureScriptの式はありません。
-`Int`や`Number`や`String`の型の式についても同様のことが成り立ちます。
-`Int`や`Number`型の式は `null`でないJavaScriptの数へと評価されますし、 `String`型の式は
-`null`でないJavaScriptの文字列へと評価されます。
+`Int`や`Number`や`String`の型の式についても似た法則が成り立ちます。
+`Int`や`Number`型の式はnullでないJavaScriptの数へと評価されますし、`String`型の式はnullでないJavaScriptの文字列へと評価されます。
`typeof`を使った場合に型`Number`の値と見分けがつかなくなるにせよ、型`Int`の式は実行時に整数に評価されます。
`Unit`についてはどうでしょうか。
`Unit`には現住 (`unit`) が1つのみで値が観測できないため、実のところ実行時に何で表現されるかは重要ではありません。
古いコードは`{}`を使って表現する傾向がありました。
しかし比較的新しいコードでは`undefined`を使う傾向にあります。
-なので、`Unit`を表現するのに使うものは本当に何でも問題にならないのですが、`undefined`を使うことが推奨されます(関数から何も返さないときも`undefined`を返します)。
+なので、`Unit`を表現するのに使うものは何であれ差し支えありませんが、`undefined`を使うことが推奨されます(関数から何も返さないときも`undefined`を返します)。
もっと複雑な型についてはどうでしょうか。
既に見てきたように、PureScriptの関数は引数が1つのJavaScriptの関数に対応しています。
厳密に言えばこうなります。
-任意の型`a`と`b`について、式`f`の型が`a -> b`で、式`x`が型`a`についての適切な実行時表現の値へと評価されるとします。
-このとき`f`はJavaScriptの関数へと評価され、`x`を評価した結果に`f`を適用すると型`b`の適切な実行時表現を持ちます。
-簡単な例としては、 `String -> String`型の式は、 `null`でないJavaScript文字列から `null`でないJavaScript文字列への関数へと評価されます。
+ある型`a`と`b`について、式`f`の型が`a -> b`で、式`x`が型`a`についての適切な実行時表現の値へと評価されるとします。
+このとき`f`はJavaScriptの関数へと評価されますが、この関数は`x`を評価した結果に`f`を適用すると型`b`の適切な実行時表現を持ちます。
+単純な例としては、`String -> String`型の式は、nullでないJavaScript文字列からnullでないJavaScript文字列への関数へと評価されます。
ご想像の通り、PureScriptの配列はJavaScriptの配列に対応しています。
しかし、PureScriptの配列は均質である、つまり全ての要素が同じ型を持っていることは覚えておいてください。
-具体的には、もしPureScriptの式 `e`が何らかの型 `a`について型 `Array a`を持つなら、
-`e`は全ての要素が(`null`でない)型 `a`の適切な実行時表現を持ったJavaScript配列へと評価されます。
+具体的には、もしPureScriptの式`e`が何らかの型`a`について型`Array
+a`を持つなら、`e`は(nullでない)JavaScript配列へと評価されます。
+この配列の全ての要素は型`a`の適切な実行時表現を持ちます。
PureScriptのレコードがJavaScriptのオブジェクトへと評価されることは既に見てきました。
-ちょうど関数や配列の場合のように、そのラベルに関連付けられている型を考慮すれば、レコードのフィールドのデータの実行時の表現についても推論できます。
-もちろん、レコードのそれぞれのフィールドは、同じ型である必要はありません。
+関数や配列の場合のように、そのラベルに関連付けられている型を考慮すれば、レコードのフィールド中のデータの実行時の表現について論証できます。
+勿論、レコードのフィールドは、同じ型である必要はありません。
### ADTの表現
-PureScriptコンパイラは、代数的データ型の全ての構築子についてそれぞれを関数として定義し、新たなJavaScriptオブジェクト型を作成します。
-これらの構築子は雛形に基づいて新しいJavaScriptオブジェクトを作成する関数に対応しています。
+代数的データ型の全ての構築子について、PureScriptコンパイラは関数を定義することで新たなJavaScriptオブジェクト型を作成します。
+これらの構築子はプロトタイプに基づいて新しいJavaScriptオブジェクトを作成する関数に対応しています。
例えば次のような単純なADTを考えてみましょう。
@@ -1359,18 +1375,18 @@ function Zero() {
Zero.value = new Zero();
```
-ここで2つのJavaScriptオブジェクト型 `Zero`と `One`を見てください。
-JavaScriptのキーワード`new`を使用すると、それぞれの型の値を作成できます。
-引数を持つ構築子については、コンパイラは `value0`、 `value1`などという名前のフィールドに、対応するデータを格納します。
+ここで2つのJavaScriptオブジェクト型`Zero`と`One`を見てください。
+JavaScriptのキーワード`new`を使用すると、各型の値を作成できます。
+引数を持つ構築子については、コンパイラは`value0`、`value1`などという名前のフィールドに、対応するデータを格納します。
PureScriptコンパイラは補助関数も生成します。
引数のない構築子については、コンパイラは構築子が使われるたびに `new`演算子を使うのではなく、データを再利用できるように
`value`プロパティを生成します。
1つ以上の引数を持つ構築子では、コンパイラは適切な表現を持つ引数を取り適切な構築子を適用する `create`関数を生成します。
-2引数以上の構築子についてはどうでしょうか。
+1引数より多く取る構築子についてはどうでしょうか。
その場合でも、PureScriptコンパイラは新しいオブジェクト型と補助関数を作成します。
-ここでは補助関数は2引数のカリー化された関数なのです。
+ただしこの場合、補助関数は2引数のカリー化された関数です。
例えば次のような代数的データ型を考えます。
```haskell
@@ -1392,20 +1408,19 @@ Two.create = function (value0) {
};
```
-ここで、オブジェクト型 `Two`の値はキーワード`new`または `Two.create`関数を使用すると作成できます。
+ここで、オブジェクト型`Two`の値はキーワード`new`または`Two.create`関数を使用すると作成できます。
newtypeの場合はまた少し異なります。
-newtypeは単一の引数を取る単一の構築子を持つよう制限された代数的データ型であることを思い出してください。
-この場合、実際のnewtypeの実行時表現は、その引数の型と同じになります。
+newtypeは代数的データ型のようなもので、単一の引数を取る単一の構築子を持つよう制限されていたことを思い出してください。
+この場合、newtypeの実行時表現は、その引数の型と同じになります。
-例えば、電話番号を表す次のようなnewtypeを考えます。
+例えば、以下の電話番号を表すnewtypeは実行時にJavaScriptの文字列として表現されます。
```haskell
newtype PhoneNumber = PhoneNumber String
```
-これは実行時にJavaScriptの文字列として表されます。
-newtypeは更なる型安全性のための層を提供しますが、実行時の関数呼び出しのオーバーヘッドがないので、ライブラリを設計するのに役に立ちます。
+newtypeは、関数呼び出しによる実行時のオーバーヘッドなく更なる型安全性のための層を提供するため、ライブラリを設計するのに便利です。
### 量化された型の表現
@@ -1435,10 +1450,10 @@ identity a = a
量化された型 `forall a. t`の実行時表現はどうなっているのでしょうか。さて、この型の実行時表現を持つ任意の式は、型 `a`をどのように選んでも型 `t`の適切な実行時表現を持っていなければなりません。上の例では、型 `forall a. a -> a`の関数は、 `String -> String`、 `Number -> Number`、 `Array Boolean -> Array Boolean`などといった型について、適切な実行時表現を持っていなければなりません。 これらは、文字列から文字列、数から数の関数でなくてはなりません。
しかし、それだけでは充分ではありません。
-量化された型の実行時表現は、これよりも更に厳しくなります。
-任意の式が*パラメトリック多相的*でなければなりません。
+量化された型の実行時表現は、これよりも更に厳しいものです。
+任意の式が*パラメトリック多相的*であることを要求しています。
つまり、その実装において、引数の型についてのどんな情報も使うことができないのです。
-この追加の条件は、考えられる多相型のうち、以下のJavaScriptの関数のような問題のある実装を防止します。
+この追加の条件は、以下のJavaScriptの関数のような問題のある実装が多相型に現住することを防止します。
```javascript
function invalid(a) {
@@ -1450,10 +1465,12 @@ function invalid(a) {
}
```
-確かにこの関数は文字列から文字列、数から数へというような関数ではありますが、追加条件を満たしていません。
-引数の実行時の型を調べており、したがって、この関数は型 `forall a. a -> a`の正しい実装だとはいえないのです。
+確かにこの関数は文字列を取って文字列を返し、数を取って数を返す、といったものです。
+しかしこの関数は追加条件を満たしていません。
+引数の実行時の型を調べており、型`forall a. a -> a`の正しい現住にはならないからです。
-関数の引数の実行時の型を検査できなければ、唯一の選択肢は引数をそのまま返すことだけであり、したがって `id`はたしかに `forall a. a -> a`の唯一の実装なのです。
+関数の引数の実行時の型を検査できなければ、唯一の選択肢は引数をそのまま返すことだけです。
+したがって`id`は確かに`forall a. a -> a`の唯一の現住なのです。
*パラメトリック多相*と*パラメトリック性*についての詳しい議論は本書の範囲を超えています。
ただ、PureScriptの型は実行時に*消去*されており、PureScriptの多相関数は(FFIを使わない限り)引数の実行時表現を検査*できない*ため、この多相的なデータの表現が適切になっているという点にはご留意ください。
@@ -1464,7 +1481,7 @@ function invalid(a) {
関数の挙動はコンパイラによって選ばれた型クラスのインスタンスに依存する可能性があるため、関数には*型クラス辞書*と呼ばれる追加の引数が与えられます。
この辞書には選ばれたインスタンスから提供される型クラスの関数の実装が含まれます。
-例えば、 `Show`型クラスを使った制約のある型を持つ、次のような単純なPureScript関数について考えます。
+例えば以下は、`Show`型クラスを使う制約付きの型を持つ、単純なPureScript関数です。
```haskell
shout :: forall a. Show a => a -> String
@@ -1509,9 +1526,9 @@ shout(showNumber)(42);
### 副作用の表現
`Effect`モナドも外部型として定義されています。
-その実行時表現はとても簡単です。
-型 `Effect a`の式は**引数なしの**JavaScript関数へと評価されます。
-この関数はあらゆる副作用を実行し型 `a`の適切な実行時表現で値を返します。
+その実行時表現はとても単純です。
+型`Effect a`の式は**引数なしの**JavaScript関数へと評価されます。
+この関数はあらゆる副作用を実行し、型`a`の適切な実行時表現を持つ値を返します。
`Effect`型構築子の定義は、 `Effect`モジュールで次のように与えられています。
@@ -1532,8 +1549,8 @@ export const random = Math.random;
```
`random`関数は実行時には引数なしの関数として表現されていることに注目してください。
-この関数は乱数生成という副作用を実行して返しますが、返り値は `Number`型の実行時表現と一致します。
-`Number`型は`null`でないJavaScriptの数です。
+この関数は乱数生成という副作用を実行して返しますが、返り値は`Number`型の実行時表現と一致します。
+`Number`型はnullでないJavaScriptの数です。
もう少し興味深い例として、`console`パッケージ中の`Effect.Console`モジュールで定義された `log`関数を考えてみましょう。
`log`関数は次の型を持っています。
@@ -1552,8 +1569,8 @@ export const log = function (s) {
};
```
-実行時の
-`log`の表現は、単一の引数のJavaScript関数で、引数なしの関数を返します。内側の関数はコンソールにメッセージを書き込むという副作用を実行します。
+実行時の `log`の表現は、単一の引数のJavaScript関数で、引数なしの関数を返します。
+内側の関数はコンソールに文言を書き込むという副作用を実行します。
`Effect a`型の式は、通常のJavaScriptのメソッドのようにJavaScriptから呼び出すことができます。例えば、この
`main`関数は何らかの型`a`について`Effect a`という型でなければならないので、次のように実行できます。
@@ -1564,5 +1581,5 @@ import { main } from 'Main'
main();
```
-`spago bundle-app --to`または `spago run`を使用するときは、`Main`モジュールが定義されている場合は常に、この
-`main`の呼び出しを自動的に生成できます。
+`spago bundle-app --to`または`spago
+run`を使用する場合、`Main`モジュールが定義されている場合は常に、この`main`の呼び出しを自動的に生成できます。
diff --git a/text-ja/chapter11.md b/text-ja/chapter11.md
index 1eaa8b27..4815b67a 100644
--- a/text-ja/chapter11.md
+++ b/text-ja/chapter11.md
@@ -4,8 +4,8 @@
この章の目標は*モナド変換子*について学ぶことです。
モナド変換子は異なるモナドから提供された副作用を合成する方法を提供します。
-NodeJSのコンソール上で遊ぶことができるテキストアドベンチャーゲームを題材として扱います。
-ゲームの様々な副作用(ロギング、状態、及び設定)が全てモナド変換子スタックによって提供されます。
+動機付けとする例は、NodeJSのコンソール上で遊ぶことができるテキストアドベンチャーゲームです。
+ゲームの様々な副作用(ロギング、状態、及び構成)が全てモナド変換子スタックによって提供されます。
## プロジェクトの準備
@@ -14,13 +14,13 @@ NodeJSのコンソール上で遊ぶことができるテキストアドベン
- `ordered-collections` は不変のマップと集合のためのデータ型を提供します
- `transformers` は標準的なモナド変換子の実装を提供します
- `node-readline`はNodeJSが提供する[`readline`](http://nodejs.org/api/readline.html)インターフェイスへのFFIバインディングを提供します
-- `optparse` はコマンドライン引数を処理するアプリカティブ構文解析器を提供します
+- `optparse`はコマンドライン引数を処理するアプリカティブ構文解析器を提供します
## ゲームの遊びかた
プロジェクトを走らせるには`spago run`を使います。
-既定では使い方が表示されます。
+既定では使い方の文言が表示されます。
```text
Monadic Adventures! A game to learn monad transformers
@@ -35,8 +35,10 @@ Available options:
-h,--help Show this help text
```
-コマンドライン引数を与えるためには、追加の引数を直接アプリケーションに渡す`-a`オプション付きで`spago run`を呼び出すか、`spago bundle-app`とすればよいです。
+コマンドライン引数を与えるためには、追加の引数を直接アプリケーションに渡す`-a`オプション付きで`spago run`を呼び出すか、`spago
+bundle-app`とすればよいです。
2つ目の方法では`node`で直接走らせられるindex.jsファイルが作られます。
+
例えば`-p`オプションを使ってプレイヤー名を与えるには次のようにします。
```text
@@ -50,14 +52,12 @@ $ node index.js -p Phil
>
```
-プロンプトからは、 `look`、 `inventory`、 `take`、 `use`、 `north`、`south`、 `east`、
-`west`などのコマンドを入力できます。
-`debug`コマンドもあり、`--debug`コマンドラインオプションを与えられていた場合に、ゲームの状態を出力するのに使えます。
+プロンプトからは、`look`、`inventory`、`take`、`use`、`north`、`south`、`east`、`west`などのコマンドを入力できます。
+`debug`コマンドもあり、`--debug`コマンドラインオプションを与えられていた場合に、ゲームの状態を出力できます。
-ゲームは2次元の碁盤の目の上が舞台で、コマンド `north`、 `south`、`east`、
-`west`を発行することによってプレイヤーが移動します。
-ゲームにはアイテムの集まりがあり、プレイヤーの所持アイテム一覧を表したり、ゲーム盤上のその位置にあるアイテムの一覧を表すのに使われます。
-`take`コマンドを使うと、プレイヤーの位置にあるアイテムを拾い上げることができます。
+ゲームは2次元の碁盤の目の上が舞台で、コマンド`north`、`south`、`east`、`west`を発行することによってプレイヤーが移動します。
+ゲームにはアイテムの集まりがあり、プレイヤーの所有物であったり、ゲームの盤上の特定の位置にあったりします。
+`take`コマンドを使うと、プレイヤーはアイテムを拾い上げられます。
参考までに、このゲームのひと通りの流れは次のようになります。
@@ -91,8 +91,7 @@ Congratulations, Phil!
You win!
```
-このゲームはとても単純ですが、この章の目的は
-`transformers`パッケージを使用してこのようなゲームを素早く開発できるようにするライブラリを構築することです。
+このゲームはとても単純ですが、この章の目的は`transformers`パッケージを使用してこのような種類のゲームを素早く開発できるようにするライブラリを構築することです。
## Stateモナド
@@ -103,8 +102,8 @@ You win!
既に`Effect`モナドによって提供される変更可能状態の手法について見てきました。
`State`はその代替を提供します。
-`State`型構築子は、状態の型 `s`、及び返り値の型 `a`という2種類の引数を取ります。
-「`State`モナド」というように説明はしていますが、実際には`Monad`型クラスのインスタンスが任意の型`s`についての `State
+`State`型構築子は、状態の型`s`、及び返り値の型`a`という2種類の引数を取ります。
+「`State`モナド」というように説明はしていますが、実際には`Monad`型クラスのインスタンスが任意の型`s`についての`State
s`型構築子に対して提供されています。
`Control.Monad.State`モジュールは以下のAPIを提供しています。
@@ -117,14 +116,13 @@ modify :: forall s. (s -> s) -> State s s
modify_ :: forall s. (s -> s) -> State s Unit
```
-なおここではこれらのAPIシグネチャは`State`型構築子を使った、単純化された形式で表されています。
+なお、ここではこれらのAPIシグネチャは`State`型構築子を使った、単純化された形式で表されています。
実際のAPIは本章の後にある「型クラス」節で押さえる`MonadState`が関わってきます。
ですからIDEのツールチップやPursuitで違うシグネチャを見たとしても心配しないでください。
例を見てみましょう。
-`State`モナドの使いかたの1つとしては、整数の配列中の値を現在の状態に加えるものが考えられます。
-状態の型`s`として`Int`を選択し、配列の走査に `traverse_`を使って、配列の要素それぞれについて
-`modify`を呼び出すと、これを実現できます。
+`State`モナドの使い方の1つとしては、整数の配列中の値を現在の状態に加えるものが考えられます。
+状態の型`s`として`Int`を選択し、配列の走査に`traverse_`を使い、配列の各要素について`modify`を呼び出すと、これを実現できます。
```haskell
import Data.Foldable (traverse_)
@@ -143,8 +141,8 @@ execState :: forall s a. State s a -> s -> s
runState :: forall s a. State s a -> s -> Tuple a s
```
-3つの関数はそれぞれ型`s`の初期状態と型`State s a`の計算を引数にとります。
-`evalState`は返り値だけを返し、 `execState`は最終的な状態だけを返し、 `runState`は `Tuple a
+3つの各関数は型`s`の初期状態と型`State s a`の計算を引数に取ります。
+`evalState`は返り値だけを返し、`execState`は最終的な状態だけを返し、`runState`は`Tuple a
s`型の値として表現された両方を返します。
先ほどの `sumArray`関数が与えられているとき、PSCiで `execState`を使うと次のように複数の配列内の数字を合計できます。
@@ -162,8 +160,7 @@ s`型の値として表現された両方を返します。
## 演習
1. (簡単)上の例で、`execState`を`runState`や`evalState`で置き換えると結果はどうなるでしょうか。
-1. (普通)括弧からなる文字列について、次の何れかであれば*平衡している*とします。
- 1つは0個以上のより短い平衡した文字列を連結したもので、もう1つはより短い平衡した文字列を一対の括弧で囲んだものです。
+ 1. (普通)括弧からなる文字列が*平衡している*のは、0個以上のより短い平衡した文字列を連結したものか、より短い平衡した文字列を一対の括弧で囲んだものかの何れかです。
`State`モナドと `traverse_`関数を使用して、次のような関数を書いてください。
@@ -171,9 +168,8 @@ s`型の値として表現された両方を返します。
testParens :: String -> Boolean
```
- これは `String`が括弧の対応が正しく付けられているかどうかを調べる関数です。
- 調べるにはまだ閉じられていない開括弧の数を把握しておきます。
- この関数は次のように動作しなくてはなりません。
+ これは、まだ閉じられていない開括弧の数を把握することで、括弧の`String`が平衡しているかどうかを調べる関数です。
+ この関数は次のように動作します。
```text
> testParens ""
@@ -206,7 +202,7 @@ ask :: forall r. Reader r r
local :: forall r a. (r -> r) -> Reader r a -> Reader r a
```
-`ask`アクションは現在の設定を読み取るために使い、 `local`アクションは変更された設定で計算するために使います。
+`ask`動作は現在の設定を読み取るために使い、`local`動作は変更された設定で計算するために使います。
例えば、権限で制御するアプリケーションを開発しており、現在の利用者の権限オブジェクトを保持するのに `Reader`モナドを使いたいとしましょう。
型 `r`を次のようなAPIを備えた型 `Permission`として選択できます。
@@ -228,7 +224,7 @@ createUser = do
else pure Nothing
```
-`local`アクションを使うと、計算の実行中に `Permissions`オブジェクトを変更し、ユーザーの権限を昇格させることもできます。
+`local`動作を使うと、計算の実行中に `Permissions`オブジェクトを変更し、ユーザーの権限を昇格させることもできます。
```haskell
runAsAdmin :: forall a. Reader Permissions a -> Reader Permissions a
@@ -259,8 +255,8 @@ type Level = Int
type Doc = Reader Level String
```
- 1. (簡単)現在の字下げの深さで文字列を出力する関数 `line`を書いてください。
- 関数は以下の型を持つ必要があります。
+ 1. (簡単)現在の字下げの深さで関数を書き出す関数`line`を書いてください。
+ 関数は以下の型を持ちます。
```haskell
line :: String -> Doc
@@ -290,7 +286,7 @@ type Doc = Reader Level String
この関数は文書を文字列として出力します。
- これで、このライブラリを次のように使うと、簡単な文書を書くことができるでしょう。
+ これでライブラリを使って以下のような単純な文書を書けるようになりました。
```haskell
render $ cat
@@ -305,14 +301,15 @@ type Doc = Reader Level String
## Writerモナド
-`Writer`モナドは、計算の返り値に加えて、もう1つの値を累積していく機能を提供します。
+`Writer`モナドでは、計算の返り値に加えてもう1つの値を累算できます。
-よくある使い方としては型 `String`もしくは `Array String`でログを累積していくというものなどがありますが、
-`Writer`モナドはこれよりもっと一般的なものです。
-累積するのに任意のモノイドの値を使うことができ、`Additive Int`モノイドを使って、合計を追跡し続けるのに使ったり、 `Disj
-Boolean`モノイドを使って途中の `Boolean`値の何れかが真であるかどうかを追跡するのに使うことができます。
+よくある使い方としては型`String`もしくは`Array
+String`でログを累算していくというものなどがありますが、`Writer`モナドはこれよりもっと一般的なものです。
+累算するのに任意のモノイドの値を使うことができるので、`Additive Int`モノイドを使って整数の合計を追跡するのに使ったり、`Disj
+Boolean`モノイドを使って途中の`Boolean`値の何れかが真であるかどうかを追跡するのに使えます。
-`Writer`型の構築子は、 `Monoid`型クラスのインスタンスである型 `w`、および返り値の型 `a`という2つの型引数を取ります。
+`Writer`型構築子は2つの型引数を取ります。
+`Monoid`型クラスのインスタンスである型`w`と返り値の型`a`です。
`Writer`のAPIで重要なのは `tell`関数です。
@@ -320,9 +317,10 @@ Boolean`モノイドを使って途中の `Boolean`値の何れかが真であ
tell :: forall w a. Monoid w => w -> Writer w Unit
```
-`tell`アクションは、与えられた値を現在の累積結果に付け加えます。
+`tell`動作は与えられた値を現在の累算結果に付け加えます。
-例として、 `Array String`モノイドを使用して、既存の関数にログ機能を追加してみましょう。 _最大公約数_ 関数の以前の実装を考えてみます。
+例として、`Array String`モノイドを使用して、既存の関数にログを追加してみましょう。
+*最大公約数*関数の以前の実装を考えてみます。
```haskell
gcd :: Int -> Int -> Int
@@ -361,8 +359,8 @@ execWriter :: forall w a. Writer w a -> w
runWriter :: forall w a. Writer w a -> Tuple a w
```
-ちょうど `State`モナドの場合と同じように、 `execWriter`が累積されたログだけを返すのに対して、
-`runWriter`は累積されたログと結果の両方を返します。
+ちょうど `State`モナドの場合と同じように、 `execWriter`が累算されたログだけを返すのに対して、
+`runWriter`は累算されたログと結果の両方を返します。
PSCiで改変した関数を試してみましょう。
@@ -378,8 +376,8 @@ Tuple 3 ["gcdLog 21 15","gcdLog 6 15","gcdLog 6 9","gcdLog 6 3","gcdLog 3 3"]
1. (普通)`Writer`モナドと `monoid`パッケージの `Additive Int`モノイドを使うように、上の
`sumArray`関数を書き換えてください。
- 1. (普通)*コラッツ関数*は、自然数 `n`が偶数なら `n / 2`、 `n`が奇数なら `3 * n + 1`であると定義されています。
- 例えば`10`で始まるコラッツ数列は次のようになります。
+ 1. (普通)*コラッツ*関数は自然数`n`上で定義され、`n`が偶数なら`n / 2`、`n`が奇数なら`3 * n + 1`です。
+ 例えば`10`で始まるコラッツ数列は以下です。
```text
10, 5, 16, 8, 4, 2, 1, ...
@@ -387,14 +385,14 @@ Tuple 3 ["gcdLog 21 15","gcdLog 6 15","gcdLog 6 9","gcdLog 6 3","gcdLog 3 3"]
コラッツ関数の有限回の適用を繰り返すと、コラッツ数列は必ず最終的に `1`になるということが予想されています。
- 数列が `1`に到達するまでに何回のコラッツ関数の適用が必要かを計算する再帰的な関数を書いてください。
+ 再帰を使い、数列が`1`に到達するまでに何回のコラッツ関数の適用が必要かを計算する関数を書いてください。
- `Writer`モナドを使用してコラッツ関数のそれぞれの適用の経過を記録するように、関数を変更してください。
+ `Writer`モナドを使用してコラッツ関数の各適用の経過を記録するように、関数を変更してください。
## モナド変換子
上の3つのモナド、`State`、`Reader`、`Writer`は、何れもいわゆる*モナド変換子*の例となっています。
-対応するモナド変換子はそれぞれ `StateT`、 `ReaderT`、 `WriterT`と呼ばれています。
+対応する各モナド変換子はそれぞれ`StateT`、`ReaderT`、`WriterT`という名前です。
モナド変換子とは何でしょうか。
さて、これまで見てきたように、モナドはPureScriptのコードを何らかの種類の副作用で拡張するものでした。
@@ -405,10 +403,11 @@ Tuple 3 ["gcdLog 21 15","gcdLog 6 15","gcdLog 6 9","gcdLog 6 3","gcdLog 3 3"]
もしくは、 `Either`モナドの純粋なエラー追跡機能と、 `State`モナドが提供する変更可能な状態が同時に欲しくなるかもしれません。
この問題を解決するのが*モナド変換子*です。
-ただし`Effect`モナドがこの問題に対する部分的な解決策を提供していたことは既に見てきました。
-モナド変換子はまた違った解決策を提供しますが、これらの手法にはそれぞれ利点と制約があります。
+ただし`Effect`モナドがこの問題を部分的に解決することは既に見ました。
+モナド変換子はまた違った解決策を提供しますが、これらの各手法には利点と制約があります。
-モナド変換子は型だけでなく別の型構築子もパラメータに取る型構築子です。モナド変換子はモナドを1つ取り、独自のいろいろな副作用を追加した別のモナドへと変換します。
+モナド変換子は型と別の型構築子を引数に取る型構築子です。
+モナドを1つ取り、独自の様々な副作用を追加した別のモナドへと変換します。
例を見てみましょう。`State`のモナド変換子版は`Control.Monad.State.Trans`モジュールで定義されている`StateT`です。
PSCiを使って `StateT`の種を見てみましょう。
@@ -446,30 +445,29 @@ Type -> Type
Type
```
-最後に、種 `Type`の何かが残りましたが、これはつまりこの型の値を探してみることができるということです。
+最後に種`Type`の何かが残りました。
+つまりこの型の値を探してみることができます。
-構築したモナド `StateT String (Either
-String)`は、エラーで失敗する可能性があり、変更可能な状態を使える計算を表しています。
+構築したモナド`StateT String (Either String)`は、エラーで失敗する可能性があり、変更可能な状態を使える計算を表しています。
-外側の `StateT String (Either
-String)`モナドのアクション(`get`、`put`、`modify`)は直接使うことができますが、梱包されている内側のモナド (`Either
-String`) の作用を使うためには、これらの関数をモナド変換子まで「持ち上げ」なくてはいけません。
-`Control.MonadTrans`モジュールでは、モナド変換子であるような型構築子を捉える`MonadTrans`型クラスを次のように定義しています。
+外側の`StateT String`モナドの動作(`get`、`put`、`modify`)は直接使えますが、梱包されているモナド (`Either
+String`) の作用を使うためには、これらの関数をモナド変換子まで「持ち上げ」る必要があります。
+`Control.Monad.Trans`モジュールは`MonadTrans`型クラスを定義しています。
+これはモナド変換子であるそうした型構築子を捕捉します。
```haskell
class MonadTrans t where
lift :: forall m a. Monad m => m a -> t m a
```
-このクラスは、基礎となる任意のモナド `m`の計算をとり、それを梱包されたモナド `t m`へと持ち上げる、
-`lift`という1つの関数だけを持っています。
-今回の場合、型構築子 `t`は `StateT String`で、 `m`は `Either String`モナドとなり、 `lift`は型
-`Either String a`の計算を、型 `State String (Either String)
-a`の計算へと持ち上げる方法を提供することになります。
-これは、型 `Either String a`の計算を使うときは、 `lift`を使えばいつでも作用 `StateT String`と `Either
-String`を一緒に使うことができることを意味します。
+このクラスは単一のメンバー`lift`を含みます。
+これは通底する任意のモナド`m`の計算を取り、梱包されたモナド`t m`へと持ち上げるものです。
+今回の場合、型構築子`t`は`StateT String`で、`m`は`Either String`モナドとなるので、`lift`は型`Either
+String a`の計算を、型`StateT String (Either String) a`の計算へと持ち上げる方法を提供することになります。
+つまり、型`Either String a`の計算を使う場合に毎回`lift`を使うのであれば、`StateT String`と`Either
+String`の作用を使えます。
-例えば、次の計算は `StateT`モナド変換子で導入されている状態を読み込み、状態が空の文字列である場合はエラーを投げます。
+例えば、次の計算は通底する状態を読み、状態が空文字列であればエラーを投げます。
```haskell
import Data.String (drop, take)
@@ -484,7 +482,7 @@ split = do
pure (take 1 s)
```
-状態が空でなければ、この計算は `put`を使って状態を `drop 1 s`(最初の文字を取り除いた `s`)へと更新し、 `take 1
+状態が空でなければ、この計算は`put`を使って状態を`drop 1 s`(つまり`s`から最初の文字を取り除いたもの)へと更新し、`take 1
s`(`s`の最初の文字)を返します。
それではPSCiでこれを試してみましょう。
@@ -497,22 +495,24 @@ Right (Tuple "t" "est")
Left "Empty string"
```
-これは `StateT`を使わなくても実装できるので、さほど驚くようなことはありません。
-しかし、モナドとして扱っているので、do記法やアプリカティブコンビネータを使って、小さな計算から大きな計算を構築していくことができます。
-例えば、2回 `split`を適用すると、文字列から最初の2文字を読むことができます。
+これは`StateT`を使わなくても実装できるので、さほど驚くようなことはありません。
+しかし、モナドの中で扱っているので、do記法やアプリカティブコンビネータを使って、小さな計算から大きな計算を構築できます。
+例えば、2回`split`を適用すると、文字列から最初の2文字を読めます。
```text
> runStateT ((<>) <$> split <*> split) "test"
(Right (Tuple "te" "st"))
```
-他にもアクションを沢山用意すれば、 `split`関数を使って、基本的な構文解析ライブラリを構築できます。これは実際に
-`parsing`ライブラリで採用されている手法です。これがモナド変換子の力なのです。必要な副作用を選択して、do記法とアプリカティブコンビネータで表現力を維持しながら、様々な問題のための特注のモナドを作成できるのです。
+`split`関数とその他沢山の動作を使えば基本的な構文解析ライブラリを構築できます。
+実際、これは`parsing`ライブラリで採用されている手法です。
+これがモナド変換子の力なのです。
+必要な副作用を選択し、do記法とアプリカティブコンビネータで表現力を維持しながら、様々な問題のための特注のモナドを作成できるのです。
## ExceptTモナド変換子
-`transformers`パッケージでは、 `Either e`モナドに対応する変換子である`ExceptT e`モナド変換子も定義されています。
-これは次のAPIを提供します。
+`transformers`パッケージでは`ExceptT e`モナド変換子も定義されています。
+これは`Either e`モナドに対応するもので、以下のAPIを提供します。
```haskell
class MonadError e m where
@@ -524,10 +524,10 @@ instance Monad m => MonadError e (ExceptT e m)
runExceptT :: forall e m a. ExceptT e m a -> m (Either e a)
```
-`MonadError`クラスは `e`型のエラーを投げたりキャッチに対応したりするモナドを取得し、 `ExceptT
+`MonadError`クラスは`e`型のエラーを投げたり捕えたりに対応するモナドを捕捉し、`ExceptT
e`モナド変換子のインスタンスが提供されます。
-`Either e`モナドの `Left`と同じように、 `throwError`アクションは失敗を示すために使われます。
-`catchError`アクションを使うと、 `throwError`でエラーが投げられたあとでも処理を継続できるようになります。
+`Either e`モナドの`Left`と同じように、`throwError`動作では失敗を示せます。
+`catchError`動作では`throwError`を使ってエラーが投げられた後に処理を継続できます。
`runExceptT`制御子を使うと、型 `ExceptT e m a`を計算できます。
@@ -538,9 +538,8 @@ e`モナド変換子のインスタンスが提供されます。
- `Exception`作用がJavaScriptの`Error`型という1つの例外の型だけを扱うのに対して、`ExceptT`は`Error`型クラスのどんな型のエラーでも扱います。つまり、
`ExceptT`では新たなエラー型を自由に定義できます。
-試しに `ExceptT`を使って `Writer`モナドを包んでみましょう。
-ここでもモナド変換子 `ExceptT e`のアクションを直接使うことも自由にできますが、`Writer`モナドの計算は
-`lift`を使って持ちあげるべきです。
+試しに`ExceptT`を使って`Writer`モナドを包んでみましょう。
+ここでもモナド変換子`ExceptT e`の動作を直接使うことも自由にできますが、`Writer`モナドの計算は`lift`を使って持ち上げるべきです。
```haskell
import Control.Monad.Except
@@ -564,22 +563,22 @@ String)`の結果を残します。
Tuple (Left "Error!") ["Before the error"]
```
-実際に追加されるログは、エラーが投げられる前に書かれたログメッセージだけであることにも注目してください。
+なお、エラーが投げられる前に書き出されるログ文言だけがログに追記されます。
## モナド変換子スタック
-これまで見てきたように、モナド変換子を使うと既存のモナドを土台に新しいモナドを構築できます。
-任意のモナド変換子 `t1`と任意のモナド`m`について、その適用 `t1 m`もまたモナドになります。
-これは*2つめの*モナド変換子 `t2`を先ほどの結果 `t1 m`に適用すると、3つ目のモナド `t2 (t1 m)`を作れることを意味しています。
-このように、構成するモナドによって提供された副作用を組み合わせる、モナド変換子の*スタック*を構築できます。
+これまで見てきたように、モナド変換子を使って既存のモナドを土台に新しいモナドを構築できます。
+何かのモナド変換子`t1`とモナド`m`について、その適用`t1 m`もまたモナドになります。
+つまり、*2つめの*モナド変換子`t2`を結果`t1 m`に適用すると、3つ目のモナド`t2 (t1 m)`を作れます。
+このようにしてモナド変換子の*スタック*を構築できます。
+これは構成されるモナドによって提供される副作用を組み合わせるものです。
-実際には、基本となるモナド`m`は、ネイティブの副作用が必要なら`Effect`モナド、さもなくば
-`Data.Identity`モジュールで定義されている`Identity`モナドになります。
+実際には、通底するモナド`m`は、ネイティブの副作用が必要なら`Effect`モナド、さもなくば`Data.Identity`モジュールで定義されている`Identity`モナドになります。
`Identity`モナドは何の新しい副作用も追加しませんから、`Identity`モナドの変換はモナド変換子の作用だけを提供します。
-実際に、`State`モナドと`Reader`モナドと`Writer`モナドは、`Identity`モナドをそれぞれ`StateT`と`ReaderT`と`WriterT`で変換することによって実装されています。
+`State`、`Reader`、`Writer`モナドは、`Identity`モナドをそれぞれ`StateT`、`ReaderT`、`WriterT`で変換することによって実装されています。
-それでは3つの副作用が組み合わされている例を見てみましょう。
-`Identity`モナドをスタックの底にして、 `StateT`作用、 `WriterT`作用、`ExceptT`作用を使います。
+3つの副作用が組み合わっている例を見てみましょう。
+`Identity`モナドをスタックの底にして、`StateT`、`WriterT`、`ExceptT`作用を使います。
このモナド変換子スタックは、可変状態、ログの蓄積、そして純粋なエラーの副作用を提供します。
このモナド変換子スタックを使うと、ロギングの機能が追加された `split`アクションに作り変えられます。
@@ -604,10 +603,9 @@ split = do
この計算をPSCiで試してみると、 `split`が実行されるたびに状態がログに追加されることがわかります。
-モナド変換子スタックに現れる順序に従って、副作用を取り除いていかなければならないことに注意してください。
-最初に `StateT`型構築子を取り除くために `runStateT`を使い、それから
-`runtWriteT`を使い、その後`runExceptT`を使います。
-最後に `unwrap`を使用して `Identity`モナドを演算します。
+なお、モナド変換子スタックに現れる順序で副作用を取り除いていかなければなりません。
+最初に`StateT`型構築子を取り除くために`runStateT`を、それから`runtWriteT`、`runExceptT`を使います。
+最後に`unwrap`を使用して`Identity`モナド中で計算します。
```text
> runParser p s = unwrap $ runExceptT $ runWriterT $ runStateT p s
@@ -626,14 +624,13 @@ split = do
(Left ["Empty string"])
```
-これは、 `ExceptT`モナド変換子が提供する副作用が、 `WriterT`モナド変換子が提供する副作用と干渉するためです。
-これはモナド変換子スタックが構成されている順序を変更することで解決できます。
-スタックの最上部に `ExceptT`変換子を移動すると、先ほど `Writer`を
-`ExceptT`に変換したときと同じように、最初のエラーまでに書かれた全てのメッセージが含まれるようになります。
+これは、`ExceptT`モナド変換子が提供する副作用と`WriterT`モナド変換子が提供する副作用との関係によるものです。
+これはモナド変換子スタックが構成されている順序を変更することで対処できます。
+スタックの最上部に`ExceptT`変換子を移動すると、先ほど`Writer`を`ExceptT`に変換したときに見たように、最初のエラーまでに書かれた全ての文言がログに含まれるようになります。
-このコードの問題の1つは、複数のモナド変換子の上まで計算を持ち上げるために、 `lift`関数を複数回使わなければならないということです。
-例えば`throwError`の呼び出しは、1回目は `WriteT`へ、2回目は `StateT`へと、2回持ちあげなければなりません。
-小さなモナド変換子スタックならなんとかなりますが、そのうち不便だと感じるようになるでしょう。
+このコードの問題の1つは、複数のモナド変換子の上まで計算を持ち上げるために、`lift`関数を複数回使わなければならないということです。
+例えば`throwError`の呼び出しは、1回目は`WriteT`へ、2回目は`StateT`へ、と2回持ちあげなければなりません。
+小さなモナド変換子スタックならなんとかなりますが、そのうちすぐに不便になるでしょう。
幸いなことに、これから見るような型クラス推論によって提供されるコードの自動生成を使うと、ほとんどの「重労働」を任せられます。
@@ -648,7 +645,7 @@ split = do
string :: String -> Parser String
```
- これは現在の状態が接頭辞に適合するか、もしくはエラーメッセージとともに失敗します。
+ これは現在の状態が接頭辞に照合するか、もしくはエラー文言とともに失敗します。
この構文解析器は次のように動作します。
@@ -657,16 +654,17 @@ split = do
(Right (Tuple (Tuple "abc" "def") ["The state is abcdef"]))
```
- *手掛かり*:出発点として`split`の実装を使うといいでしょう。
- `stripPrefix`関数も役に立ちます。
-1. (難しい)以前 `Reader`モナドを使用して書いた文書表示ライブラリを、`ReaderT`と `WriterT`モナド変換子を使用して再実装してください。
+ *手掛かり*:出発点として`split`の実装が使えます。
+ `stripPrefix`関数も役に立つかもしれません。
+1. (難しい)文書表示ライブラリを、`ReaderT`と`WriterT`モナド変換子を使用して再実装してください。
+ 以前`Reader`モナドを使用して書いたものです。
文字列を出力する `line`や文字列を連結する `cat`を使うのではなく、`WriteT`モナド変換子と一緒に `Array String`モノイドを使い、結果へ行を追加するのに `tell`を使ってください。
アポストロフィ (`'`) を付ける以外は元の実装と同じ名前を使ってください。
## 型クラスが助けに来たぞっ
-本章の最初で扱った `State`モナドを見てみると、 `State`モナドのアクションには次のような型が与えられていました。
+本章の最初で扱った `State`モナドを見てみると、 `State`モナドの動作には次のような型が与えられていました。
```haskell
get :: forall s. State s s
@@ -687,32 +685,29 @@ modify :: forall m s. MonadState s m => (s -> s) -> m Unit
予想できると思いますが、 `State s`型構築子は `MonadState
s`型クラスのインスタンスになっており、このクラスには他にも興味深いインスタンスが数多くあります。
-特に、 `transformers`パッケージではモナド変換子 `WriterT`、 `ReaderT`、`ExceptT`についての
-`MonadState`のインスタンスが提供されています。
-通底する`Monad`が`MonadState`インスタンスを持っていれば常に、これらのモナド変換子にもインスタンスがあります。
-実践的には、 `StateT`がモナド変換子スタックの*どこか*に現れ、 `StateT`より上の全てが
-`MonadState`のインスタンスであれば、 `get`、 `put`、 `modify`を直接自由に使用できます。
+特に、`transformers`パッケージではモナド変換子`WriterT`、`ReaderT`、`ExceptT`についての`MonadState`のインスタンスがあります。
+通底する`Monad`が`MonadState`インスタンスを持つなら常に、これらもインスタンスを持ちます。
+つまり実際には、`StateT`がモナド変換子スタックの*どこか*に現れ、`StateT`より上の全てが`MonadState`のインスタンスであれば、`lift`を使う必要なく`get`や`put`や`modify`を直接自由に使用できます。
-当然ですが、これまで扱ってきた `ReaderT`、 `WriterT`、
-`ExceptT`変換子についても、同じことが成り立っています。`transformers`では主な変換子それぞれについての型クラスが定義されています。これによりそれらの操作に対応するモナドの上に抽象化できるのです。
+当然ですが、これまで扱ってきた`ReaderT`、`WriterT`、`ExceptT`変換子についても、同じことが言えます。
+`transformers`では主な各変換子について型クラスが定義されています。
+これによりそれらの操作に対応するモナドの上に抽象化できるのです。
-上の `split`関数の場合、構築されたこのモナドスタックは型クラス`MonadState`、 `MonadWriter`、
-`MonadError`それぞれのインスタンスです。
-これはつまり、`lift`は全く呼び出す必要がないのです。
-まるでモナドスタック自体に定義されていたかのように、アクション `get`、`put`、 `tell`、
-`throwError`をそのまま使用できます。
+上の`split`関数の場合、構築したモナドスタックは各型クラス`MonadState`、`MonadWriter`、`MonadError`のインスタンスです。
+つまり`lift`は全く呼び出す必要がないのです。
+まるでモナドスタック自体に定義されていたかのように、動作`get`、`put`、`tell`、`throwError`をそのまま使用できます。
```haskell
{{#include ../exercises/chapter11/src/Split.purs:split}}
```
-この計算はまるで、可変状態、ロギング、エラー処理という3つの副作用に対応した、独自のプログラミング言語を拡張したかのようにみえます。
-しかし、内部的には全てはあくまで純粋な関数と普通のデータを使って実装されているのです。
+この計算は、独自のプログラミング言語を拡張し、可変状態、ロギング、エラー処理という3つの新しい副作用に対応したように見えます。
+しかし、内部的には全てはあくまで純粋な関数と不変のデータを使って実装されているのです。
## 代替
-`control`パッケージでは失敗しうる計算を制御するための抽象化が幾つか定義されています。
-その1つは `Alternative`型クラスです。
+`control`パッケージでは失敗しうる計算を扱う抽象化が数多く定義されています。
+その1つは`Alternative`型クラスです。
```haskell
class Functor f <= Alt f where
@@ -737,8 +732,8 @@ some :: forall f a. Alternative f => Lazy (f (Array a)) => f a -> f (Array a)
`Data.List`にも等価な`many`と`some`があります。
-`many`コンビネータは計算を _ゼロ回以上_ 繰り返し実行するために`Alternative`型クラスを使用しています。
-`some`コンビネータも似ていますが、成功するために少なくとも1回の計算を必要とします。
+`many`コンビネータは計算を*ゼロ回以上*繰り返し実行するために`Alternative`型クラスを使います。
+`some`コンビネータも似ていますが、最低1回は計算が成功する必要があります。
`Parser`モナド変換子スタックの場合は、`ExceptT`コンポーネントによる`Alternative`のインスタンスがあります。
このコンポーネントでは異なる分枝のエラーに`Monoid`インスタンスを使って組み合わせることによって対応しています(だから`Errors`型に`Array
@@ -771,23 +766,24 @@ class (Monad m, Alternative m) <= MonadPlus m
実際、`Parser`モナドは `MonadPlus`のインスタンスです。
-以前本書中で配列内包表記を扱ったとき、不要な結果を除いて絞り込むために使われる`guard`関数を導入しました。
-実際には`guard`関数はもっと一般的で、 `MonadPlus`のインスタンスである全てのモナドに対して使うことができます。
+以前本書中で配列内包表記を押さえたとき、`guard`関数を導入しました。
+これは欲しくない結果を取り除けるのに使えました。
+実際には`guard`関数はもっと一般的で、`MonadPlus`のインスタンスである任意のモナドに対して使えます。
```haskell
guard :: forall m. Alternative m => Boolean -> m Unit
```
`<|>`演算子は失敗時にバックトラッキングできるようにします。
-これがどのように役立つかを見るために、大文字だけに適合する `split`コンビネータの亜種を定義してみましょう。
+これがどのように役立つかを見るために、大文字だけに照合する`split`コンビネータの亜種を定義してみましょう。
```haskell
{{#include ../exercises/chapter11/src/Split.purs:upper}}
```
-ここで、文字列が大文字でない場合に失敗するよう
-`guard`を使用しています。このコードは前に見た配列内包表記とよく似ていることに注目してください。このように`MonadPlus`を使うことは、
-_モナド内包表記_ (monad comprehensions) の構築と呼ばれることがあります。
+ここで、文字列が大文字でない場合に失敗するよう、`guard`を使用しています。
+なお、このコードは前に見た配列内包表記とよく似ています。
+このように`MonadPlus`を使うことは、*モナド内包表記*の構築と呼ばれることがあります。
## バックトラッキング
@@ -798,13 +794,13 @@ _モナド内包表記_ (monad comprehensions) の構築と呼ばれることが
{{#include ../exercises/chapter11/src/Split.purs:lower}}
```
-これにより、まずもし最初の文字が大文字なら複数の大文字に適合し、さもなくばもし最初の文字が小文字なら複数の小文字に適合する、という構文解析器を定義できます。
+これにより、まずもし最初の文字が大文字なら複数の大文字に照合し、さもなくばもし最初の文字が小文字なら複数の小文字に照合する、という構文解析器を定義できます。
```text
> upperOrLower = some upper <|> some lower
```
-この構文解析器は、大文字と小文字が切り替わるまで、文字に適合し続けます。
+この構文解析器は、大文字と小文字が切り替わるまで、文字に照合し続けます。
```text
> runParser upperOrLower "abcDEF"
@@ -833,17 +829,17 @@ _モナド内包表記_ (monad comprehensions) の構築と呼ばれることが
]))
```
-繰り返しになりますが、これはモナド変換子がもたらす再利用性の威力を示しています。
+繰り返しになりますが、これはモナド変換子が齎す再利用性の威力を示しています。
標準的な抽象化を再利用することで、宣言型スタイルのバックトラック構文解析器を、ほんの数行のコードで書くことができました。
## 演習
- 1. (簡単)`string`構文解析器の実装から `lift`関数の呼び出しを取り除いてください。
- 新しい実装の型が整合していることを確認し、なぜそのようになるのかをよく納得しておきましょう。
- 1. (普通)`string`構文解析器と `many`コンビネータを使って、文字列`"a"`の連続と、それに続く文字列
- `"b"`の連続からなる文字列を認識する構文解析器`asFollowedByBs`を書いてください。
- 1. (普通)`<|>`演算子を使って、文字 `a`と文字
- `b`が任意の順序で現れるような文字列を認識する構文解析器`asOrBs`を書いてください。
+ 1. (簡単)`string`構文解析器の実装から`lift`関数の呼び出しを取り除いてください。
+ 新しい実装の型検査が通ることを確認し、そうなることを納得するまで確かめましょう。
+ 1. (普通)`string`構文解析器と`some`コンビネータを使って構文解析器`asFollowedByBs`を書いてください。
+ これは文字列`"a"`の連続と、それに続く文字列`"b"`の連続からなる文字列を認識するものです。
+ 1. (普通)`<|>`演算子を使って構文解析器`asOrBs`を書いてください。
+ これは文字`a`と文字`b`が任意の順序で現れる文字列を認識します。
1. (難しい)`Parser`モナドを次のようにも定義できます。
```haskell
@@ -855,9 +851,8 @@ _モナド内包表記_ (monad comprehensions) の構築と呼ばれることが
## RWSモナド
モナド変換子のとある特定の組み合わせは頻出なので、`transformers`パッケージ内の単一のモナド変換子として提供されています。
-`Reader`、
-`Writer`、`State`のモナドは、*Reader-Writer-State*モナドに組み合わさり、より単純に`RWS`モナドともされます。
-このモナドは `RWST`モナド変換子と呼ばれる、対応するモナド変換子を持っています。
+`Reader`、`Writer`、`State`のモナドは、*Reader-Writer-State*モナドに組み合わさり、より単純に`RWS`モナドともされます。
+このモナドは`RWST`モナド変換子という名前の、対応するモナド変換子を持ちます。
ここでは `RWS`モナドを使ってテキストアドベンチャーゲームの処理を設計していきます。
@@ -867,10 +862,12 @@ _モナド内包表記_ (monad comprehensions) の構築と呼ばれることが
type RWS r w s = RWST r w s Identity
```
-ここで、副作用を提供しない`Identity`を基底のモナドに設定することで、 `RWS`モナドが独自のモナド変換子を用いて定義されています。
+なお、`RWS`モナドは基底のモナドを`Identity`に設定することで独自のモナド変換子として定義されています。
+`Identity`は副作用を提供しないのでした。
-第1型引数 `r`は大域的な設定の型を表します。
-第2型引数 `w`はログを蓄積するために使用するモノイド、第3型引数 `s`は可変状態の型を表しています。
+最初の型引数`r`は大域的な構成の型を表します。
+2つ目の`w`はログを蓄積するために使用するモノイドを表します。
+3つ目の`s`は可変状態の型です。
このゲームの場合には、大域的な設定は
`Data.GameEnvironment`モジュールの`GameEnvironment`という名前の型で定義されています。
@@ -880,7 +877,7 @@ type RWS r w s = RWST r w s Identity
```
プレイヤー名と、ゲームがデバッグモードで動作しているか否かを示すフラグが定義されています。
-これらのオプションは、モナド変換子を実行するときにコマンドラインから設定されます。
+モナド変換子を実行するとなると、これらのオプションがコマンドラインから設定されます。
可変状態は `Data.GameState`モジュールの `GameState`と呼ばれる型で定義されています。
@@ -898,13 +895,13 @@ type RWS r w s = RWST r w s Identity
`GameState`型は2つの新しいデータ構造を使っています。
`Map`と`Set`はそれぞれ整列されたマップと整列された集合を表します。
-`items`属性は、そのゲーム平面上の座標からゲームアイテムの集合への対応付けになっています。
-`player`属性はプレイヤーの現在の座標を格納しており、 `inventory`属性は現在プレイヤーが保有するゲームアイテムの集合です。
+`items`属性は、ゲーム平面上の座標からその位置にあるゲームアイテムの集合への対応付けです。
+`player`属性はプレイヤーの現在の座標を格納し、`inventory`属性は現在プレイヤーが保有するゲームアイテムの集合を格納します。
-`Map`と `Set`のデータ構造はキーによって整列され、
-`Ord`型クラスの任意の型をキーとして使用できます。これは今回のデータ構造のキーが完全に順序付けできることを意味します。
+`Map`と`Set`のデータ構造はキーによって整列され、このキーには`Ord`型クラスの任意の型を使えます。
+つまりデータ構造中のキーは完全に順序付けされます。
-ゲームのアクションを書く上で`Map`と `Set`構造をどのように使っていくのかを見ていきます。
+ゲームの動作を書く上で`Map`と`Set`構造をどのように使っていくのかを見ていきます。
ログとしては `List String`モノイドを使います。
`Game`モナド用の型同義語を定義し、`RWS`を使って実装できます。
@@ -915,23 +912,23 @@ type RWS r w s = RWST r w s Identity
## ゲームロジックの実装
-今回は`Reader`モナドと`Writer`モナドと`State`モナドのアクションを再利用し、`Game`モナドで定義されている単純なアクションを組み合わせてゲームを構築していきます。
-このアプリケーションの最上位では`Game`モナドで純粋に計算しており、`Effect`モナドはコンソールにテキストを出力するような観測可能な副作用へと結果を変換するために使っています。
+`Reader`、`Writer`、`State`モナドの動作を再利用することで、`Game`モナドで定義されている単純な動作を組み合わせてゲームを構築していきます。
+このアプリケーションの最上位では`Game`モナドで純粋に計算しており、`Effect`モナドは結果からコンソールにテキストを出力するような観測可能な副作用へと変換するために使っています。
-このゲームで最も簡単なアクションの1つは
-`has`アクションです。このアクションはプレイヤーの持ち物に特定のゲームアイテムが含まれているかどうかを調べます。これは次のように定義されます。
+このゲームで最も簡単な動作の1つは `has`動作です。
+この動作はプレイヤーの持ち物に特定のゲームアイテムが含まれているかどうかを調べます。
+これは次のように定義されます。
```haskell
{{#include ../exercises/chapter11/src/Game.purs:has}}
```
-この関数は、現在のゲームの状態を読み取るために `MonadState`型クラスで定義されている `get`アクションを使っています。
-またそれから指定した`GameItem`が持ち物アイテムの`Set`に出現するかどうかを調べるために`Data.Set`で定義されている
-`member`関数を使っています。
+この関数は、現在のゲームの状態を読み取るために`MonadState`型クラスで定義されている`get`動作を使っています。
+それから指定した`GameItem`が持ち物のアイテムの`Set`に出現するかどうかを調べるために`Data.Set`で定義されている`member`関数を使っています。
-他にも `pickUp`アクションがあります。
+他にも`pickUp`動作があります。
現在の位置にゲームアイテムがある場合、プレイヤーの持ち物にそのアイテムを追加します。
-これには`MonadWriter`と `MonadState`型クラスのアクションを使っています。
+これには`MonadWriter`と`MonadState`型クラスの動作を使っています。
一番最初に現在のゲームの状態を読み取ります。
```haskell
@@ -945,8 +942,9 @@ type RWS r w s = RWST r w s Identity
{{#include ../exercises/chapter11/src/Game.purs:pickup_case}}
```
-`lookup`関数は `Maybe`型構築子で示されたオプショナルな結果を返します。
-`lookup`関数は、キーがマップにない場合は `Nothing`を返し、それ以外の場合は `Just`構築子で対応する値を返します。
+`lookup`関数は`Maybe`型構築子で示される省略可能な結果を返します。
+`lookup`関数は、キーがマップにない場合は`Nothing`を返します。
+それ以外の場合は`Just`構築子で対応する値を返します。
関心があるのは、指定されたゲームアイテムが対応するアイテムの集合に含まれている場合です。
ここでも`member`関数を使うとこれを調べることができます。
@@ -955,21 +953,21 @@ type RWS r w s = RWST r w s Identity
{{#include ../exercises/chapter11/src/Game.purs:pickup_Just}}
```
-この場合、 `put`を使ってゲームの状態を更新し、 `tell`を使ってログにメッセージを追加できます。
+この場合、`put`を使ってゲームの状態を更新し、`tell`を使ってログに文言を追加できます。
```haskell
{{#include ../exercises/chapter11/src/Game.purs:pickup_body}}
```
ここで2つの計算のどちらも`lift`が必要ないことに注意してください。
-なぜなら`MonadState`と `MonadWriter`の両方について `Game`モナド変換子スタック用の適切なインスタンスが存在するからです。
+なぜなら`MonadState`と`MonadWriter`の両方について`Game`モナド変換子スタック用の適切なインスタンスが存在するからです。
-`put`への引数では、レコード更新を使ってゲームの状態の `items`と`inventory`フィールドを変更しています。
-また、特定のキーの値を変更する`Data.Map`の `update`関数を使っています。
-このとき、プレイヤーの現在の位置にあるアイテムの集合を変更するのに、`delete`関数を使って指定したアイテムを集合から取り除いています。
-`insert`を使って新しいアイテムをプレイヤーの持ち物集合に加えるときにも、`inventory`は更新されます。
+`put`への引数では、レコード更新を使ってゲームの状態の`items`及び`inventory`フィールドを変更しています。
+また、特定のキーの値を変更する`Data.Map`の`update`関数を使っています。
+今回の場合、プレイヤーの現在の位置にあるアイテムの集合を変更するのに、`delete`関数を使って指定したアイテムを集合から取り除いています。
+`insert`を使って新しいアイテムをプレイヤーの持ち物の集合に加えるときにも、`inventory`は更新されます。
-最後に、`pickUp`関数は `tell`を使ってユーザに次のように通知することにより、残りの場合を処理します。
+最後に、`pickUp`関数は`tell`を使ってユーザに通知することにより、残りの場合を処理します。
```haskell
{{#include ../exercises/chapter11/src/Game.purs:pickup_err}}
@@ -982,18 +980,18 @@ type RWS r w s = RWST r w s Identity
{{#include ../exercises/chapter11/src/Game.purs:debug}}
```
-ここでは、ゲームの設定を読み込むために `ask`アクションを使用しています。
-繰り返しますが、どの計算でも`lift`は必要がなく、同じdo記法ブロック内で`MonadState`、 `MonadReader`、
-`MonadWriter`型クラスで定義されているアクションを使うことができることに注意してください。
+ここでは、ゲームの構成を読み込むために`ask`動作を使用しています。
+繰り返しますが、どの計算でも`lift`する必要がなく、同じdo記法ブロック内で`MonadState`、`MonadReader`、`MonadWriter`型クラスで定義されている動作を使える点に注意してください。
-`debugMode`フラグが設定されている場合、 `tell`アクションを使うとログに状態が追加されます。
-そうでなければ、エラーメッセージが追加されます。
+`debugMode`フラグが設定されている場合、`tell`動作を使うとログに状態が書き込まれます。
+そうでなければ、エラー文言が追加されます。
-`Game.purs`モジュールの残りの部分では、`MonadState`型クラスと`MonadReader`型クラスと`MonadWriter`型クラスでそれぞれ定義されたアクションだけを使い、同様のアクションが定義されています。
+`Game`モジュールの残りの部分では同様の動作の集合が定義されています。
+各動作は`MonadState`、`MonadReader`、`MonadWriter`型クラスにより定義された動作のみを使っています。
## 計算の実行
-このゲームロジックは `RWS`モナドで動くため、ユーザのコマンドに応答するためには計算する必要があります。
+このゲームロジックは`RWS`モナドで動くため、ユーザのコマンドに応答するために計算する必要があります。
このゲームのフロントエンドは2つのパッケージで構成されています。
アプリカティブなコマンドライン構文解析を提供する`optparse`と、対話的なコンソールベースのアプリケーションを書くことを可能にする、NodeJSの
@@ -1005,7 +1003,7 @@ type RWS r w s = RWST r w s Identity
{{#include ../exercises/chapter11/src/Game.purs:game_sig}}
```
-これを計算するには、ユーザが入力した単語のリストを文字列の配列として渡してから、 `runRWS`を使って `RWS`の計算結果を実行します。
+これを計算するには、ユーザが入力した単語のリストを文字列の配列として渡してから、`runRWS`を使って結果の`RWS`を計算します。
```haskell
data RWSResult state result writer = RWSResult state result writer
@@ -1013,8 +1011,8 @@ data RWSResult state result writer = RWSResult state result writer
runRWS :: forall r w s a. RWS r w s a -> r -> s -> RWSResult s a w
```
-`runRWS`は `runReader`、 `runWriter`、 `runState`を組み合わせたように見えます。
-これは、引数として大域的な設定及び初期状態をとり、ログ、結果、最的な終状態を含むデータ構造を返します。
+`runRWS`は`runReader`、`runWriter`、`runState`を組み合わせたように見えます。
+引数として大域的な構成及び初期状態を取り、ログ、結果、最終的な状態を含むデータ構造を返します。
このアプリケーションのフロントエンドは、次の型シグネチャを持つ関数`runGame`によって定義されます。
@@ -1026,7 +1024,7 @@ runRWS :: forall r w s a. RWS r w s a -> r -> s -> RWSResult s a w
`runGame`は関数の引数としてのゲームの設定を取ります。
`node-readline`パッケージでは`LineHandler`型が提供されています。
-これは端末からのユーザ入力を扱う `Effect`モナドのアクションを表します。
+これは端末からのユーザ入力を扱う`Effect`モナドの動作を表します。
対応するAPIは次の通りです。
```haskell
@@ -1039,8 +1037,8 @@ foreign import setLineHandler
-> Effect Unit
```
-`Interface`型はコンソールの制御対象を表しており、コンソールとやり取りする関数への引数として渡されます。
-`createConsoleInterface`関数を使用すると `Interface`を作成できます。
+`Interface`型はコンソールの制御子を表しており、コンソールとやり取りする関数への引数として渡されます。
+`createConsoleInterface`関数を使用すると`Interface`を作成できます。
```haskell
{{#include ../exercises/chapter11/src/Main.purs:import_RL}}
@@ -1067,14 +1065,14 @@ foreign import setLineHandler
この制御子は追加の最初の引数としてゲームの状態を取ります。
ゲームのロジックを実行するために`runRWS`にゲームの状態を渡さなければならないので、これは必要となっています。
-このアクションが最初に行うことは、 `Data.String`モジュールの `split`関数を使用して、ユーザーの入力を単語に分割することです。
-それから、ゲームの環境と現在のゲームの状態を渡し、 `runRWS`を使用して(`RWS`モナドで)`game`アクションを実行しています。
+この動作が最初に行うことは、`Data.String`モジュールの `split`関数を使用して、ユーザーの入力を単語に分割することです。
+それから、ゲームの環境と現在のゲームの状態を渡し、 `runRWS`を使用して(`RWS`モナドで)`game`動作を実行しています。
-純粋な計算であるゲームロジックを実行するには、画面に全てのログメッセージを出力して、ユーザに次のコマンドのためのプロンプトを表示する必要があります。
-`for_`アクションが(`List String`型の)ログを走査し、コンソールにその内容を出力するために使われています。
-最後に`setLineHandler`を使って行制御関数を更新することでゲームの状態を更新し、`prompt`アクションを使ってプロンプトを再び表示しています。
+純粋な計算であるゲームロジックを実行するには、画面に全てのログ文言を出力して、ユーザに次のコマンドのためのプロンプトを表示する必要があります。
+`for_`動作が(`List String`型の)ログを走査し、コンソールにその内容を出力するために使われています。
+最後に`setLineHandler`を使って行制御関数を更新することでゲームの状態を更新し、`prompt`動作を使ってプロンプトを再び表示しています。
-`runGame`関数は最終的にコンソールインターフェイスに最初の行制御子を取り付けて、最初のプロンプトを表示します。
+`runGame`関数は最終的にコンソールインターフェイスに最初の行制御子を取り付けて、初期プロンプトを表示します。
```haskell
{{#include ../exercises/chapter11/src/Main.purs:runGame_attach_handler}}
@@ -1084,12 +1082,12 @@ foreign import setLineHandler
1. (普通)ゲームの格子上にある全てのゲームアイテムをユーザの持ちものに移動する新しいコマンド `cheat`を実装してください。
関数`cheat :: Game Unit`を`Game`モジュールに作り、この関数を`game`から使ってください。
- 1. (難しい)`RWS`モナドの `
- Writer`コンポーネントは、エラーメッセージと情報メッセージの2つの種類のメッセージのために使われています。
+ 1. (難しい)`RWS`モナドの ` Writer`コンポーネントは、エラー文言とお知らせ文言の2つの種類の文言のために使われています。
このため、コードの幾つかの箇所では、エラーの場合を扱うためにcase式を使用しています。
- エラーメッセージを扱うのに `ExceptT`モナド変換子を使うようにし、情報メッセージを扱うのに`RWS`を使うようにするよう、コードをリファクタリングしてください。
- *補足*:この演習にはテストはありません。
+ コードをリファクタリングしてください。
+ エラー文言を扱うのに`ExceptT`モナド変換子を使い、お知らせ文言を扱うのに`RWS`を使います。
+ *補足*:この演習にはテストがありません。
## コマンドラインオプションの扱い
@@ -1114,7 +1112,7 @@ customExecParser :: forall a. ParserPrefs → ParserInfo a → Effect a
最初の引数は`optparse`ライブラリを設定するために使用されます。
今回の場合、アプリケーションが引数なしで走らされたときは、(「missing argument」エラーを表示する代わりに)`OP.prefs
-OP.showHelpOnEmpty`を使って使用方法のメッセージを表示するように設定していますが、`Options.Applicative.Builder`モジュールには他にも幾つかの選択肢を提供しています。
+OP.showHelpOnEmpty`を使って使用方法の文言を表示するように設定していますが、`Options.Applicative.Builder`モジュールには他にも幾つかの選択肢を提供しています。
2つ目の引数は解析プログラムの完全な説明です。
@@ -1124,9 +1122,9 @@ OP.showHelpOnEmpty`を使って使用方法のメッセージを表示するよ
{{#include ../exercises/chapter11/src/Main.purs:parserOptions}}
```
-ここで`OP.info`は使用方法のメッセージの書式方法のオプションの集合と共に`Parser`を結合します。
+ここで`OP.info`は、`Parser`をヘルプ文言の書式方法のためのオプションの集合と組み合わせます。
`env <**> OP.helper`は`env`と名付けられた任意のコマンドライン引数`Parser`を取り、自動的に`--help`オプションを加えます。
-使用方法のメッセージ用のオプションは型が`InfoMod`であり、これはモノイドなので`fold`関数を使って複数のオプションを一緒に追加できます。
+ヘルプ文言用のオプションは型が`InfoMod`であり、これはモノイドなので、`fold`関数を使って複数のオプションを一緒に追加できます。
解析器の面白い部分は`GameEnvironment`の構築にあります。
@@ -1134,33 +1132,33 @@ OP.showHelpOnEmpty`を使って使用方法のメッセージを表示するよ
{{#include ../exercises/chapter11/src/Main.purs:env}}
```
-`player`と`debug`は両方とも`Parser`なので、アプリカティブ演算子`<$>`と`<*>`を使って`gameEnvironment`関数を持ち上げることができます。
+`player`と`debug`は両方とも`Parser`なので、アプリカティブ演算子`<$>`と`<*>`を使って`gameEnvironment`関数を持ち上げられます。
この関数は`Parser`上で型`PlayerName -> Boolean -> GameEnvironment`を持ちます。
-`OP.strOption`は文字列値を期待するコマンドラインオプションを構築し、一緒に畳み込まれた`Mod`の集まりを介して設定されています。
+`OP.strOption`は文字列値を期待するコマンドラインオプションを構築し、一緒に畳み込まれた`Mod`の集まりを介して構成されています。
`OP.flag`は似たような動作をしますが、関連付けられた値は期待しません。
-`optparse`は多種多様なコマンドライン解析器を構築するために使える様々な修飾子について、大部の[ドキュメント](https://pursuit.purescript.org/packages/purescript-optparse)を提供しています。
+`optparse`は多様なコマンドライン解析器を構築するために使える様々な修飾子について、大部の[ドキュメント](https://pursuit.purescript.org/packages/purescript-optparse)を提供しています。
-アプリカティブ演算子による記法を使うことで、コマンドラインインターフェイスに対して簡潔で宣言的な仕様を与えることが可能になったことに注目です。
-また、`runGame`に新しい関数引数を追加し、`env`の定義中で`<*>`を使って追加の引数まで `runGame`を持ち上げるだけで、簡単に新しいコマンドライン引数を追加できます。
+アプリカティブ演算子により齎される記法を使うことで、コマンドラインインターフェイスの簡潔で宣言的な仕様を与えられた点に注目です。
+加えて、新しいコマンドライン引数を追加するのは単純で、`runGame`に新しい関数引数を追加し、`env`の定義中で`<*>`を使って追加の引数まで`runGame`を持ち上げるだけでできます。
## 演習
1. (普通)`GameEnvironment`レコードに新しい真偽値のプロパティ`cheatMode`を追加してください。
- また、 `optparse`設定に、チートモードを有効にする新しいコマンドラインフラグ `-c`を追加してください。
- チートモードが有効になっていない場合、 `cheat`コマンドは禁止されなければなりません。
+ また、`optparse`の構成に、チートモードを有効にする新しいコマンドラインフラグ`-c`を追加してください。
+ チートモードが有効になっていない場合、前の演習の`cheat`コマンドは禁止されます。
## まとめ
この章ではこれまで学んできた技術を実践的に実演しました。
-モナド変換子を使用したゲームの純粋な仕様の構築、コンソールを使用したフロントエンドを構築するための `Effect`モナドがそれです。
+モナド変換子を使用したゲームの純粋な仕様の構築、コンソールを使用したフロントエンドを構築するための`Effect`モナドがそれです。
ユーザインターフェイスからの実装を分離したので、ゲームの別のフロントエンドも作成できるでしょう。
例えば、`Effect`モナドでCanvas APIやDOMを使用して、ブラウザでゲームを描画するようなことができるでしょう。
-モナド変換子によって命令型のスタイルで安全なコードを書くことができることを見てきました。
+モナド変換子によって命令型のスタイルで安全なコードを書けることが分かりました。
このスタイルでは型システムによって作用が追跡されています。
-また、型クラスはモナドが提供するアクションへと抽象化する強力な方法を提供します。
-このモナドのお陰でコードの再利用が可能になりました。
+加えて、型クラスはモナドが提供する動作へと抽象化する強力な方法を提供し、これによりコードの再利用が可能になりました。
標準的なモナド変換子を組み合わせることにより、`Alternative`や`MonadPlus`のような標準的な抽象化を使用して、役に立つモナドを構築できました。
-モナド変換子は、高階多相や多変数型クラスなどの高度な型システムの機能を利用することによって記述でき、表現力の高いコードの優れた実演となっています。
+モナド変換子は表現力の高いコードの優れた実演となっています。
+これは高階多相や多変数型クラスなどの高度な型システムの機能を利用することによって記述できるものです。
diff --git a/text-ja/chapter12.md b/text-ja/chapter12.md
index 650ea4e3..82baa434 100644
--- a/text-ja/chapter12.md
+++ b/text-ja/chapter12.md
@@ -12,23 +12,21 @@
- `canvas`はHTML5のCanvas APIメソッドの型を与えます。
- `refs`は _大域的な変更可能領域への参照_ を使うための副作用を提供します。
-この章のソースコードは、それぞれに `main`メソッドが定義されているモジュールの集合へと分割されています。
-この章のそれぞれの節の内容は個別のファイルで実装されており、それぞれの時点での適切なファイルの`main`メソッドを実行できるように、Spagoビルドコマンドを変更することで`Main`モジュールを合わせられるようになっています。
+この章の各ソースコードは、`main`メソッドが定義されているモジュールの集合へと分割されています。
+この章の各節の内容は個別のファイルで実装されており、各時点での適切なファイルの`main`メソッドを実行できるように、Spagoビルドコマンドを変更することで、`Main`モジュールを合わせられるようになっています。
-HTMLファイル `html/index.html`には、各例で使用される単一の
-`canvas`要素、及びコンパイルされたPureScriptコードを読み込む `script`要素が含まれています。
+HTMLファイル`html/index.html`には、各例で使用される単一の`canvas`要素、及びコンパイルされたPureScriptコードを読み込む`script`要素が含まれています。
+各節のコードを試すにはブラウザでHTMLファイルを開きます。
ほとんどの演習はブラウザを対象にしているので、この章には単体試験はありません。
## 単純な図形
`Example/Rectangle.purs`ファイルには簡単な導入例が含まれています。
-この例ではcanvasの中心に青い四角形を1つ描画します。
+この例ではキャンバスの中心に青い四角形を1つ描画します。
このモジュールへは、`Effect`モジュールからの`Effect`型と、Canvas
-APIを扱うための`Effect`モナドのアクションを含む`Graphics.Canvas`モジュールをインポートします。
+APIを扱うための`Effect`モナドの動作を含む`Graphics.Canvas`モジュールをインポートします。
-他のモジュールでも同様ですが、
-`main`アクションは最初に`getCanvasElementById`アクションを使ってcanvasオブジェクトへの参照を取得します。
-また、 `getContext2D`アクションを使ってキャンバスの2D描画コンテキストを参照します。
+他のモジュールでも同様ですが、`main`動作は最初に`getCanvasElementById`動作を使ってキャンバスオブジェクトへの参照を取得し、`getContext2D`動作を使ってキャンバスの2D描画文脈にアクセスします。
`void`関数は関手を取り値を`Unit`で置き換えます。
例では`main`がシグネチャに沿うようにするために使われています。
@@ -39,9 +37,9 @@ APIを扱うための`Effect`モナドのアクションを含む`Graphics.Canva
*補足*:この`unsafePartial`の呼び出しは必須です。
これは`getCanvasElementById`の結果のパターン照合部分で、`Just`値構築子のみと照合するためです。
-ここではこれで問題ありませんが、恐らく実際の製品のコードでは`Nothing`値構築子と照合させ、適切なエラーメッセージを提供したほうがよいでしょう。
+ここではこれで問題ありませんが、恐らく実際の製品のコードでは`Nothing`値構築子と照合させ、適切なエラー文言を提供したほうがよいでしょう。
-これらのアクションの型はPSCiを使うかドキュメントを見ると確認できます。
+これらの動作の型はPSCiを使うかドキュメントを見ると確認できます。
```haskell
getCanvasElementById :: String -> Effect (Maybe CanvasElement)
@@ -52,28 +50,28 @@ getContext2D :: CanvasElement -> Effect Context2D
`CanvasElement`と `Context2D`は `Graphics.Canvas`モジュールで定義されている型です。
このモジュールでは`Canvas`作用も定義されており、モジュール内の全てのアクションで使用されています。
-グラフィックスコンテキスト`ctx`はcanvasの状態を管理し、原始的な図形を描画したり、スタイルや色を設定したり、座標変換を適用するためのメソッドを提供します。
+グラフィックス文脈`ctx`はキャンバスの状態を管理し、原始的な図形を描画したり、スタイルや色を設定したり、座標変換を適用したりするための手段を提供します。
-話を進めると、`setFillStyle`アクションを使うことで塗り潰しスタイルを濃い青に設定できます。
+話を進めると、`setFillStyle`動作を使うことで塗り潰しスタイルを濃い青に設定できます。
より長い16進数記法の`#0000FF`も青には使えますが、単純な色については略記法がより簡単です。
```haskell
{{#include ../exercises/chapter12/src/Example/Rectangle.purs:setFillStyle}}
```
-`setFillStyle`アクションがグラフィックスコンテキストを引数として取っていることに注意してください。
-これは `Graphics.Canvas`ではよくあるパターンです。
+`setFillStyle`動作がグラフィックス文脈を引数として取っていることに注意してください。
+これは`Graphics.Canvas`ではよくあるパターンです。
-最後に、 `fillPath`アクションを使用して矩形を塗り潰しています。
+最後に、`fillPath`動作を使用して矩形を塗り潰しています。
`fillPath`は次のような型を持っています。
```haskell
fillPath :: forall a. Context2D -> Effect a -> Effect a
```
-`fillPath`はグラフィックスコンテキストと描画するパスを構築する別のアクションを引数にとります。
-`rect`アクションを使うとパスを構築できます。
-`rect`はグラフィックスコンテキストと矩形の位置及びサイズを格納するレコードを引数にとります。
+`fillPath`はグラフィックスの文脈と描画するパスを構築する他の動作を引数に取ります。
+`rect`動作を使うとパスを構築できます。
+`rect`はグラフィックスの文脈と矩形の位置及びサイズを格納するレコードを取ります。
```haskell
{{#include ../exercises/chapter12/src/Example/Rectangle.purs:fillPath}}
@@ -85,12 +83,13 @@ mainモジュールの名前として`Example.Rectangle`を与えてこの長方
$ spago bundle-app --main Example.Rectangle --to dist/Main.js
```
-それでは `html/index.html`ファイルを開き、このコードによってcanvasの中央に青い四角形が描画されていることを確認してみましょう。
+それでは `html/index.html`ファイルを開き、このコードによってキャンバスの中央に青い四角形が描画されていることを確認してみましょう。
## 行多相を利用する
-パスを描画する方法は他にもあります。 `arc`関数は円弧を描画します。
-`moveTo`関数、 `lineTo`関数、 `closePath`関数は断片的な線分のパスを描画するのに使えます。
+パスを描画する方法は他にもあります。
+`arc`関数は円弧を描画します。
+`moveTo`関数、`lineTo`関数、`closePath`関数は断片的な線分のパスを描画できます。
`Shapes.purs`ファイルでは長方形と円弧と三角形の、3つの図形を描画しています。
@@ -106,7 +105,7 @@ type Rectangle =
}
```
-`x`と `y`プロパティは左上隅の位置を表しており、 `w`と `h`のプロパティはそれぞれ幅と高さを表しています。
+`x`と`y`プロパティは左上隅の位置を表しており、`w`と`h`のプロパティはそれぞれ幅と高さを表しています。
`arc`関数に以下のような型を持つレコードを渡して呼び出すと、円弧を描画できます。
@@ -120,12 +119,12 @@ type Arc =
}
```
-ここで、 `x`と `y`プロパティは弧の中心、 `r`は半径、 `start`と `end`は弧の両端の角度を弧度法で表しています。
+ここで、`x`と`y`プロパティは弧の中心、`r`は半径、`start`と`end`は弧の両端の角度を弧度法で表しています。
例えばこのコードは中心が`(300, 300)`に中心があり半径`50`の円弧を塗り潰します。
弧は1回転のうち2/3ラジアン分あります。
単位円が上下逆様になっている点に注意してください。
-これはy軸がcanvasの下向きに伸びるためです。
+これはy軸がキャンバスの下向きに伸びるためです。
```haskell
fillPath ctx $ arc ctx
@@ -137,11 +136,11 @@ type Arc =
}
```
-`Number`型の `x`と `y`というプロパティが `Rectangle`レコード型と`Arc`レコード型の両方に含まれていますね。
+`Rectangle`レコード型と`Arc`レコード型の両方共、`Number`型の`x`と`y`というプロパティを含んでいますね。
どちらの場合でもこの組は点を表しています。
-これは、何れのレコード型にも適用できる、行多相な関数を書くことができることを意味します。
+つまり、何れのレコード型にも作用する行多相な関数を書けます。
-例えば`Shapes`モジュールでは `x`と `y`のプロパティを変更し図形を並行移動する `translate`関数を定義されています。
+例えば`Shapes`モジュールでは`x`と`y`のプロパティを変更し図形を並行移動する`translate`関数が定義されています。
```haskell
{{#include ../exercises/chapter12/src/Example/Shapes.purs:translate}}
@@ -156,7 +155,7 @@ type Arc =
`shape { ... }`という式は、`shape`を元にして、括弧の中で指定された値で更新されたフィールドを持つ新たなレコードを作ります。
なお、波括弧の中の式はレコード直値のようなコロンではなく、等号でラベルと式を区切って書きます。
-`Shapes`の例からわかるように、 `translate`関数は `Rectangle`レコードと`Arc`レコード双方に対して使うことができます。
+`Shapes`の例からわかるように、`translate`関数は`Rectangle`レコードと`Arc`レコード双方に対して使えます。
`Shape`の例で描画される3つ目の型は線分の断片からなるパスです。
対応するコードは次のようになります。
@@ -179,15 +178,15 @@ mainモジュールとして`Example.Shapes`を指定して、この例をビル
$ spago bundle-app --main Example.Shapes --to dist/Main.js
```
-そしてもう一度 `html/index.html`を開き、結果を確認してください。canvas
-に3つの異なる図形が描画されるはずです。
+そしてもう一度`html/index.html`を開き、結果を確認してください。
+キャンバスに3つの異なる図形が描画されるはずです。
## 演習
- 1. (簡単)これまでの例のそれぞれについて、 `strokePath`関数や`setStrokeStyle`関数を使ってみましょう。
- 1. (簡単)関数の引数の内部のdo記法ブロックにより、`fillPath`関数と`strokePath`関数を使って共通のスタイルを持つ複雑なパスを描画できます。
- 同じ `fillPath`呼び出しで隣り合った2つの矩形を描画するように、`Rectangle`のコード例を変更してみてください。
- 線分と円弧を組み合わせて、扇形を描画してみてください。
+ 1. (簡単)これまでの各例について、`strokePath`関数や`setStrokeStyle`関数を使ってみましょう。
+ 1. (簡単)関数の引数の内部でdo記法ブロックを使うと、`fillPath`関数と`strokePath`関数は共通のスタイルを持つ複雑なパスを描画できます。
+ 同じ`fillPath`呼び出しを使って隣り合う2つの矩形を描画するように、`Rectangle`の例を変更してみてください。
+ 線分と円弧の組み合わせを使って、扇形を描画してみてください。
1. (普通)次のような2次元の点を表すレコードが与えられたとします。
```haskell
@@ -210,38 +209,38 @@ $ spago bundle-app --main Example.Shapes --to dist/Main.js
f :: Number -> Point
```
- この関数は引数として `1`から `0`の間の `Number`をとり、 `Point`を返します。
- `renderPath`関数を利用して関数 `f`のグラフを描くアクションを書いてください。
- そのアクションでは有限個の点を `f`からサンプリングすることによって近似しなければなりません。
+ この関数は引数として`1`から`0`の間の`Number`を取り、`Point`を返します。
+ `renderPath`関数を使い、関数`f`のグラフを描く動作を書いてください。
+ その動作では有限個の点で`f`を標本化することによって近似しなければなりません。
関数 `f`を変更し、様々なパスが描画されることを確かめてください。
## 無作為に円を描く
-`Example/Random.purs`ファイルには2種類の異なる副作用が混在した`Effect`モナドを使う例が含まれています。
-1つは乱数生成で、もう1つはcanvasの操作です。
+`Example/Random.purs`ファイルには、`Effect`モナドを使って2種類の副作用を綴じ合わせる例が含まれています。
+1つの副作用は乱数生成で、もう1つはキャンバスの操作です。
この例では無作為に生成された円をキャンバスに100個描画します。
-`main`アクションではこれまでのようにグラフィックスコンテキストへの参照を取得し、ストロークと塗り潰しスタイルを設定します。
+`main`動作ではこれまでのようにグラフィックス文脈への参照を取得し、線描きと塗り潰しのスタイルを設定します。
```haskell
{{#include ../exercises/chapter12/src/Example/Random.purs:style}}
```
-次のコードでは `for_`アクションを使って `0`から `100`までの整数について反復しています。
+次のコードでは`for_`動作を使って`0`から`100`までの整数について反復しています。
```haskell
{{#include ../exercises/chapter12/src/Example/Random.purs:for}}
```
-それぞれの繰り返しでのdo記法ブロックは、`0`と`1`の間に分布する3つの乱数を生成することから始まります。
-これらの数は `0`から `1`の間に無作為に分布しており、それぞれ `x`座標、 `y`座標、半径 `r`を表しています。
+各繰り返しで、do記法ブロックは`0`と`1`の間に分布する3つの乱数を生成することから始まります。
+これらの数はそれぞれ`x`座標、`y`座標、半径`r`を表しています。
```haskell
{{#include ../exercises/chapter12/src/Example/Random.purs:random}}
```
-次のコードではそれぞれの円について、これらの変数に基づいて `Arc`を作成し、最後に現在のスタイルに従って円弧を塗り潰し線描きします。
+次のコードでは各円について、これらの変数に基づいて`Arc`を作成し、最後に現在のスタイルに従って円弧を塗り潰し線描きします。
```haskell
{{#include ../exercises/chapter12/src/Example/Random.purs:path}}
@@ -258,7 +257,7 @@ $ spago bundle-app --main Example.Random --to dist/Main.js
## 座標変換
キャンバスは簡単な図形を描画するだけのものではありません。
-キャンバスは変換行列を扱うことができ、描画の前に形状を変形してから図形を描画できます。
+キャンバスは座標変換を管理しており、描画の前に図形を変形するのに使えます。
図形は平行移動、回転、拡大縮小、及び斜めに変形できます。
`canvas`ライブラリではこれらの変換を以下の関数で提供しています。
@@ -281,19 +280,19 @@ transform :: Context2D
-> Effect Context2D
```
-`translate`アクションは`TranslateTransform`レコードのプロパティで指定した大きさだけ平行移動します。
+`translate`動作は`TranslateTransform`レコードのプロパティで指定した大きさだけ平行移動します。
-`rotate`アクションは最初の引数で指定されたラジアンの値に応じて、原点を中心として回転します。
+`rotate`動作は最初の引数で指定されたラジアンの数値に応じて、原点を中心として回転します。
-`scale`アクションは原点を中心として拡大縮小します。
-`ScaleTransform`レコードは `X`軸と `y`軸に沿った拡大率を指定するのに使います。
+`scale`動作は原点を中心として拡大縮小します。
+`ScaleTransform`レコードは`x`軸と`y`軸に沿った拡大率を指定するのに使います。
-最後の `transform`はこの4つのうちで最も一般化されたアクションです。
-このアクションでは行列に従ってアフィン変換します。
+最後の `transform`はこの4つのうちで最も一般化された動作です。
+この動作では行列に従ってアフィン変換します。
-これらのアクションが呼び出された後に描画される図形は、自動的に適切な座標変換が適用されます。
+これらの動作が呼び出された後に描画される図形は、自動的に適切な座標変換が適用されます。
-実際には、これらの関数のそれぞれの作用は、コンテキストの現在の変換行列に対して変換行列を*右から乗算*していきます。
+実際には、これらの関数の各作用は、文脈の現在の変換行列に対して変換行列を*右から乗算*していきます。
つまり、もしある作用の変換をしていくと、その作用は実際には逆順に適用されていきます。
```haskell
@@ -305,11 +304,11 @@ transformations ctx = do
renderScene
```
-この一連のアクションの作用では、まずシーンが回転され、それから拡大縮小され、最後に平行移動されます。
+この一連の動作の作用では、まずシーンが回転され、それから拡大縮小され、最後に平行移動されます。
-## コンテキストの保存
+## 文脈の保存
-変換を適用してシーンの一部を描画し、それからその変換を元に戻す、という使い方はよくあります。
+座標変換を使ってシーンの一部を描画し、それからその変換を元に戻す、という使い方はよくあります。
Canvas APIにはキャンバスの状態の*スタック*を操作する`save`と`restore`メソッドが備わっています。
`canvas`ではこの機能を次のような関数で梱包しています。
@@ -324,11 +323,10 @@ restore
-> Effect Context2D
```
-`save`アクションは現在のコンテキストの状態(現在の変換行列や描画スタイル)をスタックにプッシュし、
-`restore`アクションはスタックの一番上の状態をポップし、コンテキストの状態を復元します。
+`save`動作は現在の文脈の状態(現在の変換行列や描画スタイル)をスタックにプッシュし、`restore`動作はスタックの一番上の状態をポップし、文脈の状態を復元します。
-これらのアクションにより、現在の状態を保存し、いろいろなスタイルや変換を適用してから原始的な図形を描画し、最後に元の変換と状態を復元できます。
-例えば、次の関数は回転を適用してから幾つかのキャンバスアクションを実行し、そのあとに変換を復元します。
+これらの動作により、現在の状態を保存し、いろいろなスタイルや変換を適用してから原始的な図形を描画し、最後に元の変換と状態を復元できます。
+例えば次の関数は幾つかのキャンバス動作を実行しますが、その前に回転を適用し、その後に変換を復元します。
```haskell
rotated ctx render = do
@@ -338,8 +336,7 @@ rotated ctx render = do
restore ctx
```
-こういったよくある高階関数の使われ方の抽象化として、`canvas`ライブラリでは元のコンテキスト状態を保存しつつ幾つかのキャンバスアクションを実行する
-`withContext`関数が提供されています。
+こういったよくある高階関数の使われ方の抽象化として、`canvas`ライブラリでは元の文脈状態を保存しつつ幾つかのキャンバス動作を実行する`withContext`関数が提供されています。
```haskell
withContext
@@ -361,7 +358,7 @@ rotated ctx render =
この節では `refs`パッケージを使って `Effect`モナドの別の作用について実演してみます。
-`Effect.Ref`モジュールでは、大域的に変更可能な参照のための型構築子、及び関連する作用を提供します。
+`Effect.Ref`モジュールでは、大域的に変更可能な参照のための型構築子、及びそれに紐付く作用を提供します。
```text
> import Effect.Ref
@@ -373,15 +370,15 @@ Type -> Type
型`Ref a`の値は型`a`の値を含む可変参照セルであり、大域的な変更を追跡するのに使われます。
そういったわけでこれは少しだけ使う分に留めておくべきです。
-`Example/Refs.purs`ファイルには `canvas`要素上のマウスクリックを追跡するのに `Ref`作用を使用する例が含まれています。
+`Example/Refs.purs`ファイルには `canvas`要素上のマウスクリックを追跡するのに `Ref`を使う例が含まれます。
-このコードでは最初に`new`アクションを使って値`0`を含む新しい参照を作成しています。
+このコードでは最初に`new`動作を使って値`0`を含む新しい参照を作成しています。
```haskell
{{#include ../exercises/chapter12/src/Example/Refs.purs:clickCount}}
```
-クリックイベント制御子の内部では、 `modify`アクションを使用してクリック数を更新し、更新された値が返されています。
+クリックイベント制御子の内部では、`modify`動作を使用してクリック数を更新し、更新された値が返されています。
```haskell
{{#include ../exercises/chapter12/src/Example/Refs.purs:count}}
@@ -393,13 +390,12 @@ Type -> Type
{{#include ../exercises/chapter12/src/Example/Refs.purs:withContext}}
```
-このアクションでは元の変換を保存するために
-`withContext`を使用しており、それから一連の変換を適用しています(変換が下から上に適用されることを思い出してください)。
+この動作では元の変換を保存するために`withContext`を使用しており、それから一連の変換を適用しています(変換が下から上に適用されることを思い出してください)。
- 矩形が`(-100, -100)`だけ平行移動し、中心が原点に来ます。
- 矩形が原点を中心に拡大されます。
- 矩形が原点を中心に`10`の倍数分の角度で回転します。
-- 矩形が`(300, 300)`だけ平行移動し、中心がcanvasの中心に来ます。
+- 矩形が`(300, 300)`だけ平行移動し、中心がキャンバスの中心に来ます。
このコード例をビルドしてみましょう。
@@ -413,25 +409,24 @@ $ spago bundle-app --main Example.Refs --to dist/Main.js
## 演習
1. (簡単)パスの線描と塗り潰しを同時に行う高階関数を書いてください。
- その関数を使用して `Random.purs`例を書き直してください。
- 1. (普通)`Random`作用と
- `Dom`作用を使用して、マウスがクリックされたときに、キャンバスに無作為な位置、色、半径の円を描画するアプリケーションを作成してください。
- 1. (普通)指定された座標を中心としてシーンを回転させる関数を書いてください。
- *手掛かり*:最初にシーンを原点まで平行移動しましょう。
+ その関数を使用して`Random.purs`の例を書き直してください。
+ 1. (普通)`Random`作用と`Dom`作用を使用して、マウスがクリックされたときに、キャンバスに無作為な位置、色、半径の円を描画するアプリケーションを作成してください。
+ 1. (普通)指定された座標の点を中心として回転させることでシーンを変換する関数を書いてください。
+ *手掛かり*:変換を使い、最初にシーンを原点まで平行移動しましょう。
## L-System
この章の最後の例として、 `canvas`パッケージを使用して*L-system*(またの名を*Lindenmayer
system*)を描画する関数を記述します。
-L-Systemは*アルファベット*、つまり初期状態となるアルファベットの文字列と、*生成規則*の集合で定義されています。
-各生成規則は、アルファベットの文字をとり、それを置き換える文字の配列を返します。
-この処理は文字の初期配列から始まり、複数回繰り返されます。
+1つのL-Systemは*アルファベット*、つまりアルファベット由来の文字の初期の並びと、*生成規則*の集合で定義されます。
+各生成規則は、アルファベットの文字を取り、それを置き換える文字の並びを返します。
+この処理は文字の初期の並びから始まり、複数回繰り返されます。
-もしアルファベットの各文字がcanvas上で実行される命令と対応付けられていれば、その指示に順番に従うことでL-Systemを描画できます。
+もしアルファベットの各文字がキャンバス上で実行される命令と対応付けられていれば、その指示に順番に従うことでL-Systemを描画できます。
-例えばアルファベットが文字 `L`(左回転)、 `R`(右回転)、 `F`(前進)で構成されているとします。
-また、次のような生成規則を定義します。
+例えばアルファベットが文字`L`(左回転)、`R`(右回転)、`F`(前進)で構成されているとします。
+次のような生成規則を定義できます。
```text
L -> L
@@ -447,7 +442,8 @@ FLFRRFLFRRFLFRRFLFRRFLFRRFLFRR
FLFRRFLFLFLFRRFLFRRFLFRRFLFLFLFRRFLFRRFLFRRFLF...
```
-この命令群に対応する線分パスをプロットすると、*コッホ曲線*と呼ばれる曲線に近似されます。
+というように続きます。
+この命令群に対応する線分パスをプロットすると、*コッホ曲線*に近似されます。
反復回数を増やすと、曲線の解像度が増していきます。
それでは型と関数のある言語へとこれを翻訳してみましょう。
@@ -478,10 +474,9 @@ FLFRRFLFLFLFRRFLFRRFLFRRFLFLFLFRRFLFRRFLFRRFLF...
これはまさに上記の仕様をそのまま書き写したものです。
-これで、この形式の仕様を受け取りcanvasに描画する関数 `lsystem`を実装できます。
+これで、この形式の仕様を受け取ってキャンバスに描画する関数`lsystem`を実装できます。
`lsystem`はどのような型を持っているべきでしょうか。
-この関数は初期状態 `initial`や生成規則
-`productions`のような値だけでなく、アルファベットの文字をcanvasに描画する関数を引数に取る必要があります。
+`initial`や`productions`のような値だけでなく、アルファベットの文字をキャンバスに描画できる関数を引数に取る必要があります。
`lsystem`の型の最初の大まかな設計は以下です。
@@ -495,8 +490,8 @@ Sentence
最初の2つの引数の型は、値 `initial`と `productions`に対応しています。
-3番目の引数は、アルファベットの文字を取り、canvas上の幾つかのアクションを実行することによって*解釈*する関数を表します。
-この例では、文字`L`は左回転、文字 `R`で右回転、文字 `F`は前進を意味します。
+3番目の引数は、アルファベットの文字を取り、キャンバス上の幾つかの動作を実行することによって*解釈*する関数を表します。
+この例では、文字`L`は左回転、文字`R`で右回転、文字`F`は前進を意味します。
最後の引数は、実行したい生成規則の繰り返し回数を表す数です。
@@ -512,9 +507,9 @@ forall a. Array a
```
次に気付くこととしては、「左回転」と「右回転」のような命令を実装するためには、幾つかの状態を管理する必要があります。
-具体的に言えば、その時点でパスが向いている方向を状態として持たなければなりません。
-計算を通じて状態を関数に渡すように変更する必要があります。
-ここでも `lsystem`関数は状態がどんな型でも動作したほうがよいので、型変数 `s`を使用してそれを表しています。
+具体的に言えば、その時点でパスが動いている方向を状態として持たなければなりません。
+計算を通じて状態を渡すように関数を変更する必要があります。
+ここでも`lsystem`関数は状態がどんな型でも動作したほうがよいので、型変数`s`を使用してそれを表しています。
型 `s`を追加する必要があるのは3箇所で、次のようになります。
@@ -565,12 +560,12 @@ lsystem :: forall a s
{{#include ../exercises/chapter12/src/Example/LSystem.purs:lsystem_impl}}
```
-`go`関数は第2引数に応じて再帰することで動作します。
-場合分けは2つであり、`n`がゼロであるときと `n`がゼロでないときです。
+`go`関数は第2引数について再帰することで動作します。
+場合分けは2つであり、`n`がゼロであるときと`n`がゼロでないときです。
1つ目の場合は再帰は完了し、解釈関数に応じて現在の文を解釈します。
-型`Array a`の文、型 `s`の状態、型 `s -> a -> Effect s`の関数があります。
-以前定義した `foldM`でやったことみたいです。
+型`Array a`の文、型`s`の状態、型`s -> a -> Effect s`の関数があります。
+以前定義した`foldM`の出番のようです。
この関数は`control`パッケージで手に入ります。
```haskell
@@ -585,11 +580,11 @@ lsystem :: forall a s
```
これだけです。
-`foldM`や `concatMap`のような高階関数を使うと、このようにアイデアを簡潔に表現できるのです。
+`foldM`や`concatMap`のような高階関数を使うと、アイデアを簡潔に表現できるのです。
しかし、話はこれで終わりではありません。
ここで与えた型は、実際はまだ特殊化されすぎています。
-この定義ではcanvasの操作が実装のどこにも使われていないことに注目してください。
+この定義ではキャンバスの操作が実装のどこにも使われていないことに注目してください。
それに、全く`Effecta`モナドの構造を利用していません。
実際には、この関数は*どんな*モナド`m`についても動作します。
@@ -599,14 +594,14 @@ lsystem :: forall a s
{{#include ../exercises/chapter12/src/Example/LSystem.purs:lsystem_anno}}
```
-この型で書かれていることは、この解釈関数はモナド `m`が持つ任意の副作用を完全に自由に持つことができる、ということだと理解できます。
+この型で書かれていることは、この解釈関数はモナド`m`が持つ任意の副作用を完全に自由に持つことができる、ということだと理解できます。
キャンバスに描画したり、またはコンソールに情報を出力したりするかもしれませんし、失敗や複数の戻り値に対応しているかもしれません。
こういった様々な型の副作用を使ったL-Systemを記述してみることを読者にお勧めします。
この関数は実装からデータを分離することの威力を示す良い例となっています。
-この手法の利点は、複数の異なる方法でデータを解釈する自由が得られることです。
-`lsystem`は2つの小さな関数へと分解さえできるかもしれません。
-1つ目は `concatMap`の適用の繰り返しを使って文を構築するもので、2つ目は `foldM`を使って文を解釈するものです。
+この手法の利点は、複数の異なる方法でデータを解釈できることです。
+さらに`lsystem`を2つの小さな関数へと分解できます。
+1つ目は`concatMap`の適用の繰り返しを使って文を構築するもの、2つ目は`foldM`を使って文を解釈するものです。
これは読者の演習として残しておきます。
それでは解釈関数を実装して、この章の例を完成させましょう。
@@ -625,17 +620,17 @@ lsystem :: forall a s
{{#include ../exercises/chapter12/src/Example/LSystem.purs:interpretLR}}
```
-文字 `F`(前進)を解釈するには、パスの新しい位置を計算し、線分を描画し、状態を次のように更新します。
+文字`F`(前進)を解釈するには、次のようにパスの新しい位置を計算し、線分を描画し、状態を更新します。
```haskell
{{#include ../exercises/chapter12/src/Example/LSystem.purs:interpretF}}
```
-なおこの章のソースコードでは、名前 `ctx`がスコープに入るように、`interpret`関数は `main`関数内で
+なお、この章のソースコードでは、名前 `ctx`がスコープに入るように、`interpret`関数は `main`関数内で
`let`束縛を使用して定義されています。
-`State`型がコンテキストを持つように変更できるでしょうが、それはこのシステムの状態の変化する部分ではないので不適切でしょう。
+`State`型が文脈を持つように変更できるでしょうが、それはこのシステムの状態の変化する部分ではないので不適切でしょう。
-このL-Systemを描画するには、次のような `strokePath`アクションを使用するだけです。
+このL-Systemを描画するには、次のような`strokePath`動作を使用するだけです。
```haskell
{{#include ../exercises/chapter12/src/Example/LSystem.purs:strokePath}}
@@ -658,9 +653,7 @@ $ spago bundle-app --main Example.LSystem --to dist/Main.js
1. (普通)`lsystem`関数を2つの小さな関数に分割してください。
1つ目は`concatMap`の適用の繰り返しを使用して最終的な文を構築するもので、2つ目は
`foldM`を使用して結果を解釈するものでなくてはなりません。
- 1. (普通)`setShadowOffsetX`アクション、
- `setShadowOffsetY`アクション、`setShadowBlur`アクション、
- `setShadowColor`アクションを使い、塗りつぶされた図形にドロップシャドウを追加してください。
+ 1. (普通)`setShadowOffsetX`、`setShadowOffsetY`、`setShadowBlur`、`setShadowColor`動作を使い、塗りつぶされた図形にドロップシャドウを追加してください。
*手掛かり*:PSCiを使って、これらの関数の型を調べてみましょう。
1. (普通)向きを変えるときの角度の大きさは今のところ一定 (`tau/6`) です。
これに代えて、`Letter`データ型の中に角度を移動させ、生成規則によって変更できるようにしてください。
@@ -671,8 +664,9 @@ $ spago bundle-app --main Example.LSystem --to dist/Main.js
data Letter = L Angle | R Angle | F
```
- 生成規則でこの新しい情報を使うと、どんな面白い図形を作ることができるでしょうか。
-1. (難しい)`L`(60度左回転)、 `R`(60度右回転)、`F`(前進)、 `M`(これも前進)という4つの文字からなるアルファベットでL-Systemが与えられたとします。
+ この新しい情報を生成規則でどう使うと、面白い図形を作ることができるでしょうか。
+1. (難しい)4つの文字からなるアルファベットでL-Systemが与えられたとします。
+ それぞれ`L`(60度左回転)、`R`(60度右回転)、`F`(前進)、`M`(これも前進)です。
このシステムの文の初期状態は、単一の文字 `M`です。
@@ -699,9 +693,9 @@ $ spago bundle-app --main Example.LSystem --to dist/Main.js
## まとめ
-この章では、 `canvas`ライブラリを使用することにより、PureScriptからHTML5 Canvas APIを使う方法について学びました。
-また、これまで学んできた手法の多くを利用した実用的な例について見ました。
-マップや畳み込み、レコードと行多相、副作用を扱うための `Effect`モナドなどです。
+この章では、`canvas`ライブラリを使用することにより、PureScriptからHTML5 Canvas APIを使う方法について学びました。
+また、これまで学んできた多くの手法からなる実用的な実演を見ました。
+マップや畳み込み、レコードと行多相、副作用を扱うための`Effect`モナドです。
この章の例では、高階関数の威力を示すとともに、 _実装からのデータの分離_
も実演してみせました。これは例えば、代数データ型を使用してこれらの概念を次のように拡張し、描画関数からシーンの表現を完全に分離できるようになります。
@@ -716,6 +710,6 @@ data Scene
| ...
```
-この手法は `drawing`パッケージでも採用されており、描画前にさまざまな方法でデータとしてシーンを操作できる柔軟性をもたらしています。
+この手法は`drawing`パッケージで取られており、描画前に様々な方法でシーンをデータとして操作できる柔軟性を齎しています。
-canvasに描画されるゲームの例については[cookbook](https://github.com/JordanMartinez/purescript-cookbook/blob/master/README.md#recipes)の「Behavior」と「Signal」のレシピを見てください。
+キャンバスに描画されるゲームの例については[cookbook](https://github.com/JordanMartinez/purescript-cookbook/blob/master/README.md#recipes)の「Behavior」と「Signal」のレシピを見てください。
diff --git a/text-ja/chapter13.md b/text-ja/chapter13.md
index 0fb359ed..6483a1bd 100644
--- a/text-ja/chapter13.md
+++ b/text-ja/chapter13.md
@@ -38,7 +38,7 @@ merge :: Array Int -> Array Int -> Array Int
```
典型的なテストスイートでは、手作業でこのような小さなテスト項目を幾つも作成し、結果が正しい値と等しいことを確認することでテストを実施します。
-しかし、 `merge`関数について知る必要があるものは全て、この性質に要約できます。
+しかし、`merge`関数について知る必要があるものは全て、この性質に要約できます。
- `xs`と`ys`が整列済みなら、`merge xs ys`は両方の配列が一緒に結合されて整列された結果になります。
@@ -52,9 +52,8 @@ main = do
eq (merge (sort xs) (sort ys)) (sort $ xs <> ys)
```
-このコードを実行すると、 `quickcheck`は無作為な入力 `xs`と
-`ys`を生成してこの関数に渡すことで、主張しようとしている性質を反証しようとします。
-何らかの入力に対して関数が `false`を返した場合、性質は正しくないことが示され、ライブラリはエラーを発生させます。
+このコードを実行すると、`quickcheck`は無作為な入力`xs`と`ys`を生成してこの関数に渡すことで、主張した性質を反証しようとします。
+何らかの入力に対して関数が`false`を返した場合、性質は正しくなく、ライブラリはエラーを発生させます。
幸いなことに、次のように100個の無作為なテスト項目を生成しても、ライブラリはこの性質を反証できません。
```text
@@ -75,11 +74,11 @@ Error: Test 1 failed:
Test returned false
```
-見ての通りこのエラーメッセージではあまり役に立ちませんが、少し工夫するだけで改良できます。
+見ての通りこのエラー文言ではあまり役に立ちませんが、少し工夫するだけで改良できます。
-## エラーメッセージの改善
+## エラー文言の改善
-テスト項目が失敗した時に同時にエラーメッセージを提供する上で、`quickcheck`は`>`演算子を提供しています。
+テスト項目が失敗した時に同時にエラー文言を提供する上で、`quickcheck`は`>`演算子を提供しています。
次のように性質の定義とエラー文言を`>`で区切って書くだけです。
```haskell
@@ -91,7 +90,7 @@ quickCheck \xs ys ->
eq result expected > "Result:\n" <> show result <> "\nnot equal to expected:\n" <> show expected
```
-このとき、もしバグを混入するようにコードを変更すると、最初のテスト項目が失敗したときに改良されたエラーメッセージが表示されます。
+このとき、もしバグを混入するようにコードを変更すると、最初のテスト項目が失敗したときに改良されたエラー文言が表示されます。
```text
Error: Test 1 (seed 534161891) failed:
@@ -109,7 +108,7 @@ not equal to expected:
*補足*:この新しい性質は冗長です。
というのもこの状況は既に既存の性質で押さえられているからです。
ここでは読者がQuickCheckを使う練習のための簡単なやり方を示そうとしているだけです。
- 1. (簡単)`merge`の残りの性質に対して、適切なエラーメッセージを追加してください。
+ 1. (簡単)`merge`の残りの性質に対して、適切なエラー文言を追加してください。
## 多相的なコードのテスト
@@ -120,7 +119,7 @@ not equal to expected:
mergePoly :: forall a. Ord a => Array a -> Array a -> Array a
```
-`merge`の代わりに `mergePoly`を使うように元のテストを変更すると、次のようなエラーメッセージが表示されます。
+`merge`の代わりに `mergePoly`を使うように元のテストを変更すると、次のようなエラー文言が表示されます。
```text
No type class instance was found for
@@ -131,7 +130,7 @@ The instance head contains unknown type variables.
Consider adding a type annotation.
```
-このエラーメッセージは、配列に持たせたい要素の型が何なのかわからないので、コンパイラが無作為なテスト項目を生成できなかったということを示しています。
+このエラー文言は、配列に持たせたい要素の型が何なのかわからないので、コンパイラが無作為なテスト項目を生成できなかったということを示しています。
このような場合、型註釈を使ってコンパイラが特定の型を推論するように強制できます。
例えば`Array Int`などです。
@@ -142,7 +141,7 @@ quickCheck \xs ys ->
代替案として型を指定する補助関数を使うこともできます。
こうするとより見通しのよいコードになることがあります。
-例えば同値関数の同義な関数`ints`を定義したとしましょう。
+例えば同値関数の同義語として関数`ints`を定義したとしましょう。
```haskell
ints :: Array Int -> Array Int
@@ -156,18 +155,18 @@ quickCheck \xs ys ->
eq (ints $ mergePoly (sort xs) (sort ys)) (sort $ xs <> ys)
```
-ここで、 `ints`関数が不明な型を解消するために使われているため、 `xs`と `ys`はどちらも型 `Array Int`を持っています。
+ここで、`ints`関数が不明な型の曖昧さを解消するために使われているため、`xs`と`ys`は型`Array Int`を持っています。
## 演習
- 1. (簡単)`xs`と `ys`の型を `Array Boolean`に強制する関数 `bools`を書き、
- `mergePoly`をその型でテストする性質を追加してください。
- 1. (普通)標準関数から(例えば`arrays`パッケージから)1つ関数を選び、適切なエラーメッセージを含めてQuickCheckの性質を書いてください。
+ 1. (簡単)`xs`と`ys`の型を`Array
+ Boolean`に強制する関数`bools`を書き、`mergePoly`をその型でテストする性質を追加してください。
+ 1. (普通)標準関数から(例えば`arrays`パッケージから)1つ関数を選び、適切なエラー文言を含めてQuickCheckの性質を書いてください。
その性質は、補助関数を使って多相型引数を `Int`か `Boolean`のどちらかに固定しなければいけません。
## 任意のデータの生成
-`quickcheck`ライブラリを使って性質に対するテスト項目を無作為に生成する方法について説明します。
+それでは`quickcheck`ライブラリが性質に対するテスト項目をどのように無作為に生成できているのかを見ていきます。
無作為に値を生成できるような型は、次のような型クラス `Arbitary`のインスタンスを持っています。
@@ -183,9 +182,8 @@ class Arbitrary t where
`Gen`はモナドでもアプリカティブ関手でもあるので、
`Arbitary`型クラスの新しいインスタンスを作成するのに、いつも使っているようなコンビネータを自由に使うことができます。
-例えば、 `quickcheck`ライブラリで提供されている `Int`型用の
-`Arbitrary`インスタンスを使い、256個のバイト値上の分布を作ることができます。
-これには`Gen`用に`Functor`インスタンスを使って整数から任意の整数値のバイトまでマップします。
+例えば、`quickcheck`ライブラリで提供されている`Int`型用の`Arbitrary`インスタンスを使い、256個のバイト値上の分布を作れます。
+これには`Gen`用の`Functor`インスタンスを使い、整数からバイトへの関数を任意の整数値に写します。
```haskell
newtype Byte = Byte Int
@@ -197,9 +195,9 @@ instance Arbitrary Byte where
| otherwise = intToByte (-n)
```
-ここでは、0から255までの間の整数値であるような型 `Byte`を定義しています。
-`Arbitrary`インスタンスは `map`演算子を使って、 `intToByte`関数を `arbitrary`アクションまで持ち上げています。
-`arbitrary`アクション内部の型は `Gen Int`と推論されます。
+ここでは、0から255までの間の整数値であるような型`Byte`を定義しています。
+`Arbitrary`インスタンスは`map`演算子を使って、`intToByte`関数を`arbitrary`動作まで持ち上げています。
+`arbitrary`動作内部の型は`Gen Int`と推論されます。
この考え方を `merge`用のテストに使うこともできます。
@@ -208,9 +206,8 @@ quickCheck \xs ys ->
eq (numbers $ mergePoly (sort xs) (sort ys)) (sort $ xs <> ys)
```
-このテストでは、任意の配列 `xs`と `ys`を生成しますが、 `merge`は整列済みの入力を期待しているので、 `xs`と
-`ys`を整列しておかなければなりません。
-一方で、整列された配列を表すnewtypeを作成し、整列されたデータを生成する `Arbitrary`インスタンスを書くこともできます。
+このテストでは、任意の配列`xs`と`ys`を生成しますが、`merge`は整列済みの入力を期待しているので、これらを整列しておかなければなりません。
+一方で、整列された配列を表すnewtypeを作成し、整列されたデータを生成する`Arbitrary`インスタンスを書くこともできます。
```haskell
newtype Sorted a = Sorted (Array a)
@@ -229,9 +226,9 @@ quickCheck \xs ys ->
eq (ints $ mergePoly (sorted xs) (sorted ys)) (sort $ sorted xs <> sorted ys)
```
-これは些細な変更に見えるかもしれませんが、 `xs`と `ys`の型はただの `Array Int`から `Sorted Int`へと変更されています。
-これにより、 `mergePoly`関数は整列済みの入力を取る、という*意図*を、わかりやすく示すことができます。
-理想的には、 `mergePoly`関数自体の型が `Sorted`型構築子を使うようにするといいでしょう。
+これは些細な変更に見えるかもしれませんが、`xs`と`ys`の型はただの`Array Int`から`Sorted Int`へと変更されています。
+これにより、`mergePoly`関数は整列済みの入力を取る、という*意図*をわかりやすく示すことができます。
+理想的には、`mergePoly`関数自体の型が`Sorted`型構築子を使うようにするといいでしょう。
より興味深い例として、 `Tree`モジュールでは枝の値で整列された二分木の型が定義されています。
@@ -250,7 +247,7 @@ fromArray :: forall a. Ord a => Array a -> Tree a
toArray :: forall a. Tree a -> Array a
```
-`insert`関数は新しい要素を整列済みの二分木に挿入するのに使われ、 `member`関数は特定の値の有無を木に問い合わせるのに使われます。
+`insert`関数は新しい要素を整列済みの木に挿入し、`member`関数は特定の値について木に問い合わせます。
例えば次のようになります。
```text
@@ -263,16 +260,16 @@ true
false
```
-`toArray`関数と `fromArray`関数は、整列された木と整列された配列を相互に変換するために使われます。
-`fromArray`を使うと、木についての `Arbitrary`インスタンスを書くことができます。
+`toArray`関数と`fromArray`関数は、整列された木と配列を相互に変換できます。
+`fromArray`を使うと、木についての`Arbitrary`インスタンスを書けます。
```haskell
instance (Arbitrary a, Ord a) => Arbitrary (Tree a) where
arbitrary = map fromArray arbitrary
```
-型 `a`についての`Arbitary`インスタンスが使えるなら、テストする性質の引数の型として `Tree a`を使うことができます。例えば、
-`member`テストは値を挿入した後は常に `true`を返すことをテストできます。
+型`a`用に使える`Arbitary`インスタンスがあるなら、テストする性質の引数の型として`Tree a`を使えます。
+例えば、`member`による木の確認については、値を挿入した後は常に`true`を返すことをテストできます。
```haskell
quickCheck \t a ->
@@ -287,16 +284,16 @@ quickCheck \t a ->
1. (普通)`a-z`の範囲から無作為に選ばれた文字の集まりを生成する
`Arbitrary`インスタンスを持つ、`String`のnewtypeを作ってください。
*手掛かり*:`Test.QuickCheck.Gen`モジュールから `elements`と `arrayOf`関数を使います。
- 1. (難しい)木に挿入された値は、どれだけ挿入があった後でも、その木の構成要素であることを主張する性質を書いてください。
+ 1. (難しい)木に挿入された値は、どれだけ沢山の挿入があった後でも、その木の構成要素であることを主張する性質を書いてください。
## 高階関数のテスト
-`Merge`モジュールは `merge`関数の別の一般化も定義しています。
-`mergeWith`関数は追加の関数を引数として取り、統合される要素の順序を決定するのに使われます。
-つまり `mergeWith`は高階関数です。
+`Merge`モジュールは`merge`関数の別の一般化も定義しています。
+`mergeWith`関数は追加の関数を引数として取り、統合される要素の順序を判定します。
+つまり`mergeWith`は高階関数です。
例えば`length`関数を最初の引数として渡し、既に長さの昇順になっている2つの配列を統合できます。
-このとき、結果も長さの昇順になっていなければなりません。
+その結果もまた長さの昇順になっているでしょう。
```haskell
> import Data.String
@@ -309,34 +306,33 @@ quickCheck \t a ->
```
このような関数をテストするにはどうしたらいいでしょうか。
-理想的には、関数である最初の引数を含めた、3つの引数全てについて、値を生成したいと思うでしょう。
+理想的には、関数である最初の引数を含めた3つの引数全てについて、値を生成したいところです。
-関数を無作為に生成できるようにする、もう1つの型クラスがあります。
-この型クラスは `Coarbitrary`と呼ばれており、次のように定義されています。
+無作為に生成された関数を作れるようにする、2つ目の型クラスがあります。
+`Coarbitrary`という名前で次のように定義されています。
```haskell
class Coarbitrary t where
coarbitrary :: forall r. t -> Gen r -> Gen r
```
-`coarbitrary`関数は、型 `t`と、関数の結果の型 `r`についての乱数生成器を関数の引数としてとり、乱数生成器を _かき乱す_
-のにこの引数を使います。つまり関数の引数を使って、乱数生成器の無作為な出力を変更しているのです。
+`coarbitrary`関数は、型`t`の関数の引数と、型`r`の関数の結果の乱数生成器を取ります。
+この関数引数を使って乱数生成器を*かき乱し*ます。
+つまり、関数の引数を使って乱数生成器の無作為な出力を変更し、結果としているのです。
-また、もし関数の定義域が `Coarbitrary`で、値域が
-`Arbitrary`なら、`Arbitrary`の関数を与える型クラスインスタンスが存在します。
+また、もし関数の定義域が`Coarbitrary`で値域が`Arbitrary`なら、`Arbitrary`の関数を与える型クラスインスタンスが存在します。
```haskell
instance (Coarbitrary a, Arbitrary b) => Arbitrary (a -> b)
```
-実は、これが意味しているのは、引数として関数を取るような性質を記述できるということです。
+実際のところ、引数として関数を取るような性質を記述できます。
`mergeWith`関数の場合では、新しい引数を考慮するようにテストを修正すると、最初の引数を無作為に生成できます。
結果が整列されていることは保証できません。
-必ずしも`Ord`インスタンスを持っているとさえ限らないのです。
-しかし、引数として渡す関数 `f`に従って結果が整列されていることは期待されます。
-更に、2つの入力配列が `f`に従って整列されている必要がありますので、`sortBy`関数を使って関数 `f`が適用されたあとの比較に基づいて
-`xs`と`ys`を整列します。
+`Ord`インスタンスを持っているとさえ限らないのです。
+しかし、引数として渡す関数`f`に従って結果が整列されていることは期待されます。
+更に、2つの入力配列が`f`に従って整列されている必要がありますので、`sortBy`関数を使って関数`f`が適用されたあとの比較に基づいて`xs`と`ys`を整列します。
```haskell
quickCheck \xs ys f ->
@@ -366,8 +362,8 @@ intToBool = id
instance (Arbitrary a, Coarbitrary b) => Coarbitrary (a -> b)
```
-これは値の生成が単純な関数だけに限定されるものではないことを意味しています。
-つまり*高階関数*や、引数が高階関数であるような関数もまた、無作為に生成できるのです。
+つまり値や関数だけに制限されません。
+*高階関数*や、引数が高階関数であるような関数やその他諸々もまた、無作為に生成できるのです。
## Coarbitraryのインスタンスを書く
@@ -382,8 +378,8 @@ instance (Arbitrary a, Coarbitrary b) => Coarbitrary (a -> b)
instance Coarbitrary a => Coarbitrary (Tree a) where
```
-型 `Tree a`の値が与えられたときに、乱数発生器をかき乱す関数を記述する必要があります。
-入力値が `Leaf`であれば、そのままにしておく生成器を返します。
+型`Tree a`の値が与えられたときに、乱数発生器をかき乱す関数を記述する必要があります。
+入力値が`Leaf`であれば、そのままにしておく生成器を返します。
```haskell
coarbitrary Leaf = id
@@ -400,14 +396,15 @@ instance Coarbitrary a => Coarbitrary (Tree a) where
```
これで、木を引数にとるような関数を引数に含む性質を自由に書くことができるようになりました。
-例えば`Tree`モジュールでは`anywhere`が定義されており、これは述語が引数のどんな部分木についても成り立っているかを調べる関数です。
+例えば`Tree`モジュールでは関数`anywhere`が定義されています。
+これは述語が引数のどんな部分木についても満たされるかを調べます。
```haskell
anywhere :: forall a. (Tree a -> Boolean) -> Tree a -> Boolean
```
-これで、この述語関数`anywhere`を無作為に生成できるようになりました。
-例えば、 `anywhere`関数は*ある命題のもとで不変*であることが期待されます。
+今となっては述語関数を無作為に生成できます。
+例えば、`anywhere`関数は*選言の法則を満たす*ことが期待されます。
```haskell
quickCheck \f g t ->
@@ -424,9 +421,9 @@ treeOfInt = id
## 副作用のないテスト
-通常、テストの目的ではテストスイートの `main`アクションに`quickCheck`関数の呼び出しが含まれています。
-しかし、副作用を使わない`quickCheckPure`と呼ばれる `quickCheck`関数の亜種もあります。
-`quickCheckPure`は、入力として乱数の種をとり、テスト結果の配列を返す純粋な関数です。
+通常、テストの目的ではテストスイートの`main`動作に`quickCheck`関数の呼び出しが含まれています。
+しかし`quickCheck`関数には亜種があり、`quickCheckPure`という名前です。
+副作用を使わない代わりに、入力として乱数の種を取ってテスト結果の配列を返す純粋な関数です。
PSCiを使用して `quickCheckPure`を試せます。
ここでは `merge`操作が結合法則を満たすことをテストします。
@@ -446,7 +443,7 @@ PSCiを使用して `quickCheckPure`を試せます。
Success : Success : ...
```
-`quickCheckPure`は乱数の種、生成するテスト項目数、テストする性質の3つの引数をとります。
+`quickCheckPure`は乱数の種、生成するテスト項目数、テストする性質の3つの引数を取ります。
もし全てのテスト項目が成功したら、`Success`データ構築子の配列がコンソールに出力されます。
`quickCheckPure`は、性能ベンチマークの入力データ生成や、webアプリケーションのフォームデータ例を無作為に生成するというような状況で便利かもしれません。
@@ -462,11 +459,11 @@ Success : Success : ...
data OneTwoThree a = One a | Two a a | Three a a a
```
- *手掛かり*:`Test.QuickCheck.Gen`で定義された `oneOf`関数を使って `Arbitrary`インスタンスを定義してください。
- 1. (普通)`all`を使って `quickCheckPure`関数の結果を単純化してください。
+ *手掛かり*:`Test.QuickCheck.Gen`で定義された`oneOf`関数を使って`Arbitrary`インスタンスを定義してください。
+ 1. (普通)`all`を使って`quickCheckPure`関数の結果を単純化してください。
この新しい関数は型`List Result -> Boolean`を持ち、全てのテストが通れば`true`を、そうでなければ`false`を返します。
2. (普通)`quickCheckPure`の結果を単純にする別の手法として、関数`squashResults :: List Result -> Result`を書いてみてください。
- `Data.Maybe.First`の`First`モノイドと共に`foldMap`関数を使うことで、失敗した場合の最初のエラーを保存することを検討してください。
+ `Data.Maybe.First`の`First`モノイドと共に`foldMap`関数を使うことで、失敗した場合の最初のエラーを保持することを検討してください。
## まとめ
@@ -474,7 +471,6 @@ Success : Success : ...
これを使うと*生成的テスティング*のパラダイムを使って、宣言的な方法でテストを書くことができました。具体的には以下です。
- `spago test`を使ってQuickCheckのテストを自動化する方法を見ました。
-- 性質を関数として書く方法とエラーメッセージを改良する `>`演算子の使い方を説明しました。
-- `Arbitrary`と
- `Coarbitrary`型クラスによって、如何にして定型的なテストコードの自動生成を可能にし、またどうすれば高階な性質関数が可能になるかを見てきました。
+- 性質を関数として書く方法とエラー文言を改良する`>`演算子の使い方を説明しました。
+- `Arbitrary`と`Coarbitrary`型クラスによって定型的なテストコードの自動生成を可能にする方法や、高階な性質のテストを可能にする方法を見ました。
- 独自のデータ型に対して `Arbitrary`と `Coarbitrary`インスタンスを実装する方法を見ました。
diff --git a/text-ja/chapter14.md b/text-ja/chapter14.md
index 96dff977..d8f13a6c 100644
--- a/text-ja/chapter14.md
+++ b/text-ja/chapter14.md
@@ -5,23 +5,23 @@
この章では多数の標準的な手法を使い、PureScriptにおける*領域特化言語*(または*DSL*)の実装について探求していきます。
領域特化言語とは、特定の問題領域での開発に適した言語のことです。
-領域特化言語の構文及び機能は、その領域内の考え方を表現するコードの読みやすさを最大限に発揮すべく選択されます。
+構文及び機能は、その領域内の考え方を表現するに使われるコードの読みやすさを最大化すべく選択されます。
本書の中では、既に領域特化言語の例を幾つか見てきています。
-- 第11章で開発された `Game`モナドと関連するアクションは、 _テキストアドベンチャーゲーム開発_
- という領域に対しての領域特化言語を構成しています。
-- 第13章で扱った `quickcheck`パッケージは、 _生成的テスティング_
- の領域の領域特化言語です。このコンビネータはテストの性質に対して特に表現力の高い記法を可能にします。
+- 第11章で開発された`Game`モナドと関連する動作は、*テキストアドベンチャーゲーム開発*という領域に対しての領域特化言語を構成しています。
+- 第13章で扱った`quickcheck`パッケージは、*生成的テスティング*の領域に向けた領域特化言語です。
+ このコンビネータはテストの性質に対して特に表現力の高い記法を可能にします。
-この章では、領域特化言語の実装において、幾つかの標準的な技法による構造的な手法に迫ります。
-これがこの話題の完全な説明だということでは決してありませんが、自分の目的に合う具体的なDSLを構築するのには充分な知識をもたらすことでしょう。
+この章では、領域特化言語の実装において、幾つかの標準的な技法にについて構造的な手法を取ります。
+この話題の完全な解説では決してありませんが、目的に合う実践的なDSLを構築するのに充分な知識は得られるでしょう。
-この章で実行している例は、HTML文書を作成するための領域特化言語です。
+ここでの実行例はHTML文書を作成するための領域特化言語です。
正しいHTML文書を記述するための型安全な言語を開発することが目的で、素朴な実装を徐々に改善しつつ進めていきます。
## プロジェクトの準備
-この章で使うプロジェクトには新しい依存性が1つ追加されます。これから使う道具の1つである*Freeモナド*が定義されている `free`ライブラリです。
+この章に付随するプロジェクトには新しい依存性が1つ追加されます。
+これから使う道具の1つである*Freeモナド*が定義されている`free`ライブラリです。
このプロジェクトをPSCiを使って試していきます。
@@ -47,9 +47,9 @@ newtype Attribute = Attribute
}
```
-`Element`型はHTMLの要素を表しています。
-各要素は要素名、属性の対の配列と、要素の内容で構成されています。
-contentプロパティは、`Maybe`タイプを適切に使って、要素が開いている(他の要素やテキストを含む)か閉じているかを示しています。
+`Element`型はHTMLの要素を表します。
+各要素は要素名、属性の対の配列と、内容で構成されます。
+内容のプロパティには`Maybe`型を適切に使い、要素が開いている(他の要素やテキストを含む)か閉じているかを示します。
このライブラリの鍵となる機能は次の関数です。
@@ -89,7 +89,7 @@ unit
現状のライブラリには幾つもの問題があります。
-- HTML文書の作成に手がかかります。
+- HTML文書の作成に手が掛かります。
全ての新しい要素に少なくとも1つのレコードと1つのデータ構築子が必要です。
- 無効な文書を表現できてしまいます。
- 開発者が要素名の入力を間違えるかもしれません
@@ -100,9 +100,9 @@ unit
## スマート構築子
-最初に導入する手法は方法こそ単純なものですが、とても効果的です。
-モジュールの使用者にデータの表現を露出する代わりに、モジュールエクスポートリストを使ってデータ構築子 `Element`、 `Content`、
-`Attribute`を隠蔽し、正しいことが明らかなデータだけ構築する、いわゆる*スマート構築子*だけをエクスポートします。
+最初に導入する手法は単純ですがとても効果的です。
+モジュールの使用者にデータの表現を露出する代わりに、モジュールエクスポートリストを使ってデータ構築子`Element`、`Content`、`Attribute`を隠蔽します。
+そして正しいことが分かっているデータを構築する、いわゆる*スマート構築子*だけをエクスポートします。
例を示しましょう。まず、HTML要素を作成するための便利な関数を提供します。
@@ -115,8 +115,8 @@ element name attribs content = Element
}
```
-次に、欲しいHTML要素を利用者が作れるように、スマート構築子を作成します。
-これには`element`関数を適用します。
+次にHTML要素のためのスマート構築子を作成します。
+この要素は利用者が`element`関数を適用して作成できるようになってほしいものです。
```haskell
a :: Array Attribute -> Array Content -> Element
@@ -150,21 +150,22 @@ module Data.DOM.Smart
- 値(ないし関数)。その値の名前により指定されます。
- 型クラス。クラス名により指定されます。
-- 型構築子と関連するデータ構築子。型名とそれに続くエクスポートされるデータ構築子の括弧で囲まれたリストで指定されます。
+- 型構築子とそれに紐付くデータ構築子。
+ 型名とそれに続くエクスポートされるデータ構築子の括弧で囲まれたリストで指定されます。
-ここでは、 `Element`の*型*をエクスポートしていますが、データ構築子はエクスポートしていません。
-もしデータ構築子をエクスポートすると、モジュールの使用者が不正なHTML要素を構築できてしまいます。
+ここでは、`Element`の*型*をエクスポートしていますが、データ構築子はエクスポートしていません。
+もしデータ構築子をエクスポートすると、使用者が不正なHTML要素を構築できてしまいます。
`Attribute`と `Content`型についてはデータ構築子を全てエクスポートしています(エクスポートリストの記号 `..`で示されています)。
すぐ後で、これらの型にもスマート構築子の手法を適用していきます。
既にライブラリに幾つもの大きな改良が加わっていることに注目です。
-- 不正な名前を持つHTML要素は表現できません(もちろん、ライブラリが提供する要素名に制限されています)。
+- 不正な名前を持つHTML要素は表現できません(勿論ライブラリが提供する要素名に制限されています)。
- 閉じた要素は構築するときに内容を含められません。
`Content`型にとても簡単にこの手法を適用できます。
-単にエクスポートリストから `Content`型のデータ構築子を取り除き、次のスマート構築子を提供します。
+単にエクスポートリストから`Content`型のデータ構築子を取り除き、次のスマート構築子を提供します。
```haskell
text :: String -> Content
@@ -188,8 +189,8 @@ attribute key value = Attribute
infix 4 attribute as :=
```
-この定義では元の `Element`型と同じ問題に直面しています。
-存在しなかったり、名前が間違っているような属性を表現できます。
+この定義では元の`Element`型と同じ問題に直面しています。
+存在しなかったり、名前が間違って入力された属性を表現できます。
この問題を解決するために、属性名を表すnewtypeを作成します。
```haskell
@@ -269,15 +270,14 @@ $ spago repl
unit
```
-しかし、基盤をなすデータ表現は変更されなかったので、 `render`関数を変更する必要はなかったことにも注目してください。
+しかし、基盤をなすデータ表現は全く変更されなかったので、`render`関数を変更する必要はなかったことにも注目してください。
これはスマート構築子による手法の利点のひとつです。
-外部APIの使用者によって認識される表現からモジュールの内部データ表現を分離できるのです。
+外部APIの使用者によって認識される表現から、モジュールの内部データ表現を分離できるのです。
## 演習
1. (簡単)`Data.DOM.Smart`モジュールで `render`を使った新しいHTML文書の作成を試してみましょう。
- 1. (普通)`checked`と `disabled`など、値を要求しないHTML属性がありますが、これらは次のような _空の属性_
- として表示されるかもしれません。
+ 1. (普通)`checked`や`disabled`といったHTML属性は値を要求せず、*空の属性*として書き出せます。
```html
@@ -388,11 +388,10 @@ unit
## 演習
- 1. (簡単)ピクセルまたはパーセントの長さの何れかを表すデータ型を作成してください。
- その型について `IsValue`のインスタンスを書いてください。
- この型を使うように `width`と `height`属性を変更してください。
- 1. (難しい)幻影型を使って真偽値 `true`、 `false`用の最上位の表現を定義することで、 `AttributeKey`が
- `disabled`や `checked`のような*空の属性*を表現しているかどうかをエンコードできます。
+ 1. (簡単)ピクセルまたはパーセントの何れかの長さを表すデータ型を作成してください。
+ その型について`IsValue`のインスタンスを書いてください。
+ この新しい型を使うように`width`と`height`属性を変更してください。
+ 1. (難しい)真偽値`true`、`false`用の最上位の表現を定義することで、幻影型を使って`AttributeKey`が`disabled`や`checked`のような*空の属性*を表現しているかどうかをエンコードできます。
```haskell
data True
@@ -403,8 +402,9 @@ unit
## Freeモナド
-APIに施す最後の変更は、 `Content`型をモナドにしてdo記法を使えるようにするために、 _Freeモナド_
-と呼ばれる構造を使うことです。これによって入れ子になった要素がわかりやすくなるようにHTML文書を構造化できます。以下の代わりに……
+APIに施す最後の変更では、`Content`型をモナドにしてdo記法を使えるようにするために、*Freeモナド*と呼ばれる構造を使っていきます。
+これによって入れ子になった要素がわかりやすくなるような形式でHTML文書を構造化できます。
+以下の代わりに……
```haskell
p [ _class := "main" ]
@@ -429,10 +429,10 @@ p [ _class := "main" ] $ do
text "A cat"
```
-しかし、do記法だけがFreeモナドの恩恵だというわけではありません。Freeモナドがあれば、モナドのアクションの _表現_ をその _解釈_
-から分離し、同じアクションに _複数の解釈_ を持たせることさえできます。
+しかし、do記法だけがFreeモナドの恩恵ではありません。
+Freeモナドがあれば、モナドの動作の*表現*をその*解釈*から分離し、同じ動作に*複数の解釈*を持たせることさえできます。
-`Free`モナドは `free`ライブラリの `Control.Monad.Free`モジュールで定義されています。
+`Free`モナドは`free`ライブラリの`Control.Monad.Free`モジュールで定義されています。
PSCiを使うと、次のようにFreeモナドについての基本的な情報を見ることができます。
```text
@@ -443,12 +443,12 @@ PSCiを使うと、次のようにFreeモナドについての基本的な情報
```
`Free`の種は、引数として型構築子を取り、別の型構築子を返すことを示しています。
-実は、`Free`モナドを使えば任意の`Functor`を`Monad`にできます。
+実はなんと、`Free`モナドを使えば任意の`Functor`を`Monad`にできるのです。
-モナドのアクションの*表現*の定義から始めます。
-こうするには、対応する各モナドアクションそれぞれについて、1つのデータ構築子を持つ `Functor`を作成する必要があります。
-今回の場合、2つのモナドのアクションは `elem`と `text`になります。
-実際には、 `Content`型を次のように変更するだけです。
+モナドの動作の*表現*の定義から始めます。
+これには対応したい各モナド動作について、1つのデータ構築子を持つ`Functor`を作成する必要があります。
+今回の場合、2つのモナドの動作は`elem`と`text`になります。
+`Content`型を次のように変更するだけでできます。
```haskell
data ContentF a
@@ -460,9 +460,9 @@ instance Functor ContentF where
map f (ElementContent e x) = ElementContent e (f x)
```
-ここで、この `ContentF`型構築子は以前の `Content`データ型とよく似ています。
+ここで、この`ContentF`型構築子は以前の`Content`データ型とよく似ています。
しかし、ここでは型引数`a`を取り、それぞれのデータ構築子は型`a`の値を追加の引数として取るように変更されています。
-`Functor`インスタンスでは、単に各データ構築子で型 `a`の構成要素に関数 `f`を適用します。
+`Functor`インスタンスでは、単に各データ構築子で型`a`の値に関数`f`を適用します。
これにより、新しい`Content`モナドを`Free`モナド用の型シノニムとして定義できます。
これは最初の型引数として `ContentF`型構築子を使うことで構築されます。
@@ -471,8 +471,8 @@ instance Functor ContentF where
type Content = Free ContentF
```
-型シノニムの代わりにnewtypeを使用して、使用者に対してライブラリの内部表現を露出することを避けられます。
-`Content`データ構築子を隠すことで、提供しているモナドのアクションだけを使うことを使用者に制限しています。
+型同義語の代わりに`newtype`を使用して、使用者に対してライブラリの内部表現を露出することを避けられます。
+`Content`データ構築子を隠すことで、提供しているモナドの動作だけを使うことを使用者に制限しています。
`ContentF`は `Functor`なので、 `Free ContentF`用の`Monad`インスタンスが自動的に手に入ります。
@@ -487,16 +487,16 @@ newtype Element = Element
}
```
-また、 `Content`モナドについての新しいモナドのアクションになる `elem`と `text`関数を変更する必要があります。
+また、`Content`モナドについての新しいモナドの動作になるよう、`elem`と`text`関数を変更する必要があります。
これには`Control.Monad.Free`モジュールで提供されている `liftF`関数が使えます。
-この関数の型は次のようになっています。
+以下がその型です。
```haskell
liftF :: forall f a. f a -> Free f a
```
-`liftF`により、何らかの型 `a`について、型 `f a`の値からFreeモナドのアクションを構築できるようになります。
-今回の場合、 `ContentF`型構築子のデータ構築子をそのまま使うだけです。
+`liftF`により、何らかの型`a`について、型`f a`の値からFreeモナドの動作を構築できるようになります。
+今回の場合、`ContentF`型構築子のデータ構築子をそのまま使えます。
```haskell
text :: String -> Content Unit
@@ -529,18 +529,17 @@ runFreeM
-> m a
```
-`runFree`関数は、 _純粋な_ 結果を計算するために使用されます。
-`runFreeM`関数があればFreeモナドのアクションを解釈するためにモナドが使えます。
+`runFree`関数は、*純粋な*結果を計算するために使用されます。
+`runFreeM`関数があればFreeモナドの動作を解釈するためにモナドが使えます。
-*補足*:厳密には、より強い`MonadRec`制約を満たすモナド `m`を使用するよう制限されています。
-実際には、これはスタックオーバーフローを心配する必要がないことを意味します。
-なぜなら `m`は安全な*末尾再帰モナド*に対応しているからです。
+*補足*:厳密には、より強い`MonadRec`制約を満たすモナド`m`に制限されています。
+実際、ら`m`は安全な*末尾再帰モナド*に対応してため、スタックオーバーフローを心配する必要はありません。
-まず、アクションを解釈できるモナドを選ばなければなりません。
-`Writer String`モナドを使って、結果のHTML文字列を累積することにします。
+まず、動作を解釈できるモナドを選ばなければなりません。
+`Writer String`モナドを使って、結果のHTML文字列を累算することにします。
新しい`render`メソッドが開始すると、補助関数
-`renderElement`に移譲し、`execWriter`を使って`Writer`モナドで計算を走らせます。
+`renderElement`に移譲し、`execWriter`を使って`Writer`モナドで計算します。
```haskell
render :: Element -> String
@@ -555,7 +554,7 @@ render = execWriter <<< renderElement
renderElement (Element e) = do
```
-`renderElement`の定義は直感的で、複数の小さな文字列を累積するために `Writer`モナドの `tell`アクションを使っています。
+`renderElement`の定義は直感的で、複数の小さな文字列を累算するために`Writer`モナドの`tell`動作を使っています。
```haskell
tell "<"
@@ -602,7 +601,7 @@ render = execWriter <<< renderElement
renderContentItem :: ContentF (Content Unit) -> Writer String (Content Unit)
```
-`ContentF`の2つのデータ構築子でパターン照合するだけでこの関数を実装できます。
+`ContentF`の2つのデータ構築子でパターン照合すればこの関数を実装できます。
```haskell
renderContentItem (TextContent s rest) = do
@@ -613,8 +612,8 @@ render = execWriter <<< renderElement
pure rest
```
-それぞれの場合において、式 `rest`は型 `Content Unit`を持っており、解釈計算の残りを表しています。
-`rest`アクションを呼び出すことによってそれぞれの場合を完了できます。
+それぞれの場合において、式`rest`は型`Content Unit`を持っており、解釈された計算の残りを表しています。
+`rest`動作を返すことでそれぞれの場合を完成できます。
できました。
PSCiで、次のようにすれば新しいモナドのAPIを試すことができます。
@@ -636,28 +635,26 @@ unit
## 演習
- 1. (普通)`ContentF`型に新しいデータ構築子を追加して、生成されたHTMLにコメントを出力する新しいアクション
- `comment`に対応してください。
- `liftF`を使ってこの新しいアクションを実装してください。
- 新しい構築子を適切に解釈するように、解釈 `renderContentItem`を更新してください。
+ 1. (普通)`ContentF`型に新しいデータ構築子を追加して、生成されたHTMLにコメントを出力する新しい動作`comment`に対応してください。
+ `liftF`を使ってこの新しい動作を実装してください。
+ 新しい構築子を適切に解釈するように、解釈`renderContentItem`を更新してください。
## 言語の拡張
-全てのアクションが型 `Unit`の何かを返すようなモナドは、さほど興味深いものではありません。
-実際のところ、概ね良くなったと思われる構文は別として、このモナドは `Monoid`以上の機能を何ら追加していません。
+全動作が型`Unit`の何かを返すようなモナドは、さほど興味深いものではありません。
+実際のところ、概ね良くなったと思われる構文は別として、このモナドは`Monoid`以上の機能を何ら追加していません。
-非自明な結果を返す新しいモナドアクションでこの言語を拡張することで、Freeモナド構造の威力をお見せしましょう。
+非自明な結果を返す新しいモナド動作でこの言語を拡張することで、Freeモナドを構築する威力をお見せしましょう。
-*アンカー*を使用して文書のさまざまな節へのハイパーリンクが含まれているHTML文書を生成するとします。
-これは既に達成できています。
-手作業でアンカーの名前を生成して文書中で少なくとも2回それらを含めればよいのです。
-1つはアンカーの定義自身に、もう1つはそれぞれのハイパーリンクにあります。
-しかし、この方法には根本的な問題が幾つかあります。
+*アンカー*を使用して文書の様々な節へのハイパーリンクが含まれているHTML文書を生成したいとします。
+手作業でアンカーの名前を生成して文書中で少なくとも2回それらを含めれば、これは達成できます。
+1つはアンカーの定義自身に、もう1つは各ハイパーリンクにあります。
+しかし、この手法には基本的な問題が幾つかあります。
- 開発者が一意なアンカー名の生成をし損なうかもしれません。
- 開発者がアンカー名を1つ以上の箇所で打ち間違うかもしれません。
-開発者が誤ちを犯すことを防ぐために、アンカー名を表す新しい型を導入し、新しい一意な名前を生成するためのモナドアクションを提供できます。
+開発者が誤ちを犯すことを防ぐために、アンカー名を表す新しい型を導入し、新しい一意な名前を生成するためのモナド動作を提供できます。
最初の工程は名前の型を新しく追加することです。
@@ -671,7 +668,7 @@ runName (Name n) = n
繰り返しになりますが、`Name`は
`String`のnewtypeとして定義しているものの、モジュールのエクスポートリスト内でデータ構築子をエクスポートしないように注意する必要があります。
-次に、属性値として `Name`を使うことができるように、新しい型に`IsValue`型クラスのインスタンスを定義します。
+次に、属性値に`Name`を使えるよう、新しい型に`IsValue`型クラスのインスタンスを定義します。
```haskell
instance IsValue Name where
@@ -711,7 +708,9 @@ data ContentF a
| NewName (Name -> a)
```
-`NewName`データ構築子は型 `Name`の値を返すアクションに対応しています。データ構築子の引数として `Name`を要求するのではなく、型 `Name -> a`の _関数_ を提供するように使用者に要求していることに注意してください。型 `a`は _計算の残り_ を表していることを思い出すと、この関数は、型 `Name`の値が返されたあとで、計算を継続する方法を提供しているのだとわかります。
+`NewName`データ構築子は型`Name`の値を返す動作に対応しています。
+データ構築子の引数として`Name`を要求するのではなく、型`Name -> a`の*関数*を提供するように使用者に要求していることに注意してください。
+型`a`は*計算の残り*を表していることを思い出すと、この関数は、型`Name`の値が返されたあとで、計算を継続する方法を提供しているのだとわかります。
新しいデータ構築子を考慮するよう、次のように`ContentF`用の`Functor`インスタンスを更新する必要もあります。
@@ -722,7 +721,7 @@ instance Functor ContentF where
map f (NewName k) = NewName (f <<< k)
```
-これで、以前と同じように`liftF`関数を使って新しいアクションを構築できます。
+これで、以前と同じように`liftF`関数を使って新しい動作を構築できます。
```haskell
newName :: Content Name
@@ -730,13 +729,13 @@ newName = liftF $ NewName id
```
`id`関数を継続として提供していることに注意してください。
-これは型 `Name`の結果を変更せずに返すということを意味しています。
+つまり型`Name`の結果を変更せずに返しています。
-最後に、新しいアクションを解釈するために解釈関数を更新する必要があります。
-以前は計算を解釈するために `Writer
-String`モナドを使っていましたが、このモナドは新しい名前を生成する能力を持っていないので、何か他のものに切り替えなければなりません。
-`WriterT`モナド変換子を`State`モナドと一緒に使うと、必要な作用を組み合わせることができます。
-型注釈を短く保てるように、この解釈モナドを型同義語として定義しておきます。
+最後に、新しい動作を解釈させるように解釈関数を更新する必要があります。
+以前は計算を解釈するために`Writer
+String`モナドを使っていましたが、このモナドは新しい名前を生成できないので、何か他のものに切り替えなければなりません。
+`WriterT`モナド変換子を`State`モナドと一緒に使うと、必要な作用を組み合わせられます。
+型注釈が短く保たれるよう、この解釈モナドを型同義語として定義できます。
```haskell
type Interp = WriterT String (State Int)
@@ -744,10 +743,10 @@ type Interp = WriterT String (State Int)
ここで、`Int`型の状態は増加していくカウンタとして振舞い、一意な名前を生成するのに使われます。
-`Writer`と `WriterT`モナドはそれらのアクションを抽象化するのに同じ型クラスメンバを使うので、どのアクションも変更する必要がありません。
-必要なのは、 `Writer String`への参照全てを `Interp`で置き換えることだけです。
-しかし、これを計算するために使われる制御子を変更しなければいけません。
-こうなると単なる`execWriter`の代わりに、ここでも`evalState`を使う必要があります。
+`Writer`と`WriterT`モナドはそれらの動作を抽象化するのに同じ型クラスの構成要素を使うので、どの動作も変更する必要がありません。
+必要なのは、`Writer String`への参照全てを`Interp`で置き換えることだけです。
+しかし、計算に使われる制御子は変更する必要があります。
+単なる`execWriter`の代わりに、`evalState`も使う必要があります。
```haskell
render :: Element -> String
@@ -769,7 +768,7 @@ renderContentItem (NewName k) = do
`get`を使って状態を読み、その状態を使って一意な名前を生成し、それから `put`で状態に1だけ足すのです。
最後に、継続にこの新しい名前を渡して、計算を完了します。
-以上をもって、この新しい機能をPSCiで試すことができます。
+以上をもって、この新しい機能をPSCiで試せます。
これには`Content`モナドの内部で一意な名前を生成し、要素の名前とハイパーリンクのリンク先の両方として使います。
```text
@@ -790,38 +789,40 @@ renderContentItem (NewName k) = do
unit
```
-複数回の `newName`の呼び出しの結果が、実際に一意な名前になっていることも確かめられます。
+複数回の`newName`の呼び出しの結果が、実際に一意な名前になっていることも確かめられます。
## 演習
1. (普通)使用者から `Element`型を隠蔽すると、更にAPIを簡素にできます。
次の手順に従って、これらの変更を加えてください。
- - `p`や `img`のような(返る型が `Element`の)関数を `elem`アクションと結合して、型 `Content
- Unit`を返す新しいアクションを作ってください。
+ - `p`や`img`のような(返る型が`Element`の)関数を`elem`動作と結合して、型`Content
+ Unit`を返す新しい動作を作ってください。
- `Element`の代わりに型`Content Unit`の引数を受け付けるように`render`関数を変更してください。
- 1. (普通)型同義語の代わりに`newtype`を使うことによって`Content`モナドの実装を隠してください。
+ 1. (普通)型同義語の代わりに`newtype`を使って`Content`モナドの実装を隠してください。
`newtype`用のデータ構築子はエクスポートすべきではありません。
- 1. (難しい)`ContentF`型を変更して以下の新しいアクションに対応してください。
+ 1. (難しい)`ContentF`型を変更して以下の新しい動作に対応してください。
```haskell
isMobile :: Content Boolean
```
- このアクションは、この文書がモバイルデバイス上での表示のためにレンダリングされているかどうかを示す真偽値を返します。
+ この動作は、この文書がモバイルデバイス上での表示のために描画されているかどうかを示す真偽値を返します。
- *手掛かり*:`ask`アクションと`ReaderT`モナド変換子を使って、このアクションを解釈してください。
+ *手掛かり*:`ask`動作と`ReaderT`モナド変換子を使って、この動作を解釈してください。
あるいは、`RWS`モナドを使うほうが好みの人もいるかもしれません。
## まとめ
この章では、幾つかの標準的な技術を使って、素朴な実装を段階的に改善することにより、HTML文書を作成するための領域特化言語を開発しました。
-- _スマート構築子_ を使ってデータ表現の詳細を隠し、利用者には _構築により正しい_ 文書だけを作ることを許しました。
+- *スマート構築子*を使ってデータ表現の詳細を隠し、利用者には*構築により正しい*文書だけを作ることを許しました。
- *独自に定義された中置2引数演算子*を使い、言語の構文を改善しました。
-- _幻影型_ を使ってデータの型の中に追加の情報を折り込みました。これにより利用者が誤った型の属性値を与えることを防いでいます。
-- _Freeモナド_
- を使って内容の集まりの配列表現をdo記法に対応したモナドな表現に変えました。それからこの表現を新しいモナドアクションに対応するよう拡張し、標準モナド変換子を使ってモナドの計算を解釈しました。
+- *幻影型*を使ってデータの型の中に追加の情報を折り込みました。
+ これにより利用者が誤った型の属性値を与えることを防いでいます。
+- *Freeモナド*を使って内容の集まりの配列表現をdo記法に対応したモナドな表現に変えました。
+ それからこの表現を新しいモナド動作に対応するよう拡張し、標準的なモナド変換子を使ってモナドの計算を解釈しました。
これらの手法は全て、使用者が間違いを犯すのを防いだり領域特化言語の構文を改良したりするために、PureScriptのモジュールと型システムを活用しています。
-関数型プログラミング言語による領域特化言語の実装は活発に研究されている分野ですが、幾つかの簡単な技法に対して役に立つ導入を提供し、表現力豊かな型を持つ言語で作業することの威力を示すことができていれば幸いです。
+関数型プログラミング言語による領域特化言語の実装は活発に研究されている分野です。
+それでも、幾つかの単純な技法に対して役に立つ導入を提供し、表現力豊かな型を持つ言語で作業することの威力を示すことができていれば幸いです。
diff --git a/text-ja/chapter2.md b/text-ja/chapter2.md
index 2166389b..7175c813 100644
--- a/text-ja/chapter2.md
+++ b/text-ja/chapter2.md
@@ -2,8 +2,8 @@
## この章の目標
-この章では実際のPureScriptの開発環境を立ち上げ、幾つかの演習を解き、この本で提供されているテストを使って答えを確認します。
-もし映像を見る学習の仕方が合っているようでしたら、[この章を通しで進めるビデオ](https://www.youtube.com/watch?v=GPjPwb6d-70)が役に立つでしょう。
+本章では実際のPureScriptの開発環境を立ち上げ、幾つかの演習を解き、本書で提供されているテストを使って答えを確認します。
+もし映像を見る学習の仕方が合っているようでしたら、[本章を通しで進めるビデオ](https://www.youtube.com/watch?v=GPjPwb6d-70)が役に立つでしょう。
## 環境構築
@@ -22,13 +22,16 @@ PureScriptを書く上で(例えば本書の演習を解くなど)お好み
## 演習を解く
-ここまでで必要な開発ツールをインストールできているので、この本のリポジトリをクローンしてください。
+ここまでで必要な開発ツールをインストールできているので、本書のリポジトリをクローンしてください。
```sh
git clone https://github.com/purescript-contrib/purescript-book.git
```
-本のリポジトリにはPureScriptのコード例とそれぞれの章に付属する演習のための単体テストが含まれます。演習の解法を白紙に戻すために必要な初期設定があり、この設定をすることで解く準備ができます。この工程は`resetSolutions.sh`スクリプトを使えば簡単にできます。また`removeAnchors.sh`スクリプトで全てのアンカーコメントを取り除いておくのもよいでしょう(これらのアンカーはコードスニペットを本の変換後のMarkdownにコピーするために使われており、自分のローカルリポジトリではこのアンカーで散らかっていないほうがよいでしょう)。
+本書のリポジトリにはPureScriptのコード例と各章に付属する演習のための単体テストが含まれます。
+演習の解法を白紙に戻すために必要な初期設定があり、この設定をすることで解く準備ができます。
+この工程は`resetSolutions.sh`スクリプトを使えば簡単にできます。
+また`removeAnchors.sh`スクリプトで全てのアンカーコメントを取り除いておくと良いでしょう(これらのアンカーはコード片を本書のMarkdownから書き出した媒体に複製するために使われており、自分のローカルリポジトリではこのアンカーで散らかっていないほうが良いでしょう)。
```sh
cd purescript-book
@@ -55,15 +58,15 @@ spago test
All 2 tests passed! 🎉
```
-なお、`answer`関数(`src/Euler.purs`にあります)は、任意の整数以下の3と5の倍数を見付けるように変更されています。
-この`answer`関数のためのテストスート(`test/Main.purs`にあります)ははじめの手引きの冒頭にあるテストよりも網羅的です。
-はじめの章を読んでいる間はこのテストフレームワークの仕組みを理解しようと思い詰めなくて大丈夫です。
+なお、(`src/Euler.purs`にある)`answer`関数は任意の整数以下の3と5の倍数を見付けるように変更されています。
+(`test/Main.purs`にある)この`answer`関数のためのテストスートははじめの手引きの冒頭にあるテストよりも網羅的です。
+前の方の章を読んでいる間はこのテストフレームワークの仕組みを理解しようと思い詰めなくて大丈夫です。
-本の残りの部分には多くの演習が含まれます。
+本書の残りの部分には多くの演習が含まれます。
`Test.MySolutions`モジュール (`test/MySolutions.purs`)
に自分の解法を書けば、提供されているテストスートを使って確認できます。
-テスト駆動開発のスタイルでこの次の演習を一緒に進めてみましょう。
+テスト駆動開発でこの次の演習を一緒に進めてみましょう。
## 演習
@@ -93,7 +96,8 @@ at test/Main.purs:21:27 - 21:35 (line 21, column 27 - line 21, column 35)
Unknown value diagonal
```
-まずは、この関数が欠陥のあるバージョンになっているときに何が起こるのか見てみましょう。以下のコードを`test/MySolutions.purs`に追加してください。
+まずはこの関数に欠陥があるときに何が起こるのか見てみましょう。
+以下のコードを`test/MySolutions.purs`に追加してください。
```hs
import Data.Number (sqrt)
@@ -147,14 +151,14 @@ All 4 tests passed! 🎉
この章ではPureScriptコンパイラとSpagoツールをインストールしました。
演習の解答の書き方と正しさの確認方法も学びました。
-この先の章にはもっと沢山の演習があり、それらに取り組むうちに学習の助けになっているでしょう。
-演習のどこかでお手上げになったら、この本の[困ったときは](chapter1.ja.md#getting-help)の節に挙げられているコミュニティの資料のどれかを見てみたり、この[本のリポジトリ](https://github.com/purescript-contrib/purescript-book/issues)にイシューを報告したりできます。
-こうした演習の敷居を下げることに繋がる読者のフィードバックが、本の向上の助けになっています。
+この先の章にはもっと沢山の演習があり、それらに取り組むうちに内容を学ぶ助けになっているでしょう。
+演習のどこかでお手上げになったら、本書の[困ったときは](chapter1.ja.md#getting-help)の節に挙げられているコミュニティの資料のどれかに手を伸ばしたり、[本書のリポジトリ](https://github.com/purescript-contrib/purescript-book/issues)でイシューを報告したりできます。
+こうした演習の敷居を下げることに繋がる読者のフィードバックのお陰で本書が改善されています。
章の全ての演習を解いたら、`no-peeking/Solutions.purs`にあるものと解答とを比べられます。
-ただしカンニングしてはだめで、これらの演習を誠実に自力で解く労力を払わないことがないようにしてください。
+カンニングはせず、演習を誠実に自力で解く労力を割いてください。
そしてたとえ行き詰まったにしても、まずはコミュニティメンバーに尋ねてみるようにしてください。
-演習のネタバレをするよりも、小さな手掛かりをあげたいからです。
-もっとエレガントな解法(とはいえ本の内容で押さえられている知識のみを必要とするもの)を見つけたときはPRを送ってください。
+ネタバレをするよりも小さな手掛かりをあげたいからです。
+もっとエレガントな解法(とはいえ本書で押さえられている知識のみで済むもの)を見つけたときはPRを送ってください。
リポジトリは継続して改訂されているため、それぞれの新しい章を始める前に更新を確認するようにしてください。
diff --git a/text-ja/chapter3.md b/text-ja/chapter3.md
index 3e9e8037..b4b8516f 100644
--- a/text-ja/chapter3.md
+++ b/text-ja/chapter3.md
@@ -21,15 +21,19 @@
ここでは、幾つかのモジュールをインポートします。
+- `Prelude`モジュールには標準的な定義と関数の小さな集合が含まれます。
+ `purescript-prelude`ライブラリから多くの基礎的なモジュールを再エクスポートしているのです。
- `Control.Plus`モジュールには`empty`値が定義されています。
-- `Data.List`モジュールは`lists`パッケージで提供されておりSpagoを使ってインストールできます。
- 連結リストを使うために必要な幾つかの関数が含まれています。
-- `Data.Maybe`モジュールは、オプショナルな値を扱うためのデータ型と関数を定義しています。
+- `Data.List`モジュールは`lists`パッケージで提供されています。
+ またこのパッケージはSpagoを使ってインストールできます。
+ モジュールには連結リストを使うために必要な幾つかの関数が含まれています。
+- `Data.Maybe`モジュールは、省略可能な値を扱うためのデータ型と関数を定義しています。
訳者注:ダブルドット (`..`) を使用すると、
指定された型コンストラクタのすべてのデータコンストラクタをインポートできます。
-このモジュールのインポート内容が括弧内で明示的に列挙されていることに注目してください。明示的な列挙はインポート内容の衝突を避けるのに役に立つので、一般に良い習慣です。
+これらのモジュールのインポート内容が括弧内で明示的に列挙されていることに注目してください(`Prelude`は除きます。これは一括インポートされるのが普通です)。
+明示的な列挙はインポート内容の衝突を避けるのに役に立つので、一般に良い習慣です。
ソースコードリポジトリをクローンしたと仮定すると、この章のプロジェクトは次のコマンドでSpagoを使用して構築できます。
@@ -40,10 +44,9 @@ $ spago build
## 単純な型
-JavaScriptのプリミティブ型に対応する組み込みデータ型として、PureScriptでは数値型と文字列型、真偽型の3つが定義されています。
+JavaScriptの原始型に対応する組み込みデータ型として、PureScriptでは数値型と文字列型、真偽型の3つが定義されています。
これらは`Prim`モジュールで定義されており、全てのモジュールに暗黙にインポートされます。
-これらはそれぞれ `Number`、 `String`、
-`Boolean`と呼ばれており、PSCiで`:type`コマンドを使うと簡単な値の型を表示させて確認できます。
+それぞれ`Number`、`String`、`Boolean`と呼ばれており、PSCiで簡単な値の型を表示するのに`:type`コマンドを使うと確認できます。
```text
$ spago repl
@@ -87,7 +90,8 @@ Array Boolean
Could not match type Int with type Boolean.
```
-最後の例で起きているエラーは型検証器によって報告されたもので、配列の2つの要素の型を*単一化*(Unification、等価にする意)しようとして失敗したことを示しています。
+最後の例は型検証器によるエラーを示しています。
+配列の2つの要素の型を*単一化*(つまり等価にする意)するのに失敗したのです。
レコードはJavaScriptのオブジェクトに対応しており、レコード直値はJavaScriptのオブジェクト直値と同じ構文になっています。
@@ -100,8 +104,9 @@ Could not match type Int with type Boolean.
}
```
-この型が示しているのは、指定されたオブジェクトは、 `String`型のフィールド `name` と `Array String`つまり
-`String`の配列の型のフィールド `interests` という2つの _フィールド_ (field) を持っているということです。
+この型が示しているのは指定されたオブジェクトが2つの*フィールド*を持っているということです。
+それぞれ`String`型のフィールド`name`と`Array
+String`型のフィールド`interests`で、後者は`String`の配列ということです。
ドットに続けて参照したいフィールドのラベルを書くとレコードのフィールドを参照できます。
@@ -113,29 +118,22 @@ Could not match type Int with type Boolean.
["Functional Programming","JavaScript"]
```
-PureScriptの関数はJavaScriptの関数に対応しています。PureScriptの標準ライブラリは多くの関数の例を提供しており、この章ではそれらをもう少し詳しく見ていきます。
-
-```text
-> import Prelude
-> :type flip
-forall a b c. (a -> b -> c) -> b -> a -> c
-
-> :type const
-forall a b. a -> b -> a
-```
-
-ファイルのトップレベルでは、等号の直前に引数を指定することで関数を定義できます。
+PureScriptの関数はJavaScriptの関数に対応します。
+関数はファイルの最上位で定義でき、等号の前に引数を指定します。
```haskell
+import Prelude -- (+) 演算子をスコープに持ち込みます
+
add :: Int -> Int -> Int
add x y = x + y
```
-バックスラッシュに続けて空白文字で区切られた引数名のリストを書くことで、関数をインラインでも定義できます。
-PSCiで複数行の宣言を入力するには、 `:paste`コマンドを使用して「貼り付けモード」に入ります。
-このモードでは、*Control-D*キーシーケンスを使用して宣言を終了します。
+代えて、バックスラッシュ文字に続けて空白文字で区切られた引数名のリストを書くことで、関数をインラインでも定義できます。
+PSCiで複数行の宣言を入力するには、`:paste`コマンドを使用して「貼り付けモード」に入ります。
+このモードでは、*Control-D*キーシーケンスを使って宣言を終了します。
```text
+> import Prelude
> :paste
… add :: Int -> Int -> Int
… add = \x y -> x + y
@@ -149,53 +147,11 @@ PSCiでこの関数が定義されていると、次のように関数の隣に2
30
```
-## 量化された型
-
-前の節ではPreludeで定義された関数の型を幾つか見てきました。
-例えば`flip`関数は次のような型を持っていました。
-
-```text
-> :type flip
-forall a b c. (a -> b -> c) -> b -> a -> c
-```
-
-この `forall`キーワードは、 `flip`が _全称量化された型_ (universally quantified type)
-を持っていることを示しています。これは、 `a`や `b`、 `c`をどの型に置き換えても、 `flip`はその型でうまく動作するという意味です。
-
-例えば、 `a`を `Int`、 `b`を `String`、 `c`を `String`というように選んでみたとします。この場合、
-`flip`の型を次のように*特殊化* (specialize) できます。
-
-```text
-(Int -> String -> String) -> String -> Int -> String
-```
-
-量化された型を特殊化したいということをコードで示す必要はありません。
-特殊化は自動的に行われます。
-例えば次のように単に`flip`を使用できます。
-あたかも既にその型の`flip`を持っていたかのようです。
-
-```text
-> flip (\n s -> show n <> s) "Ten" 10
-
-"10Ten"
-```
-
-`a`、 `b`、 `c`の型はどんな型でも選ぶことができるといっても、型の不整合は生じないようにしなければなりません。
-`flip`に渡す関数の型は、他の引数の型と整合性がなくてはなりません。
-第2引数として文字列 `"Ten"`、第3引数として数 `10`を渡したのはそれが理由です。
-もし引数が逆になっているとうまくいかないでしょう。
-
-```text
-> flip (\n s -> show n <> s) 10 "Ten"
-
-Could not match type Int with type String
-```
-
## 字下げについての注意
PureScriptのコードは字下げの大きさに意味があります。ちょうどHaskellと同じで、JavaScriptとは異なります。コード内の空白の多寡は無意味ではなく、Cのような言語で中括弧によってコードのまとまりを示しているように、PureScriptでは空白がコードのまとまりを示すために使われているということです。
-宣言が複数行にわたる場合は、最初の行以外は最初の行の字下げより深くしなければなりません。
+宣言が複数行にわたる場合、最初の行以外は最初の行の字下げより深くしなければなりません。
したがって、次は正しいPureScriptコードです。
@@ -211,7 +167,7 @@ add x y z = x +
y + z
```
-後者では、PureScriptコンパイラはそれぞれの行ごとに1つ、つまり*2つ*の宣言であると構文解析します。
+後者では、PureScriptコンパイラはそれぞれの行毎に1つ、つまり*2つ*の宣言であると構文解析します。
一般に、同じブロック内で定義された宣言は同じ深さで字下げする必要があります。
例えばPSCiでlet文の宣言は同じ深さで字下げしなければなりません。
@@ -233,19 +189,30 @@ y + z
… ^D
```
-PureScriptの幾つかの予約語(例えば `where`や `of`、
-`let`)は新たなコードのまとまりを導入しますが、そのコードのまとまり内の宣言はそれより深く字下げされている必要があります。
+PureScriptの幾つかのキーワードは新たなコードのまとまりを導入します。
+その中での宣言はそれより深く字下げされなければなりません。
```haskell
-example x y z = foo + bar
- where
+example x y z =
+ let
foo = x * y
bar = y * z
+ in
+ foo + bar
```
-ここで `foo`や `bar`の宣言は `example`の宣言より深く字下げされていることに注意してください。
+これはコンパイルされません。
-ただし、ソースファイルの先頭、最初の `module`宣言における予約語 `where`だけは、この規則の唯一の例外になっています。
+```haskell
+example x y z =
+ let
+ foo = x * y
+ bar = y * z
+ in
+ foo + bar
+```
+
+より多くを学びたければ(あるいは何か問題に遭遇したら)[構文](https://github.com/purescript/documentation/blob/master/language/Syntax.md#syntax)のドキュメントを参照してください。
## 独自の型の定義
@@ -255,10 +222,10 @@ PureScriptで新たな問題に取り組むときは、まずはこれから扱
{{#include ../exercises/chapter3/src/Data/AddressBook.purs:Entry}}
```
-これは `Entry`という*型同義語*(type synonym、型シノニム)を定義しています。
-型 `Entry`は等号の右辺と同じ型ということです。
-レコードの型は何れも文字列である `firstName`、 `lastName`、 `phone`という3つのフィールドからなります。
-前者の2つのフィールドは型 `String`を持ち、 `address`は以下のように定義された型 `Address`を持っています。
+これは`Entry`という*型同義語*を定義しています。
+型`Entry`は等号の右辺と等価ということです。
+レコードの型は`firstName`、`lastName`、`phone`という3つのフィールドからなります。
+2つの名前のフィールドは型`String`を持ち、`address`は以下で定義された型`Address`を持ちます。
```haskell
{{#include ../exercises/chapter3/src/Data/AddressBook.purs:Address}}
@@ -266,23 +233,24 @@ PureScriptで新たな問題に取り組むときは、まずはこれから扱
なお、レコードには他のレコードを含めることができます。
-それでは、3つめの型同義語も定義してみましょう。住所録のデータ構造としては、単に項目の連結リストとして格納することにします。
+それでは、住所録のデータ構造として3つめの型同義語も定義してみましょう。
+単に項目の連結リストとして表すことにします。
```haskell
{{#include ../exercises/chapter3/src/Data/AddressBook.purs:AddressBook}}
```
-`List Entry`は `Array Entry`とは同じではないということに注意してください。 `Array Entry`は住所録の項目の
-_配列_ を意味しています。
+なお、`List Entry`は `Array Entry`とは同じではありません。
+後者は項目の*配列*を表しています。
## 型構築子と種
-`List`は*型構築子*(type constructor、型コンストラクタ)の一例になっています。`List`そのものは型ではなく、何らかの型
-`a`があるとき `List a`が型になっています。つまり、 `List`は _型引数_ (type argument) `a`をとり、新たな型
-`List a`を _構築_ するのです。
+`List`は*型構築子*の一例になっています。
+`List`そのものは型ではなく、何らかの型 `a`があるとき `List a`が型になっています。
+つまり、 `List`は*型引数*`a`を取り、新たな型 `List a`を*構築*するのです。
-ちょうど関数適用と同じように、型構築子は他の型に並べることで適用されることに注意してください。型 `List Entry`は実は型構築子
-`List`が型 `Entry`に _適用_ されたものです。これは住所録項目のリストを表しています。
+なお、ちょうど関数適用と同じように、型構築子は他の型に並置するだけで適用されます。
+実際、型`List Entry`は型構築子`List`が型`Entry`に*適用*されたもので、項目のリストを表しています。
もし間違って(型注釈演算子 `::`を使って)型 `List`の値を定義しようとすると、今まで見たことのない種類のエラーが表示されるでしょう。
@@ -316,20 +284,71 @@ Type
PureScriptの _種システム_ は他にも面白い種に対応していますが、それらについては本書の他の部分で見ていくことになるでしょう。
+## 量化された型
+
+説明しやすくするため、任意の2つの引数を取り最初のものを返す原始的な関数を定義しましょう。
+
+```text
+> :paste
+… constantlyFirst :: forall a b. a -> b -> a
+… constantlyFirst = \a b -> a
+… ^D
+```
+
+> なお、`:type`を使って`constantlyfirst`の型について尋ねた場合、もっと冗長になります。
+>
+> ```text
+> : type constantlyFirst
+> forall (a :: Type) (b :: Type). a -> b -> a
+> ```
+>
+> 型シグネチャには追加で種の情報が含まれます。
+> `a`と`b`が具体的な型であることが明記されています。
+
+この`forall`キーワードは、`constantlyFirst`が*全称量化された型*を持つことを示しています。
+つまり`a`や`b`をどの型に置き換えても良く、`constantlyFirst`はその型で動作するのです。
+
+例えば、`a`を`Int`、`b`を`String`と選んだとします。
+その場合、`constantlyFirst`の型を次のように*特殊化*できます。
+
+```text
+Int -> String -> Int
+```
+
+量化された型を特殊化したいということをコードで示す必要はありません。
+特殊化は自動的に行われます。
+例えば、あたかも既にその型に備わっていたかの如く`constantlyFirst`を使えます。
+
+```text
+> constantlyFirst 3 "ignored"
+
+3
+```
+
+`a`と`b`にはどんな型でも選べますが、`constantlyFirst`が返す型は最初の引数の型と同じでなければなりません(両方とも同じ`a`に「紐付く」からです)。
+
+```text
+:type constantlyFirst true "ignored"
+Boolean
+
+:type constantlyFirst "keep" 3
+String
+```
+
## 住所録の項目の表示
それでは最初に、文字列で住所録の項目を表現する関数を書いてみましょう。
まずは関数に型を与えることから始めます。
型の定義は省略できますが、ドキュメントとしても役立つので型を書いておくようにすると良いでしょう。
-実際、トップレベルの宣言に型註釈が含まれていないと、PureScriptコンパイラが警告を出します。
+実際、最上位の宣言に型註釈が含まれていないと、PureScriptコンパイラが警告を出します。
型宣言は関数の名前とその型を `::`記号で区切るようにして書きます。
```haskell
{{#include ../exercises/chapter3/src/Data/AddressBook.purs:showEntry_signature}}
```
-`showEntry`は引数として `Entry`を取り `String`を返す関数であるということを、この型シグネチャは言っています。
-`showEntry`のコードは次の通りです。
+この型シグネチャが言っているのは、`showEntry`は引数として`Entry`を取り`String`を返す関数であるということです。
+以下は`showEntry`のコードです。
```haskell
{{#include ../exercises/chapter3/src/Data/AddressBook.purs:showEntry_implementation}}
@@ -389,8 +408,8 @@ $ spago repl
## 住所録の作成
-今度は住所録の操作を支援する関数を幾つか書いてみましょう。
-空の住所録を表す値が必要ですが、これには空のリストを使います。
+今度は住所録を扱う補助関数を幾つか書いてみましょう。
+空の住所録を表す値が必要ですが、これは空のリストです。
```haskell
{{#include ../exercises/chapter3/src/Data/AddressBook.purs:emptyBook}}
@@ -402,15 +421,14 @@ $ spago repl
{{#include ../exercises/chapter3/src/Data/AddressBook.purs:insertEntry_signature}}
```
-この型シグネチャに書かれているのは、最初の引数として `Entry`、第二引数として `AddressBook`を取り、新しい
-`AddressBook`を返すということです。
+この型シグネチャに書かれているのは、最初の引数として`Entry`、第2引数として`AddressBook`を取り、新しい`AddressBook`を返すということです。
-既存の `AddressBook`を直接変更することはしません。
-その代わりに、同じデータが含まれている新しい `AddressBook`を返すようにします。
-このように、 `AddressBook`は*不変データ構造*の一例となっています。
+既存の`AddressBook`を直接変更することはしません。
+代わりに、同じデータが含まれている新しい`AddressBook`を返します。
+このように`AddressBook`は*不変データ構造*の一例となっています。
これはPureScriptにおける重要な考え方です。
-変更はコードの副作用であり、効率の良いコードの挙動を考えるときの妨げになります。
-そのため、可能な限り純粋な関数や不変のデータにする方が好ましいのです。
+変更はコードの副作用であり、効率良く挙動を探る上で妨げになります。
+そのため可能な限り純粋関数や不変なデータにする方が好ましいのです。
`insertEntry`を実装するのに`Data.List`の`Cons`関数が使えます。
この関数の型を見るには、PSCiを起動し `:type`コマンドを使います。
@@ -421,10 +439,10 @@ $ spago repl
> import Data.List
> :type Cons
-forall a. a -> List a -> List a
+forall (a :: Type). a -> List a -> List a
```
-この型シグネチャで書かれているのは、`Cons`が何らかの型`a`の値と型`a`を要素に持つリストを引数に取り、同じ型の要素を持つ新しいリストを返すということです。
+この型シグネチャで書かれているのは、`Cons`が何らかの型`a`の値と型`a`の要素のリストを取り、同じ型の項目を持つ新しいリストを返すということです。
`a`を`Entry`型として特殊化してみましょう。
```haskell
@@ -447,24 +465,75 @@ Entry -> AddressBook -> AddressBook
insertEntry entry book = Cons entry book
```
-等号の左側にある2つの引数`entry`と`book`がスコープに導入されますから、これらに `Cons`関数を適用して結果の値を作成しています。
+こうすると、等号の左側にある2つの引数`entry`と`book`がスコープに導入されます。
+それから`Cons`関数を適用し、結果を作成しています。
## カリー化された関数
-PureScriptでは、関数は常に1つの引数だけを取ります。`insertEntry`関数は2つの引数を取るように見えますが、これは実際には*カリー化された関数*の一例となっています。
+PureScriptの関数はきっかり1つの引数を取ります。
+`insertEntry`関数は2つの引数を取るように見えますが、*カリー化された関数*の一例なのです。
+PureScriptでは全ての関数はカリー化されたものと見做されます。
+
+カリー化が意味するのは複数の引数を取る関数を1度に1つ取る関数に変換することです。
+関数を呼ぶときに1つの引数を渡し、これまた1つの引数を取る別の関数を返し、といったことを全ての引数が渡されるまで続けます。
+
+例えば`add`に`5`に渡すと別の関数が得られます。
+その関数は整数を取り、5を足し、合計を結果として返します。
+
+```haskell
+add :: Int -> Int -> Int
+add x y = x + y
+
+addFive :: Int -> Int
+addFive = add 5
+```
+
+`addFive`は*部分適用*の結果です。
+つまり複数の引数を取る関数に、引数の全個数より少ない数だけ渡すのです。
+試してみましょう。
+
+> なお、お済みでなければ`add`関数を定義しなくてはなりません。
+>
+> ```text
+> > import Prelude
+> > :paste
+>… add :: Int -> Int -> Int
+>… add x y = x + y
+>… ^D
+> ```
+
+```text
+> :paste
+… addFive :: Int -> Int
+… addFive = add 5
+… ^D
+
+> addFive 1
+6
+
+> add 5 1
+6
+```
+
+カリー化と部分適用をもっと理解するには、例にあった`add`とは別の関数を2、3作ってみてください。
+そしてそれができたら`insertEntry`に戻りましょう。
+
+```haskell
+{{#include ../exercises/chapter3/src/Data/AddressBook.purs:insertEntry_signature}}
+```
-`insertEntry`の型に含まれる `->`は右結合の演算子であり、つまりこの型はコンパイラによって次のように解釈されます。
+(型シグネチャ中の)`->`演算子は右結合です。
+つまりコンパイラは型を次のように解釈します。
```haskell
Entry -> (AddressBook -> AddressBook)
```
-すなわち、 `insertEntry`は関数を返す関数である、ということです。
-この関数は単一の引数 `Entry`を取り、それから単一の引数 `AddressBook`を取り新しい
-`AddressBook`を返す新しい関数を返すのです。
+`insertEntry`は単一の引数`Entry`を取り、新しい関数を返します。
+そして今度はその関数が単一の引数`AddressBook`を取り、新しい`AddressBook`を返します。
-これは例えば、最初の引数だけを与えると `insertEntry`を _部分適用_ (partial application)
-できることを意味します。PSCiでこの結果の型を見てみましょう。
+これはつまり、最初の引数だけを与えて`insertEntry`を*部分適用*できたりするということです。
+PSCiで結果の型が見られます。
```text
> :type insertEntry entry
@@ -480,14 +549,15 @@ AddressBook -> AddressBook
AddressBook
```
-ここで括弧は不要であることにも注意してください。次の式は同等です。
+ただし、ここでの括弧は不要です。
+以下は等価です。
```text
> :type insertEntry entry emptyBook
AddressBook
```
-これは関数適用が左結合であるためで、なぜ空白で区切った引数を関数に指定するだけでいいのかの説明にもなっています。
+これは関数適用が左に結合するためで、なぜ空白で区切った引数を次々に関数に指定するだけでいいのかの説明にもなっています。
関数の型の`->`演算子は関数の*型構築子*です。
この演算子は2つの型引数を取ります。
@@ -503,10 +573,10 @@ insertEntry :: Entry -> AddressBook -> AddressBook
insertEntry entry book = Cons entry book
```
-もし式の右辺に明示的に括弧をつけるなら、 `(Cons entry) book`となります。
-`insertEntry entry`はその引数が単に関数 `(Cons entry)`に渡されるような関数だということです。
-でもこの2つの関数はどんな入力についても同じ結果を返しますから、つまりこれらは同じ関数です。
-よって、両辺から引数 `book`を削除できます。
+もし式の右辺に明示的に括弧をつけるなら、`(Cons entry) book`となります。
+つまり`insertEntry entry`はその引数が単に関数`(Cons entry)`に渡されるような関数だということです。
+ところがこの2つの関数はどんな入力についても同じ結果を返すので、となると同じ関数ではないですか。
+よって、両辺から引数`book`を削除できます。
```haskell
insertEntry :: Entry -> AddressBook -> AddressBook
@@ -519,10 +589,11 @@ insertEntry entry = Cons entry
{{#include ../exercises/chapter3/src/Data/AddressBook.purs:insertEntry}}
```
-この処理は _イータ変換_ (eta conversion) と呼ばれ、(その他の技法を併用して)引数を参照することなく関数を定義する
-_ポイントフリー形式_ (point-free form) へと関数を書き換えるのに使うことができます。
+この処理は*イータ変換*と呼ばれ、(その他の技法を併用して)*ポイントフリー形式*へと関数を書き換えるのに使えます。
+つまり、引数を参照せずに関数を定義できるのです。
-`insertEntry`の場合には、イータ変換によって「`insertEntry`は単にリストに対するconsだ」となり、関数の定義はとても明確になりました。しかし、一般的にポイントフリー形式のほうがいいのかどうかには議論の余地があります。
+`insertEntry`の場合、イータ変換によって「`insertEntry`は単にリストにおけるconsだ」となり、とても明快な関数の定義になりました。
+しかし、一般にポイントフリー形式のほうがいいのかどうかには議論の余地があります。
## プロパティ取得子
@@ -559,13 +630,14 @@ _.address.city
## 住所録に問い合わせる
-最小限の住所録アプリケーションの実装で必要になる最後の関数は、名前で人を検索し適切な
-`Entry`を返すものです。これは小さな関数を組み合わせることでプログラムを構築するという、関数型プログラミングで鍵となる考え方のよい応用例になるでしょう。
+最小限の住所録アプリケーションの実装で必要になる最後の関数は、名前で人を検索し適切な`Entry`を返すものです。
+これは小さな関数を組み合わせることでプログラムを構築するという、関数型プログラミングで鍵となる考え方のよい応用例になるでしょう。
-まずは住所録を絞り込み、該当する姓名を持つ項目だけを保持するようにするのがいいでしょう。それから、結果のリストの先頭の (head)
-要素を返すだけです。
+住所録を絞り込めば該当する姓名を持つ項目だけを保持するようにできます。
+そうすれば結果のリストの先頭(つまり最初)の要素を返せます。
-この大まかな仕様に従って、この関数の型を計算できます。まずPSCiを起動し、 `filter`関数と `head`関数の型を見てみましょう。
+この大まかな道筋の仕様があれば関数の型を計算できます。
+まずPSCiを開いて`filter`関数と`head`関数の型を探してみましょう。
```text
$ spago repl
@@ -573,24 +645,23 @@ $ spago repl
> import Data.List
> :type filter
-forall a. (a -> Boolean) -> List a -> List a
+forall (a :: Type). (a -> Boolean) -> List a -> List a
> :type head
-forall a. List a -> Maybe a
+forall (a :: Type). List a -> Maybe a
```
型の意味を理解するために、これらの2つの型の一部を取り出してみましょう。
-`filter`はカリー化された2引数の関数です。
-最初の引数は、リストの要素を取り `Boolean`値を結果として返す関数です。
+`filter`は2引数のカリー化された関数です。
+最初の引数は関数で、リストの要素を取り`Boolean`値を返します。
第2引数は要素のリストで、返り値は別のリストです。
-`head`は引数としてリストを取り、 `Maybe a`という今まで見たことがないような型を返します。
-`Maybe a`は型 `a`のオプショナルな値、つまり
-`a`の値を持つか持たないかのどちらかの値を示しており、JavaScriptのような言語で値がないことを示すために使われる
-`null`の型安全な代替手段を提供します。
-これについては後の章で詳しく扱います。
+`head`は引数としてリストを取り、 `Maybe a`という今までに見たことがない型を返します。
+`Maybe
+a`は型`a`の省略可能な値を表しており、JavaScriptのような言語で値がないことを示すための`null`を使う代わりとなる、型安全な代替を提供します。
+後の章で改めて詳しく見ていきます。
`filter`と `head`の全称量化された型は、PureScriptコンパイラによって次のように _特殊化_ (specialized)
されます。
@@ -601,7 +672,7 @@ filter :: (Entry -> Boolean) -> AddressBook -> AddressBook
head :: AddressBook -> Maybe Entry
```
-検索する関数の引数として姓と名前を渡す必要があるのもわかっています。
+関数の引数として姓名を渡す必要があるだろうということは分かっています。
`filter`に渡す関数も必要になることもわかります。この関数を `filterEntry`と呼ぶことにしましょう。 `filterEntry`は `Entry -> Boolean`という型を持っています。 `filter filterEntry`という関数適用の式は、 `AddressBook -> AddressBook`という型を持つでしょう。もしこの関数の結果を `head`関数に渡すと、型 `Maybe Entry`の結果を得ることになります。
@@ -611,8 +682,8 @@ head :: AddressBook -> Maybe Entry
{{#include ../exercises/chapter3/src/Data/AddressBook.purs:findEntry_signature}}
```
-この型シグネチャで書かれているのは、`findEntry`が姓と名前の2つの文字列及び`AddressBook`を引数に取り、`Entry`のオプション型の値を結果として返すということです。
-オプショナルな結果は名前が住所録で発見された場合にのみ値を持ちます。
+この型シグネチャで書かれているのは、`findEntry`が姓と名前の2つの文字列及び`AddressBook`を引数に取り、省略可能な`Entry`を返すということです。
+省略可能な結果は名前が住所録に見付かった場合にのみ値を持ちます。
そして、 `findEntry`の定義は次のようになります。
@@ -628,8 +699,8 @@ findEntry firstName lastName book = head (filter filterEntry book)
`findEntry`は、どちらも文字列型である `firstName`と `lastName`、`AddressBook`型の
`book`という3つの名前をスコープに導入します。
-定義の右辺では `filter`関数と `head`関数が組み合わされています。
-まず項目のリストを絞り込み、その結果に `head`関数を適用しています。
+定義の右辺では`filter`関数と`head`関数が組み合わさっています。
+まず項目のリストを絞り込み、その結果に`head`関数を適用しています。
真偽型を返す関数 `filterEntry`は `where`節の内部で補助的な関数として定義されています。
このため、 `filterEntry`関数はこの定義の内部では使用できますが、外部では使用できません。
@@ -637,32 +708,33 @@ findEntry firstName lastName book = head (filter filterEntry book)
`firstName`と `lastName`を使用しているので、 `filterEntry`が
`findEntry`の内部にあることは必須になっています。
-最上位での宣言と同じように、必ずしも
-`filterEntry`の型シグネチャを指定しなくてもよいことに注意してください。ただし、ドキュメントとしても役に立つので型シグネチャを書くことは推奨されています。
+なお、最上位での宣言と同じように、必ずしも`filterEntry`の型シグネチャを指定しなくても構いません。
+ただし、ドキュメントの一形態として指定しておくことが推奨されます。
## 中置の関数適用
-これまでお話しした関数のほとんどは _前置_ 関数適用でした。関数名が引数の _前_
-に置かれていたということです。例えば`insertEntry`関数を使って`Entry` (`john`)
-を空の`AddressBook`に追加する場合、以下のように書けます。
+これまでお話しした関数のほとんどは*前置*関数適用でした。
+関数名が引数の*前*に置かれていたということです。
+例えば`insertEntry`関数を使って`Entry` (`john`) を空の`AddressBook`に追加する場合、以下のように書けます。
```haskell
> book1 = insertEntry john emptyBook
```
-しかしこの章には _中置_
-[2引数演算子](https://github.com/purescript/documentation/blob/master/language/Syntax.md#binary-operators)の例も含まれています。例えば`filterEntry`の定義中の`==`演算子で、演算子が2つの引数の
-_間_ に置かれています。実はこうした中置演算子はPureScriptのソースコードで、背後にある _前置_
-版の実装への中置別称として定義されています。例えば`==`は以下の行により前置の`eq`関数の中置別称として定義されています。
+しかし本章には*中置*[2引数演算子](https://github.com/purescript/documentation/blob/master/language/Syntax.md#binary-operators)の例も含まれています。
+`filterEntry`の定義中の`==`演算子がそうで、2つの引数の*間*に置かれています。
+PureScriptのソースコードでこうした中置演算子は隠れた*前置*の実装への中置別称として定義されています。
+例えば`==`は以下の行により前置の`eq`関数の中置別称として定義されています。
```haskell
infix 4 eq as ==
```
したがって`filterEntry`中の`entry.firstName == firstName`は`eq entry.firstName
-firstName`で置き換えられます。この節の後のほうで中置演算子を定義する例にもう少し触れます。
+firstName`で置き換えられます。
+この節の後のほうで中置演算子を定義する例にもう少し触れます。
-前置関数を演算子としての中置の位置に置くとより読みやすいコードになる場面があります。
+前置関数を演算子としての中置の位置に置くと、より読みやすいコードになる場面があります。
その一例が`mod`関数です。
```text
@@ -670,8 +742,9 @@ firstName`で置き換えられます。この節の後のほうで中置演算
2
```
-上の用例は正しく動きますが、読みづらいです。
-より馴染みのある表現の仕方は「8 mod 3」ですが、バックスラッシュ (\`) の中に前置関数を包めばこのように書けます。
+上の用例でも充分動作しますが、読みにくいです。
+より馴染みのある表現の仕方は「8 mod 3」です。
+バックスラッシュ (\`) の中に前置関数を包むとそのように書けます。
```text
> 8 `mod` 3
@@ -707,7 +780,7 @@ infixr 5 insertEntry as ++
book5 = john ++ (peggy ++ (ned ++ emptyBook))
```
-そして新しい`++`演算子が右結合なので意味を変えずに括弧を除去できます。
+新しい`++`演算子の右結合性により、意味を変えずに括弧を除去できます。
```haskell
book6 = john ++ peggy ++ ned ++ emptyBook
@@ -725,7 +798,7 @@ book7 = insertEntry john $ insertEntry peggy $ insertEntry ned emptyBook
この記号の意味を覚えるための記憶術として、ドル記号を2つの括弧に打ち消し線が引かれたものと見ることで、これで括弧が不必要になったのだと推測できるという方法があります。
なお、`($)`は言語にハードコードされた特別な構文ではありません。
-単に`apply`という名前の通常の関数のための中置演算子であって、`Data.Function`で以下のように定義されています。
+単に`apply`という名前の普通の関数のための中置演算子であって、`Data.Function`で以下のように定義されています。
```haskell
apply :: forall a b. (a -> b) -> a -> b
@@ -811,7 +884,8 @@ filter filterEntry >>> head
どちらにしても、これは「`findEntry`は絞り込み関数と`head`関数の合成である」という
`findEntry`関数のわかりやすい定義を与えます。
-どちらの定義のほうがわかりやすいかの判断はお任せしますが、このように関数を部品として捉えると有用なことがよくあります。関数は1つの役目だけをこなし、機能を関数合成で組み立てるというように。
+どちらの定義のほうが分かりやすいかの判断はお任せしますが、このように関数を部品として捉えるとしばしば有用です。
+各関数は1つの役目をこなすようにし、解法を関数合成を使って組み立てるのです。
## 演習
@@ -826,24 +900,25 @@ filter filterEntry >>> head
実装した関数をPSCiと`spago test`を走らせてテストしてください。
1. (普通)`filterEntry`を(`<<<`や`>>>`を使った)合成で置き換えて、`findEntryByStreet`を書き直してください。
合成の対象は、プロパティ取得子(`_.`記法を使います)と、与えられた文字列引数が与えられた通りの住所に等しいかを判定する関数です。
- 1. (普通)指定された名前が `AddressBook`に存在するかどうかを調べて真偽値で返す関数`isInBook`を書いてみましょう。
- *手掛かり*:リストが空かどうかを調べる `Data.List.null`関数の型をPSCiで調べてみてみましょう。
- 1. (難しい)「重複」している項目を住所録から削除する関数 `removeDuplicates`を書いてみましょう。
+ 1. (普通)名前が`AddressBook`に存在するかどうかを調べて真偽値で返す関数`isInBook`を書いてみましょう。
+ *手掛かり*:PSCiを使って`Data.List.null`関数の型を見付けてください。
+ この関数はリストが空かどうかを調べます。
+ 1. (難しい)「重複」している住所録の項目を削除する関数`removeDuplicates`を書いてみましょう。
項目が同じ姓名を共有していれば`address`フィールドに関係なく、項目が重複していると考えます。
- *手掛かり*:関数 `Data.List.nubBy`の型を、PSCiを使用して調べてみましょう。
- この関数は値同士の等価性を定義する述語関数に基づいてリストから重複要素を削除します。
- なお、それぞれの重複する項目の集合における最初の要素(リストの先頭に最も近い)が保持する項目です。
+ *手掛かり*:`Data.List.nubByEq`関数の型をPSCiを使って調べましょう。
+ この関数は等価性の述語に基づいてリストから重複要素を削除します。
+ なお、それぞれの重複する項目の集合において最初の要素(リストの先頭に最も近い)が保持する項目です。
## まとめ
-この章では、関数型プログラミングの新しい概念を幾つも導入しました。
+この章では関数型プログラミングの新しい概念を幾つか押さえ、以下の方法を学びました。
-- 対話的モードのPSCiを使用して、関数を調べるなどの思いついたことを試す方法
-- 検証や実装の道具としての型の役割
-- 多引数関数を表現する、カリー化された関数の使用
-- 関数合成で小さな部品を組み合わせてのプログラムの構築
-- `where`節を利用したコードの構造化
-- `Maybe`型を使用してnull値を回避する方法
-- イータ変換や関数合成のような手法を利用した、よりわかりやすいコードへの再構成
+- 対話的モードのPSCiを使用して、関数で実験したり思いついたことを試したりする。
+- 正確さのための道具として、また実装のための道具として型を使う。
+- 多引数の関数を表現するためにカリー化された関数を使う。
+- 合成により小さな部品からプログラムを作る。
+- `where`式を使ってコードを手際良く構造化する。
+- `Maybe`型を使用してnull値を回避する。
+- イータ変換や関数合成のような技法を使ってより分かりやすい仕様にリファクタする。
次の章からは、これらの考えかたに基づいて進めていきます。
diff --git a/text-ja/chapter4.md b/text-ja/chapter4.md
index 2a89ea8a..37b5187a 100644
--- a/text-ja/chapter4.md
+++ b/text-ja/chapter4.md
@@ -2,12 +2,14 @@
## この章の目標
-この章では、アルゴリズムを構造化するときに再帰関数をどのように使うかについて見ていきましょう。再帰は関数型プログラミングの基本的な手法であり、この本の全体に亙って使われます。
+この章では、アルゴリズムを構造化するときに再帰関数をどのように使うかについて見ていきましょう。
+再帰は関数型プログラミングの基本的な手法であり、本書全体に亙って使われます。
また、PureScriptの標準ライブラリから標準的な関数を幾つか取り扱います。
`map`や`fold`といった関数だけでなく、`filter`や`concatMap`といった特別な場合において便利なものについても見ていきます。
-この章では、仮想的なファイルシステムを操作する関数のライブラリを動機付けに用います。この章で学ぶ手法を応用して、擬似的なファイルシステムによって表されるファイルのプロパティを計算する関数を記述します。
+この章では、仮想的なファイルシステムを操作する関数のライブラリを動機付けに用います。
+この章で学ぶ技術を応用し、ファイルシステムのモデルにより表現されるファイルのプロパティを計算する関数を書きます。
## プロジェクトの準備
@@ -31,7 +33,7 @@ test`を走らせることで解答を確認してください。
再帰は一般のプログラミングでも重要な手法ですが、特に純粋関数型プログラミングでは当たり前のように用いられます。この章で見ていくように、再帰はプログラムの変更可能な状態を減らすために役立つからです。
再帰は*分割統治*戦略と密接な関係があります。
-分割統治とはすなわち、何らかの入力としての問題を解くにあたり、入力を小さな部分に分割してそれぞれの部分について問題を解き、部分ごとの答えから最終的な答えを組み立てるということです。
+分割統治とはすなわち、何らかの入力としての問題を解くにあたり、入力を小さな部分に分割してそれぞれの部分について問題を解き、部分毎の答えから最終的な答えを組み立てるということです。
それでは、PureScriptにおける再帰の簡単な例を幾つか見てみましょう。
@@ -41,9 +43,11 @@ test`を走らせることで解答を確認してください。
{{#include ../exercises/chapter4/test/Examples.purs:factorial}}
```
-部分問題へ問題を分割することによって階乗関数がどのように計算されるかがわかります。より小さい数へと階乗を計算していくということです。ゼロに到達すると、答えは直ちに求まります。
+このように、問題を部分問題へ分割することによって階乗関数の計算方法が見てとれます。
+より小さい数の階乗を計算していくということです。
+ゼロに到達すると、答えは直ちに求まります。
-次は、 _フィボナッチ関数_ (Fibonacci function) を計算するという、これまたよくある例です。
+次は、*フィボナッチ関数*を計算するという、これまたよくある例です。
```haskell
{{#include ../exercises/chapter4/test/Examples.purs:fib}}
@@ -53,7 +57,8 @@ test`を走らせることで解答を確認してください。
このとき、`fib (n - 1)`と`fib (n - 2)`という式に対応した、2つの部分問題があります。
これらの2つの部分問題が解決されていれば、この部分的な答えを加算することで、全体の答えを組み立てることができます。
-なお上の`factorial`と`fib`の例は意図通りに動きますが、よりPureScriptらしい実装では`if`や`then`や`else`を使う代わりにパターン照合を使うものでしょう。パターン照合の技法は後の章でお話しします。
+> なお、上の`factorial`と`fib`の例は意図通りに動きますが、よりPureScriptらしい実装では`if`や`then`や`else`を使う代わりにパターン照合を使うものでしょう。
+> パターン照合の技法は後の章でお話しします。
## 配列上での再帰
@@ -72,26 +77,30 @@ import Data.Maybe (fromMaybe)
```
この関数では配列が空かどうかで分岐するために`if ... then ... else`式を使っています。
-この`null`関数は配列が空のときに`true`を返します。
-空の配列の長さはゼロであり、空でない配列の長さは配列の先頭を取り除いた残りの部分の長さより1大きいというわけです。
+この`null`関数は空の配列で`true`を返します。
+空の配列の長さはゼロであり、空でない配列の長さは尾鰭の長さより1大きいというわけです。
-`tail`関数は与えられた配列から最初の要素を除いたものを`Maybe`に包んで返します。配列が空であれば(つまり尾鰭がなければ)`Nothing`が返ります。`fromMaybe`関数は既定値と`Maybe`値を取ります。後者が`Nothing`であれば既定値を返し、そうでなければ`Just`に包まれた値を返します。
+`tail`関数は与えられた配列から最初の要素を除いたものを`Maybe`に包んで返します。
+配列が空であれば(つまり尾鰭がなければ)`Nothing`が返ります。
+`fromMaybe`関数は既定値と`Maybe`値を取ります。
+後者が`Nothing`であれば既定値を返し、そうでなければ`Just`に包まれた値を返します。
-JavaScriptで配列の長さを調べるのには、この例はどう見ても実用的な方法とはいえませんが、次の演習を完遂するための手がかりとしては充分でしょう。
+JavaScriptで配列の長さを調べるのには、この例はどう見ても実用的な方法とはいえませんが、次の演習を完遂するための手掛かりとしては充分でしょう。
## 演習
- 1. (簡単)入力が偶数であるとき、かつそのときに限り`true`に返すような再帰関数を書いてみましょう。
- 2. (少し難しい)配列内の偶数の数を数える再帰関数`countEven`を書いてみましょう。
- *手掛かり*:`Data.Array`モジュールの`head`関数を使うと、空でない配列の最初の要素を見つけることができます。
+ 1. (簡単)入力が偶数であるとき、かつそのときに限り`true`に返す再帰関数`isEven`を書いてみましょう。
+ 2. (普通)配列内の偶数の整数を数える再帰関数`countEven`を書いてみましょう。
+ *手掛かり*:`head`関数(これも`Data.Array`モジュールから手に入ります)を使うと、空でない配列の最初の要素を見つけられます。
## マップ
-`map`関数は配列に対する再帰関数の1つです。この関数を使うと、配列の各要素に順番に関数を適用することで、配列の要素を変換できます。そのため、配列の*内容*は変更されますが、その*形状*(ここでは「長さ」)は保存されます。
+`map`関数は配列に対する再帰関数の一例です。
+配列の各要素に順番に関数を適用し、配列の要素を変換するのに使われます。
+そのため、配列の*内容*は変更されますが、その*形状*(ここでは「長さ」)は保存されます。
-本書の後半で _型クラス_ (type class)
-の内容を押さえるとき、`map`関数が形状保存関数のより一般的な様式の一例であることを見ていきます。これは _関手_ (functor)
-と呼ばれる型構築子のクラスを変換するものです。
+本書の後半で*型クラス*の内容を押さえるとき、`map`関数が形状を保存する関数のより一般的な様式の一例であることを見ていきます。
+この関数は*関手*と呼ばれる型構築子のクラスを変換するものです。
それでは、PSCiで`map`関数を試してみましょう。
@@ -117,7 +126,7 @@ $ spago repl
この構文は _中置関数適用_ と呼ばれ、どんな関数でもこのように中置できます。普通は2引数の関数に対して使うのが最適でしょう。
-配列を扱うときは、`map`関数と等価な`<$>`という演算子が存在します。この演算子は他の二項演算子と同じように中置で使用できます。
+配列を扱う際は`map`関数と等価な`<$>`という演算子が存在します。
```text
> (\n -> n + 1) <$> [1, 2, 3, 4, 5]
@@ -128,13 +137,13 @@ $ spago repl
```text
> :type map
-forall a b f. Functor f => (a -> b) -> f a -> f b
+forall (f :: Type -> Type) (a :: Type) (b :: Type). Functor f => (a -> b) -> f a -> f b
```
実は`map`の型は、この章で必要とされているものよりも一般的な型になっています。今回の目的では、`map`は次のようなもっと具体的な型であるかのように考えるとよいでしょう。
```text
-forall a b. (a -> b) -> Array a -> Array b
+forall (a :: Type) (b :: Type). (a -> b) -> Array a -> Array b
```
この型では、`map`関数に適用するときには`a`と`b`という2つの型を自由に選ぶことができる、ということも示されています。
@@ -151,7 +160,7 @@ forall a b. (a -> b) -> Array a -> Array b
中置演算子`<$>`は特別な構文のように見えるかもしれませんが、実はPureScriptの普通の関数の別称です。
中置構文を使用した単なる*適用*にすぎません。
実際、括弧でその名前を囲むと、この関数を通常の関数のように使用できます。
-これは、`map`代わりに、括弧で囲まれた`(<$>)`という名前を使って配列に関数を適用できるということです。
+これは、`map`代わりに、括弧で囲まれた`(<$>)`という名前が使えるということです。
```text
> (<$>) show [1, 2, 3, 4, 5]
@@ -181,7 +190,7 @@ infix 8 range as ..
上記の例では、`1 .. 5`という式は括弧で囲まれていましたが、実際にはこれは必要ありません。
なぜなら、`Data.Array`モジュールは、`<$>`に割り当てられた優先順位より高い優先順位を`..`演算子に割り当てているからです。
-上の例では、`..`の優先順位は、予約語`infix`のあとに書かれた数の`8` と定義されていました。
+上の例では、`..`の優先順位は、キーワード`infix`のあとに書かれた数の`8` と定義されていました。
ここでは`<$>`の優先順位よりも高い優先順位を`..`に割り当てており、このため括弧を付け加える必要がないということです。
```text
@@ -189,12 +198,13 @@ infix 8 range as ..
["1","2","3","4","5"]
```
-中置演算子に(左または右の)*結合性*を与えたい場合は、代わりに予約語`infixl`と`infixr`を使います。`infix`を使うと何ら結合性は割り当てられず、同じ演算子を複数回使ったり複数の同じ優先度の演算子を使ったりするときに、式を括弧で囲まなければいけなくなります。
+中置演算子に(左または右の)*結合性*を与えたい場合は、代わりにキーワード`infixl`と`infixr`を使います。
+`infix`を使うと何ら結合性は割り当てられず、同じ演算子を複数回使ったり複数の同じ優先度の演算子を使ったりするときに、式を括弧で囲まなければいけなくなります。
## 配列の絞り込み
`Data.Array`モジュールでは他にも、よく`map`と一緒に使われる関数`filter`も提供しています。
-この関数は、述語関数に適合する要素のみを残し、既存の配列から新しい配列を作成する機能を提供します。
+この関数は、述語関数に照合する要素のみを残し、既存の配列から新しい配列を作成する機能を提供します。
例えば1から10までの数で、偶数であるような数の配列を計算したいとします。
これは次のようにできます。
@@ -227,13 +237,14 @@ infix 8 range as ..
> import Data.Array
> :type concat
-forall a. Array (Array a) -> Array a
+forall (a :: Type). Array (Array a) -> Array a
> concat [[1, 2, 3], [4, 5], [6]]
[1, 2, 3, 4, 5, 6]
```
-関連する関数として、`concat`と`map`を組み合わせたような`concatMap`と呼ばれる関数もあります。`map`は(相異なる型の可能性がある)値からの値への関数を引数に取りますが、それに対して`concatMap`は値から値の配列への関数を取ります。
+関連する関数として、`concat`と`map`を組み合わせた`concatMap`と呼ばれる関数もあります。
+`map`は(相異なる型の可能性がある)値からの値への関数を引数に取りますが、それに対して`concatMap`は値から値の配列への関数を取ります。
実際に動かして見てみましょう。
@@ -241,7 +252,7 @@ forall a. Array (Array a) -> Array a
> import Data.Array
> :type concatMap
-forall a b. (a -> Array b) -> Array a -> Array b
+forall (a :: Type) (b :: Type). (a -> Array b) -> Array a -> Array b
> concatMap (\n -> [n, n * n]) (1 .. 5)
[1,1,2,4,3,9,4,16,5,25]
@@ -333,9 +344,8 @@ PSCiを対話式の開発環境として使用し、1つずつこの手順を進
しかし、このコードの可読性は大幅に向上できます。`map`や`concatMap`は基本的な関数であり、 _do記法_ (do notation)
と呼ばれる特別な構文の基礎になっています(もっと厳密にいえば、それらの一般化である`map`と`bind`が基礎をなしています)。
-*補足*:`map`と`concatMap`が _配列内包表記_ を書けるようにしているように、もっと一般的な演算子である`map`と`bind`は
-_モナド内包表記_ (monad comprehensions) と呼ばれているものを書けるようにします。本書の後半では _モナド_ (monad)
-の例をたっぷり見ていくことになりますが、この章では配列のみを考えます。
+> *補足*:`map`と`concatMap`があることで*配列内包表記*を書けるように、もっと一般的な演算子である`map`と`bind`があることで*モナド内包表記*と呼ばれているものが書けます。
+> 本書の後半では*モナド*の例をたっぷり見ていくことになりますが、この章では配列のみを考えます。
do記法を使うと、先ほどの`factors`関数を次のように書き直すことができます。
@@ -343,12 +353,13 @@ do記法を使うと、先ほどの`factors`関数を次のように書き直す
{{#include ../exercises/chapter4/test/Examples.purs:factors}}
```
-キーワード`do`はdo記法を使うコードのブロックを導入します。このブロックは幾つかの種類の式で構成されています。
+キーワード`do`はdo記法を使うコードのブロックを導入します。
+このブロックは幾つかの種類の式で構成されています。
- 配列の要素を名前に束縛する式。
- これは後ろ向きの矢印`<-`で示されていて、その左側は名前、右側は配列の型を持つ式です。
+ これは後ろ向きの矢印`<-`で示されており、左側には名前が、右側には配列の型を持つ式があります。
- 名前に配列の要素を束縛しない式。
- `do`の _結果_ はこの種類の式の一例であり、最後の行の`pure [i, j]`に示されています。
+ `do`の*結果*はこの種類の式の一例であり、最後の行の`pure [i, j]`に示されています。
- `let`キーワードを使用し、式に名前を与える式。
この新しい記法を使うと、アルゴリズムの構造がわかりやすくなることがあります。
@@ -362,7 +373,7 @@ do記法を使うと、先ほどの`factors`関数を次のように書き直す
```
配列の場合、`pure`は単に1要素の配列を作成します。
-実際、`factors`関数を変更して、`pure`の代わりにこの形式も使うようにできます。
+`factors`関数を変更して、`pure`の代わりにこの形式も使うようにできます。
```haskell
{{#include ../exercises/chapter4/test/Examples.purs:factorsV2}}
@@ -388,7 +399,7 @@ import Control.Alternative (guard)
> import Control.Alternative
> :type guard
-forall m. Alternative m => Boolean -> m Unit
+forall (m :: Type -> Type). Alternative m => Boolean -> m Unit
```
今回の場合は、PSCiは次の型を報告するものと考えてください。
@@ -409,7 +420,8 @@ Boolean -> Array Unit
0
```
-つまり、`guard`が`true`に評価される式を渡された場合、単一の要素を持つ配列を返すのです。もし式が`false`と評価された場合は、その結果は空です。
+つまり、`guard`が`true`に評価される式を渡された場合、単一の要素を持つ配列を返すのです。
+もし式が`false`と評価された場合は、その結果は空です。
ガードが失敗した場合、配列内包表記の現在の分岐は、結果なしで早めに終了されることを意味します。
これは、`guard`の呼び出しが、途中の配列に対して`filter`を使用するのと同じだということです。
@@ -418,24 +430,25 @@ Boolean -> Array Unit
## 演習
- 1. (簡単)整数の引数が素数であるかどうかを調べる関数`isPrime`を定義してみましょう。
+ 1. (簡単)関数`isPrime`を書いてください。
+ この関数は整数の引数が素数であるかを調べます。
*手掛かり*:`factors`関数を使ってください。
1. (普通)do記法を使い、2つの配列の*直積集合*を見つけるための関数`cartesianProduct`を書いてみましょう。
直積集合とは、要素`a`、`b`の全ての組み合わせの集合のことです。
ここで`a`は最初の配列の要素、`b`は2つ目の配列の要素です。
- 1. (普通)数値`n`を取って構成要素(値`a`, `b`, `c`)がそれぞれ`n`以下であるような全てのピタゴラスの3つ組
- (pythagorean triples) を返す関数`triples :: Int -> Array (Array
- Int)`を書いてください。
- *ピタゴラスの3つ組*は数値の配列`[a, b, c]`で `a² + b² = c²` です。
+ 1. (普通)関数`triples :: Int -> Array (Array Int)`を書いてください。
+ この関数は数値`n`を取り、構成要素(値`a`、`b`、`c`)がそれぞれ`n`以下であるような全てのピタゴラスの3つ組
+ (pythagorean triples) を返します。
+ *ピタゴラスの3つ組*は`a² + b² = c²`であるような数値の配列`[a, b, c]`です。
*手掛かり*:配列内包表記で`guard`関数を使ってください。
- 1. (難しい)`factors`関数を使用して、数`n`の[素因数分解](https://www.mathsisfun.com/prime-factorization.html)を求める関数`primeFactors`を定義してみましょう。
- 数`n`の素因数分解とは、素数の積が`n`であるような整数の配列のことです。
+ 1. (難しい)`factors`関数を使用して、`n`の[素因数分解](https://www.mathsisfun.com/prime-factorization.html)を求める関数`primeFactors`を定義してみましょう。
+ `n`の素因数分解とは、積が`n`であるような素数の配列のことです。
*手掛かり*:1より大きい整数について、問題を2つの部分問題に分解してください。
最初の因数を探し、それから残りの因数を探すのです。
## 畳み込み
-配列における左右の畳み込みは、再帰を用いて実装される別の興味深い一揃いの関数を提供します。
+配列における左右の畳み込みは、再帰を用いて実装できる別の興味深い一揃いの関数を提供します。
PSCiを使って、`Data.Foldable`モジュールをインポートし、`foldl`と`foldr`関数の型を調べることから始めましょう。
@@ -443,30 +456,38 @@ PSCiを使って、`Data.Foldable`モジュールをインポートし、`foldl`
> import Data.Foldable
> :type foldl
-forall a b f. Foldable f => (b -> a -> b) -> b -> f a -> b
+forall (f :: Type -> Type) (a :: Type) (b :: Type). Foldable f => (b -> a -> b) -> b -> f a -> b
> :type foldr
-forall a b f. Foldable f => (a -> b -> b) -> b -> f a -> b
+forall (f :: Type -> Type) (a :: Type) (b :: Type). Foldable f => (a -> b -> b) -> b -> f a -> b
```
これらの型は、現在興味があるものよりも一般化されています。
-この章の目的に対して、PSCiは以下の(より具体的な)答えをくれていると考えておきましょう。
+この章では単純化して以下の(より具体的な)型シグネチャと見て構いません。
```text
-> :type foldl
+-- foldl
forall a b. (b -> a -> b) -> b -> Array a -> b
-> :type foldr
+-- foldr
forall a b. (a -> b -> b) -> b -> Array a -> b
```
-どちらの型でも、`a`は配列の要素の型に対応しています。
-型`b`は、配列を走査 (traverse) したときの結果を累積する「累積器」(accumulator) の型だと考えることができます。
+どちらの場合でも、型`a`は配列の要素の型に対応しています。
+型`b`は「累算器」の型として考えることができます。
+累算器とは配列を走査しつつ結果を累算するものです。
`foldl`関数と`foldr`関数の違いは走査の方向です。
`foldr`が「右から」配列を畳み込むのに対して、`foldl`は「左から」配列を畳み込みます。
-実際にこれらの関数の動きを見てみましょう。`foldl`を使用して数の配列の和を求めてみます。型`a`は`Int`になり、結果の型`b`も`Int`として選択できます。ここでは、次の要素を累積器に加算する`Int -> Int -> Int`という型の関数、`Int`型の累積器の初期値、和を求めたい`Int`の配列という、3つの引数を提供する必要があります。最初の引数としては、加算演算子を使用できますし、累積器の初期値はゼロになります。
+実際にこれらの関数の動きを見てみましょう。
+`foldl`を使用して数の配列の和を求めてみます。
+型`a`は`Int`になり、結果の型`b`も`Int`として選択できます。
+ここでは3つの引数を与える必要があります。
+1つ目は次の要素を累算器に加算する`Int -> Int -> Int`という型の関数です。
+2つ目は累算器の`Int`型の初期値です。
+3つ目は和を求めたい`Int`の配列です。
+最初の引数としては、加算演算子を使用できますし、累算器の初期値はゼロになります。
```text
> foldl (+) 0 (1 .. 5)
@@ -480,8 +501,8 @@ forall a b. (a -> b -> b) -> b -> Array a -> b
15
```
-`foldl`と`foldr`の違いを説明するために、畳み込み関数の選択が影響する例も書いてみましょう。
-加算関数の代わりに、文字列連結を使用して文字列を作ってみます。
+違いを説明するために、畳み込み関数の選択が大事になってくる例も書きましょう。
+加算関数の代わりに、文字列連結を使用して文字列を構築しましょう。
```text
> foldl (\acc n -> acc <> show n) "" [1,2,3,4,5]
@@ -497,7 +518,7 @@ forall a b. (a -> b -> b) -> b -> Array a -> b
((((("" <> show 1) <> show 2) <> show 3) <> show 4) <> show 5)
```
-それに対し、右畳み込みは以下に相当します。
+それに対し、右畳み込みは以下と等価です。
```text
((((("" <> show 5) <> show 4) <> show 3) <> show 2) <> show 1)
@@ -505,8 +526,8 @@ forall a b. (a -> b -> b) -> b -> Array a -> b
## 末尾再帰
-再帰はアルゴリズムを定義するための強力な手法ですが、問題も抱えています。
-JavaScriptで再帰関数を評価するとき、入力が大きすぎるとスタックオーバーフローでエラーを起こす可能性があるのです。
+再帰はアルゴリズムを指定する強力な手法ですが、問題も抱えています。
+JavaScriptで再帰関数を評価するとき、入力が大き過ぎるとスタックオーバーフローでエラーを起こす可能性があるのです。
PSCiで次のコードを入力すると、この問題を簡単に検証できます。
@@ -525,18 +546,20 @@ PSCiで次のコードを入力すると、この問題を簡単に検証でき
RangeError: Maximum call stack size exceeded
```
-これは問題です。関数型プログラミングの基本的な手法として再帰を採用しようとするなら、境界がない可能性がある再帰でも扱える方法が必要です。
+これは問題です。
+関数型プログラミングの標準的な手法として再帰を採用しようとするなら、境界がない再帰がありうるときでも扱える方法が必要です。
-PureScriptは _末尾再帰最適化_ (tail recursion optimization)
-の形でこの問題に対する部分的な解決策を提供しています。
+PureScriptは*末尾再帰最適化*の形でこの問題に対する部分的な解決策を提供しています。
-*補足*:この問題へのより完全な解決策としては、いわゆる*トランポリン*を使用したライブラリで実装する方法がありますが、それはこの章で扱う範囲を超えています。
-この内容に興味のある読者は[`free`](https://pursuit.purescript.org/packages/purescript-free)や[`tailrec`](https://pursuit.purescript.org/packages/purescript-tailrec)パッケージのドキュメントを参照してみてください。
+> *補足*:この問題へのより完全な解決策としては、いわゆる*トランポリン*を使用するライブラリで実装できますが、それはこの章で扱う範囲を超えています。
+> 興味のある読者は[`free`](https://pursuit.purescript.org/packages/purescript-free)や[`tailrec`](https://pursuit.purescript.org/packages/purescript-tailrec)パッケージのドキュメントをあたると良いでしょう。
-末尾再帰最適化を可能にする上で鍵となる観点は以下となります。 _末尾位置_ (tail position)
-にある関数の再帰的な呼び出しは、スタックフレームが確保されない _ジャンプ_
-に置き換えることができます。関数が戻るより前の最後の呼び出しであるとき、呼び出しが _末尾位置_
-にあるといいます。なぜこの例でスタックオーバーフローが見られたのかはこれが理由です。この`f`の再帰呼び出しは、末尾位置 _ではない_ からです。
+末尾再帰最適化を可能にする上で鍵となる観点は以下となります。
+*末尾位置*にある関数の再帰的な呼び出しは*ジャンプ*に置き換えられます。
+このジャンプではスタックフレームが確保されません。
+関数が戻るより前の最後の呼び出しであるとき、呼び出しが*末尾位置*にあるといいます。
+なぜ先の例でスタックオーバーフローが見られたのかはこれが理由です。
+`f`の再帰呼び出しが末尾位置*でなかったからです。
実際には、PureScriptコンパイラは再帰呼び出しをジャンプに置き換えるのではなく、再帰的な関数全体を _whileループ_ に置き換えます。
@@ -546,13 +569,14 @@ PureScriptは _末尾再帰最適化_ (tail recursion optimization)
{{#include ../exercises/chapter4/test/Examples.purs:factorialTailRec}}
```
-`fact`への再帰呼び出しは、この関数の中で起こる最後のものである、つまり末尾位置にあることに注意してください。
+`factorialTailRec`への再帰呼び出しがこの関数の最後にある点に注目してください。
+つまり末尾位置にあるのです。
-## 累積器
+## 累算器
-末尾再帰ではない関数を末尾再帰関数に変える一般的な方法としては、 _累積器引数_ (accumulator parameter)
-を使用する方法があります。累積器引数は関数に追加される余剰の引数で返り値を _累積_
-するものです。これは結果を累積するために返り値を使うのとは対称的です。
+末尾再帰ではない関数を末尾再帰関数に変える一般的な方法は、*累算器引数*を使用することです。
+累算器引数は関数に追加される余剰の引数で、返り値を*累算*するものです。
+これは結果を累算するために返り値を使うのとは対照的です。
例えば章の初めに示した`length`関数を再考しましょう。
@@ -571,19 +595,23 @@ length arr =
{{#include ../exercises/chapter4/test/Examples.purs:lengthTailRec}}
```
-ここでは、配列を逆転させる作業を補助関数`length'`に委譲しています。`length'`は末尾再帰です。その唯一の再帰呼び出しは、最後の場合の末尾位置にあります。これは、生成されたコードが
-_whileループ_ となり、大きな入力でもスタックが溢れないことを意味します。
+ここでは補助関数`length'`に委譲しています。
+この関数は末尾再帰です。
+その唯一の再帰呼び出しは、最後の場合の末尾位置にあります。
+つまり、生成されるコードは*whileループ*となり、大きな入力でもスタックが溢れません。
-`lengthTailRec`の実装を理解するために補助関数`length'`に着目しましょう。この関数は必然的に累積器引数を使って追加の状態……これは部分的な結果です……を保持しています。0から始まり、入力の配列中の全ての要素それぞれについて1ずつ足されて大きくなっていきます。
+`lengthTailRec`の実装を理解する上では、補助関数`length'`が基本的に累算器引数を使って追加の状態を保持していることに注目してください。
+追加の状態とは、部分的な結果です。
+0から始まり、入力の配列中の全ての各要素について1ずつ足されて大きくなっていきます。
-累積器を「状態」と考えることもできますが、直接には変更されているわけではないことにも注意してください。
+なお、累算器を「状態」と考えることもできますが、直接には変更されていません。
## 明示的な再帰より畳み込みを選ぼう
-末尾再帰を使用して再帰関数を記述できれば末尾再帰最適化の恩恵を受けることができるので、全ての関数をこの形で書こうとする誘惑にかられます。
+末尾再帰を使用して再帰関数を記述できれば末尾再帰最適化の恩恵を受けられるので、全ての関数をこの形で書こうとする誘惑にかられます。
しかし、多くの関数は配列やそれに似たデータ構造に対する折り畳みとして直接書くことができることを忘れがちです。
`map`や`fold`のような組み合わせの部品を使って直接アルゴリズムを書くことには、コードの単純さという利点があります。
-これらの部品はよく知られており、明示的な再帰よりもアルゴリズムの*意図*をよりはっきりとさせるのです。
+これらの部品はよく知られており、だからこそ明示的な再帰よりもアルゴリズムの*意図*がより良く伝わるのです。
例えば`foldr`を使って配列を反転できます。
@@ -607,13 +635,13 @@ _whileループ_ となり、大きな入力でもスタックが溢れないこ
2. (普通。テストなし)関数`foldl (==) false xs`が真を返すような配列`xs`とはどのようなものか説明してください。
言い換えると、「関数は`xs`が……を含むときに`true`を返す」という文を完成させることになります。
3. (普通)末尾再帰の形式を取っていること以外は`fib`と同じような関数`fibTailRec`を書いてください。
- *手掛かり*:累積器引数を使ってください。
+ *手掛かり*:累算器引数を使ってください。
4. (普通)`foldl`を使って`reverse`を書いてみましょう。
## 仮想ファイルシステム
-この節では、これまで学んだことを応用して、模擬的なファイルシステムで動作する関数を書いていきます。
-事前に定義されたAPIで動作するように、マップ、畳み込み、及びフィルタを使用します。
+この節ではこれまで学んだことを応用してファイルシステムのモデルを扱う関数を書きます。
+事前に定義されたAPIを扱う上でマップ、畳み込み、及びフィルタを使用します。
`Data.Path`モジュールでは、次のように仮想ファイルシステムのAPIが定義されています。
@@ -621,7 +649,7 @@ _whileループ_ となり、大きな入力でもスタックが溢れないこ
- ルートディレクトリを表すパス`root`があります。
- `ls`関数はディレクトリ内のファイルを列挙します。
- `filename`関数は`Path`のファイル名を返します。
-- `size`関数は`Path`が示すファイルの大きさを返します。
+- `size`関数はファイルを表す`Path`のファイルの大きさを返します。
- `isDirectory`関数はファイルかディレクトリかを調べます。
型について言うと、次のような型定義があります。
@@ -655,19 +683,23 @@ true
[/bin/,/etc/,/home/]
```
-`Test.Examples`モジュールでは`Data.Path`APIを使用する関数を定義しています。
+`Test.Examples`モジュールでは`Data.Path` APIを使用する関数を定義しています。
`Data.Path`モジュールを変更したり定義を理解したりする必要はありません。
全て`Test.Examples`モジュールだけで作業します。
## 全てのファイルの一覧
-それでは、ディレクトリの中身を含めた全てのファイルを列挙する関数を書いてみましょう。この関数は以下のような型を持つでしょう。
+それでは、ディレクトリの中身を含めた全てのファイルを深く列挙する関数を書いてみましょう。
+この関数は以下のような型を持つでしょう。
```haskell
{{#include ../exercises/chapter4/test/Examples.purs:allFiles_signature}}
```
-再帰を使ってこの関数を定義できます。まずは`ls`を使用してディレクトリの直接の子を列挙します。それぞれの子について再帰的に`allFiles`を適用すると、それぞれパスの配列が返ってくるでしょう。同時に`concatMap`を適用すると、この結果を平坦にできます。
+再帰を使ってこの関数を定義できます。
+`ls`を使うとディレクトリ直下の子が列挙されます。
+それぞれの子について再帰的に`allFiles`を適用すると、それぞれパスの配列が返ります。
+`concatMap`を使うと、`allFiles`を適用して平坦化するまでを一度にできます。
最後に、cons演算子`:`を使って現在のファイルも含めます。
@@ -675,8 +707,8 @@ true
{{#include ../exercises/chapter4/test/Examples.purs:allFiles_implementation}}
```
-*補足*:実はcons演算子`:`は、不変な配列に対して効率性が悪いので、一般的には推奨されません。
-連結リストやシーケンスなどの他のデータ構造を使用すると、効率性を向上させることができます。
+> *補足*:実はcons演算子`:`は、不変な配列に対して効率性が悪いので、一般的には推奨されません。
+> 連結リストやシーケンスなどの他のデータ構造を使用すると、効率性を向上させられます。
それではPSCiでこの関数を試してみましょう。
@@ -694,8 +726,9 @@ do記法で配列内包表記を使ってもこの関数を書くことができ
逆向きの矢印は配列から要素を選択するのに相当することを思い出してください。
最初の工程は引数の直接の子から要素を選択することです。
-それから、単にそのファイルに対してこの再帰関数を呼びします。
-do記法を使用しているので、再帰的な結果を全て連結する`concatMap`が暗黙に呼び出されています。
+それからそのファイルに対して再帰関数を呼び出します。
+do記法を使用しているので`concatMap`が暗黙に呼び出されています。
+この関数は再帰的な結果を全て連結します。
新しいバージョンは次のようになります。
@@ -703,7 +736,9 @@ do記法を使用しているので、再帰的な結果を全て連結する`co
{{#include ../exercises/chapter4/test/Examples.purs:allFiles_2}}
```
-PSCiで新しいコードを試してみてください。同じ結果が返ってくるはずです。どちらのほうがわかりやすいかの選定はお任せします。
+PSCiで新しいコードを試してみてください。
+同じ結果が返ってくるはずです。
+どちらのほうがわかりやすいかの選定はお任せします。
## 演習
@@ -722,11 +757,11 @@ PSCiで新しいコードを試してみてください。同じ結果が返っ
```
*手掛かり*:この関数をdo記法を使った配列内包表記で書いてみましょう。
- 3. (難しい)`Path`を取って`Path`に最大のファイルと最小のファイルを1つずつ含む配列を返す関数`largestSmallest`を書いてください。
- *補足*:空配列や1要素の配列を返すことで、`Path`にゼロか1個のファイルがある場合についても考慮してください。
+ 3. (難しい)`Path`中の最大のファイルと最小のファイルを1つずつ含む配列を返す関数`largestSmallest`を書いてください。
+ *補足*:空配列や1要素の配列を返すことで、`Path`にそれぞれゼロか1個のファイルがある場合についても考慮してください。
## まとめ
-この章では、アルゴリズムを簡潔に表現する手段として、PureScriptでの再帰の基本を説明しました。
+この章ではアルゴリズムを簡潔に表現するためにPureScriptでの再帰の基本を押さえました。
また、独自の中置演算子や、マップ、絞り込みや畳み込みなどの配列に対する標準関数、及びこれらの概念を組み合わせた配列内包表記を導入しました。
-最後に、スタックオーバーフローエラーを回避するために末尾再帰を使用することの重要性、累積器引数を使用して末尾再帰形に関数を変換する方法を示しました。
+最後に、スタックオーバーフローエラーを回避するために末尾再帰を使用することの重要性、累算器引数を使用して末尾再帰形に関数を変換する方法を示しました。
diff --git a/text-ja/chapter5.md b/text-ja/chapter5.md
index 3afe1547..3d08a94c 100644
--- a/text-ja/chapter5.md
+++ b/text-ja/chapter5.md
@@ -2,22 +2,25 @@
## この章の目標
-この章では、代数的データ型とパターン照合という、2つの新しい概念を導入します。また、行多相というPureScriptの型システムの興味深い機能についても簡単に取り扱います。
+この章では、代数的データ型とパターン照合という、2つの新しい概念を導入します。
+また、行多相というPureScriptの型システムの興味深い機能についても簡単に取り扱います。
-パターン照合は関数型プログラミングにおける一般的な手法で、複数の場合に実装を分解することにより、開発者は水面下では複雑な動作をする関数を簡潔に書くことができます。
+パターン照合は関数型プログラミングにおける一般的な手法であり、開発者が簡潔に関数を書けるようになります。
+関数の実装を複数の場合に分解することにより、水面下の複雑なアイディアが表現されるのです。
-代数的データ型はPureScriptの型システムの機能であり、型のある言語において同等の水準の表現力を可能にしています。パターン照合とも密接に関連しています。
+代数的データ型はPureScriptの型システムの機能であり、型のある言語において同等の水準の表現力を可能にしています。
+パターン照合とも密接に関連しています。
-この章の目的は、代数的データ型やパターン照合を使用して、単純なベクターグラフィックスを描画し操作するためのライブラリを書くことです。
+この章の目的は、代数的データ型やパターン照合を使用して、単純なベクターグラフィックスを記述し操作するためのライブラリを書くことです。
## プロジェクトの準備
この章のソースコードはファイル `src/Data/Picture.purs`で定義されています。
-`Data.Picture`モジュールは、簡単な図形を表すデータ型 `Shape`や、図形の集合である型
-`Picture`、及びこれらの型を扱うための関数を定義しています。
+`Data.Picture`モジュールは簡単な図形を表すデータ型`Shape`やその図形の集合である型`Picture`を定義します。
+また、これらの型を扱うための関数もあります。
-このモジュールでは、データ構造の畳込みを行う関数を提供する `Data.Foldable`モジュールもインポートします。
+このモジュールでは、データ構造を畳込む関数を提供する`Data.Foldable`モジュールもインポートします。
```haskell
{{#include ../exercises/chapter5/src/Data/Picture.purs:module_picture}}
@@ -29,14 +32,16 @@
{{#include ../exercises/chapter5/src/Data/Picture.purs:picture_import_as}}
```
-こうすると型や関数をモジュール内で使用できるようになりますが、`Number.max`のように*修飾名*を使ったときに限定されます。重複したインポートを避けたり、どのモジュールからインポートされたのかを明らかにするのに役立ちます。
+こうすると型や関数をモジュール内で使用できるようになりますが、
+`Number.max`のように*修飾名*を使ったときに限定されます。
+重複したインポートを避けたり、どのモジュールからインポートされたのかを明らかにするのに役立ちます。
-*補足*:元のモジュールと同じモジュール名を修飾名に使用する必要はありません。
-`import Math as M`などのより短い名前にできますし、かなりよく見掛けます。
+> *補足*:元のモジュールと同じモジュール名を修飾名に使用する必要はありません。
+> `import Math as M`などのより短い名前にできますし、かなりよく見掛けます。
## 単純なパターン照合
-それではコード例を見ることから始めましょう。
+例を見ることから始めましょう。
パターン照合を使用して2つの整数の最大公約数を計算する関数は、次のようになります。
```haskell
@@ -45,13 +50,13 @@
このアルゴリズムはユークリッドの互除法と呼ばれています。
その定義をオンラインで検索すると、恐らく上記のコードによく似た数学の方程式が見つかるでしょう。
-パターン照合の利点の1つは、上記のようにコードを場合分けして定義でき、数学関数の定義と似たような簡潔で宣言型なコードを書くことができることです。
+パターン照合の利点の1つは、コードを場合分けして定義でき、数学関数の仕様に似た単純で宣言型なコードを定義できることです。
パターン照合を使用して書かれた関数は、条件と結果の組み合わせによって動作します。
この定義の各行は*選択肢*や*場合*と呼ばれています。
等号の左辺の式は*パターン*と呼ばれており、それぞれの場合は空白で区切られた1つ以上のパターンで構成されています。
場合の集まりは、等号の右側の式が評価され値が返される前に、引数が満たさなければならない条件を表現しています。
-それぞれの場合は上からこの順番に試されていき、最初に入力に適合した場合が返り値を決定します。
+それぞれの場合は上からこの順番に試されていき、最初にパターンが入力に照合した場合が返り値を決定します。
例えば`gcd`関数は次の手順で評価されます。
@@ -63,21 +68,21 @@
なお、パターンでは値を名前に束縛できます。
この例の各行では `n`や`m`という名前の何れかまたは両方に入力された値を束縛しています。
-さまざまな種類のパターンについて学んでいくうちに、それぞれの種類のパターンが入力の引数から名前を選ぶさまざまな方法に対応することがわかるでしょう。
+様々な種類のパターンについて学んでいくうちに、それぞれの種類のパターンが入力の引数から名前を選ぶ様々な方法に対応することがわかるでしょう。
## 単純なパターン
上記のコード例では、2種類のパターンを示しました。
-- `Int`型の値が正確に一致する場合にのみ適合する、整数直値パターン
+- `Int`型の値が正確に一致する場合にのみ照合する、整数直値パターン
- 引数を名前に束縛する、変数パターン
単純なパターンには他にも種類があります。
- `Number`、`String`、`Char`、そして`Boolean`といった直値
-- どんな引数とも適合するが名前に束縛はしない、アンダースコア (`_`) で表されるワイルドカードパターン
+- どんな引数とも照合するが名前に束縛はしない、アンダースコア (`_`) で表されるワイルドカードパターン
-ここではこれらの単純なパターンを使用して、もう2つ例を示します。
+これらの単純なパターンを使用する実演として、もう2つの例が以下です。
```haskell
{{#include ../exercises/chapter5/src/ChapterExamples.purs:fromString}}
@@ -89,16 +94,19 @@ PSCiでこれらの関数を試してみてください。
## ガード
-ユークリッドの互除法の例では、`m > n`のときと `m <= n`のときの2つに分岐するために `if .. then .. else`式を使っていました。こういうときには他に _ガード_ (guard) を使うという選択肢もあります。
+ユークリッドの互除法の例では、`m > n`のときと`m <= n`のときの2つの選択肢の間を切り替えるために`if .. then .. else`式を使いました。
+こういうときには*ガード*を使うという他の選択肢もあります。
-ガードはパターンによる制約に加えて満たされなくてはいけない真偽値の式です。
-ガードを使用してユークリッドの互除法を書き直すと、次のようになります。
+ガードとは、パターンにより課された制約に加えて満たされなくてはいけない真偽値の式です。
+ガードを使用してユークリッドのアルゴリズムを書き直すと、次のようになります。
```haskell
{{#include ../exercises/chapter5/src/ChapterExamples.purs:gcdV2}}
```
-この場合、3行目ではガードを使用して、最初の引数が第2引数よりも厳密に大きいという条件を付け加えています。最後の行でのガードは式`otherwise`を使っており、キーワードのようにも見えますが、実際はただの`Prelude`にある通常の束縛です。
+この場合、3行目ではガードを使用して、最初の引数が第2引数よりも厳密に大きいという条件を課しています。
+最後の行でのガードは式`otherwise`を使っています。
+これはキーワードのようにも見えますが、実際はただの`Prelude`にある普通の束縛です。
```text
> :type otherwise
@@ -121,7 +129,7 @@ true
数式\\( n! / k! (n - k)! \\)を使ってください。
ここで \\( ! \\) は前に書いた階乗関数です。
*手掛かり*:パターン照合を使って特殊な場合を取り扱ってください。
- 長い時間が掛かったりコールスタックのエラーでクラッシュしたりしたら、もっと使用例を追加してみてください。
+ 長い時間が掛かったりコールスタックのエラーでクラッシュしたりしたら、特殊な場合を更に追加してみてください。
1. (普通)[_パスカルの法則_](https://en.wikipedia.org/wiki/Pascal%27s_rule)を使って前の演習と同じ2項係数を計算する関数`pascal`を書いてください。
## 配列パターン
@@ -134,13 +142,13 @@ true
{{#include ../exercises/chapter5/src/ChapterExamples.purs:isEmpty}}
```
-次の関数では、長さ5の配列と適合し、配列の5つの要素をそれぞれ違った方法で束縛しています。
+次の関数では、長さ5の配列と照合し、配列の5つの要素をそれぞれ違った方法で束縛しています。
```haskell
{{#include ../exercises/chapter5/src/ChapterExamples.purs:takeFive}}
```
-最初のパターンは、第1要素と第2要素がそれぞれ0と1であるような、5要素の配列にのみ適合します。
+最初のパターンは、第1要素と第2要素がそれぞれ0と1であるような、5要素の配列にのみ照合します。
その場合、関数は第3要素と第4要素の積を返します。
それ以外の場合は、関数は0を返します。
例えばPSCiで試してみると次のようになります。
@@ -161,8 +169,9 @@ true
0
```
-配列の直値パターンでは、固定長の配列と一致させることはできますが、PureScriptは不特定の長さの配列を照合させる手段を提供していません。
-そのような方法で不変な配列を分解すると、実行速度が低下する可能性があるためです。
+配列の直値パターンでは、固定長の配列と一致させることはできます。
+しかしPureScriptは不特定の長さの配列を照合させる手段は全く提供して*いません*。
+そのような類の方法で不変な配列を分解すると、実行速度が低下する可能性があるためです。
このように照合できるデータ構造が必要な場合は、`Data.List`を使うことをお勧めします。
その他の操作について、より優れた漸近性能を提供するデータ構造も存在します。
@@ -187,7 +196,7 @@ true
> showPerson { first: x, last: y } = y <> ", " <> x
> :type showPerson
-forall r. { first :: String, last :: String | r } -> String
+forall (r :: Row Type). { first :: String, last :: String | r } -> String
```
この型変数 `r`は何でしょうか。
@@ -201,9 +210,9 @@ PSCiで`showPerson`を使ってみると、面白いことがわかります。
"Freeman, Phil"
```
-レコードにそれ以外のフィールドが追加されていても、`showPerson`関数はそのまま動作するのです。レコードに少なくとも型が`String`であるようなフィールド
-`first`と `last`が含まれていれば、関数適用は正しく型付けされます。しかし、フィールドが _不足_ していると、
-`showPerson`の呼び出しは _不正_ となります。
+レコードにそれ以外のフィールドが追加されていても、`showPerson`関数はそのまま動作するのです。
+レコードに少なくとも型が`String`であるようなフィールド`first`と`last`が含まれていれば、関数適用は正しく型付けされます。
+しかし、フィールドが*不足*していると、`showPerson`の呼び出しは*不正*となります。
```text
> showPerson { first: "Phil" }
@@ -221,12 +230,12 @@ _厳密に_ `first`と`last`フィールドしかないレコードのみを受
> showPerson p = p.last <> ", " <> p.first
```
-この場合も、 PSCiは先ほどと同じ型を推論するでしょう。
+そしてPSCiは同じ型を推論することでしょう。
## レコード同名利用
-`showPerson`関数は引数内のレコードと照合し、`first`と`last`フィールドを`x`と
-`y`という名前の値に束縛していたのでした。別の方法として、フィールド名自体を再利用するだけで、このようなパターン照合を次のように単純化できます。
+`showPerson`関数は引数内のレコードと照合し、`first`と`last`フィールドを`x`と`y`という名前の値に束縛していたのでした。
+別の方法として、フィールド名自体を再利用してこのような類のパターン照合を次のように単純化できます。
```haskell
{{#include ../exercises/chapter5/src/ChapterExamples.purs:showPersonV2}}
@@ -242,11 +251,14 @@ _厳密に_ `first`と`last`フィールドしかないレコードのみを受
{{#include ../exercises/chapter5/src/ChapterExamples.purs:unknownPerson}}
```
-こうすると、状況によってはコードの可読性向上に役立ちます。
+こうすると、状況によってはコードの可読性が向上します。
## 入れ子になったパターン
-配列パターンとレコードパターンはどちらも小さなパターンを組み合わせることで大きなパターンを構成しています。これまでの例ではほとんどの場合で配列パターンとレコードパターンの内部に単純なパターンを使用していましたが、パターンを自由に*入れ子*にできることも知っておくのが大切です。入れ子になったパターンを使うと、潜在的に複雑なデータ型に対して条件分岐を用いて関数を定義できるようになります。
+配列パターンとレコードパターンはどちらも小さなパターンを組み合わせることで大きなパターンを構築しています。
+これまでのほとんどの例では配列パターンとレコードパターンの内部で単純なパターンを使用していました。
+しかし特筆すべきこととして、パターンは自由に*入れ子*にできます。
+これにより潜在的に複雑なデータ型についての条件を使って関数を定義できます。
例えばこのコードは2つのレコードパターンを組み合わせています。
@@ -265,8 +277,8 @@ _厳密に_ `first`と`last`フィールドしかないレコードのみを受
{{#include ../exercises/chapter5/src/ChapterExamples.purs:sortPair}}
```
-このようにすれば対が既に整列されているときに新しい配列を割り当てなくて済みます。なおもし入力の配列が _厳密に_
-2つの要素を含んでいなければ、たとえ整列されていなかったとしても、この関数は単に元のまま変えずに返しています。
+このようにすれば対が既に整列されているときに新しい配列を割り当てなくて済みます。
+なお、もし入力の配列が*厳密に*2つの要素を含んでいなければ、たとえ整列されていなかったとしても、この関数は単に元のまま変えずに返します。
## 演習
@@ -274,12 +286,16 @@ _厳密に_ `first`と`last`フィールドしかないレコードのみを受
1. (普通)行多相を考慮すると、 `sameCity`関数の最も一般的な型は何でしょうか。
先ほど定義した`livesInLA`関数についてはどうでしょうか。
*補足*:この演習にテストはありません。
-1. (普通)配列直値パターンを使って、1要素の配列の唯一のメンバーを抽出する関数`fromSingleton`を書いてみましょう。1要素だけを持つ配列でない場合、関数は指定された既定値を返します。この関数は
- `forall a. a -> Array a -> a`という型を持っています。
+1. (普通)配列直値パターンを使って、1要素の配列の唯一のメンバーを抽出する関数`fromSingleton`を書いてみましょう。
+ 1要素だけを持つ配列でない場合、関数は与えられた既定値を返します。
+ この関数は`forall a. a -> Array a -> a`という型を持ちます。
-## Case式
+## case式
-パターンは最上位にある関数宣言だけに現れるわけではありません。`case`式を使って計算の途中の値に対してパターン照合を使うことができます。case式には無名関数に似たような便利さがあります。関数に名前を与えることがいつも望ましいわけではないように、パターン照合を使いたいためだけに関数に名前をつけるようなことを避けられるようになります。
+パターンが現れるのは最上位にある関数宣言だけではありません。
+`case`式を使う計算中の途中の値に対してパターン照合を使えます。
+case式には無名関数に似た便利さがあります。
+関数に名前を与えることがいつも望ましいわけではないように、パターンを使いたいためだけに関数に名前をつけるようなことを避けられるようになります。
例を示しましょう。
次の関数は、配列の「最長ゼロ末尾」(和がゼロであるような、最も長い配列の末尾)を計算します。
@@ -300,12 +316,15 @@ _厳密に_ `first`と`last`フィールドしかないレコードのみを受
[-1, -2, 3]
```
-この関数は場合ごとの分析によって動作します。もし配列が空なら、唯一の選択肢は空の配列を返すことです。配列が空でない場合は、更に2つの場合に分けるためにまず
-`case`式を使用します。配列の合計がゼロであれば、配列全体を返します。そうでなければ、配列の残りに対して再帰します。
+この関数は場合毎の分析によって動作します。
+もし配列が空なら、唯一の選択肢は空の配列を返すことです。
+配列が空でない場合は、更に2つの場合に分けるためにまず`case`式を使用します。
+配列の合計がゼロであれば、配列全体を返します。
+そうでなければ、配列の残りに対して再帰します。
## パターン照合の失敗と部分関数
-case式のパターンを順番に照合していって、もし選択肢の何れの場合も入力が適合しなかった時は何が起こるのでしょうか。
+case式のパターンを順番に照合していって、どの選択肢の場合も入力が照合しなかった時はどうなるのでしょう。
この場合、*パターン照合失敗*によって、case式は実行時に失敗します。
簡単な例でこの動作を見てみましょう。
@@ -316,7 +335,9 @@ case式のパターンを順番に照合していって、もし選択肢の何
{{#include ../exercises/chapter5/src/ChapterExamples.purs:partialFunction}}
```
-この関数は単一の場合しか含んでおらず、単一の入力である`true`にのみ照合します。このファイルをコンパイルしてPSCiでそれ以外の値を与えてテストすると、実行時エラーが発生します。
+この関数は単一の場合しか含んでいません。
+そしてその場合は単一の入力である`true`にのみ照合します。
+このファイルをコンパイルしてPSCiでそれ以外の値を与えて試すと実行時エラーが発生します。
```text
> partialFunction false
@@ -324,17 +345,16 @@ case式のパターンを順番に照合していって、もし選択肢の何
Failed pattern match
```
-どんな入力の組み合わせに対しても値を返すような関数は _全関数_ (total function) と呼ばれ、そうでない関数は _部分的_
-(partial) であると呼ばれます。
+どんな入力の組み合わせに対しても値を返すような関数は*全関数*と呼ばれ、そうでない関数は*部分的*であると呼ばれます。
一般的には、可能な限り全関数として定義したほうが良いと考えられています。
もし関数が何らかの妥当な入力の集合について結果を返さないことがわかっているなら、大抵は失敗であることを示すことができる値を返すほうがよいでしょう。
例えば何らかの`a`についての型`Maybe a`で、妥当な結果を返せないときは`Nothing`を使います。
この方法なら、型安全な方法で値の有無を示すことができます。
-PureScriptコンパイラは、パターンマッチが不完全で関数が全関数ではないことを検出するとエラーを生成します。
-部分関数が安全である場合、`unsafePartial`関数を使ってこれらのエラーを抑制できます(その部分関数が安全だと言い切れるなら)。
-もし上記の `unsafePartial`関数の呼び出しを取り除くと、コンパイラは次のエラーを生成します。
+PureScriptコンパイラは、パターン照合が不完全であるために関数が全関数ではないことが検出されると、エラーを出します。
+`unsafePartial`関数を使うとこうしたエラーを抑制できます(ただしその部分関数が安全だと言い切れるなら)。
+もし上記の`unsafePartial`関数の呼び出しを取り除くと、コンパイラは次のエラーを出します。
```text
A case expression could not be determined to cover all inputs.
@@ -360,9 +380,10 @@ partialFunction true = true
Partial => Boolean -> Boolean
```
-本書ではのちに`=>`記号を含むいろいろな型を見ることになります(これらは*型クラス*に関連しています)。しかし、今のところは、PureScriptは型システムを使って部分関数を把握していることと、安全な場合に型検証器に明示する必要があることを確認すれば充分です。
+本書では以降、`=>`記号が絡む(*型クラス*に関連する)型をもっと見ていきます。
+しかし現時点では、PureScriptは型システムを使って部分関数を把握していることと、安全な場合に型検証器に明示する必要があることを確認すれば充分です。
-コンパイラは、定義されたパターンが _冗長_ であることを検出した場合(前の方に定義されたパターンのみに一致する場合)でも警告を生成します。
+コンパイラは、*冗長*な場合を検出したとき(つまり、その場合より前の方に定義された場合にのみ一致するとき)などにも警告を出します。
```haskell
redundantCase :: Boolean -> Boolean
@@ -379,7 +400,8 @@ A case expression contains unreachable cases:
false
```
-*補足*:PSCiは警告を表示しないので、この例を再現するには、この関数をファイルとして保存し、 `pulp build`を使ってコンパイルします。
+> *補足*:PSCiは警告を表示しません。
+> そのため、この例を再現するには、この関数をファイルとして保存し、`spago build`を使ってコンパイルします。
## 代数的データ型
@@ -394,10 +416,10 @@ A case expression contains unreachable cases:
しかし、この方針は大きな欠点を1つ抱えています。
`Shape`を抽象的に扱うためには、実行したいと思う可能性のある全ての操作を事前に把握し、`Shape`インターフェースに定義する必要があるのです。
-このため、モジュール性を壊さずに新しい操作を追加することが難しくなります。
+モジュール性を壊さずに新しい操作を追加することが難しくなります。
-もし図形の種類が事前にわかっているなら、代数的データ型はこうした問題を解決する型安全な方法を提供します。モジュール性のある方法で
-`Shape`に新たな操作を定義し、型安全性を維持できます。
+もし図形の種類が事前にわかっているなら、代数的データ型はこうした問題を解決する型安全な方法を提供します。
+モジュール性のある方法で `Shape`に新たな操作を定義しつつ、型安全性を維持できます。
代数的データ型としてどのように`Shape`が表現されるかを次に示します。
@@ -411,12 +433,14 @@ A case expression contains unreachable cases:
`Shape`は、中央 `Point`と半径(数値)を持つ `Circle`か、`Rectangle`、 `Line`、 `Text`の何れかです。
他に`Shape`型の値を構築する方法はありません。
-代数的データ型の定義はキーワード `data`から始まり、それに新しい型の名前と任意個の型引数が続きます。その型の構築子(あるいは _データ構築子_
-(data constructor))は等号の後に定義され、パイプ文字 (`|`)
-で区切られます。ADTの構築子が持つデータは原始型に限りません。構築子にはレコード、配列、また他のADTさえも含むことができます。
+代数的データ型 (algebraic data type; ADT)
+の定義はキーワード`data`から始まり、それに新しい型の名前と任意個の型引数が続きます。
+その型の構築子(これを*データ構築子*と言います)は等号の後に定義され、パイプ文字 (`|`) で区切られます。
+ADTの構築子が持つデータは原始型に限りません。
+構築子にはレコード、配列、また他のADTさえも含められます。
それではPureScriptの標準ライブラリから別の例を見てみましょう。
-オプショナルな値を定義するのに使われる `Maybe`型を本書の冒頭で扱いました。
+省略可能な値を定義するのに使われる `Maybe`型を本書の冒頭で扱いました。
`maybe`パッケージでは `Maybe`を次のように定義しています。
```haskell
@@ -426,7 +450,7 @@ data Maybe a = Nothing | Just a
この例では型引数 `a`の使用方法を示しています。パイプ文字を「または」と読むことにすると、この定義は「`Maybe a`型の値は、無い
(`Nothing`) か、ただの (`Just`) 型 `a`の値だ」とほぼ英語のように読むことができます。
-なおデータ定義のどこにも構文`forall a`を使っていません。
+なお、データ定義のどこにも構文`forall a`を使っていません。
`forall`構文は関数には必須ですが、`data`によるADTや`type`での型別称を定義するときは使われません。
データ構築子は再帰的なデータ構造を定義するためにも使用できます。更に例を挙げると、要素が型
@@ -469,14 +493,15 @@ data List a = Nil | Cons a (List a)
各構築子はパターンとして使用でき、構築子への引数はそのパターンで束縛できます。
`showShape`の最初の場合を考えてみましょう。
-もし `Shape`が `Circle`構築子に適合した場合、2つの変数パターン `c`と
-`r`を使って`Circle`の引数(中心と半径)がスコープに導入されます。その他の場合も同様です。
+もし `Shape`が `Circle`構築子に照合した場合、2つの変数パターン `c`と
+`r`を使って`Circle`の引数(中心と半径)がスコープに導入されます。
+その他の場合も同様です。
## 演習
1. (簡単)`Circle`(型は`Shape`)を構築する関数`circleAtOrigin`を書いてください。
中心は原点にあり、半径は`10.0`です。
-1. (普通)`Shape`を原点を中心として`2.0`倍に拡大する関数`doubleScaleAndCenter`を書いてみましょう。
+1. (普通)原点を中心として`Shape`の大きさを`2.0`倍に拡大する関数`doubleScaleAndCenter`を書いてみましょう。
1. (普通)`Shape`からテキストを抽出する関数`shapeText`を書いてください。
この関数は`Maybe
String`を返しますが、もし入力が`Text`を使用して構築されたのでなければ、返り値には`Nothing`構築子を使ってください。
@@ -545,14 +570,19 @@ a`の*単一*の値を期待します)。
newtype CouldError err a = CouldError (Either err a)
```
-また、newtypeの構築子はよくnewtype自身と同じ名前を持つことがあります。ただこれは必須ではありません。例えば別個の名前であっても妥当です。
+また、newtypeの構築子はよくnewtype自身と同じ名前を持つことがあります。
+ただこれは必須ではありません。
+例えば別個の名前であっても正しいものです。
```haskell
{{#include ../exercises/chapter5/src/ChapterExamples.purs:Coulomb}}
```
-この場合`Coulomb`は*型構築子*(引数はゼロ)で`MakeCoulomb`は _データ構築子_
-です。これらの構築子は異なる名前空間に属しており、`Volt`の例でそうだったように、名前に一意性があります。これは全てのADTについて言えることです。なお、型構築子とデータ構築子は異なる名前を持つことができますが、実際には同じ名前を共有するのが普通です。前述の`Amp`と`Volt`の場合がこれです。
+この場合`Coulomb`は(引数ゼロの)*型構築子*で、`MakeCoulomb`は*データ構築子*です。
+これらの構築子は異なる名前空間に属しており、`Volt`の例でそうだったように、名前には一意性があります。
+これは全てのADTについて言えることです。
+なお、型構築子とデータ構築子には異なる名前を付けられますが、実際には同じ名前を共有するのが普通です。
+前述の`Amp`と`Volt`の場合がこれです。
newtypeの別の応用は、実行時表現を変えることなく、既存の型に異なる _挙動_ を加えることです。その利用例については次章で _型クラス_
をお話しするときに押さえます。
@@ -571,7 +601,8 @@ calculateWattage :: Amp -> Volt -> Watt
これまで定義してきたデータ型を使って、ベクターグラフィックスを扱う簡単なライブラリを作成していきましょう。
-ただの `Shape`の配列であるような、 `Picture`という型同義語を定義しておきます。
+`Picture`という型同義語を定義しておきます。
+これはただの`Shape`の配列です。
```haskell
{{#include ../exercises/chapter5/src/Data/Picture.purs:Picture}}
@@ -608,7 +639,7 @@ $ spago repl
{{#include ../exercises/chapter5/src/Data/Picture.purs:Bounds}}
```
-`Picture`内の `Shape`の配列を走査し、最小の外接矩形を累積するため、`bounds`には `Data.Foldable`の
+`Picture`内の `Shape`の配列を走査し、最小の外接矩形を累算するため、`bounds`には `Data.Foldable`の
`foldl`関数を使用しています。
```haskell
@@ -618,9 +649,10 @@ $ spago repl
基底の場合では、空の
`Picture`の最小外接矩形を求める必要がありますが、`emptyBounds`で定義される空の外接矩形がその条件を満たしています。
-累積関数 `combine`は `where`ブロックで定義されています。`combine`は
-`foldl`の再帰呼び出しで計算された外接矩形と、配列内の次の `Shape`を引数にとり、ユーザ定義の演算子
-`union`を使って2つの外接矩形の和を計算しています。`shapeBounds`関数は、パターン照合を使用して、単一の図形の外接矩形を計算します。
+累算関数`combine`は`where`ブロックで定義されています。
+`combine`は`foldl`の再帰呼び出しで計算された外接矩形と、配列内の次の
+`Shape`を引数に取り、ユーザ定義の演算子`union`を使って2つの外接矩形の和を計算しています。
+`shapeBounds`関数は、パターン照合を使用して、単一の図形の外接矩形を計算します。
## 演習
@@ -636,8 +668,10 @@ $ spago repl
この章では、関数型プログラミングから基本的ながら強力なテクニックであるパターン照合を扱いました。複雑なデータ構造の一部分と照合するために、簡単なパターンの使い方だけではなく、配列パターンやレコードパターンを使った深さのあるデータ構造の一部分との照合方法を見てきました。
-また、この章ではパターン照合に密接に関連する代数的データ型を紹介しました。代数的データ型のおかげでデータ構造を簡潔に記述でき、新たな操作でデータ型を拡張する上でモジュール性のある方法がもたらされることを見てきました。
+また、この章ではパターン照合に密接に関連する代数的データ型も紹介しました。
+代数的データ型のおかげでデータ構造を簡潔に記述でき、新たな操作でデータ型を拡張する上で、モジュール性のある方法が齎されるのでした。
-最後に強力な抽象化である _行多相_ を扱いました。これにより多くの既存のJavaScript関数に型を与えられます。
+最後に*行多相*を扱いました。
+これは強力な抽象化をする型であり、これにより多くの既存のJavaScript関数に型を与えられます。
本書では今後も代数的データ型とパターン照合をいろんなところで使用するので、今のうちにこれらに習熟しておくと後で実を結ぶことでしょう。これ以外にも独自の代数的データ型を作成し、パターン照合を使用してそれらの型を使う関数を書いてみてください。
diff --git a/text-ja/chapter6.md b/text-ja/chapter6.md
index e2c79526..9dbe5b0c 100644
--- a/text-ja/chapter6.md
+++ b/text-ja/chapter6.md
@@ -2,9 +2,11 @@
## この章の目標
-この章では、PureScriptの型システムによって可能になる強力な抽象化の手法である、型クラスを導入します。
+この章では、PureScriptの型システムにより可能になっている強力な抽象化の形式を導入します。
+そう、型クラスです。
-この章ではデータ構造をハッシュ化するためのライブラリを題材に説明していきます。データ自身の構造について直接考えることなく複雑な構造のデータのハッシュ値を求める上で、型クラスの仕組みがどのようにして働くのかを見ていきます。
+データ構造をハッシュ化するためのライブラリを本章の動機付けの例とします。
+データ自体の構造について直接考えることなく複雑な構造のデータのハッシュ値を求める上で、型クラスの仕組みがどのように働くかを見ていきます。
また、PureScriptのPreludeや標準ライブラリに含まれる、標準的な型クラスも見ていきます。PureScriptのコードは概念を簡潔に表現するために型クラスの強力さに大きく依存しているので、これらのクラスに慣れておくと役に立つでしょう。
@@ -17,7 +19,7 @@
このプロジェクトには以下の依存関係があります。
-- `maybe`: オプショナルな値を表す `Maybe`データ型が定義されています。
+- `maybe`: 省略可能な値を表す `Maybe`データ型が定義されています。
- `tuples`: 値の組を表す `Tuple`データ型が定義されています。
- `either`: 非交和を表す `Either`データ型が定義されています。
- `strings`: 文字列を操作する関数が定義されています。
@@ -37,7 +39,7 @@ class Show a where
show :: a -> String
```
-このコードでは、型変数 `a`でパラメータ化された、`Show`という新しい _型クラス_ (type class) を宣言しています。
+このコードでは、型変数`a`を引数に取る`Show`という新しい*型クラス*を宣言しています。
型クラス _インスタンス_ には、型クラスで定義された関数の、その型に特殊化された実装が含まれています。
@@ -50,8 +52,8 @@ instance Show Boolean where
```
このコードは `showBoolean`という名前の型クラスのインスタンスを宣言します。
-PureScriptでは、生成されたJavaScriptの可読性を良くするために、型クラスインスタンスに名前をつけます。このとき、
-_`Boolean`型は `Show`型クラスに属している_ といいます。
+PureScriptでは、生成されたJavaScriptの可読性を良くするために、型クラスインスタンスに名前を付けられます。
+このとき、`Boolean`型は*`Show`型クラスに属している*といいます。
PSCiで、いろいろな型の値を`Show`型クラスを使って表示してみましょう。
@@ -68,7 +70,7 @@ PSCiで、いろいろな型の値を`Show`型クラスを使って表示して
"\"Hello World\""
```
-この例ではさまざまなプリミティブ型の値を `show`しましたが、もっと複雑な型を持つ値も`show`できます。
+この例では様々な原始型の値を `show`しましたが、もっと複雑な型を持つ値も`show`できます。
```text
> import Data.Tuple
@@ -82,7 +84,10 @@ PSCiで、いろいろな型の値を`Show`型クラスを使って表示して
"(Just \"testing\")"
```
-`show`の出力は、REPLに(あるいは`.purs`ファイルに)もう一度貼り付ければ、表示されたものを再作成できるような文字列であるべきです。以下では`logShow`を使っていますが、これは単に`show`と`log`を順に呼び出して、引用符なしに文字列を表示するものです。`unit`の表示は無視してください。第8章で`Effect`を調べるときに押さえます。`Effect`を持つものには`log`などがあります。
+`show`の出力は、REPLに(あるいは`.purs`ファイルに)貼り戻せば、表示されたものを再作成できるような文字列であるべきです。
+以下では`logShow`を使っていますが、これは単に`show`と`log`を順に呼び出すものであり、引用符なしに文字列が表示されます。
+`unit`の表示は無視してください。
+第8章で`log`のような`Effect`を調べるときに押さえます。
```text
> import Effect.Console
@@ -96,7 +101,7 @@ unit
unit
```
-型 `Data.Either`の値を表示しようとすると、興味深いエラーメッセージが表示されます。
+型 `Data.Either`の値を表示しようとすると、興味深いエラー文言が表示されます。
```text
> import Data.Either
@@ -110,10 +115,10 @@ has type variables which are not mentioned in the body of the type. Consider add
```
ここでの問題は `show`しようとしている型に対する
-`Show`インスタンスが存在しないということではなく、PSCiがこの型を推論できなかったということです。このエラーメッセージで _未知の型_
-`a`と表示されているのがそれです。
+`Show`インスタンスが存在しないということではなく、PSCiがこの型を推論できなかったということです。
+これは推論された型で*未知の型*`a`とされていることが示しています。
-`::`演算子を使って式に対して型注釈を加えると、PSCiが正しい型クラスインスタンスを選ぶことができるようになります。
+`::`演算子を使って式に註釈を付けてPSCiが正しい型クラスインスタンスを選べるようにできます。
```text
> show (Left 10 :: Either Int String)
@@ -122,7 +127,7 @@ has type variables which are not mentioned in the body of the type. Consider add
`Show`インスタンスを全く持っていない型もあります。
関数の型 `->`がその一例です。
-`Int`から `Int`への関数を `show`しようとすると、型検証器によってその旨のエラーメッセージが表示されます。
+`Int`から `Int`への関数を `show`しようとすると、型検証器によってその旨のエラー文言が表示されます。
```text
> import Prelude
@@ -158,15 +163,17 @@ No type class instance was found for
### Eq
-`Eq`型クラスは、2つの値が等しいかどうかを調べる`eq`関数を定義しています。
-等値演算子 (`==`) は`eq`の別名にすぎません。
+`Eq`型クラスは`eq`関数を定義しています。
+この関数は2つの値について等値性を調べます。
+実は`==`演算子は`eq`の別名です。
```haskell
class Eq a where
eq :: a -> a -> Boolean
```
-なお、異なる型の2つの値を比較しても意味がありませんから、等しいにせよ等しくないにせよ2つの引数は同じ型を持つ必要があります。
+何れにせよ、2つの引数は同じ型を持つ必要があります。
+異なる型の2つの値を等値性に関して比較しても意味がありません。
PSCiで `Eq`型クラスを試してみましょう。
@@ -180,7 +187,9 @@ true
### Ord
-`Ord`型クラスは順序付け可能な型に対して2つの値を比較する `compare`関数を定義します。比較演算子 `<`、 `>`と、その仲間の厳密な大小比較ではない`<=`、 `>=`も、`compare`を使って定義されます。
+`Ord`型クラスは`compare`関数を定義します。
+この関数は2つの値を比較するのに使えるもので、その値の型は順序付けに対応したものです。
+比較演算子`<`、`>`と厳密な大小比較ではない`<=`、`>=`は`compare`を用いて定義されます。
*補足*:以下の例ではクラスシグネチャに`<=`が含まれています。
この文脈での`<=`の使われ方はEqがOrdの上位クラスであり、比較演算子としての`<=`の用途を表す意図はありません。
@@ -193,11 +202,12 @@ class Eq a <= Ord a where
compare :: a -> a -> Ordering
```
-`compare`関数は2つの値を比較して `Ordering`の3つの値のうち何れかを返します。
+`compare`関数は2つの値を比較して`Ordering`を返します。
+これには3つ選択肢があります。
-- `LT`- 最初の引数が2番目の値より小さいとき
-- `EQ`- 最初の引数が2番目の値と等しい(または比較できない)とき
-- `GT`- 最初の引数が2番目の値より大きいとき
+- `LT`- 最初の引数が2番目の値より小さいとき。
+- `EQ`- 最初の引数が2番目の値と等しいとき。
+- `GT`- 最初の引数が2番目の値より大きいとき。
ここでも`compare`関数についてPSCiで試してみましょう。
@@ -211,19 +221,19 @@ LT
### Field
-`Field`型クラスは加算、減算、乗算、除算などの数値演算子が使える型を示します。必要に応じて再利用できるように、これらの演算子を抽象化するわけです。
+`Field`型クラスは加算、減算、乗算、除算などの数値演算子に対応した型を示します。
+これらの演算子を抽象化して提供されているので、適切な場合に再利用できるのです。
-*補足*:型クラス `Eq`や `Ord`のクラスとちょうど同じように、`Field`型のクラスはPureScriptコンパイラで特別に扱われ、`1 +
-2 * 3`のような単純な式は単純なJavaScriptへと変換されます。
-型クラスの実装に基いて呼び出される関数呼び出しとは対照的です。
+> *補足*:型クラス`Eq`ないし`Ord`とちょうど同じように、`Field`型クラスはPureScriptコンパイラで特別に扱われ、`1 + 2 * 3`のような単純な式は単純なJavaScriptへと変換されます。
+> 型クラスの実装に基いて呼び出される関数呼び出しとは対照的です。
```haskell
class EuclideanRing a <= Field a
```
-`Field`型クラスは、幾つかのより抽象的な*上位クラス* (Super Class) が組み合わさってできています。
-これは、その型は`Field`型クラスの操作の全てを提供しているわけではないが、その一部を提供する、というように抽象的に説明できます。
-例えば、自然数の型は加算及び乗算については閉じていますが、減算については閉じていません。
+`Field`型クラスは、幾つかのより抽象的な*上位クラス*が組み合わさってできています。
+このため、`Field`の操作の全てを提供しているわけではないが、その一部を提供する型について抽象的に説明できます。
+例えば、自然数の型は加算及び乗算については閉じていますが、減算については必ずしも閉じていません。
そのため、この型は`Semiring`クラス(これは`Num`の上位クラスです)のインスタンスですが、`Ring`や`Field`のインスタンスではありません。
上位クラスについては、この章の後半で詳しく説明します。
@@ -239,8 +249,8 @@ class Semigroup a where
append :: a -> a -> a
```
-文字列は普通の文字列連結について半群をなし、同様に配列も半群をなします。その他の標準的なインスタンスの幾つかは、
-`prelude`パッケージで提供されています。
+文字列は普通の文字列連結について半群をなし、配列も同様です。
+その他の標準的なインスタンスは`prelude`パッケージで提供されています。
以前に見た `<>`連結演算子は、 `append`の別名として提供されています。
@@ -253,9 +263,9 @@ class Semigroup m <= Monoid m where
ここでも文字列や配列はモノイドの簡単な例になっています。
-`Monoid`型クラスインスタンスでは、「空」の値から始めて新たな値を合成していき、その型で*累積*した結果を返すにはどうするかを記述する型クラスです。
-例えば、畳み込みを使って幾つかのモノイドの値の配列を連結する関数を書くことができます。
-PSCiで試すと次のようになります。
+ある型にとっての`Monoid`型クラスインスタンスとは、「空」の値から始めて新たな結果に組み合わせ、その型を持つ結果を*累算*する方法を記述するものです。
+例えば、畳み込みを使って何らかのモノイドの値の配列を連結する関数を書けます。
+PSCiで以下の通りです。
```haskell
> import Prelude
@@ -287,14 +297,14 @@ class Foldable f where
foldMap :: forall a m. Monoid m => (a -> m) -> f a -> m
```
-この定義は `f`を配列の型構築子として特殊化して考えてみるとわかりやすくなります。
-この場合、全ての `a`について `f a`を `Array a`に置き換える事ができますが、`foldl`と
-`foldr`の型が、最初に見た配列に対する畳み込みの型になるとわかります。
+`f`を配列の型構築子として特殊化すると分かりやすいです。
+この場合、任意の`a`について`f a`を`Array
+a`に置き換えられますが、そうすると`foldl`と`foldr`の型が、最初に配列に対する畳み込みで見た型になると気付きます。
`foldMap`についてはどうでしょうか。
これは `forall a m. Monoid m => (a -> m) -> Array a -> m`になります。
-この型シグネチャは、型 `m`が `Monoid`型クラスのインスタンスであればどんな型でも返り値の型として選ぶことができると言っています。
-配列の要素をそのモノイドの値へと変換する関数を与えれば、そのモノイドの構造を利用して配列を畳み込み、1つの値にして返すことができます。
+この型シグネチャでは、型`m`が`Monoid`型クラスのインスタンスであれば、返り値の型として任意に選べると書かれています。
+配列の要素をそのモノイドの値へと変える関数を与えられれば、そのモノイドの構造を利用して配列上で累算し、1つの値にして返せます。
それではPSCiで `foldMap`を試してみましょう。
@@ -305,18 +315,20 @@ class Foldable f where
"12345"
```
-ここでは繋ぎ合わせるためのモノイドとして文字列を、そして`Int`を文字列として表示する
-`show`関数を選びました。そうして数の配列を渡すと、それぞれの数を `show`して1つの文字列へと連結した結果が出力されました。
+ここでは文字列用のモノイドと`show`関数を選んでいます。
+前者は文字列を連結するもので、後者は`Int`を文字列として書き出すものです。
+そうして数の配列を渡すと、それぞれの数を`show`して1つの文字列へと連結した結果を得ました。
-しかし畳み込み可能な型は配列だけではありません。`foldable-traversable`では `Maybe`や `Tuple`のような型の
-`Foldable`インスタンスが定義されており、`lists`のような他のライブラリでは、そのライブラリのそれぞれのデータ型に対して
-`Foldable`インスタンスが定義されています。`Foldable`は _順序付きコンテナ_ (ordered container)
-の概念を見据えたものなのです。
+しかし畳み込み可能な型は配列だけではありません。
+`foldable-traversable`では`Maybe`や`Tuple`のような型にも`Foldable`インスタンスが定義されており、`lists`のような他のライブラリでは、各々のデータ型に対して`Foldable`インスタンスが定義されています。
+`Foldable`は*順序付きコンテナ*の概念を見据えたものなのです。
### 関手と型クラス則
-PureScriptで副作用を伴う関数型プログラミングのスタイルを可能にするための`Functor`と `Applicative`、
-`Monad`といった型クラスがPreludeでは定義されています。これらの抽象化については後ほど本書で扱いますが、まずは`map`関数の形で既に見てきた`Functor`型クラスの定義を見てみましょう。
+PureScriptでは、副作用を伴う関数型プログラミングのスタイルを可能にするための型クラスの集まりも定義されています。
+`Functor`や`Applicative`、`Monad`といったものです。
+これらの抽象化については後ほど本書で扱いますが、ここでは`Functor`型クラスの定義を見てみましょう。
+既に`map`関数の形で見たものです。
```haskell
class Functor f where
@@ -352,8 +364,8 @@ class Functor f where
最初の法則は _恒等射律_ (identity law)
です。これは、恒等関数(引数を変えずに返す関数)をその構造まで持ち上げると、元の構造をそのまま返すという意味です。恒等関数は入力を変更しませんから、これは理にかなっています。
-第二の法則は _合成律_ (composition law)
-です。構造を1つの関数で写してから2つめの関数で写すのは、2つの関数の合成で構造を写すのと同じだ、と言っています。
+第2の法則は*合成律*です。
+構造を1つの関数で写してから2つめの関数で写すのは、2つの関数の合成で構造を写すのと同じだ、と言っています。
「持ち上げ」の一般的な意味が何であれ、データ構造に対する持ち上げ関数の正しい定義はこれらの法則に従っていなければなりません。
@@ -381,7 +393,9 @@ class Functor f where
2. (簡単)`Eq`インスタンスを`Complex`に導出してください。
*補足*:代わりにこのインスタンスを手作業で書いてもよいですが、しなくていいのになぜすることがありましょう。
-3. (普通)`Semiring`インタンスを`Complex`に定義してください。*補足*:[`Data.Newtype`](https://pursuit.purescript.org/packages/purescript-newtype/docs/Data.Newtype)の`wrap`と`over2`を使ってより簡潔な解答をつくることができます。もしそうするのでしたら、`Data.Newtype`から`class
+3. (普通)`Semiring`インタンスを`Complex`に定義してください。
+ *補足*:[`Data.Newtype`](https://pursuit.purescript.org/packages/purescript-newtype/docs/Data.Newtype)の`wrap`と`over2`を使ってより簡潔な解答を作れます。
+ もしそうするのでしたら、`Data.Newtype`から`class
Newtype`をインポートしたり、`Newtype`インスタンスを`Complex`に導出したりする必要も出てくるでしょう。
4. (簡単)(`newtype`を介して)`Ring`インスタンスを`Complex`に導出してください。
@@ -400,9 +414,9 @@ class Functor f where
## 型クラス制約
-型クラスを使うと、関数の型に制約を加えることができます。
+型クラスを使うと、関数の型に制約を加えられます。
例を示しましょう。
-`Eq`型クラスのインスタンスで定義された等値性を使って、3つの値が等しいかどうかを調べる関数を書きたいとします。
+`Eq`型クラスインスタンスを使って定義された等値性を使って、3つの値が等しいかどうかを調べる関数を書きたいとします。
```haskell
threeAreEqual :: forall a. Eq a => a -> a -> a -> Boolean
@@ -443,10 +457,12 @@ PSCiで `Semiring`のような標準の型クラスの何れかを使って、
> import Prelude
> :type \x -> x + x
-forall a. Semiring a => a -> a
+forall (a :: Type). Semiring a => a -> a
```
-ここで、この関数には`Int -> Int`または`Number -> Number`と注釈を付けることが考えられますが、最も一般的な型が`Semiring`で動作するため、PSCiでは`Int`と `Number`の両方で関数を実行させることができます。
+ここで、この関数に`Int -> Int`または`Number -> Number`と註釈を付けることはできます。
+しかし、PSCiでは最も一般的な型が`Semiring`で動作することが示されています。
+こうすると`Int`と`Number`の両方で関数を使えます。
## インスタンスの依存関係
@@ -460,7 +476,7 @@ instance Show a => Show (Array a) where
...
```
-型クラスインスタンスが複数の他のインスタンスに依存する場合、括弧で囲んでそれらのインスタンスをコンマで区切り、それを`=>`シンボルの左側に置く必要があります。
+型クラスインスタンスが複数の他のインスタンスに依存する場合、括弧で囲んでそれらのインスタンスをコンマで区切り、それを`=>`シンボルの左側に置くことになります。
```haskell
instance (Show a, Show b) => Show (Either a b) where
@@ -481,7 +497,7 @@ instance (Show a, Show b) => Show (Either a b) where
```
`Eq a`と`Eq (Array a)`のインスタンスを再利用し、型`NonEmpty`に`Eq`インスタンスを書いてください。
- *補足*:代わりに`Eq`インスタンスも導出できます。
+ *補足*:代わりに`Eq`インスタンスを導出できます。
1. (普通)`Array`の`Semigroup`インスタンスを再利用して、`NonEmpty`への`Semigroup`インスタンスを書いてください。
@@ -499,7 +515,7 @@ instance (Show a, Show b) => Show (Either a b) where
*手掛かり*:配列への`Foldable`インスタンスを再利用してください。
1. (難しい)順序付きコンテナを定義する(そして `Foldable`のインスタンスを持っている)ような型構築子
- `f`が与えられたとき、追加の要素を先頭に含めるような新たなコンテナ型を作ることができます。
+ `f`が与えられたとき、追加の要素を先頭に含める新たなコンテナ型を作れます。
```haskell
{{#include ../exercises/chapter6/test/no-peeking/Solutions.purs:OneMore}}
@@ -522,7 +538,8 @@ instance (Show a, Show b) => Show (Either a b) where
## 多変数型クラス
-型クラスは必ずしも1つの型だけを型変数としてとるわけではありません。型変数が1つだけなのが最も一般的ですが、実際には型クラスは*ゼロ個以上の*型変数を持つことができます。
+型クラスが引数として1つの型だけを取れるのかというと、そうではありません。
+その場合がほとんどですが、型クラスは*ゼロ個以上の*型変数を持てます。
それでは2つの型引数を持つ型クラスの例を見てみましょう。
@@ -551,7 +568,8 @@ instance Stream String Char where
このモジュールでは2つの型クラスインスタンスが定義されています。
`uncons`がパターン照合で配列の先頭の要素を取り除くような配列のインスタンスと、文字列から最初の文字を取り除くような文字列のインスタンスです。
-任意のストリーム上で動作する関数を記述できます。例えば、ストリームの要素に基づいて `Monoid`に結果を累積する関数は次のようになります。
+任意のストリーム上で動作する関数を記述できます。
+例えば、ストリームの要素に基づいて `Monoid`に結果を累算する関数は次のようになります。
```haskell
import Prelude
@@ -568,14 +586,14 @@ PSCiで使って、異なる `Stream`の型や異なる `Monoid`の型につい
## 関数従属性
-多変数型クラスは非常に便利ですが、混乱しやすい型や型推論の問題にも繋がります。
-簡単な例として、上記の `Stream`クラスを使って汎用的な`tail`関数をストリームに書くことを考えてみましょう。
+多変数型クラスは非常に便利ですが、紛らわしい型や型推論の問題にも繋がります。
+単純な例として、上記で与えられた`Stream`クラスを使い、ストリームに対して汎用的な`tail`関数を書くことを考えてみましょう。
```haskell
genericTail xs = map _.tail (uncons xs)
```
-これはやや複雑なエラーメッセージを出力します。
+これはやや複雑なエラー文言を出力します。
```text
The inferred type
@@ -603,8 +621,8 @@ has type variables which are not mentioned in the body of the type. Consider add
ここでは、コンパイラが `streamString`インスタンスを選択することを期待しています。
結局のところ、 `String`は `Char`のストリームであり、他の型のストリームであってはなりません。
-コンパイラは自動的にそう推論できず、`streamString`インスタンスを割り当てることができません。
-しかし、型クラス定義に手掛かりを追加すると、コンパイラを助けることができます。
+コンパイラは自動的にそう推論できず、`streamString`インスタンスに目が向きません。
+しかし、型クラス定義に手掛かりを追加すると、コンパイラを補助できます。
```haskell
class Stream stream element | stream -> element where
@@ -617,20 +635,23 @@ class Stream stream element | stream -> element where
```text
> :type genericTail
-forall stream element. Stream stream element => stream -> Maybe stream
+forall (stream :: Type) (element :: Type). Stream stream element => stream -> Maybe stream
> genericTail "testing"
(Just "esting")
```
-多種の型のクラスを使用して何らかのAPIを設計する場合、関数従属性は非常に有用です。
+多変数の型クラスを使用して何らかのAPIを設計する場合、関数従属性が便利なことがあります。
## 型変数のない型クラス
ゼロ個の型変数を持つ型クラスさえも定義できます。
-これらは関数に対するコンパイル時のアサーションに対応しており、型システム内のコードの大域的な性質を把握できます。
+これらは関数に対するコンパイル時の表明に対応しており、型システム内においてそのコードの大域的な性質を把握できます。
-重要な一例として、前に部分関数についてお話しした際に見た`Partial`クラスがあります。`Data.Array.Partial`に定義されている関数`head`と`tail`を例に取りましょう。この関数は配列の先頭と尾鰭を`Maybe`に包むことなく取得できます。なので配列が空のときに失敗する可能性があります。
+重要な一例として、前に部分関数についてお話しした際に見た`Partial`クラスがあります。
+`Data.Array.Partial`に定義されている関数`head`と`tail`を例に取りましょう。
+この関数は配列の先頭と尾鰭を`Maybe`に包むことなく取得できます。
+そのため配列が空のときに失敗する可能性があります。
```haskell
head :: forall a. Partial => Array a -> a
@@ -683,34 +704,34 @@ unsafePartial :: forall a. (Partial => a) -> a
そしてクラス定義で逆向きの太い矢印 (`<=`) を使って上位クラス関係を示します。
[既に上位クラスの関係の例を目にしました](#ord)。
-`Eq`クラスは `Ord`の上位クラスですし、`Semigroup`クラスは`Monoid`の上位クラスです。
+`Eq`クラスは`Ord`の上位クラスですし、`Semigroup`クラスは`Monoid`の上位クラスです。
`Ord`クラスの全ての型クラスインスタンスについて、その同じ型に対応する `Eq`インスタンスが存在しなければなりません。
これは理に適っています。
-`compare`関数が2つの値の大小を付けられないと報告した時は、それらが実は同値であるかどうかを判定するために
-`Eq`クラスを使いたいことが多いでしょうから。
+多くの場合、`compare`関数が2つの値の大小を付けられないと報告した時は、同値であるかを判定するために`Eq`クラスを使うことでしょう。
-一般に、下位クラスの法則が上位クラスのメンバに言及しているとき、上位クラス関係を定義するのは筋が通っています。
-例えば、任意の`Ord`と`Eq`のインスタンスの対について、もし2つの値が`Eq`インスタンスの下で同値であるなら、`compare`関数は
-`EQ`を返すはずだと見做すのは理に適っています。
-言い換えれば、`a == b`が真であるのはちょうど`compare a b`が`EQ`に評価されるときなのです。
-法則のレベルでのこの関係は`Eq`と`Ord`の間の上位クラス関係の正当性を示します。
+一般に、下位クラスの法則が上位クラスの構成要素に言及しているとき、上位クラス関係を定義するのは筋が通っています。
+例えば、任意の`Ord`と`Eq`のインスタンスの対について、もし2つの値が`Eq`インスタンスの下で同値であるなら、`compare`関数は`EQ`を返すはずだと推定するのは理に適っています。
+言い換えれば、`a == b`が真であるのは`compare a b`が厳密に`EQ`に評価されるときなのです。
+法則のレベルでのこの関係は、`Eq`と`Ord`の間の上位クラス関係の正当性を示します。
-上位クラス関係を定義する別の理由となるのは、この2つのクラスの間に明らかに "is-a" の関係があることです。
-下位クラスの全てのメンバは、上位クラスのメンバでもあるということです。
+上位クラス関係を定義する別の理由となるのは、この2つのクラスの間に明白な "is-a" の関係があることです。
+下位クラスの全ての構成要素は、上位クラスの構成要素でもあるということです。
## 演習
-1. (普通)部分関数`unsafeMaximum :: Partial => Array Int ->
- Int`を定義してください。この関数は空でない整数の配列の最大値を求めます。`unsafePartial`を使ってPSCiで関数をテストしてください。*手掛かり*:`Data.Foldable`の
- `maximum`関数を使います。
+1. (普通)部分関数`unsafeMaximum :: Partial => Array Int -> Int`を定義してください。
+ この関数は空でない整数の配列の最大値を求めます。
+ `unsafePartial`を使ってPSCiで関数を試してください。
+ *手掛かり*:`Data.Foldable`の`maximum`関数を使います。
-1. (普通)次の `Action`クラスは、ある型の別の型での動作 (action) を定義する、多変数型クラスです。
+1. (普通)次の `Action`クラスは、ある型の別の型での動作を定義する、多変数型クラスです。
```haskell
{{#include ../exercises/chapter6/test/no-peeking/Solutions.purs:Action}}
```
- *動作*はモノイドな値を使って他の型の値を変更する方法を決めるやり方を記述する関数です。`Action`型クラスには2つの法則があります。
+ *動作*とは、他の型の値を変更する方法を決めるために使われるモノイドな値を記述する関数です。
+ `Action`型クラスには2つの法則があります。
- `act mempty a = a`
- `act (m1 <> m2) a = act m1 (act m2 a)`
@@ -738,9 +759,9 @@ unsafePartial :: forall a. (Partial => a) -> a
インスタンスが上で挙げた法則を見たさなくてはならないことを思い出してください。
-1. (難しい)実は`Action Multiply Int`のインスタンスを実装するには複数の方法があります。
+1. (難しい)`Action Multiply Int`のインスタンスを実装するには複数の方法があります。
どれだけ思い付きますか。
- PureScriptは同じインスタンスの複数の実装を許さないため、元の実装を置き換える必要があります。
+ PureScriptは同じインスタンスの複数の実装を許さないため、元の実装を置き換える必要があるでしょう。
*補足*:テストでは4つの実装を押さえています。
1. (普通)入力の文字列を何回か繰り返す`Action`インスタンスを書いてください。
@@ -751,7 +772,7 @@ unsafePartial :: forall a. (Partial => a) -> a
```
*手掛かり*:Pursuitでシグネチャが[`String -> Int -> String`](https://pursuit.purescript.org/search?q=String%20-%3E%20Int%20-%3E%20String)の補助関数を検索してください。
- なお`String`は(`Monoid`のような)より汎用的な型として現れます。
+ なお、`String`は(`Monoid`のような)より汎用的な型として現れます。
このインスタンスは上に挙げた法則を満たすでしょうか。
@@ -776,16 +797,17 @@ unsafePartial :: forall a. (Partial => a) -> a
この最後の節では、章の残りを使ってデータ構造をハッシュ化するライブラリを作ります。
-なお、このライブラリの説明だけを目的としており、堅牢なハッシュ化の仕組みの提供は目的としていません。
+> なお、このライブラリは説明だけを目的としており、堅牢なハッシュ化の仕組みの提供は意図していません。
ハッシュ関数に期待される性質とはどのようなものでしょうか。
- ハッシュ関数は決定的でなくてはなりません。
- つまり、同じ値には同じハッシュ値を対応させなければなりません。
+ つまり、同じ値は同じハッシュコードに写さなければなりません。
- ハッシュ関数はいろいろなハッシュ値の集合で結果が一様に分布しなければなりません。
-最初の性質はまさに型クラスの法則のように見える一方で、2番目の性質はよりくだけた規約の条項のようなもので、PureScriptの型システムによって確実に強制できるようなものではなさそうです。
-しかし、これは型クラスについて次のような直感的理解を与えるはずです。
+最初の性質はちゃんとした型クラスの法則のように見えます。
+その一方で、2番目の性質はよりくだけた規約の条項のようなもので、PureScriptの型システムによって確実に強制できるようなものではなさそうです。
+しかし、これは型クラスから次のような直感が得られるでしょう。
```haskell
{{#include ../exercises/chapter6/src/Data/Hashable.purs:Hashable}}
@@ -803,8 +825,8 @@ unsafePartial :: forall a. (Partial => a) -> a
`combineHashes`関数は2つのハッシュ値を混ぜて結果を0-65535の間に分布します。
-それでは、入力の種類を制限する `Hashable`制約を使う関数を書いてみましょう。
-ハッシュ関数を必要とするよくある目的としては、2つの値が同じハッシュ値にハッシュ化されるかどうかを決定することです。
+それでは、`Hashable`制約を使って入力の種類を制限する関数を書いてみましょう。
+ハッシュ関数を必要とするよくある目的としては、2つの値が同じハッシュコードにハッシュ化されるかどうかを判定することです。
`hashEqual`関係はそのような機能を提供します。
```haskell
@@ -817,8 +839,8 @@ unsafePartial :: forall a. (Partial => a) -> a
原始型の `Hashable`インスタンスを幾つか書いてみましょう。
まずは整数のインスタンスです。
-`HashCode`は実際には単なる梱包された整数なので、これは簡単です。
-`hashCode`補助関数を使うことができます。
+`HashCode`は実際には単なる梱包された整数なので、単純です。
+`hashCode`補助関数を使えます。
```haskell
{{#include ../exercises/chapter6/src/Data/Hashable.purs:hashInt}}
@@ -851,17 +873,17 @@ unsafePartial :: forall a. (Partial => a) -> a
```
これらの `Hashable`インスタンスが先ほどの型クラスの法則を満たしていることを証明するにはどうしたらいいでしょうか。
-同じ値が等しいハッシュ値を持っていることを確認する必要があります。
-`Int`、 `Char`、 `String`、
-`Boolean`の場合は、`Eq`の意味では同じ値でも厳密には同じではない、というような型の値は存在しないので簡単です。
+同じ値が等しいハッシュコードを持っていることを確認する必要があります。
+`Int`、`Char`、`String`、`Boolean`のような場合は単純です。
+`Eq`の意味では同じ値でも厳密には同じではない、というような型の値は存在しないからです。
もっと面白い型についてはどうでしょうか。
-この場合、配列の長さに関する帰納を使うと、型クラスの法則を証明できます。
+`Array`インスタンスの型クラスの法則を証明するにあたっては、配列の長さに関する帰納を使えます。
長さゼロの唯一の配列は `[]`です。
配列の `Eq`の定義により、任意の2つの空でない配列は、それらの先頭の要素が同じで配列の残りの部分が等しいとき、またその時に限り等しくなります。
この帰納的な仮定により、配列の残りの部分は同じハッシュ値を持ちますし、もし `Hashable
-a`インスタンスがこの法則を満たすなら、先頭の要素も同じハッシュ値をもつことがわかります。
-したがって、2つの配列は同じハッシュ値を持ち、`Hashable (Array a)`も同様に型クラス法則を満たしています。
+a`インスタンスがこの法則を満たすなら、先頭の要素も同じハッシュ値を持つことがわかります。
+したがって、2つの配列は同じハッシュ値を持ち、`Hashable (Array a)`も同様に型クラス法則に従います。
この章のソースコードには、 `Maybe`と `Tuple`型のインスタンスなど、他にも `Hashable`インスタンスの例が含まれています。
@@ -869,7 +891,8 @@ a`インスタンスがこの法則を満たすなら、先頭の要素も同じ
1. (簡単)PSCiを使って、定義した各インスタンスのハッシュ関数をテストしてください。
*補足*:この演習には単体試験がありません。
- 1. (普通)ハッシュと値の同値性に基づいて配列が重複する要素を持っているかどうかを調べる関数`arrayHasDuplicates`を書いてください。
+ 1. (普通)関数`arrayHasDuplicates`を書いてください。
+ この関数はハッシュと値の同値性に基づいて配列が重複する要素を持っているかどうかを調べます。
まずハッシュ同値性を`hashEqual`関数で確認し、それからもし重複するハッシュの対が見付かったら`==`で値の同値性を確認してください。
*手掛かり*:`Data.Array`の `nubByEq`関数はこの問題をずっと簡単にしてくれるでしょう。
1. (普通)型クラスの法則を満たす、次のnewtypeの `Hashable`インスタンスを書いてください。
@@ -888,9 +911,12 @@ a`インスタンスがこの法則を満たすなら、先頭の要素も同じ
## まとめ
-この章では、型に基づく抽象化で、コードの再利用のための強力な形式化を可能にする _型クラス_
-を導入しました。PureScriptの標準ライブラリから標準の型クラスを幾つか見てきました。また、ハッシュ値を計算する型クラスに基づく独自のライブラリを定義しました。
+この章では*型クラス*を導入しました。
+型クラスは型に基づく抽象化で、コードの再利用のために強力な形式化ができます。
+PureScriptの標準ライブラリから標準の型クラスを幾つか見てきました。
+また、ハッシュ値を計算するための型クラスに基づく独自のライブラリを定義しました。
-この章では型クラス法則の考え方も導入しましたが、これは抽象化に型クラスを使うコードについての性質を証明する手法でした。
-型クラス法則は*等式推論*と呼ばれる大きな分野の一部であり、プログラミング言語の性質と型システムがプログラムを論理的に推論するために使われています。
+この章では型クラス法則も導入しましたが、これは抽象化に型クラスを使うコードについての性質を証明する手法でした。
+型クラス法則は*等式推論*と呼ばれるより大きな分野の一部です。
+そちらではプログラミング言語の性質と型システムがプログラムを論理的に追究するために使われています。
これは重要な考え方で、本書では今後あらゆる箇所で立ち返る話題となるでしょう。
diff --git a/text-ja/chapter7.md b/text-ja/chapter7.md
index 3f8adef9..6aa28689 100644
--- a/text-ja/chapter7.md
+++ b/text-ja/chapter7.md
@@ -2,14 +2,18 @@
## この章の目標
-この章では、`Applicative`型クラスによって表現される _アプリカティブ関手_ (applicative functor)
-という重要な抽象化と新たに出会うことになります。名前が難しそうに思えても心配しないでください。フォームデータの検証という実用的な例を使ってこの概念の動機付けをします。アプリカティブ関手を使うと、通常であれば大量の決まり文句を伴うようなコードを、簡潔で宣言的な記述へと変えることができるようになります。
+この章では重要な抽象化と新たに出会うことになります。
+`Applicative`型クラスによって表現される*アプリカティブ関手*です。
+名前が難しそうに思えても心配しないでください。
+フォームデータの検証という実用的な例を使ってこの概念の動機付けをします。
+アプリカティブ関手の技法があることにより、通常であれば大量の決まり文句の検証を伴うようなコードを、簡潔で宣言的なフォームの記述へと変えられます。
また、*巡回可能関手*を表現する`Traversable`という別の型クラスにも出会います。現実の問題への解決策からこの概念が自然に生じることがわかるでしょう。
-この章では第3章に引き続き住所録を例として扱います。
+この章のコードでは第3章に引き続き住所録を例とします。
今回は住所録のデータ型を拡張し、これらの型の値を検証する関数を書きます。
-これらの関数は、例えばデータ入力フォームの一部で、使用者へエラーを表示するwebユーザインターフェースで使われると考えてください。
+これらの関数は、例えばwebユーザインターフェースで使えることが分かります。
+データ入力フォームの一部として、使用者へエラーを表示するのに使われます。
## プロジェクトの準備
@@ -21,13 +25,14 @@
- `control` - `Applicative`のような、型クラスを使用して制御フローを抽象化する関数が定義されています。
- `validation` - この章の主題である _アプリカティブによる検証_ のための関手が定義されています。
-`Data.AddressBook`モジュールには、このプロジェクトのデータ型とそれらの型に対する`Show`インスタンスが定義されており、`Data.AddressBook.Validation`モジュールにはそれらの型の検証規則が含まれています。
+`Data.AddressBook`モジュールにはこのプロジェクトのデータ型とそれらの型に対する`Show`インスタンスが定義されています。
+また、`Data.AddressBook.Validation`モジュールにはそれらの型の検証規則が含まれています。
## 関数適用の一般化
_アプリカティブ関手_ の概念を理解するために、以前扱った型構築子`Maybe`について考えてみましょう。
-このモジュールのソースコードでは、次のような型を持つ`address`関数が定義されています。
+このモジュールのソースコードでは、次の型を持つ`address`関数が定義されています。
```haskell
{{#include ../exercises/chapter7/src/Data/AddressBook.purs:address_anno}}
@@ -62,7 +67,8 @@ with type
String
```
-`address`は`Maybe String`型ではなく文字列型の引数を取るので、もちろんこれは期待通り型エラーになります。
+勿論、これは期待通り型エラーになります。
+`address`は`Maybe String`型の値ではなく、文字列を引数として取るためです。
しかし、もし`address`関数を「持ち上げる」ことができれば、`Maybe`型で示される省略可能な値を扱うことができるはずだという予想は理に適っています。実際それは可能で、`Control.Apply`で提供されている関数`lift3`が、まさに求めているものです。
@@ -93,7 +99,7 @@ Just ({ street: "123 Fake St.", city: "Faketown", state: "CA" })
```text
> :type lift3
-forall a b c d f. Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
+forall (a :: Type) (b :: Type) (c :: Type) (d :: Type) (f :: Type -> Type). Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
```
上の`Maybe`の例では型構築子`f`は`Maybe`ですから、`lift3`は次のように特殊化されます。
@@ -102,9 +108,9 @@ forall a b c d f. Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
forall a b c d. (a -> b -> c -> d) -> Maybe a -> Maybe b -> Maybe c -> Maybe d
```
-この型が言っているのは、3引数の任意の関数を取り、その関数を引数と返り値が`Maybe`で包まれた新しい関数へと持ち上げる、ということです。
+この型で書かれているのは、3引数の任意の関数を取り、その関数を引数と返り値が`Maybe`で包まれた新しい関数へと持ち上げられる、ということです。
-もちろんどんな型構築子`f`についても持ち上げができるわけではないのですが、それでは`Maybe`型を持ち上げができるようにしているものは何なのでしょうか。
+勿論、どんな型構築子`f`についても持ち上げができるわけではないのですが、それでは`Maybe`型を持ち上げができるようにしているものは何なのでしょうか。
さて、先ほどの型の特殊化では、`f`に対する型クラス制約から`Apply`型クラスを取り除いていました。
`Apply`はPreludeで次のように定義されています。
@@ -118,7 +124,8 @@ class Functor f <= Apply f where
`Apply`型クラスは`Functor`の下位クラスであり、追加の関数`apply`を定義しています。`<$>`が`map`の別名として定義されているように、`Prelude`モジュールでは`<*>`を`apply`の別名として定義しています。これから見ていきますが、これら2つの演算子はよく一緒に使われます。
-なおこの[`apply`](https://pursuit.purescript.org/packages/purescript-prelude/docs/Control.Apply#v:apply)は`Data.Function`の[`apply`](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Function#v:apply)(中置で`$`)とは異なります。幸いにも後者はほぼ常に中置記法として使われるので、名前の衝突については心配ご無用です。
+なお、この[`apply`](https://pursuit.purescript.org/packages/purescript-prelude/docs/Control.Apply#v:apply)は`Data.Function`の[`apply`](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Function#v:apply)(中置で`$`)とは異なります。
+幸いにも後者はほぼ常に中置記法として使われるので、名前の衝突については心配ご無用です。
`apply`の型は`map`の型と実によく似ています。
`map`と`apply`の違いは、`map`がただの関数を引数に取るのに対し、`apply`の最初の引数は型構築子`f`で包まれているという点です。
@@ -134,7 +141,7 @@ instance Apply Maybe where
apply _ _ = Nothing
```
-この型クラスのインスタンスで書かれているのは、任意のオプショナルな値にオプショナルな関数を適用でき、その両方が定義されている時に限り結果も定義される、ということです。
+この型クラスのインスタンスで書かれているのは、任意の省略可能な値に省略可能な関数を適用でき、その両方が定義されている時に限り結果も定義される、ということです。
それでは、`map`と`apply`を一緒に使い、引数が任意個の関数を持ち上げる方法を見ていきましょう。
@@ -157,7 +164,7 @@ lift3 :: forall a b c d f
lift3 f x y z = f <$> x <*> y <*> z
```
-この式の型がちゃんと整合しているかの確認は、読者への演習として残しておきます。
+> この式に関する型の検証は、読者への演習として残しておきます。
例として、`<$>`と`<*>`をそのまま使うと、`Maybe`上に`address`関数を持ち上げることができます。
@@ -171,9 +178,9 @@ Nothing
同様にして、引数が異なる他のいろいろな関数を`Maybe`上に持ち上げてみてください。
-この代わりにお馴染の _do記法_ に似た見た目の _アプリカティブdo記法_ が同じ目的で使えます。以下では`lift3`に
-_アプリカティブdo記法_
-を使っています。なお、`ado`が`do`の代わりに使われており、生み出された値を示すために最後の行で`in`が使われています。
+この代わりに、お馴染の*do記法*に似た見た目の*アプリカティブdo記法*が同じ目的で使えます。
+以下では`lift3`に*アプリカティブdo記法*を使っています。
+なお、`ado`が`do`の代わりに使われており、生み出された値を示すために最後の行で`in`が使われています。
```haskell
lift3 :: forall a b c d f
@@ -209,20 +216,20 @@ instance Applicative Maybe where
pure x = Just x
```
-アプリカティブ関手は関数を持ち上げることを可能にする関手だと考えるとすると、`pure`は引数のない関数の持ち上げだというように考えることができます。
+アプリカティブ関手は関数を持ち上げることを可能にする関手だと考えるとすると、`pure`は引数のない関数の持ち上げだというように考えられます。
## アプリカティブに対する直感的理解
PureScriptの関数は純粋であり、副作用は持っていません。Applicative関手は、関手`f`によって表現されるある種の副作用を提供するような、より大きな「プログラミング言語」を扱えるようにします。
-例えば関手`Maybe`はオプショナルな値の副作用を表現しています。
+例えば関手`Maybe`は欠けている可能性がある値の副作用を表現しています。
その他の例としては、型`err`のエラーの可能性の副作用を表す`Either err`や、大域的な構成を読み取る副作用を表すArrow関手 (arrow functor) `r ->`があります。
ここでは`Maybe`関手についてのみ考えることにします。
もし関手`f`が作用を持つ、より大きなプログラミング言語を表すとすると、`Apply`と`Applicative`インスタンスは小さなプログラミング言語
(PureScript) から新しい大きな言語へと値や関数を持ち上げることを可能にします。
-`pure`は純粋な(副作用がない)値をより大きな言語へと持ち上げますし、関数については上で述べた通り`map`と`apply`を使うことができます。
+`pure`は純粋な(副作用がない)値をより大きな言語へと持ち上げますし、関数については上で述べた通り`map`と`apply`を使えます。
ここで疑問が生まれます。
もしPureScriptの関数と値を新たな言語へ埋め込むのに`Applicative`が使えるなら、どうやって新たな言語は大きくなっているというのでしょうか。
@@ -249,9 +256,9 @@ a`の式を見つけたなら、その式はそのより大きな言語だけに
Freeman, Phillip A
```
-この関数は、クエリパラメータとして与えられた3つの引数を持つ、(とっても簡単な)webサービスの実装であるとしましょう。
-使用者が3つの引数全てを与えたことを確かめたいので、引数が存在するかどうかを表す`Maybe`型を使うことになるでしょう。
-`fullName`を`Maybe`の上へ持ち上げると、省略された引数を確認するwebサービスを実装できます。
+この関数が、クエリ引数として与えられた3つの引数を持つ、(とっても簡単な)webサービスの実装を形成しているとしましょう。
+使用者が3つの各引数を与えたことを確かめたいので、引数が存在するかどうかを表す`Maybe`型を使うことになるでしょう。
+`fullName`を`Maybe`の上へ持ち上げると、欠けている引数を検査するwebサービスの実装を作成できます。
```text
> import Data.Maybe
@@ -288,11 +295,11 @@ Nothing
この持ち上げた関数は、引数の何れかが`Nothing`なら`Nothing`を返すことに注意してください。
-これで、もし引数が不正ならWebサービスからエラー応答を送信できるので、なかなかいい感じです。
-しかし、どのフィールドが間違っていたのかを応答で示せると、もっと良くなるでしょう。
+引数が不正のときにwebサービスからエラー応答を送り返せるのは良いことです。
+しかし、どのフィールドが不正確なのかを応答で示せると、もっと良くなるでしょう。
-`Meybe`上へ持ち上げる代わりに`Either String`上へ持ち上げるようにすると、エラーメッセージを返すことができるようになります。
-まずは`Either String`を使ってオプショナルな入力をエラーを発信できる計算に変換する演算子を書きましょう。
+`Meybe`上へ持ち上げる代わりに`Either String`上へ持ち上げるようにすると、エラー文言を返せるようになります。
+まずは`Either String`を使い、省略可能な入力からエラーを発信できる計算に変換する演算子を書きましょう。
```text
> import Data.Either
@@ -304,7 +311,7 @@ Nothing
*補足*:`Either err`アプリカティブ関手において、`Left`構築子は失敗を表しており、`Right`構築子は成功を表しています。
-これで`Either String`上へ持ち上げることで、それぞれの引数について適切なエラーメッセージを提供できるようになります。
+これで`Either String`上へ持ち上げることで、それぞれの引数について適切なエラー文言を提供できるようになります。
```text
> :paste
@@ -330,7 +337,7 @@ Nothing
Maybe String -> Maybe String -> Maybe String -> Either String String
```
-これでこの関数は`Maybe`の3つの省略可能な引数を取り、`String`のエラーメッセージか`String`の結果のどちらかを返します。
+これでこの関数は`Maybe`を使う3つの省略可能な引数を取り、`String`のエラー文言か`String`の結果のどちらかを返します。
いろいろな入力でこの関数を試してみましょう。
@@ -345,8 +352,8 @@ Maybe String -> Maybe String -> Maybe String -> Either String String
(Left "Last name was missing")
```
-このとき、全てのフィールドが与えられば成功の結果が表示され、そうでなければ省略されたフィールドのうち最初のものに対応するエラーメッセージが表示されます。
-しかし、もし複数の入力が省略されているとき、最初のエラーしか見ることができません。
+このとき、全てのフィールドが与えられば成功の結果が表示され、そうでなければ省略されたフィールドのうち最初のものに対応するエラー文言が表示されます。
+しかし、もし複数の入力が省略されているとき、最初のエラーしか見られません。
```text
> fullNameEither Nothing Nothing Nothing
@@ -358,7 +365,7 @@ String`よりも強力なものが必要です。この章の後半で解決策
## 作用の結合
-抽象的にアプリカティブ関手を扱う例として、アプリカティブ関手`f`によって表現された副作用を一般的に組み合わせる関数を書く方法をこの節では示します。
+抽象的にアプリカティブ関手を扱う例として、この節ではアプリカティブ関手`f`によって表現された副作用を一般的に組み合わせる関数を書く方法を示します。
これはどういう意味でしょうか。
何らかの`a`について型`f a`で包まれた引数のリストがあるとしましょう。
@@ -368,10 +375,10 @@ String`よりも強力なものが必要です。この章の後半で解決策
しかし、まだ`f`によって追跡される副作用が残ります。
つまり、元のリストの中の作用を「結合する」ことにより、型`List (f a)`の何かを型`f (List a)`の何かへと変換できると考えられます。
-任意の固定長リストの長さ`n`について、その引数を要素に持った長さ`n`のリストを構築するような`n`引数の関数が存在します。
+任意の固定長リストの長さ`n`について、`n`引数からその引数を要素に持つ長さ`n`のリストを構築する関数が存在します。
例えばもし`n`が`3`なら、関数は`\x y z -> x : y : z : Nil`です。
-この関数の型は`a -> a -> a -> List a`です。
-`Applicative`インスタンスを使うと、この関数を`f`の上へ持ち上げて関数型`f a -> f a -> f a -> f (List a)`を得ることができます。
+この関数は型`a -> a -> a -> List a`を持ちます。
+`Applicative`インスタンスを使うと、この関数を`f`の上へ持ち上げられ、関数型`f a -> f a -> f a -> f (List a)`が得られます。
しかし、いかなる`n`についてもこれが可能なので、いかなる引数の*リスト*についても同じように持ち上げられることが確かめられます。
したがって、次のような関数を書くことができるはずです。
@@ -414,31 +421,31 @@ combineList (Cons x xs) = Cons <$> x <*> combineList xs
Nothing
```
-`Meybe`へ特殊化して考えると、リストの全ての要素が`Just`であるときに限りこの関数は`Just`を返しますし、そうでなければ`Nothing`を返します。
-これはオプショナルな値に対応する、より大きな言語に取り組む上での直感から一貫しています。
-オプショナルな結果を返す計算のリストは、全ての計算が結果を持っているならばそれ自身の結果のみを持つのです。
+`Meybe`へ特殊化すると、リストの全ての要素が`Just`であるときに限りこの関数は`Just`を返しますし、そうでなければ`Nothing`を返します。
+これは省略可能な値に対応する、より大きな言語に取り組む上での直感と一貫しています。
+省略可能な結果を生む計算のリストは、全ての計算が結果を持っているならばそれ自身の結果のみを持つのです。
-しかし、`combineList`関数はどんな`Applicative`に対しても機能します。
-`Either err`を使ってエラーを発信する可能性を持たせたり、`r ->`を使って大域的な状態を読み取る計算を連鎖させるときにも使えるのです。
+ところが`combineList`関数はどんな`Applicative`に対しても機能するのです。
+`Either err`を使ってエラーを発信する可能性を持たせたり、`r ->`を使って大域的な構成を読み取る計算を組み合わせるためにも使えます。
`combineList`関数については、後ほど`Traversable`関手について考えるときに再訪します。
## 演習
- 1. (普通)数値演算子`+`、`-`、`*`、`/`のオプショナル引数(つまり`Maybe`に包まれた引数)を扱って`Maybe`に包まれた値を返す版を書いてください。
- これらの関数には`addMaybe`、`subMaybe`、`mulMaybe`、`divMaybe`と名前を付けます。
+ 1. (普通)数値演算子`+`、`-`、`*`、`/`の別のバージョンを書いてください。
+ ただし省略可能な引数(つまり`Maybe`に包まれた引数)を扱って`Maybe`に包まれた値を返します。
+ これらの関数には`addMaybe`、`subMaybe`、`mulMaybe`、`divMaybe`と名前を付けてください。
*手掛かり*:`lift2`を使ってください。
1. (普通)上の演習を(`Maybe`だけでなく)全ての`Apply`型で動くように拡張してください。
これらの新しい関数には`addApply`、`subApply`、`mulApply`、`divApply`と名前を付けます。
- 1. (難しい)型`combineMaybe : forall a f. (Applicative f) => Maybe (f a) -> f
- (Maybe a)`
- を持つ関数`combineMaybe`を書いてください。
- この関数は副作用をもつオプショナルな計算をとり、オプショナルな結果をもつ副作用のある計算を返します。
+ 1. (難しい)型`forall a f. Applicative f => Maybe (f a) -> f (Maybe
+ a)`を持つ関数`combineMaybe`を書いてください。
+ この関数は副作用を持つ省略可能な計算を取り、省略可能な結果を持つ副作用のある計算を返します。
## アプリカティブによる検証
-この章のソースコードでは住所録アプリケーションで使うことのできるいろいろなデータ型が定義されています。
-詳細はここでは割愛しますが、`Data.AddressBook`モジュールからエクスポートされる重要な関数は次のような型を持っています。
+この章のソースコードでは住所録アプリケーションで使うであろう幾つかのデータ型が定義されています。
+詳細はここでは割愛しますが、`Data.AddressBook`モジュールからエクスポートされる鍵となる関数は次のような型を持ちます。
```haskell
{{#include ../exercises/chapter7/src/Data/AddressBook.purs:address_anno}}
@@ -454,8 +461,8 @@ Nothing
{{#include ../exercises/chapter7/src/Data/AddressBook.purs:PhoneType}}
```
-これらの関数は住所録の項目を表す`Person`を構築するのに使えます。
-例えば、`Data.AddressBook`には次のような値が定義されています。
+これらの関数は住所録の項目を表す`Person`を構築できます。
+例えば、`Data.AddressBook`では以下の値が定義されています。
```haskell
{{#include ../exercises/chapter7/src/Data/AddressBook.purs:examplePerson}}
@@ -500,8 +507,9 @@ String`関手の使い方を見ました。例えば、データ構造の2つの
{{#include ../exercises/chapter7/src/Data/AddressBook/Validation.purs:validatePerson1Ado}}
```
-最初の2行では`nonEmpty`関数を使って空文字列でないことを検証しています。
-もし入力が空なら`nonEMpty`はエラーを返し(`Left`構築子で示されています)、そうでなければ`Right`構築子を使って値を包んで返します。
+最初の2行では`nonEmpty1`関数を使って空文字列でないことを検証しています。
+もし入力が空なら`nonEmpty1`は`Left`構築子で示されるエラーを返します。
+そうでなければ`Right`構築子で包まれた値を返します。
最後の2行では何の検証も実行せず、単に`address`フィールドと`phones`フィールドを残りの引数として`person`関数へと提供しています。
@@ -512,12 +520,13 @@ String`関手の使い方を見ました。例えば、データ構造の2つの
(Left "Field cannot be empty")
```
-`Either String`アプリカティブ関手は遭遇した最初のエラーだけを返します。
-でもこの入力では、名前の不足と姓の不足という2つのエラーがわかるようにしたくなるでしょう。
+`Either String`アプリカティブ関手は最初に遭遇したエラーだけを返します。
+仮にこの入力だったとすると、2つのエラーが分かったほうが良いでしょう。
+1つは名前の不足で、2つ目は姓の不足です。
`validation`ライブラリでは別のアプリカティブ関手も提供されています。
-これは単に`V`と呼ばれていて、何らかの*半群*でエラーを返す機能があります。
-例えば`V (Array String)`を使うと、新しいエラーを配列の最後に連結していき、`String`の配列をエラーとして返すことができます。
+これは`V`という名前で、何らかの*半群*でエラーを返せます。
+例えば`V (Array String)`を使うと、新しいエラーを配列の最後に連結していき、`String`の配列をエラーとして返せます。
`Data.Validation`モジュールは`Data.AddressBook`モジュールのデータ構造を検証するために`V (Array
String)`アプリカティブ関手を使っています。
@@ -625,7 +634,7 @@ invalid (["Field 'Number' did not match the required format"])
{{#include ../exercises/chapter7/src/Data/AddressBook/Validation.purs:validatePersonAdo}}
```
-`validatePhoneNumbers`はこれまでに見たことのない新しい関数、`traverse`を使います。
+`validatePhoneNumbers`はこれまでに見たことのない新しい関数である`traverse`を使っています。
`traverse`は`Data.Traversable`モジュールの`Traversable`型クラスで定義されています。
@@ -638,8 +647,9 @@ class (Functor t, Foldable t) <= Traversable t where
`Traversable`は _巡回可能関手_
の型クラスを定義します。これらの関数の型は少し難しそうに見えるかもしれませんが、`validatePerson`は良いきっかけとなる例です。
-全ての巡回可能関手は`Functor`と`Foldable`のどちらでもあります(*畳み込み可能関手*は、構造を1つの値へと纏める畳み込み操作を提供する型構築子であったことを思い出してください)。
-それに加えて、`Traversable`関手はその構造に依存した副作用の集まりを連結する機能を提供します。
+全ての巡回可能関手は`Functor`と`Foldable`のどちらでもあります(*畳み込み可能関手*は畳み込み操作に対応する型構築子であったことを思い出してください。
+畳み込みとは構造を1つの値へと簡約するものでした)。
+それに加えて、巡回可能関手はその構造に依存した副作用の集まりを組み合わせられます。
複雑そうに聞こえるかもしれませんが、配列の場合に特殊化して簡単にした上で考えてみましょう。配列型構築子は`Traversable`であり、つまりは次のような関数が存在するということです。
@@ -659,12 +669,12 @@ Errors`アプリカティブ関手に特殊化して考えてみましょう。
traverse :: forall a b. (a -> V Errors b) -> Array a -> V Errors (Array b)
```
-この型シグネチャは、型`a`についての検証関数`m`があれば、`traverse m`は型`Array
-a`の配列についての検証関数であるということを言っています。
-これはまさに、今必要になっている`Person`データ構造体の`phones`フィールドを検証する検証器そのものです。
-それぞれの要素が成功するかどうかを検証する検証関数を作るために、`validatePhoneNumber`を`traverse`へ渡しています。
+この型シグネチャでは、型`a`についての検証関数`m`があれば、`traverse m`は型`Array
+a`の配列についての検証関数であると書かれています。
+ところがこれは正に`Person`データ構造体の`phones`フィールドを検証できるようにするのに必要なものです。
+各要素が成功するかを検証する検証関数を作るために、`validatePhoneNumber`を`traverse`へ渡しています。
-一般に、`traverse`はデータ構造の要素を1つずつ辿っていき、副作用を伴いつつ計算し、結果を累積します。
+一般に、`traverse`はデータ構造の要素を1つずつ辿っていき、副作用を伴いつつ計算し、結果を累算します。
`Traversable`のもう1つの関数、`sequence`の型シグネチャには見覚えがあるかもしれません。
@@ -679,7 +689,11 @@ sequence :: forall a m. Applicative m => t (m a) -> m (t a)
combineList :: forall f a. Applicative f => List (f a) -> f (List a)
```
-巡回可能関手は、作用のある計算を集めてその作用を結合するという、データ構造走査の考え方を見据えたものです。実際、`sequence`と`traversable`は`Traversable`を定義する上でどちらも同じくらい重要です。これらはお互いがお互いを利用して実装できます。これについては興味ある読者への演習として残しておきます。
+巡回可能関手はデータ構造走査の考え方を見据えたものです。
+これにより作用のある計算の集合を集めてその作用を結合します。
+実際、`sequence`と`traversable`は`Traversable`を定義する上でどちらも同じくらい重要です。
+これらはお互いがお互いを利用して実装できます。
+これについては興味ある読者への演習として残しておきます。
`Data.List`で与えられているリストの`Traversable`インスタンスは次の通り。
@@ -692,8 +706,10 @@ traverse f (Cons x xs) = Cons <$> f x <*> traverse f xs
(実際の定義は後にスタック安全性を向上するために変更されました。その変更についてより詳しくは[こちら](https://github.com/purescript/purescript-lists/pull/87)で読むことができます)
-入力が空のリストのときには、単に`pure`を使って空の配列を返すことができます。配列が空でないときは、関数`f`を使うと先頭の要素から型`f
-b`の計算を作成できます。また、配列の残りに対して`traverse`を再帰的に呼び出すことができます。最後に、アプリカティブ関手`m`まで`Cons`構築子を持ち上げて、2つの結果を組み合わせます。
+入力が空のリストのときには、`pure`を使って空のリストを返せます。
+リストが空でないときは、関数`f`を使うと先頭の要素から型`f b`の計算を作成できます。
+また、尾鰭に対して`traverse`を再帰的に呼び出せます。
+最後に、アプリカティブ関手`m`まで`Cons`構築子を持ち上げて、2つの結果を組み合わせられます。
巡回可能関手の例はただの配列やリスト以外にもあります。
以前に見た`Maybe`型構築子も`Traversable`のインスタンスを持っています。
@@ -716,11 +732,12 @@ pure ((Just "Testing"))
これらの例では、`Nothing`の値の走査は検証なしで`Nothing`の値を返し、`Just
x`を走査すると`x`を検証するのに検証関数が使われるということを示しています。
-要は、`traverse`は型`a`についての検証関数をとり、`Maybe
-a`についての検証関数、つまり型`a`のオプショナルな値についての検証関数を返すのです。
+要は、`traverse`は型`a`についての検証関数を取り、`Maybe
+a`についての検証関数、つまり型`a`の省略可能な値についての検証関数を返すのです。
-他の巡回可能関手には`Array`、また任意の型`a`について`Tuple a`、`Either
-a`が含まれます。一般的に、「容器」のようなデータの型構築子は大抵`Traversable`インスタンスを持っています。例として、演習では二分木の型の`Traversable`インスタンスを書くことになります。
+他の巡回可能関手には、任意の型`a`についての`Array a`、`Tuple a`、`Either a`が含まれます。
+一般に、「容器」のようなほとんどのデータ型構築子は`Traversable`インスタンスを持っています。
+一例として、演習には二分木の型の`Traversable`インスタンスを書くことが含まれます。
## 演習
@@ -734,7 +751,7 @@ a`が含まれます。一般的に、「容器」のようなデータの型構
`Show`の出力には多くの「正しい」書式の選択肢があります。
この演習のテストでは以下の空白スタイルを期待しています。
- これはたまたま一般化されたshowの既定の書式と合致しているため、このインスタンスを手作業で書くつもりのときだけ、このことを念頭に置いておいてください。
+ これは一般化されたshowの既定の書式と合致しているため、このインスタンスを手作業で書くつもりのときだけ、このことを念頭に置いておいてください。
```haskell
(Branch (Branch Leaf 8 Leaf) 42 Leaf)
@@ -752,7 +769,7 @@ a`が含まれます。一般的に、「容器」のようなデータの型構
1. (普通)木を帰り掛け順に巡回する関数`traversePostOrder`を書いてください。作用は左右根と実行されます。
- 1. (普通)`homeAddress`フィールドがオプショナル(`Maybe`を使用)な新しい版の`Person`型をつくってください。
+ 1. (普通)`homeAddress`フィールドが省略可能(`Maybe`を使用)な新しい版の`Person`型をつくってください。
それからこの新しい`Person`を検証する新しい版の`validatePerson`(`validatePersonOptionalAddress`と改名します)を書いてください。
*手掛かり*:`traverse`を使って型`Maybe a`のフィールドを検証してください。
@@ -770,13 +787,13 @@ a`が含まれます。一般的に、「容器」のようなデータの型構
しかし一般には、アプリカティブ関手はこれよりももっと一般的です。
アプリカティブ関手の規則は、その計算の副作用にどんな順序付けも強制しません。
-実際、並列に副作用を実行するためのアプリカティブ関手というものは妥当になりえます。
+実際、並列に副作用を実行するアプリカティブ関手は妥当でしょう。
-例えば`V`検証関手はエラーの*配列*を返しますが、その代わりに`Set`半群を選んだとしてもやはり正常に動き、このときどんな順序でそれぞれの検証器を実行しても問題はありません。
+例えば`V`検証関手はエラーの*配列*を返しますが、その代わりに`Set`半群を選んだとしてもやはり正常に動き、このときどんな順序で各検証器を実行しても問題はありません。
データ構造に対して並列にこれの実行さえできるのです。
-別の例として、`parallel`パッケージは、*並列計算*に対応する`Parallel`型クラスを与えます。
-`Parallel`は関数`parallel`を提供しており、何らかの`Applicative`関手を使って入力の計算を*並列に*計算できます。
+2つ目の例として、`parallel`パッケージは*並列計算*に対応する`Parallel`型クラスを提供します。
+`Parallel`は関数`parallel`を提供しており、何らかの`Applicative`関手を使って入力の計算の結果を*並列に*計算します。
```haskell
f <$> parallel computation1
@@ -787,19 +804,21 @@ f <$> parallel computation1
この考え方の詳細は、本書の後半で _コールバック地獄_ の問題に対してアプリカティブ関手を応用するときに見ていきます。
-アプリカティブ関手は並列に結合できる副作用を一纏めにする自然な方法です。
+アプリカティブ関手は並列に結合できる副作用を捉える自然な方法です。
## まとめ
この章では新しい考え方を沢山扱いました。
-- 関数適用の概念を副作用の観念を捉えた型構築子へと一般化する、 _アプリカティブ関手_ の概念を導入しました。
-- データ構造の検証という課題にアプリカティブ関手がどのような解決策を与えるか、どうすれば単一のエラーの報告からデータ構造を横断する全てのエラーの報告へ変換できるのかを見てきました。
+- *アプリカティブ関手*の概念を導入しました。
+ これは、関数適用の概念から副作用の観念を捉えた型構築子へと一般化するものです。
+- データ構造の検証という課題をアプリカティブ関手やその切り替えで解く方法を見てきました。
+ 単一のエラーの報告からデータ構造を横断する全てのエラーの報告へ変更できました。
- `Traversable`型クラスに出会いました。*巡回可能関手*の考え方を内包するものであり、要素が副作用を持つ値の結合に使うことができる容れ物でした。
アプリカティブ関手は多くの問題に対して優れた解決策を与える興味深い抽象化です。
本書を通じて何度も見ることになるでしょう。
-今回の場合、アプリカティブ関手は宣言的な流儀で書く手段を提供していましたが、これにより検証器が*どうやって*検証するかではなく、*何を*検証すべきなのかを定義できました。
-一般に、アプリカティブ関手は*領域特化言語*を設計する上で便利な道具になります。
+今回の場合、アプリカティブ関手は宣言的な流儀で書く手段を提供していましたが、これにより検証器が*どうやって*検証を実施するかではなく、*何を*検証すべきなのかを定義できました。
+一般にアプリカティブ関手が*領域特化言語*を設計する上で便利な道具になることを見ていきます。
次の章では、これに関連する考え方である*モナド*クラスを見て、アドレス帳の例をブラウザで実行させられるように拡張しましょう。
diff --git a/text-ja/chapter8.md b/text-ja/chapter8.md
index 6d26638d..98c02bed 100644
--- a/text-ja/chapter8.md
+++ b/text-ja/chapter8.md
@@ -2,18 +2,19 @@
## この章の目標
-前章では、オプショナルな型やエラーメッセージ、データの検証など、 _副作用_
-を扱いを抽象化するアプリカティブ関手を導入しました。この章では、より表現力の高い方法で副作用を扱うための別の抽象化、 _モナド_ を導入します。
+前章では、*副作用*を扱うのに使う抽象化であるアプリカティブ関手を導入しました。
+副作用とは省略可能な値、エラー文言、検証などです。
+この章では、副作用を扱うためのより表現力の高い別の抽象化である*モナド*を導入します。
-この章の目的は、なぜモナドが便利な抽象化なのかということと、 _do記法_ との関係を説明することです。
+この章の目的は、なぜモナドが便利な抽象化なのかということと、*do記法*との関係を説明することです。
## プロジェクトの準備
このプロジェクトでは、以下の依存関係が追加されています。
-- `effect`:
- 章の後半の主題である`Effect`モナドを定義しています。この依存関係は全てのプロジェクトで始めから入っているものなので(これまでの全ての章でも依存関係にありました)、明示的にインストールしなければいけないことは稀です。
-- `react-basic-hooks`: アドレス帳アプリに使うWebフレームワークです。
+- `effect`: 章の後半の主題である`Effect`モナドを定義しています。
+ この依存関係は全てのプロジェクトで始めから入っているものなので(これまでの全ての章でも依存関係にありました)、明示的にインストールしなければいけないことは稀です。
+- `react-basic-hooks`: アドレス帳アプリに使うwebフレームワークです。
## モナドとdo記法
@@ -25,9 +26,9 @@ do記法は*配列内包表記*を扱うときに初めて導入されました
- 最初の投擲で値 `x`を _選択_ します。
- 2回目の投擲で値 `y`を _選択_ します。
-- もし `x`と `y`の和が `n`なら組 `[x, y]`を返し、そうでなければ失敗します。
+- もし`x`と`y`の和が`n`なら組`[x, y]`を返し、そうでなければ失敗します。
-配列内包表記を使うと、この非決定的アルゴリズムを自然に書くことができます。
+配列内包表記を使うと、この非決定的アルゴリズムを自然に書けます。
```hs
import Prelude
@@ -50,14 +51,13 @@ PSCiでこの関数の動作を見てみましょう。
[[6,6]]
```
-前の章では、*オプショナルな値*に対応したより大きなプログラミング言語へとPureScriptの関数を埋め込む、`Maybe`アプリカティブ関手についての直感的理解を養いました。
+前の章では、*省略可能な値*に対応したより大きなプログラミング言語へとPureScriptの関数を埋め込む、`Maybe`アプリカティブ関手についての直感的理解を養いました。
同様に*配列モナド*についても、*非決定選択*に対応したより大きなプログラミング言語へPureScriptの関数を埋め込む、というような直感的理解を得ることができます。
-一般に、ある型構築子 `m`のモナドは、型 `m a`の値を持つdo記法を使う手段を提供します。
-上の配列内包表記では、全ての行に何らかの型 `a`についての型 `Array a`の計算が含まれていることに注目してください。
-一般に、do記法ブロックの全ての行は、何らかの型 `a`とモナド `m`について、型 `m a`の計算を含んでいます。
-モナド `m`は全ての行で同じでなければなりません(つまり、副作用の種類は固定されます)が、型
-`a`は異なることもあります(言い換えると、個々の計算は異なる型の結果を持つことができます)。
+一般に、ある型構築子`m`のモナドは、型`m a`の値を持つdo記法を使う手段を提供します。
+上の配列内包表記に注意すると、何らかの型`a`について全行に型`Array a`の計算が含まれています。
+一般に、do記法ブロックの全行は、何らかの型`a`とモナド`m`について、型`m a`の計算を含みます。
+モナド`m`は全行で同じでなければなりません(つまり副作用は固定)が、型`a`は異なることもあります(つまり個々の計算は異なる型の結果にできる)。
以下はdo記法の別の例です。
今回は型構築子 `Maybe`に適用されています。
@@ -81,10 +81,9 @@ userCity root = do
pure city
```
-`userCity`関数は子の要素である `profile`を探し、 `profile`要素の中にある `address`要素、最後に
-`address`要素から `city`要素を探します。
-これらの要素の何れかが欠落している場合は、返り値は `Nothing`になります。
-そうでなければ、返り値は `city`ノードから `Just`を使って構築されています。
+`userCity`関数は子の`profile`要素、`profile`要素の中にある`address`要素、最後に`address`要素の中にある`city`要素を探します。
+これらの要素の何れかが欠落している場合、返り値は`Nothing`になります。
+そうでなければ、返り値は`city`ノードから`Just`を使って構築されます。
最後の行にある`pure`関数は、全ての`Applicative`関手について定義されているのでした。
`Maybe`の`Applicative`関手の`pure`関数は`Just`として定義されており、最後の行を `Just
@@ -103,7 +102,7 @@ class (Applicative m, Bind m) <= Monad m
ここで鍵となる関数は `Bind`型クラスで定義されている演算子 `bind`で、`Functor`及び `Apply`型クラスにある `<$>`や `<*>`などの演算子と同様に、`Prelude`では `>>=`として `bind`の中置の別名が定義されています。
-`Monad`型クラスは、既に見てきた `Applicative`型クラスの操作で `Bind`を拡張します。
+`Monad`型クラスは、既に見てきた`Applicative`型クラスの操作で`Bind`を拡張します。
`Bind`型クラスの例を幾つか見てみるのがわかりやすいでしょう。
配列についての `Bind`の妥当な定義は次のようになります。
@@ -125,7 +124,8 @@ instance Bind Maybe where
この定義は欠落した値がdo記法ブロックを通じて伝播するという直感的理解を裏付けるものです。
-`Bind`型クラスとdo記法がどのように関係しているかを見て行きましょう。最初に何らかの計算結果からの値の束縛から始まる簡単なdo記法ブロックについて考えてみましょう。
+`Bind`型クラスとdo記法がどのように関係しているかを見て行きましょう。
+最初に、何らかの計算結果からの値の束縛から始まる、単純なdo記法ブロックについて考えてみましょう。
```hs
do value <- someComputation
@@ -157,7 +157,9 @@ userCity root =
pure city
```
-do記法を使って表現されたコードは、`>>=`演算子を使って書かれた同じ意味のコードよりしばしば読みやすくなることも特筆すべき点です。一方で、明示的に `>>=`を使って束縛を書くと、しばしば*ポイントフリー*形式でコードが書けるようになります。ただし、読みやすさにはやはり注意がいります。
+do記法を使って表現されたコードは、`>>=`演算子を使う等価なコードより遥かに読みやすくなることがよくあることも特筆すべき点です。
+しかしながら、明示的に`>>=`を使って束縛を書くと、*ポイントフリー*形式でコードが書けるようになることがよくあります。
+ただし、読みやすさにはやはり注意が要ります。
## モナド則
@@ -210,10 +212,10 @@ c2 = do
m3
```
-これらの計算にはそれぞれ、3つのモナドの式`m1`、`m2`、`m3`が含まれています。
-どちらの場合でも`m1`の結果は名前`x`に束縛され、`m2`の結果は名前`y`に束縛されます。
+これらの各計算には3つのモナドの式`m1`、`m2`、`m3`が含まれています。
+どちらの場合でも`m1`の結果は結局は名前`x`に束縛され、`m2`の結果は名前`y`に束縛されます。
-`c1`では2つの式 `m1`と `m2`がそれぞれのdo記法ブロック内にグループ化されています。
+`c1`では2つの式`m1`と`m2`が各do記法ブロック内にグループ化されています。
`c2`では`m1`、`m2`、`m3`の3つ全ての式が同じdo記法ブロックに現れています。
@@ -232,10 +234,11 @@ c3 = do
## モナドで畳み込む
抽象的にモナドを扱う例として、この節では `Monad`型クラス中の任意の型構築子で機能する関数を紹介していきます。
-これはモナドによるコードが副作用を伴う「より大きな言語」でのプログラミングと対応しているという直感的理解を補強しますし、モナドによるプログラミングがもたらす一般性も示しています。
+これはモナドによるコードが副作用を伴う「より大きな言語」でのプログラミングと対応しているという直感的理解を補強しますし、モナドによるプログラミングが齎す一般性も示しています。
-これから `foldM`と呼ばれる関数を書いてみます。これは以前扱った
-`foldl`関数をモナドの文脈へと一般化します。型シグネチャは次のようになっています。
+これから書いていく関数は`foldM`という名前です。
+以前見た`foldl`関数をモナドの文脈へと一般化するものです。
+型シグネチャは以下です。
```hs
foldM :: forall m a b. Monad m => (a -> b -> m a) -> a -> List b -> m a
@@ -244,15 +247,14 @@ foldl :: forall a b. (a -> b -> a) -> a -> List b -> a
モナド `m`が現れている点を除いて、 `foldl`の型と同じであることに注意しましょう。
-直感的には、 `foldM`はさまざまな副作用の組み合わせに対応した文脈での配列の畳み込みを行うと捉えることができます。
+直感的には、`foldM`は様々な副作用の組み合わせに対応した文脈で配列を畳み込むものと捉えられます。
-例として `m`が `Maybe`であるとすると、この畳み込みはそれぞれの段階で `Nothing`を返すことで失敗させられます。
-それぞれの段階ではオプショナルな結果を返しますから、それゆえ畳み込みの結果もオプショナルになります。
+例として`m`として`Maybe`を選ぶとすると、各段階で`Nothing`を返すことでこの畳み込みを失敗させられます。
+各段階では省略可能な結果を返しますから、それ故畳み込みの結果も省略可能になります。
-もし `m`として配列の型構築子
-`Array`を選ぶとすると、畳み込みのそれぞれの段階で複数の結果を返すことができ、畳み込みは結果それぞれに対して次の手順を継続します。
-最後に、結果の集まりは、可能な経路全ての畳み込みから構成されることになります。
-これはグラフの走査と対応しています。
+もし`m`として型構築子`Array`を選ぶとすると、畳み込みの各段階で0以上の結果を返せるため、畳み込みは各結果に対して独立に次の手順を継続します。
+最後に、結果の集まりは可能な経路の全ての畳み込みから構成されることになります。
+これはグラフの走査と対応していますね。
`foldM`を書くには、単に入力のリストについて場合分けをするだけです。
@@ -262,7 +264,7 @@ foldl :: forall a b. (a -> b -> a) -> a -> List b -> a
foldM _ a Nil = pure a
```
-なお`a`をモナド `m`まで持ち上げるために `pure`を使わなくてはいけません。
+なお、`a`をモナド `m`まで持ち上げるために `pure`を使わなくてはいけません。
リストが空でない場合はどうでしょうか。
その場合、型 `a`の値、型 `b`の値、型 `a -> b -> m a`の関数があります。
@@ -277,10 +279,13 @@ foldM f a (b : bs) = do
foldM f a' bs
```
-なお、do記法を除けば、この実装は配列に対する `foldl`の実装とほとんど同じです。
+なお、この実装はリストに対する`foldl`の実装とほとんど同じです。
+ただしdo記法である点を除きます。
-PSCiでこれを定義し、試してみましょう。
-以下では例として、除算可能かどうかを調べて、失敗を示すために `Maybe`型構築子を使う、整数の「安全な除算」関数を定義するとしましょう。
+PSCiでこの関数を定義して試せます。
+以下は一例です。
+整数の「安全な除算」関数を定義するとします。
+0による除算かを確認し、失敗を示すために `Maybe`型構築子を使うのです。
```hs
{{#include ../exercises/chapter8/test/Examples.purs:safeDivide}}
@@ -299,8 +304,8 @@ PSCiでこれを定義し、試してみましょう。
Nothing
```
-もし何れかの時点で整数にならない除算が行われようとしたら、`foldM safeDivide`関数は `Nothing`を返します。
-そうでなければ、除算を繰り返した累積の結果を`Just`構築子に包んで返します。
+もし何れかの時点で0による除算が試みられたら、`foldM safeDivide`関数は`Nothing`を返します。
+そうでなければ、累算値を繰り返し除算した結果を`Just`構築子に包んで返します。
## モナドとアプリカティブ
@@ -318,15 +323,16 @@ ap mf ma = do
もし`m`に`Monad`型クラスの法則の縛りがあれば、`ap`で与えられる`m`について妥当な `Apply`インスタンスが存在します。
-興味のある読者は、これまで登場した `Array`、 `Maybe`、 `Either e`といったモナドについて、この `ap`が
-`apply`と一致することを確かめてみてください。
+興味のある読者はこれまで登場したモナドについてこの`ap`が`apply`として充足することを確かめてみてください。
+モナドは`Array`、`Maybe`、`Either e`といったものです。
もし全てのモナドがアプリカティブ関手でもあるなら、アプリカティブ関手についての直感的理解を全てのモナドについても適用できるはずです。
特に、モナドが更なる副作用の組み合わせで増強された「より大きな言語」でのプログラミングといろいろな意味で一致することを予想するのはもっともです。
`map`と `apply`を使って、引数が任意個の関数をこの新しい言語へと持ち上げることができるはずです。
-しかし、モナドはアプリカティブ関手でできること以上を行うことができ、重要な違いはdo記法の構文で強調されています。
-利用者情報をエンコードしたXML文書から利用者の都市を検索する、`userCity`の例についてもう一度考えてみましょう。
+しかし、モナドはアプリカティブ関手でできること以上ができ、重要な違いはdo記法の構文で強調されています。
+`userCity`の例についてもう一度考えてみましょう。
+利用者情報をエンコードしたXML文書から利用者の市町村を検索するものでした。
```hs
userCity :: XML -> Maybe XML
@@ -343,17 +349,17 @@ do記法では2番目の計算が最初の結果 `prof`に依存し、3番目の
`pure`と `apply`だけを使って `userCity`を書こうとしてみれば、これが不可能であることがわかるでしょう。
アプリカティブ関手ができるのは関数の互いに独立した引数を持ち上げることだけですが、モナドはもっと興味深いデータの依存関係に関わる計算を書くことを可能にします。
-前の章では `Applicative`型クラスは並列処理を表現できることを見ました。
+前の章では`Applicative`型クラスは並列処理を表現できることを見ました。
持ち上げられた関数の引数は互いに独立していますから、これはまさにその通りです。
-`Monad`型クラスは計算が前の計算の結果に依存できるようにしますから、同じようにはなりません。
-モナドは副作用を順番に組み合わせなければいけません。
+`Monad`型クラスは計算が前の計算の結果に依存できるようになっており、同じようにはなりません。
+つまりモナドは副作用を順番に組み合わせなければならないのです。
## 演習
1. (簡単)3つ以上の要素がある配列の3つ目の要素を返す関数`third`を書いてください。
関数は適切な`Maybe`型で返します。
*手掛かり*:`arrays`パッケージの`Data.Array`モジュールから`head`と`tail`関数の型を見つけ出してください。
- これらの関数を繋げるには`Maybe`モナドと共にdo記法を使ってください。
+ これらの関数を組み合わせるには`Maybe`モナドと共にdo記法を使ってください。
1. (普通)一掴みの硬貨を使ってできる可能な全ての合計を決定する関数 `possibleSums`を、 `foldM`を使って書いてみましょう。
入力の硬貨は、硬貨の価値の配列として与えられます。この関数は次のような結果にならなくてはいけません。
@@ -365,13 +371,14 @@ do記法では2番目の計算が最初の結果 `prof`に依存し、3番目の
[0,1,2,3,10,11,12,13]
```
- *手掛かり*:`foldM`を使うと1行でこの関数を書くことが可能です。
- 重複を取り除いたり、結果を並び替えたりするのに、`nub`関数や `sort`関数を使いたくなるかもしれません。
-1. (普通)`Maybe`型構築子について、 `ap`関数と `apply`演算子が一致することを確認してください。
+ *手掛かり*:`foldM`を使うと1行でこの関数を書けます。
+ 重複を取り除いたり、結果を並び替えたりするのに、`nub`関数や`sort`関数を使うことでしょう。
+1. (普通)`ap`関数と`apply`演算子が`Maybe`モナドを充足することを確かめてください。
*補足*:この演習にはテストがありません。
-1. (普通)`maybe`パッケージで定義されている`Maybe`型についての `Monad`インスタンスが、モナド則を満たしていることを検証してください。
+1. (普通)`Maybe`型についての`Monad`インスタンスが、モナド則を満たしていることを検証してください。
+ このインスタンスは`maybe`パッケージで定義されています。
*補足*:この演習にはテストがありません。
-1. (普通)配列上の `filter`の関数を一般化した関数`filterM`を書いてください。
+1. (普通)リスト上の`filter`の関数を一般化した関数`filterM`を書いてください。
この関数は次の型シグネチャを持ちます。
```hs
@@ -392,7 +399,7 @@ do記法では2番目の計算が最初の結果 `prof`に依存し、3番目の
lift2 f (pure a) (pure b) = pure (f a b)
```
- ここで、 `Applly`インスタンスは上で定義された `ap`関数を使用しています。
+ ここで、`Applly`インスタンスは上で定義された`ap`関数を使用しています。
`lift2`が次のように定義されていたことを思い出してください。
```hs
@@ -404,13 +411,13 @@ do記法では2番目の計算が最初の結果 `prof`に依存し、3番目の
## ネイティブな作用
-ここではPureScriptの中核となる重要なモナド、 `Effect`モナドについて見ていきます。
+ここではPureScriptで中心的な重要性のあるモナドの1つ、`Effect`モナドについて見ていきます。
`Effect`モナドは `Effect`モジュールで定義されています。かつてはいわゆる _ネイティブ_
副作用を管理していました。Haskellに馴染みがあれば、これは`IO`モナドと同等のものです。
ネイティブな副作用とは何でしょうか。
-この副作用はPureScript特有の式からJavaScriptの式を区別するものです。
+この副作用はPureScript特有の式とJavaScriptの式とを2分するものです。
PureScriptの式は概して副作用とは無縁なのです。
ネイティブな作用の例を以下に示します。
@@ -433,33 +440,37 @@ PureScriptの式は概して副作用とは無縁なのです。
- 配列やリストで表現される多値関数
これらの区別はわかりにくいので注意してください。
-エラーメッセージは例外の形でJavaScriptの式の副作用となることがあります。
-その意味では例外はネイティブな副作用を表していて、 `Effect`を使用して表現できます。
-しかし、 `Either`を使用して実装されたエラーメッセージはJavaScriptランタイムの副作用ではなく、
-`Effect`を使うスタイルでエラーメッセージを実装するのは適切ではありません。
+例えば、エラー文言は例外の形でJavaScriptの式の副作用となることがあると言えます。
+その意味では例外はネイティブな副作用を表していて、`Effect`を使用して表現できます。
+しかし、`Either`を使用して実装されたエラー文言はJavaScript実行時の副作用ではなく、`Effect`を使うスタイルでエラー文言を実装するのは不適切です。
そのため、ネイティブなのは作用自体というより、実行時にどのように実装されているかです。
## 副作用と純粋性
-PureScriptのような言語が純粋であるとすると、疑問が浮かんできます。副作用がないなら、どうやって役に立つ実際のコードを書くことができるというのでしょうか。
+PureScriptのような純粋な言語では、ある疑問が浮かんできます。
+副作用がないなら、どうやって役に立つ実際のコードを書くことができるのでしょうか。
-その答えはPureScriptの目的は副作用を排除することではないということです。これは、純粋な計算と副作用のある計算とを型システムにおいて区別できるような方法で、副作用を表現することを目的としているのです。この意味で、言語はあくまで純粋だということです。
+その答えはPureScriptの目的は副作用を排除することではないということです。
+純粋な計算と副作用のある計算とを、型システムにおいて区別できるような方法で表現します。
+この意味で、言語はあくまで純粋なのです。
-副作用のある値は、純粋な値とは異なる型を持っています。そういうわけで、例えば副作用のある引数を関数に渡すことはできず、予期せず副作用を持つようなことが起こらなくなります。
+副作用のある値は、純粋な値とは異なる型を持っています。
+そういうわけで、例えば副作用のある引数を関数に渡すことはできず、予期せず副作用を持つようなことが起こらなくなります。
-`Effect`モナドで管理された副作用を実行する唯一の方法は、型 `Effect a`の計算をJavaScriptから実行することです。
+`Effect`モナドで管理された副作用を現す手段は、型`Effect a`の計算をJavaScriptから実行することです。
Spagoビルドツール(や他のツール)は早道を用意しており、アプリケーションの起動時に`main`計算を呼び出すための追加のJavaScriptコードを生成します。
-`main`は `Effect`モナドでの計算であることが要求されます。
+`main`は`Effect`モナドでの計算であることが要求されます。
## 作用モナド
`Effect`は副作用のある計算を充分に型付けするAPIを提供すると同時に、効率的なJavaScriptを生成します。
-馴染みのある`log`関数から返る型をもう少し見てみましょう。
+馴染みのある`log`関数から返る型を見てみましょう。
`Effect`はこの関数がネイティブな作用を生み出すことを示しており、この場合はコンソールIOです。
+
`Unit`はいかなる*意味のある*データも返らないことを示しています。
-`Unit`はC、Javaなど他の言語での`void`キーワードと似たようなものとして考えられます。
+`Unit`はC、Javaなど他の言語での`void`キーワードと似たものとして考えられます。
```hs
log :: String -> Effect Unit
@@ -503,22 +514,24 @@ spago run --main Test.Random
コンソールに出力 `0.0`と `1.0`の間で無作為に選ばれた数が表示されるでしょう。
-> 余談:`spago run`は既定で`Main`モジュールとその中の`main`関数を探索します。
-`--main`フラグで代替のモジュールを入口として指定でき、上の例ではそうしています。
-この代替のモジュールもまた`main`関数を含んでいることに注目してください。
+> 余談:`spago run`は既定で`main`関数を`Main`モジュールの中から探索します。
+> `--main`フラグで代替のモジュールを入口として指定することも可能で、上の例ではそうしています。
+> この代替のモジュールにも`main`関数が含まれているようにはしてください。
なお、不浄な作用付きのコードに訴えることなく、「乱択された」(技術的には疑似乱択された)データも生成できます。
この技法は「テストを生成する」章で押さえます。
以前言及したように`Effect`モナドはPureScriptで核心的な重要さがあります。
-なぜ核心かというと、それはPureScriptの`外部関数インターフェース`とやりとりする上での常套手段だからです。
+なぜ核心かというと、それはPureScriptの`外部関数インターフェース`とやり取りする上での常套手段だからです。
`外部関数インターフェース`はプログラムを実行したり副作用を発生させたりする仕組みを提供します。
`外部関数インターフェース`を使うことは避けるのが望ましいのですが、どのように動作しどう使うのか理解することもまた極めて大事なことですので、実際にPureScriptで何か動かす前にその章を読まれることをお勧めします。
-要は`Effect`モナドは結構単純なのです。幾つかの補助関数がありますが、それを差し置いても副作用を内包すること以外には大したことはしません。
+要は`Effect`モナドは結構単純なのです。
+幾つかの補助関数がありますが、副作用を内包すること以外には大したことはしません。
## 例外
-2つの _ネイティブな_ 副作用が絡む`node-fs`パッケージの関数を調べましょう。ここでの副作用は可変状態の読み取りと例外です。
+2つの*ネイティブな*副作用が絡む`node-fs`パッケージの関数を調べましょう。
+ここでの副作用は可変状態の読み取りと例外です。
```hs
readTextFile :: Encoding -> String -> Effect String
@@ -588,8 +601,8 @@ exceptionHead l = case l of
純粋関数プログラミングを知っているなら、共有される変更可能な状態は問題を引き起こしやすいということも知っているでしょう。
しかし、`ST`作用は型システムを使って安全で*局所的な*状態変化を可能にし、状態の共有を制限するのです。
-`ST`作用は
-`Control.Monad.ST`モジュールで定義されています。これがどのように動作するかを確認するには、そのアクションの型を見る必要があります。
+`ST`作用は `Control.Monad.ST`モジュールで定義されています。
+この挙動を確認するには、その動作の型を見る必要があります。
```hs
new :: forall a r. a -> ST r (STRef r a)
@@ -601,12 +614,13 @@ write :: forall a r. a -> STRef r a -> ST r a
modify :: forall r a. (a -> a) -> STRef r a -> ST r a
```
-`new`は型`STRef r a`の変更可能な参照領域を新しく作るのに使われます。
-`STRef r a`は `read`アクションを使って状態を読み取ったり、`write`アクションや
-`modify`アクションで状態を変更するのに使われます。
-型`a`は領域に格納された値の型で、型 `r`は型システムで*メモリ領域*(または*ヒープ*)を表しています。
+`new`は型`STRef r a`の可変参照領域を新規作成するのに使われます。
+この領域は`read`動作を使って読み取ったり、`write`動作や`modify`動作で状態を変更するのに使えます。
+型`a`は領域に格納された値の型を、型`r`は*メモリ領域*(または*ヒープ*)を、それぞれ型システムで表しています。
-例を示します。小さな時間刻みで簡単な更新関数の実行を何度も繰り返すことによって、重力に従って落下する粒子の落下の動きをシミュレートしたいとしましょう。
+例を示します。
+重力に従って落下する粒子の落下の動きをシミュレートしたいとしましょう。
+これには小さな時間刻みで簡単な更新関数の実行を何度も繰り返します。
粒子の位置と速度を保持する変更可能な参照領域を作成し、領域に格納された値を更新するのにforループを使うことでこれを実現できます。
@@ -633,8 +647,8 @@ simulate x0 v0 time = do
計算の最後では、参照領域の最終的な値を読み取り、粒子の位置を返しています。
-なお、この関数が変更可能な状態を使っていても、その参照領域`ref`がプログラムの他の部分で使われるのが許されない限り、これは純粋な関数のままです。
-`ST`作用が禁止するものが正確には何であるのかについては後ほど見ます。
+なお、この関数が変更可能な状態を使っていても、その参照領域`ref`がプログラムの他の部分での使用が許されない限り、これは純粋な関数のままです。
+このことが正に`ST`作用が禁止するものであることを見ていきます。
`ST`作用付きで計算するには、`run`関数を使用する必要があります。
@@ -643,11 +657,10 @@ run :: forall a. (forall r. ST r a) -> a
```
ここで注目して欲しいのは、領域型 `r`が関数矢印の左辺にある*括弧の内側で*量化されているということです。
-`run`に渡したどんなアクションでも、*任意の領域*`r`が何であれ動作するということを意味しています。
+`run`に渡したどんな動作でも、*任意の領域*`r`が何であれ動作するということを意味しています。
-しかし、ひとたび参照領域が
-`new`によって作成されると、その領域の型は既に固定されており、`run`によって限定されたコードの外側で参照領域を使おうとしても型エラーになるでしょう。
-`run`が安全に `ST`作用を除去でき、`simulate`を純粋関数にできるのはこれが理由なのです。
+しかし、ひとたび参照領域が`new`によって作成されると、その領域の型は既に固定されており、`run`によって限定されたコードの外側で参照領域を使おうとしても型エラーになるでしょう。
+`run`が安全に`ST`作用を除去でき、`simulate`を純粋関数にできるのはこれが理由なのです。
```hs
simulate' :: Number -> Number -> Int -> Number
@@ -694,7 +707,7 @@ simulate x0 v0 time =
pure final.x
```
-参照領域はそのスコープから逃れることができないことがコンパイラにわかりますし、安全に`ref`を`var`に変換できます。
+そうして、参照領域はそのスコープから逃れられないことと、安全に`ref`を`var`に変換できることにコンパイラが気付きます。
`run`が埋め込まれた`simulate`に対して生成されたJavaScriptは次のようになります。
```javascript
@@ -722,9 +735,9 @@ var simulate = function (x0) {
};
```
-なおこの結果として得られたJavaScriptは最適化の余地があります。
-詳細は[この課題](https://github.com/purescript-contrib/purescript-book/issues/121)を参照してください。
-上記の抜粋はその課題が解決されたら更新されるでしょう。
+> なお、この結果として得られたJavaScriptは最適化の余地があります。
+> 詳細は[こちらの課題](https://github.com/purescript-contrib/purescript-book/issues/121)を参照してください。
+> 上記の抜粋はそちらの課題が解決されたら更新されるでしょう。
比較としてこちらが埋め込まれていない形式で生成されたJavaScriptです。
@@ -753,8 +766,8 @@ var simulate = function (x0) {
};
```
-局所的な変更可能状態を扱うとき、特に作用が絡むループを生成する`for`、 `foreach`、
-`while`のようなアクションを一緒に使うときには、`ST`作用は短いJavaScriptを生成する良い方法となります。
+局所的な変更可能状態を扱うとき、`ST`作用は短いJavaScriptを生成する良い方法となります。
+作用を持つ繰り返しを生成する`for`、`foreach`、`while`のような動作を一緒に使うときは特にそうです。
## 演習
@@ -776,12 +789,11 @@ var simulate = function (x0) {
DOMを直接扱ったり、オープンソースのDOMライブラリを扱ったりするPureScriptパッケージが沢山あります。
例えば以下です。
-- [`web-dom`](https://github.com/purescript-web/purescript-web-dom)はW3C
- のDOM規格に向けた型定義と低水準インターフェース実装を提供します。
+- [`web-dom`](https://github.com/purescript-web/purescript-web-dom)はW3CのDOM規格に向けた型定義と低水準インターフェース実装を提供します。
- [`web-html`](https://github.com/purescript-web/purescript-web-html)はW3CのHTML5規格に向けた型定義と低水準インターフェース実装を提供します。
- [`jquery`](http://github.com/paf31/purescript-jquery)は[jQuery](http://jquery.org)ライブラリのバインディングの集まりです。
-上記のライブラリを抽象化するPureScriptライブラリもあります。
+上記のライブラリを土台に抽象化を進めたPureScriptライブラリもあります。
以下のようなものです。
- [`thermite`](https://github.com/paf31/purescript-thermite)は[`react`](https://github.com/purescript-contrib/purescript-react)を土台に構築されています。
@@ -799,12 +811,12 @@ Reactライブラリの完全な入門はこの章の範囲をはるかに超え
目的に応じて、Reactは `Effect`モナドの実用的な例を提供してくれます。
利用者が住所録に新しい項目を追加できるフォームを構築することにしましょう。
-フォームには、さまざまなフィールド(姓、名前、都市、州など)のテキストボックス、及び検証エラーが表示される領域が含まれます。
-テキストボックスに利用者がテキストを入力すると、検証エラーが更新されます。
+フォームには、様々なフィールド(姓、名、市町村、州など)のテキストボックス、及び検証エラーが表示される領域が含まれます。
+テキストボックスに利用者がテキストを入力する度に、検証エラーが更新されます。
簡潔さを保つために、フォームは固定の形状とします。電話番号は種類(自宅、携帯電話、仕事、その他)ごとに別々のテキストボックスへ分けることにします。
-`exercises/chapter8`ディレクトリから以下のコマンドでWebアプリを立ち上げることができます。
+`exercises/chapter8`ディレクトリから以下のコマンドでwebアプリを立ち上げることができます。
```shell
$ npm install
@@ -820,7 +832,7 @@ $ npx parcel src/index.html --open
また、[`purs
ide`](https://github.com/purescript/purescript/tree/master/psc-ide)に対応していたり[`pscid`](https://github.com/kRITZCREEK/pscid)を走らせていたりする[エディタ](https://github.com/purescript/documentation/blob/master/ecosystem/Editor-and-tool-support.md#editors)を使っていれば、ファイルを保存したときに自動的にページが再構築される(そして自動的にページが再読み込みされる)ように設定できます。
-このアドレス帳アプリでフォームフィールドにいろいろな値を入力すると、ページ上に出力された検証エラーを見ることができるでしょう。
+このアドレス帳アプリでフォームフィールドにいろいろな値を入力すると、ページ上で出力された検証エラーが見られます。
動作の仕組みを散策しましょう。
@@ -910,7 +922,7 @@ propsからJSXへの関数は単にこうです。
`props`は無視されており、`D.text`は`JSX`を返し、そして`pure`は描画されたJSXに持ち上げます。
これで`component`には`ReactComponent`を生成するのに必要な全てがあります。
-次に完全なアドレス帳コンポーネントにある幾つかの複雑な事柄をもう少し調べていきます。
+次に、完全なアドレス帳コンポーネントにある幾つかの複雑な事柄を調べていきます。
これらは完全なコンポーネントの最初の数行です。
@@ -928,7 +940,7 @@ Tuple person setPerson <- useState examplePerson
```
なお、複数回`useState`を呼び出すことで、コンポーネントの状態を複数の状態の部品に分解することが自在にできます。
-例えば`Person`のそれぞれのレコードフィールドについて分離した状態の部品を使って、このアプリを書き直すことができるでしょう。
+例えば`Person`の各レコードフィールドについて分離した状態の部品を使って、このアプリを書き直すことができるでしょう。
しかしこの場合にそうすると僅かに利便性を損なうアーキテクチャになってしまいます。
他の例では`Tuple`用の`/\`中置演算子に出喰わすかもしれません。
@@ -962,7 +974,7 @@ setPerson :: (state -> state) -> Effect Unit
`state`の限定された型は初期の既定値によって決定されます。
これは`examplePerson`の型なのでこの場合は`Person` `Record`です。
-`person`はそれぞれの再描画の時点で現在の状態にアクセスする方法です。
+`person`は各再描画の時点で現在の状態にアクセスする方法です。
`setPerson`は状態を更新する方法です。
単に現在の状態を新しい状態に変形する方法を記述する関数を提供します。
@@ -1024,7 +1036,10 @@ pure
}
```
-ここでDOMの意図した状態を表現する`JSX`を生成しています。このJSXはHTMLタグ(例:`div`、`form`、`h3`、`li`、`ul`、`label`、`input`)に対応し、典型的には単一のHTML要素を作る関数を適用することで作られます。これらのHTML要素は実はReactコンポーネントそのものによりJSXに変換されます。通常これらの関数にはそれぞれ3つの種類があります。
+ここでDOMの意図した状態を表現する`JSX`を生成しています。
+このJSXは単一のHTML要素を作るHTMLタグ(例:`div`、`form`、`h3`、`li`、`ul`、`label`、`input`)に対応する関数を適用することで作られるのが普通です。
+これらのHTML要素はそれ自体がReactコンポーネントであり、JSXに変換されます。
+通常これらの関数にはそれぞれ3つの種類があります。
- `div_`: 子要素の配列を受け付けます。
既定の属性を使います。
@@ -1074,7 +1089,8 @@ handler :: forall a. EventFn SyntheticEvent a -> (a -> Effect Unit) -> EventHand
targetValue :: EventFn SyntheticEvent (Maybe String)
```
-JavaScriptでは`input`要素の`onChange`イベントは実は`String`値と一緒になっているのですが、JavaScriptの文字列はnullになりえるので、安全のために`Maybe`が使われています。
+JavaScriptでは`input`要素の`onChange`イベントには`String`値が伴います。
+しかし、JavaScriptの文字列はnullになり得るので、安全のために`Maybe`が使われています。
したがって`(a -> Effect Unit)`の`handler`への2つ目の引数は、このシグネチャを持ちます。
@@ -1095,9 +1111,9 @@ onChange:
handler targetValue handleValue
```
-`setValue`はそれぞれの`formField`の呼び出しに与えた関数で、文字列を取り`setPerson`フックに適切なレコード更新呼び出しを実施します。
+`setValue`は`formField`の各呼び出しに与えた関数で、文字列を取り`setPerson`フックに適切なレコード更新呼び出しを実施します。
-なお`handleValue`は以下のようにも置き換えられます。
+なお、`handleValue`は以下のようにも置き換えられます。
```hs
onChange: handler targetValue $ traverse_ setValue
@@ -1105,7 +1121,8 @@ onChange: handler targetValue $ traverse_ setValue
`traverse_`の定義を調査して、両方の形式が確かに等価であることをご確認ください。
-これでコンポーネント実装の基本を押さえました。しかし、コンポーネントの仕組みを完全に理解するためには、この章に付随する出典元をお読みください。
+これでコンポーネント実装の基本を押さえました。
+しかし、コンポーネントの仕組みを完全に理解するためには、この章に付随するソースをお読みください。
明らかに、このユーザーインターフェースには改善すべき点が沢山あります。
演習ではアプリケーションがより使いやすくなるような方法を追究していきます。
@@ -1117,12 +1134,14 @@ onChange: handler targetValue $ traverse_ setValue
1. (簡単)このアプリケーションを変更し、職場の電話番号を入力できるテキストボックスを追加してください。
1. (普通)現時点でアプリケーションは検証エラーを単一の「pink-alert」背景に集めて表示させています。
- 空の線で分割することにより、それぞれの検証エラーにpink-alert背景を持たせるように変更してください。
+ 空行で分離することにより、各検証エラーにpink-alert背景を持たせるように変更してください。
- *手掛かり*:リスト中の検証エラーを表示するのに`ul`要素を使う代わりに、コードを変更し、それぞれのエラーに`alert`と`alert-danger`装飾を持つ`div`を作ってください。
-1. (難しい、発展)このユーザーインターフェイスの問題の1つは、検証エラーがその発生源であるフォームフィールドの隣に表示されていないことです。コードを変更してこの問題を解決してください。
+ *手掛かり*:リスト中の検証エラーを表示するのに`ul`要素を使う代わりに、コードを変更し、各エラーに`alert`と`alert-danger`装飾を持つ`div`を作ってください。
+1. (難しい、発展)このユーザーインターフェイスの問題の1つは、検証エラーがその発生源であるフォームフィールドの隣に表示されていないことです。
+ コードを変更してこの問題を解決してください。
- *手掛かり*:検証器によって返されるエラーの型は、エラーの原因となっているフィールドを示すために拡張する必要があります。次のような変更されたエラー型を使用したくなるでしょう。
+ *手掛かり*:検証器によって返されるエラーの型を、エラーの原因となっているフィールドを示すために拡張するべきです。
+ 以下の変更されたエラー型を使うと良いでしょう。
```hs
data Field = FirstNameField
@@ -1143,12 +1162,13 @@ onChange: handler targetValue $ traverse_ setValue
この章ではPureScriptでの副作用の扱いについての多くの考え方を導入しました。
-- `Monad`型クラスとdo記法との関係性に出会いました。
+- `Monad`型クラスとdo記法との関係性を見ました。
- モナド則を導入し、do記法を使って書かれたコードを変換する方法を見ました。
-- 異なる副作用を扱うコードを書くために、モナドを抽象的に使う方法を見ました。
+- 異なる副作用を扱うコードを書く上で、モナドを抽象的に使う方法を見ました。
- モナドがアプリカティブ関手の一例であること、両者がどのように副作用のある計算を可能にするのかということ、そして2つの手法の違いを説明しました。
-- ネイティブな作用の概念を定義し、ネイティブな副作用を処理するために使用する `Effect`モナドを導入しました。
-- 乱数生成、例外、コンソール入出力、変更可能な状態、及びReactを使ったDOM操作といった、さまざまな作用を扱うために
+- ネイティブな作用の概念を定義し、`Effect`モナドを見ました。
+ これはネイティブな副作用を扱うものでした。
+- 乱数生成、例外、コンソール入出力、変更可能な状態、及びReactを使ったDOM操作といった、様々な作用を扱うために
`Effect`モナドを使いました。
`Effect`モナドは実際のPureScriptコードにおける基本的なツールです。本書ではこのあとも、多くの場面で副作用を処理するために使っていきます。
diff --git a/text-ja/chapter9.md b/text-ja/chapter9.md
index b638aeac..ae76c574 100644
--- a/text-ja/chapter9.md
+++ b/text-ja/chapter9.md
@@ -2,7 +2,10 @@
## この章の目標
-この章では`Aff`モナドに集中します。これは`Effect`モナドに似ていますが、*非同期*な副作用を表現するものです。ファイルシステムとやりとりしてHTTPリクエストを作る、非同期な例を実演していきます。また非同期作用の直列ないし並列な実行の管理方法も押さえます。
+この章では`Aff`モナドに集中します。
+これは`Effect`モナドに似ていますが、*非同期*な副作用を表現するものです。
+非同期にファイルシステムとやり取りしたりHTTPリクエストしたりする例を実演していきます。
+また非同期作用の直列ないし並列な実行の管理方法も押さえます。
## プロジェクトの準備
@@ -13,7 +16,7 @@
- `affjax` - AJAXと`Aff`を使ったHTTPリクエスト。
- `parallel` - `Aff`の並列実行。
-(Node.js環境のような)ブラウザ外で実行する場合、`affjax`ライブラリは`xhr2`NPMモジュールが必要です。
+(Node.js環境のような)ブラウザ外で実行する場合、`affjax`ライブラリには`xhr2`NPMモジュールが必要です。
このモジュールはこの章の`package.json`中の依存関係に挙げられています。
以下を走らせてインストールします。
@@ -58,10 +61,11 @@ PureScriptでの`Aff`モナドはJavaScriptの`async`/`await`構文に似た人
なお、`main`は`Effect
Unit`でなければならないので、`launchAff_`を使って`Aff`から`Effect`へと変換せねばなりません。
-上のコード片をコールバックや同期関数を使って書き換えることも可能ですが(例えば`Node.FS.Async`や`Node.FS.Sync`をそれぞれ使います)、JavaScriptで前にお話ししたのと同じ短所がここでも通用するため、それらのコーディング形式は推奨されません。
+上のコード片をコールバックや同期関数を使って書き換えることも可能です(例えば`Node.FS.Async`や`Node.FS.Sync`をそれぞれ使います)。
+しかし、JavaScriptで前にお話ししたのと同じ短所がここでも通用するため、それらのコーディング形式は推奨されません。
`Aff`を扱う文法は`Effect`を扱うものと大変似ています。
-どちらもモナドですし、したがってdo記法で書くことができます。
+どちらもモナドですし、したがってdo記法で書けます。
例えば`readTextFile`のシグネチャを見れば、これがファイルの内容を`String`とし、`Aff`に包んで返していることがわかります。
@@ -93,7 +97,8 @@ attempt :: forall a. Aff a -> Aff (Either Error a)
1. (簡単)2つのテキストファイルを連結する関数`concatenateFiles`を書いてください。
- 1. (普通)入力ファイル名の配列と出力ファイル名が与えられたとき、複数のテキストファイルを連結する関数`concatenateMany`を書いてください。
+ 1. (普通)複数のテキストファイルを連結する関数`concatenateMany`を書いてください。
+ 入力ファイル名の配列と出力ファイル名が与えられます。
*手掛かり*:`traverse`を使ってください。
1. (普通)ファイル中の文字数を返すか、エラーがあればそれを返す関数`countCharacters :: FilePath -> Aff
@@ -102,7 +107,7 @@ attempt :: forall a. Aff a -> Aff (Either Error a)
## 更なるAffの資料
もしまだ[公式のAffの手引き](https://pursuit.purescript.org/packages/purescript-aff/)を見ていなければ、今ざっと目を通してください。
-この章の残りの演習を完了する上で事前に必要なことではありませんが、Pursuitで何らかの関数を見付けだす助けになるかもしれません。
+この章の残りの演習を完了する上で事前に直接必要なことではありませんが、Pursuitで何らかの関数を見付けだす助けになるかもしれません。
以下の補足資料についてもあたってみるとよいでしょう。しかし繰り返しになりますがこの章の演習はこれらの内容に依りません。
@@ -111,11 +116,12 @@ attempt :: forall a. Aff a -> Aff (Either Error a)
## HTTPクライアント
-`affjax`ライブラリは`Aff`で非同期AJAX HTTP要求する便利な手段を提供します。
+`affjax`ライブラリは`Aff`で非同期なAJAXのHTTP要求をする上での便利な手段を提供します。
対象としている環境が何であるかによって、[purescript-affjax-web](https://github.com/purescript-contrib/purescript-affjax-web)または[purescript-affjax-node](https://github.com/purescript-contrib/purescript-affjax-node)のどちらかのライブラリを使う必要があります。
-この章の以降ではNodeを対象としていくので、`purescript-affjax-node`を使います。
+
+この章の以降ではnodeを対象としていくので、`purescript-affjax-node`を使います。
より詳しい使用上の情報は[affjaxのドキュメント](https://pursuit.purescript.org/packages/purescript-affjax)にあたってください。
-以下は与えられたURLに向けてHTTP GETを要求し、応答本文ないしエラー文言を返す例です。
+以下は与えられたURLに向けてHTTPのGET要求をして、応答本文ないしエラー文言を返す例です。
```hs
{{#include ../exercises/chapter9/test/HTTP.purs:getUrl}}
@@ -152,7 +158,7 @@ unit
`parallel`パッケージは`Aff`のようなモナドのための型クラス`Parallel`を定義しており、並列実行に対応しています。
以前に本書でアプリカティブ関手に出会ったとき、並列計算を合成するときにアプリカティブ関手がどれほど便利なのかを見ました。
-実は`Parallel`のインスタンスは、(`Aff`のような)モナド`m`と、並列に計算を合成するために使われるアプリカティブ関手`f`との対応関係を定義しているのです。
+実は`Parallel`のインスタンスは、(`Aff`のような)モナド`m`と、並列に計算を組み合わせるために使えるアプリカティブ関手`f`との対応関係を定義しているのです。
```hs
class (Monad m, Applicative f) <= Parallel f m | m -> f, f -> m where
@@ -165,16 +171,16 @@ class (Monad m, Applicative f) <= Parallel f m | m -> f, f -> m where
- `parallel`:モナド`m`中の計算を取り、アプリカティブ関手`f`中の計算に変えます。
- `sequential`:反対方向に変換します。
-`aff`ライブラリは `Aff`モナドの `Parallel`インスタンスを提供します。
-これは、2つの継続 (continuation) のどちらが呼び出されたかを把握することによって、変更可能な参照を使用して並列に
-`Aff`アクションを組み合わせます。
-両方の結果が返されたら、最終結果を計算してメインの継続に渡すことができます。
+`aff`ライブラリは`Aff`モナドの`Parallel`インスタンスを提供します。
+これは、2つの継続のどちらが呼び出されたかを把握することによって、変更可能な参照を使用して並列に`Aff`動作を組み合わせます。
+両方の結果が返されたら、最終結果を計算してメインの継続に渡せます。
アプリカティブ関手では任意個の引数の関数の持ち上げができるので、このアプリカティブコンビネータを使ってより多くの計算を並列に実行できます。
`traverse`や`sequence`といった、アプリカティブ関手を扱う全ての標準ライブラリ関数から恩恵を受けることもできます。
-必要に応じて
-`parralel`と`sequential`を使って型構築子を変更することで、do記法ブロック中でアプリカティブコンビネータを使い、直列的なコードの一部で並列計算を結合したり、またはその逆を行ったりできます。
+直列的なコードの一部と並列計算を組み合わせることもできます。
+それにはdo記法ブロック中でアプリカティブコンビネータを使います。
+その逆も然りで、必要に応じて`parralel`と`sequential`を使って型構築子を変更すれば良いのです。
直列実行と並列実行の間の違いを実演するために、100個の10ミリ秒の遅延からなる配列をつくり、それからその遅延を両方の手法で実行します。REPLで試すと`seqDelay`が`parDelay`より遥かに遅いことに気付くでしょう。並列実行が`sequence_`を`parSequence_`で置き換えるだけで有効になるところに注目です。
@@ -229,8 +235,8 @@ unit
1. (難しい)「根」のファイルを取り、そのファイルの中の全てのパスの一覧(そして一覧にあるファイルの中の一覧も)の配列を返す`recurseFiles`関数を書いてください。
一覧にあるファイルを並列に読んでください。
- パスはそのファイルが表れたディレクトリから相対的なものです。
- *手掛かり*:`node_path`モジュールにはディレクトリを扱う上で便利な関数があります。
+ パスはそのファイルが現れたディレクトリから相対的なものです。
+ *手掛かり*:`node-path`モジュールにはディレクトリを扱う上で便利な関数があります。
例えば次のような`root.txt`ファイルから始まるとします。
@@ -261,8 +267,8 @@ $ cat c/a/a.txt
## まとめ
-この章では非同期エフェクトと以下の方法を押さえました。
+この章では非同期作用と以下の方法を押さえました。
- `aff`ライブラリを使って`Aff`モナド中で非同期コードを走らせる。
-- `affjax`ライブラリを使って非同期にHTTPリクエストを行う。
+- `affjax`ライブラリを使って非同期にHTTPリクエストする。
- `parallel`ライブラリを使って並列に非同期コードを走らせる。
diff --git a/text-ja/index.md b/text-ja/index.md
index b2a34cad..f3ef8779 100644
--- a/text-ja/index.md
+++ b/text-ja/index.md
@@ -10,10 +10,10 @@ PureScriptのエコシステムの最新の機能を紹介すべく書き直さ
## 現状
-この本は言語の進化に伴って継続的に更新されているため、内容に関して発見したどんな[問題](https://github.com/purescript-contrib/purescript-book/issues)でもご報告ください。
+本書は言語の進化に伴って継続的に更新されているため、内容に関して発見したどんな[問題](https://github.com/purescript-contrib/purescript-book/issues)でもご報告ください。
より初心者にやさしくできそうな分かりづらい節を指摘するような単純なものであれ、共有いただいたどんなフィードバックにも感謝します。
-それぞれの章には単体テストも加えられているので、演習への自分の回答が正しいかどうか確かめることができます。
+各章には単体テストも加えられているので、演習への自分の回答が正しいかどうか確かめることができます。
テストの最新の状態については[#79](https://github.com/purescript-contrib/purescript-book/issues/79)を見てください。
## 本書について
@@ -25,10 +25,10 @@ Haskellで書かれ、またこの言語から着想を得ています。
JavaScriptでの関数型プログラミングは最近かなりの人気を誇るようになりましたが、コードを書く上で統制された環境が欠けていることが大規模なアプリケーション開発の妨げとなっています。
PureScriptは、強力に型付けされた関数型プログラミングの力をJavaScriptでの開発の世界に持ち込むことにより、この問題の解決を目指しています。
-この本は、基礎(開発環境の立ち上げ)から応用に至るまでの、PureScriptプログラミング言語の始め方を示します。
+本書は、基礎(開発環境の立ち上げ)から応用に至るまでの、PureScriptプログラミング言語の始め方を示します。
-それぞれの章は特定の課題により動機付けられており、その問題を解いていく過程において、新しい関数型プログラミングの道具と技法が導入されていきます。
-以下はこの本で解いていく課題の幾つかの例です。
+各章は特定の課題により動機付けられており、その問題を解いていく過程において、新しい関数型プログラミングの道具と技法が導入されていきます。
+以下は本書で解いていく課題の幾つかの例です。
- マップと畳み込みを使ったデータ構造の変換
- アプリカティブ関手を使ったフォームフィールドの検証
@@ -47,23 +47,13 @@ The text of this book is licensed under the Creative Commons
Attribution-NonCommercial-ShareAlike 3.0 Unported License:
.
-※以降の原文の使用許諾に関する和訳は法的効力を持ちません。
-本書のテキストは表示 - 非営利 -
-継承3.0非移植 (CC BY-NC-SA 3.0)のもとに使用が許諾される。
-
Some text is derived from the [PureScript Documentation
Repo](https://github.com/purescript/documentation), which uses the same
license, and is copyright [various
contributors](https://github.com/purescript/documentation/blob/master/CONTRIBUTORS.md).
-幾つかのテキストは[PureScriptのドキュメントリポジトリ](https://github.com/purescript/documentation)から派生している。
-派生元も同じ使用許諾であり、[様々な形で貢献された方々](https://github.com/purescript/documentation/blob/master/CONTRIBUTORS.md)の著作権が含まれる。
-
The exercises are licensed under the MIT license.
-演習はMITライセンスの下に使用が許諾される。
-
- - -
diff --git a/translation/README.md b/translation/README.md
index 3c5047c5..2d8bb968 100644
--- a/translation/README.md
+++ b/translation/README.md
@@ -22,6 +22,9 @@
[po4a]: https://po4a.org/
+翻訳の際には`terms.txt`に挙げた簡単な用語集のメモが役に立つかもしれません。
+追加や変更の提案をいただいても構いません。
+
生成物に対しての自動的な検証を幾つか導入しています。
`npm run lint`で検証します。
diff --git a/translation/all.pot b/translation/all.pot
index 85b3a5a7..d6f5439e 100644
--- a/translation/all.pot
+++ b/translation/all.pot
@@ -7,7 +7,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2023-06-22 08:06+0900\n"
+"POT-Creation-Date: 2023-08-25 21:33+0900\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -322,7 +322,7 @@ msgstr ""
#, markdown-text
msgid ""
"Libraries such as [UnderscoreJS](https://underscorejs.org) allow the "
-"developer to leverage tried-and-trusted functions such as `map`, `filter` "
+"developer to leverage tried-and-trusted functions such as `map`, `filter`, "
"and `reduce` to create larger programs from smaller programs by composition:"
msgstr ""
@@ -386,10 +386,10 @@ msgstr ""
#: text/chapter1.md:38
#, markdown-text
msgid ""
-"Functions enable a simple form of abstraction which can yield great "
-"productivity gains. However, functional programming in JavaScript has its "
-"own disadvantages: JavaScript is verbose, untyped, and lacks powerful forms "
-"of abstraction. Unrestricted JavaScript code also makes equational reasoning "
+"Functions enable a simple form of abstraction that can yield great "
+"productivity gains. However, functional programming in JavaScript has "
+"disadvantages: JavaScript is verbose, untyped, and lacks powerful forms of "
+"abstraction. Unrestricted JavaScript code also makes equational reasoning "
"very difficult."
msgstr ""
@@ -397,54 +397,62 @@ msgstr ""
#: text/chapter1.md:40
#, markdown-text
msgid ""
-"PureScript is a programming language which aims to address these issues. It "
+"PureScript is a programming language that aims to address these issues. It "
"features lightweight syntax, which allows us to write very expressive code "
"which is still clear and readable. It uses a rich type system to support "
"powerful abstractions. It also generates fast, understandable code, which is "
-"important when interoperating with JavaScript, or other languages which "
+"important when interoperating with JavaScript or other languages that "
"compile to JavaScript. All in all, I hope to convince you that PureScript "
"strikes a very practical balance between the theoretical power of purely "
-"functional programming, and the fast-and-loose programming style of "
+"functional programming and the fast-and-loose programming style of "
"JavaScript."
msgstr ""
+#. type: Plain text
+#: text/chapter1.md:42
+#, markdown-text, no-wrap
+msgid ""
+"> Note that PureScript can target other backends, not only JavaScript, but "
+"this book focuses on targeting web browser and node environments.\n"
+msgstr ""
+
#. type: Title ##
-#: text/chapter1.md:41
+#: text/chapter1.md:43
#, markdown-text, no-wrap
msgid "Types and Type Inference"
msgstr ""
#. type: Plain text
-#: text/chapter1.md:44
+#: text/chapter1.md:46
#, markdown-text
msgid ""
"The debate over statically typed languages versus dynamically typed "
"languages is well-documented. PureScript is a _statically typed_ language, "
-"meaning that a correct program can be given a _type_ by the compiler which "
-"indicates its behavior. Conversely, programs which cannot be given a type "
-"are _incorrect programs_, and will be rejected by the compiler. In "
-"PureScript, unlike in dynamically typed languages, types exist only at "
-"_compile-time_, and have no representation at runtime."
+"meaning that a correct program can be given a _type_ by the compiler, which "
+"indicates its behavior. Conversely, programs that cannot be given a type are "
+"_incorrect programs_, and will be rejected by the compiler. In PureScript, "
+"unlike in dynamically typed languages, types exist only at _compile-time_ "
+"and have no representation at runtime."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:46
+#: text/chapter1.md:48
#, markdown-text
msgid ""
-"It is important to note that in many ways, the types in PureScript are "
+"It is important to note that, in many ways, the types in PureScript are "
"unlike the types that you might have seen in other languages like Java or "
"C#. While they serve the same purpose at a high level, the types in "
"PureScript are inspired by languages like ML and Haskell. PureScript's types "
"are expressive, allowing the developer to assert strong claims about their "
"programs. Most importantly, PureScript's type system supports _type "
-"inference_ - it requires far fewer explicit type annotations than other "
+"inference_ – it requires far fewer explicit type annotations than other "
"languages, making the type system a _tool_ rather than a hindrance. As a "
"simple example, the following code defines a _number_, but there is no "
"mention of the `Number` type anywhere in the code:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter1.md:47
+#: text/chapter1.md:49
#, no-wrap
msgid ""
"iAmANumber =\n"
@@ -453,16 +461,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:54
+#: text/chapter1.md:56
#, markdown-text
msgid ""
"A more involved example shows that type-correctness can be confirmed without "
-"type annotations, even when there exist types which are _unknown to the "
+"type annotations, even when there exist types that are _unknown to the "
"compiler_:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter1.md:55
+#: text/chapter1.md:57
#, no-wrap
msgid ""
"iterate f 0 x = x\n"
@@ -470,7 +478,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:61
+#: text/chapter1.md:63
#, markdown-text
msgid ""
"Here, the type of `x` is unknown, but the compiler can still verify that "
@@ -479,7 +487,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:63
+#: text/chapter1.md:65
#, markdown-text
msgid ""
"In this book, I will try to convince you (or reaffirm your belief) that "
@@ -492,32 +500,32 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:65
+#: text/chapter1.md:67
#, markdown-text
msgid ""
"In addition, the safety net provided by a type system enables more advanced "
"forms of abstraction. In fact, PureScript provides a powerful form of "
-"abstraction which is fundamentally type-driven: type classes, made popular "
-"in the functional programming language Haskell."
+"abstraction that is fundamentally type-driven: type classes, made popular in "
+"the functional programming language Haskell."
msgstr ""
#. type: Title ##
-#: text/chapter1.md:66
+#: text/chapter1.md:68
#, markdown-text, no-wrap
msgid "Polyglot Web Programming"
msgstr ""
#. type: Plain text
-#: text/chapter1.md:69
+#: text/chapter1.md:71
#, markdown-text
msgid ""
-"Functional programming has its success stories - applications where it has "
+"Functional programming has its success stories – applications where it has "
"been particularly successful: data analysis, parsing, compiler "
"implementation, generic programming, parallelism, to name a few."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:71
+#: text/chapter1.md:73
#, markdown-text
msgid ""
"It would be possible to practice end-to-end application development in a "
@@ -528,23 +536,23 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:73
+#: text/chapter1.md:75
#, markdown-text
msgid ""
"However, one of PureScript's strengths is its interoperability with other "
"languages which target JavaScript. Another approach would be to use "
-"PureScript for a subset of your application's development, and to use one or "
+"PureScript for a subset of your application's development and to use one or "
"more other languages to write the rest of the JavaScript."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:75
+#: text/chapter1.md:77
#, markdown-text
msgid "Here are some examples:"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:79
+#: text/chapter1.md:81
#, markdown-text
msgid ""
"Core logic written in PureScript, with the user interface written in "
@@ -552,7 +560,7 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:79
+#: text/chapter1.md:81
#, markdown-text
msgid ""
"Application written in JavaScript or another compile-to-JS language, with "
@@ -560,7 +568,7 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:79
+#: text/chapter1.md:81
#, markdown-text
msgid ""
"PureScript used to automate user interface tests for an existing "
@@ -568,7 +576,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:81
+#: text/chapter1.md:83
#, markdown-text
msgid ""
"In this book, we'll focus on solving small problems with PureScript. The "
@@ -577,13 +585,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter1.md:82
+#: text/chapter1.md:84
#, markdown-text, no-wrap
msgid "Prerequisites"
msgstr ""
#. type: Plain text
-#: text/chapter1.md:85
+#: text/chapter1.md:87
#, markdown-text
msgid ""
"The software requirements for this book are minimal: the first chapter will "
@@ -593,17 +601,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:87
+#: text/chapter1.md:89
#, markdown-text
msgid ""
-"The PureScript compiler itself can be downloaded as a binary distribution, "
-"or built from source on any system running an up-to-date installation of the "
+"The PureScript compiler itself can be downloaded as a binary distribution or "
+"built from source on any system running an up-to-date installation of the "
"GHC Haskell compiler, and we will walk through this process in the next "
"chapter."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:90
+#: text/chapter1.md:92
#, markdown-text
msgid ""
"The code in this version of the book is compatible with versions `0.15.*` of "
@@ -611,13 +619,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter1.md:91
+#: text/chapter1.md:93
#, markdown-text, no-wrap
msgid "About You"
msgstr ""
#. type: Plain text
-#: text/chapter1.md:94
+#: text/chapter1.md:96
#, markdown-text
msgid ""
"I will assume that you are familiar with the basics of JavaScript. Any prior "
@@ -627,21 +635,21 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:96
+#: text/chapter1.md:98
#, markdown-text
msgid ""
"No prior knowledge of functional programming is required, but it certainly "
"won't hurt. New ideas will be accompanied by practical examples, so you "
-"should be able to form an intuition for the concepts from functional "
+"should be able to form an intuition for the concepts from the functional "
"programming that we will use."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:98
+#: text/chapter1.md:100
#, markdown-text
msgid ""
"Readers who are familiar with the Haskell programming language will "
-"recognize a lot of the ideas and syntax presented in this book, because "
+"recognize a lot of the ideas and syntax presented in this book because "
"PureScript is heavily influenced by Haskell. However, those readers should "
"understand that there are a number of important differences between "
"PureScript and Haskell. It is not necessarily always appropriate to try to "
@@ -650,16 +658,16 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter1.md:99
+#: text/chapter1.md:101
#, markdown-text, no-wrap
msgid "How to Read This Book"
msgstr ""
#. type: Plain text
-#: text/chapter1.md:102
+#: text/chapter1.md:104
#, markdown-text
msgid ""
-"The chapters in this book are largely self contained. A beginner with little "
+"The chapters in this book are largely self-contained. A beginner with little "
"functional programming experience would be well-advised, however, to work "
"through the chapters in order. The first few chapters lay the groundwork "
"required to understand the material later on in the book. A reader who is "
@@ -670,11 +678,11 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:104
+#: text/chapter1.md:106
#, markdown-text
msgid ""
"Each chapter will focus on a single practical example, providing the "
-"motivation for any new ideas introduced. Code for each chapter are available "
+"motivation for any new ideas introduced. Code for each chapter is available "
"from the book's [GitHub "
"repository](https://github.com/purescript-contrib/purescript-book). Some "
"chapters will include code snippets taken from the chapter's source code, "
@@ -685,13 +693,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:106
+#: text/chapter1.md:108
#, markdown-text
-msgid "Code samples will appear in a monospaced font, as follows:"
+msgid "Code samples will appear in a monospaced font as follows:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter1.md:107
+#: text/chapter1.md:109
#, no-wrap
msgid ""
"module Example where\n"
@@ -702,7 +710,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:116
+#: text/chapter1.md:118
#, markdown-text
msgid ""
"Commands which should be typed at the command line will be preceded by a "
@@ -710,22 +718,22 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter1.md:117 text/chapter3.md:313
+#: text/chapter1.md:119 text/chapter3.md:330
#, no-wrap
msgid "$ spago build\n"
msgstr ""
#. type: Plain text
-#: text/chapter1.md:122
+#: text/chapter1.md:124
#, markdown-text
msgid ""
"Usually, these commands will be tailored to Linux/Mac OS users, so Windows "
-"users may need to make small changes such as modifying the file separator, "
+"users may need to make small changes, such as modifying the file separator "
"or replacing shell built-ins with their Windows equivalents."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:124
+#: text/chapter1.md:126
#, markdown-text
msgid ""
"Commands which should be typed at the PSCi interactive mode prompt will be "
@@ -733,7 +741,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter1.md:125
+#: text/chapter1.md:127
#, no-wrap
msgid ""
"> 1 + 2\n"
@@ -741,33 +749,33 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:131
+#: text/chapter1.md:133
#, markdown-text
msgid ""
-"Each chapter will contain exercises, labelled with their difficulty "
-"level. It is strongly recommended that you attempt the exercises in each "
-"chapter to fully understand the material."
+"Each chapter will contain exercises labelled with their difficulty level. It "
+"is strongly recommended that you attempt the exercises in each chapter to "
+"fully understand the material."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:133
+#: text/chapter1.md:135
#, markdown-text
msgid ""
"This book aims to provide an introduction to the PureScript language for "
"beginners, but it is not the sort of book that provides a list of template "
"solutions to problems. For beginners, this book should be a fun challenge, "
"and you will get the most benefit if you read the material, attempt the "
-"exercises, and most importantly of all, try to write some code of your own."
+"exercises, and, most importantly of all, try to write some code of your own."
msgstr ""
#. type: Title ##
-#: text/chapter1.md:134
+#: text/chapter1.md:136
#, markdown-text, no-wrap
msgid "Getting Help"
msgstr ""
#. type: Plain text
-#: text/chapter1.md:137
+#: text/chapter1.md:139
#, markdown-text
msgid ""
"If you get stuck at any point, there are a number of resources available "
@@ -775,26 +783,24 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:146
+#: text/chapter1.md:148
#, markdown-text
msgid ""
"The [PureScript Discord server](https://discord.gg/vKn9up84bp) is a great "
"place to chat about issues you may be having. The server is dedicated to "
-"chat about PureScript"
+"chatting about PureScript"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:146
+#: text/chapter1.md:148
#, markdown-text
msgid ""
"The [Purescript Discourse Forum](https://discourse.purescript.org/) is "
-"another good place to search for solutions to common problems. Questions you "
-"ask here will be available to help future readers, whereas on Slack, message "
-"history is only kept for approximately 2 weeks."
+"another good place to search for solutions to common problems."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:146
+#: text/chapter1.md:148
#, markdown-text
msgid ""
"[PureScript: Jordan's "
@@ -805,7 +811,7 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:146
+#: text/chapter1.md:148
#, markdown-text
msgid ""
"[Pursuit](https://pursuit.purescript.org) is a searchable database of "
@@ -814,7 +820,7 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:146
+#: text/chapter1.md:148
#, markdown-text
msgid ""
"The unofficial [PureScript "
@@ -823,61 +829,63 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:146
+#: text/chapter1.md:148
#, markdown-text
msgid ""
"The [PureScript documentation "
"repository](https://github.com/purescript/documentation) collects articles "
-"and examples on a wide variety of topics, written by PureScript developers "
+"and examples on a wide variety of topics written by PureScript developers "
"and users."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:146
+#: text/chapter1.md:148
#, markdown-text
msgid ""
"The [PureScript website](https://www.purescript.org) contains links to "
-"several learning resources, including code samples, videos and other "
+"several learning resources, including code samples, videos, and other "
"resources for beginners."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter1.md:146
+#: text/chapter1.md:148
#, markdown-text
msgid ""
-"[Try PureScript!](https://try.purescript.org) is a website which allows "
-"users to compile PureScript code in the web browser, and contains several "
-"simple examples of code."
+"[Try PureScript!](https://try.purescript.org) is a website that allows users "
+"to compile PureScript code in the web browser and contains several simple "
+"examples of code."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:148
+#: text/chapter1.md:150
#, markdown-text
msgid ""
-"If you prefer to learn by reading examples, the `purescript`, "
-"`purescript-node` and `purescript-contrib` GitHub organizations contain "
-"plenty of examples of PureScript code."
+"If you prefer to learn by reading examples, the "
+"[purescript](https://github.com/purescript), "
+"[purescript-node](https://github.com/purescript-node), and "
+"[purescript-contrib](https://github.com/purescript-contrib) GitHub "
+"organizations contain plenty of examples of PureScript code."
msgstr ""
#. type: Title ##
-#: text/chapter1.md:149
+#: text/chapter1.md:151
#, markdown-text, no-wrap
msgid "About the Author"
msgstr ""
#. type: Plain text
-#: text/chapter1.md:152
+#: text/chapter1.md:154
#, markdown-text
msgid ""
"I am the original developer of the PureScript compiler. I'm based in Los "
"Angeles, California, and started programming at an early age in BASIC on an "
-"8-bit personal computer, the Amstrad CPC. Since then I have worked "
+"8-bit personal computer, the Amstrad CPC. Since then, I have worked "
"professionally in a variety of programming languages (including Java, Scala, "
"C#, F#, Haskell and PureScript)."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:154
+#: text/chapter1.md:156
#, markdown-text
msgid ""
"Not long into my professional career, I began to appreciate functional "
@@ -886,7 +894,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter1.md:156
+#: text/chapter1.md:158
#, markdown-text
msgid ""
"I started working on the PureScript compiler in response to my experience "
@@ -895,13 +903,13 @@ msgid ""
"environment in which to apply them. Solutions at the time included various "
"attempts to compile Haskell to JavaScript while preserving its semantics "
"(Fay, Haste, GHCJS), but I was interested to see how successful I could be "
-"by approaching the problem from the other side - attempting to keep the "
+"by approaching the problem from the other side – attempting to keep the "
"semantics of JavaScript, while enjoying the syntax and type system of a "
"language like Haskell."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:158
+#: text/chapter1.md:160
#, markdown-text
msgid ""
"I maintain [a blog](https://blog.functorial.com), and can be [reached on "
@@ -909,33 +917,33 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter1.md:159
+#: text/chapter1.md:161
#, markdown-text, no-wrap
msgid "Acknowledgements"
msgstr ""
#. type: Plain text
-#: text/chapter1.md:162
+#: text/chapter1.md:164
#, markdown-text
msgid ""
"I would like to thank the many contributors who helped PureScript to reach "
"its current state. Without the huge collective effort which has been made on "
-"the compiler, tools, libraries, documentation and tests, the project would "
+"the compiler, tools, libraries, documentation, and tests, the project would "
"certainly have failed."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:164
+#: text/chapter1.md:166
#, markdown-text
msgid ""
"The PureScript logo which appears on the cover of this book was created by "
-"Gareth Hughes, and is gratefully reused here under the terms of the "
-"[Creative Commons Attribution 4.0 "
+"Gareth Hughes and is gratefully reused here under the terms of the [Creative "
+"Commons Attribution 4.0 "
"license](https://creativecommons.org/licenses/by/4.0/)."
msgstr ""
#. type: Plain text
-#: text/chapter1.md:165
+#: text/chapter1.md:167
#, markdown-text
msgid ""
"Finally, I would like to thank everyone who has given me feedback and "
@@ -962,7 +970,7 @@ msgstr ""
#, markdown-text
msgid ""
"This chapter will introduce PureScript's _foreign function interface_ (or "
-"_FFI_), which enables communication from PureScript code to JavaScript code, "
+"_FFI_), which enables communication from PureScript code to JavaScript code "
"and vice versa. We will cover how to:"
msgstr ""
@@ -1011,10 +1019,10 @@ msgstr ""
#: text/chapter10.md:17
#, markdown-text
msgid ""
-"There is also an addendum which covers some additional topics which are not "
-"as commonly sought-after. Feel free to read these sections, but don't let "
-"them stand in the way of progressing through the remainder of the book if "
-"they're less relevant to your learning objectives:"
+"There is also an addendum covering some additional topics that are not as "
+"commonly sought-after. Feel free to read these sections, but don't let them "
+"stand in the way of progressing through the remainder of the book if they're "
+"less relevant to your learning objectives:"
msgstr ""
#. type: Bullet: '- '
@@ -1043,7 +1051,7 @@ msgstr ""
#, markdown-text
msgid ""
"The source code for this module is a continuation of the source code from "
-"chapters 3, 7 and 8. As such, the source tree includes the appropriate "
+"chapters 3, 7, and 8. As such, the source tree includes the appropriate "
"source files from those chapters."
msgstr ""
@@ -1086,9 +1094,9 @@ msgid ""
"PureScript provides a straightforward foreign function interface to make "
"working with JavaScript as simple as possible. However, it should be noted "
"that the FFI is an _advanced_ feature of the language. To use it safely and "
-"effectively, you should have an understanding of the runtime representation "
-"of the data you plan to work with. This chapter aims to impart such an "
-"understanding as pertains to code in PureScript's standard libraries."
+"effectively, you should understand the runtime representation of the data "
+"you plan to work with. This chapter aims to impart such an understanding as "
+"pertains to code in PureScript's standard libraries."
msgstr ""
#. type: Plain text
@@ -1096,8 +1104,8 @@ msgstr ""
#, markdown-text
msgid ""
"PureScript's FFI is designed to be very flexible. In practice, this means "
-"that developers have a choice, between giving their foreign functions very "
-"simple types, or using the type system to protect against accidental misuses "
+"that developers have a choice between giving their foreign functions very "
+"simple types or using the type system to protect against accidental misuses "
"of foreign code. Code in the standard libraries tends to favor the latter "
"approach."
msgstr ""
@@ -1153,7 +1161,7 @@ msgstr ""
#, markdown-text, no-wrap
msgid ""
"This function has the correct runtime representation for the function type "
-"`String -> String`, since it takes non-null strings to non-null strings, and "
+"`String -> String`, since it takes non-null strings to non-null strings and "
"has no other side-effects.\n"
msgstr ""
@@ -1176,7 +1184,7 @@ msgstr ""
#, markdown-text
msgid ""
"We also need to write a foreign JavaScript module to import it from. A "
-"corresponding foreign JavaScript module is one of the same name but "
+"corresponding foreign JavaScript module is one of the same name but the "
"extension changed from `.purs` to `.js`. If the Purescript module above is "
"saved as `URI.purs`, then the foreign JavaScript module is saved as "
"`URI.js`. Since `encodeURIComponent` is already defined, we have to export "
@@ -1293,7 +1301,7 @@ msgstr ""
#, markdown-text
msgid ""
"Recall that functions in PureScript are _curried_. `diagonal` is a function "
-"that takes a `Number` and returns a _function_, that takes a `Number` and "
+"that takes a `Number` and returns a _function_ that takes a `Number` and "
"returns a `Number`."
msgstr ""
@@ -1400,8 +1408,8 @@ msgstr ""
#: text/chapter10.md:166
#, markdown-text
msgid ""
-"We can then call it with `runFn2` which takes the uncurried function then "
-"the arguments."
+"We can then call it with `runFn2`, which takes the uncurried function and "
+"then the arguments."
msgstr ""
#. type: Fenced code block (text)
@@ -1434,9 +1442,9 @@ msgstr ""
#: text/chapter10.md:181
#, markdown-text
msgid ""
-"PureScript's curried functions has certain advantages. It allows us to "
+"PureScript's curried functions have certain advantages. It allows us to "
"partially apply functions, and to give type class instances for function "
-"types - but it comes with a performance penalty. For performance critical "
+"types – but it comes with a performance penalty. For performance-critical "
"code, it is sometimes necessary to define uncurried JavaScript functions "
"which accept multiple arguments."
msgstr ""
@@ -1505,7 +1513,7 @@ msgstr ""
#: text/chapter10.md:211
#, markdown-text
msgid ""
-"and the resulting generated code, which is less compact due to the nested "
+"And the resulting generated code, which is less compact due to the nested "
"functions:"
msgstr ""
@@ -1532,7 +1540,7 @@ msgstr ""
#: text/chapter10.md:225
#, markdown-text
msgid ""
-"The arrow function syntax we saw earlier is an ES6 feature, and so it is "
+"The arrow function syntax we saw earlier is an ES6 feature, which is "
"incompatible with some older browsers (namely IE11). As of writing, it is "
"[estimated that arrow functions are unavailable for the 6% of "
"users](https://caniuse.com/#feat=arrow-functions) who have not yet updated "
@@ -1543,17 +1551,16 @@ msgstr ""
#: text/chapter10.md:227
#, markdown-text
msgid ""
-"In order to be compatible with the most users, the JavaScript code generated "
-"by the PureScript compiler does not use arrow functions. It is also "
-"recommended to **avoid arrow functions in public libraries** for the same "
-"reason."
+"To be compatible with the most users, the JavaScript code generated by the "
+"PureScript compiler does not use arrow functions. It is also recommended to "
+"**avoid arrow functions in public libraries** for the same reason."
msgstr ""
#. type: Plain text
#: text/chapter10.md:229
#, markdown-text
msgid ""
-"You may still use arrow functions in your own FFI code, but then should "
+"You may still use arrow functions in your own FFI code, but then you should "
"include a tool such as [Babel](https://github.com/babel/babel#intro) in your "
"deployment workflow to convert these back to ES5 compatible functions."
msgstr ""
@@ -1598,23 +1605,23 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter10.md:246 text/chapter10.md:321 text/chapter10.md:476
-#: text/chapter10.md:714 text/chapter10.md:891 text/chapter10.md:1074
-#: text/chapter10.md:1311 text/chapter11.md:140 text/chapter11.md:224
-#: text/chapter11.md:343 text/chapter11.md:549 text/chapter11.md:716
-#: text/chapter11.md:915 text/chapter11.md:960 text/chapter12.md:153
+#: text/chapter10.md:246 text/chapter10.md:321 text/chapter10.md:478
+#: text/chapter10.md:716 text/chapter10.md:893 text/chapter10.md:1076
+#: text/chapter10.md:1313 text/chapter11.md:141 text/chapter11.md:223
+#: text/chapter11.md:342 text/chapter11.md:548 text/chapter11.md:715
+#: text/chapter11.md:914 text/chapter11.md:959 text/chapter12.md:153
#: text/chapter12.md:361 text/chapter12.md:555 text/chapter13.md:90
#: text/chapter13.md:137 text/chapter13.md:244 text/chapter13.md:387
#: text/chapter14.md:249 text/chapter14.md:352 text/chapter14.md:569
-#: text/chapter14.md:704 text/chapter2.md:124 text/chapter3.md:714
+#: text/chapter14.md:704 text/chapter2.md:124 text/chapter3.md:774
#: text/chapter4.md:70 text/chapter4.md:181 text/chapter4.md:363
#: text/chapter4.md:521 text/chapter4.md:615 text/chapter5.md:99
#: text/chapter5.md:228 text/chapter5.md:392 text/chapter5.md:465
#: text/chapter5.md:524 text/chapter6.md:130 text/chapter6.md:321
-#: text/chapter6.md:412 text/chapter6.md:609 text/chapter6.md:743
+#: text/chapter6.md:411 text/chapter6.md:608 text/chapter6.md:742
#: text/chapter7.md:381 text/chapter7.md:540 text/chapter7.md:642
-#: text/chapter8.md:308 text/chapter8.md:666 text/chapter8.md:973
-#: text/chapter9.md:87 text/chapter9.md:133 text/chapter9.md:200
+#: text/chapter8.md:308 text/chapter8.md:667 text/chapter8.md:974
+#: text/chapter9.md:87 text/chapter9.md:134 text/chapter9.md:201
#, markdown-text, no-wrap
msgid "Exercises"
msgstr ""
@@ -1670,10 +1677,9 @@ msgstr ""
#, markdown-text
msgid ""
"To demonstrate passing `Array`s, here's how to call a JavaScript function "
-"which takes an `Array` of `Int` and returns the cumulative sum as another "
-"array. Recall that, since JavaScript does not have a separate type for "
-"`Int`, both `Int` and `Number` in PureScript translate to `Number` in "
-"JavaScript."
+"that takes an `Array` of `Int` and returns the cumulative sum as another "
+"array. Recall that since JavaScript does not have a separate type for `Int`, "
+"both `Int` and `Number` in PureScript translate to `Number` in JavaScript."
msgstr ""
#. type: Fenced code block (hs)
@@ -1713,9 +1719,9 @@ msgstr ""
#, markdown-text
msgid ""
"To demonstrate passing `Records`, here's how to call a JavaScript function "
-"which takes two `Complex` numbers as records, and returns their sum as "
-"another record. Note that a `Record` in PureScript is represented as an "
-"`Object` in JavaScript:"
+"that takes two `Complex` numbers as records and returns their sum as another "
+"record. Note that a `Record` in PureScript is represented as an `Object` in "
+"JavaScript:"
msgstr ""
#. type: Fenced code block (hs)
@@ -1758,10 +1764,10 @@ msgstr ""
#, markdown-text
msgid ""
"Note that the above techniques require trusting that JavaScript will return "
-"the expected types, as PureScript is not able to apply type checking to "
-"JavaScript code. We will describe this type safety concern in more detail "
-"later on in the JSON section, as well as cover techniques to protect against "
-"type mismatches."
+"the expected types, as PureScript cannot apply type checking to JavaScript "
+"code. We will describe this type safety concern in more detail later on in "
+"the JSON section, as well as cover techniques to protect against type "
+"mismatches."
msgstr ""
#. type: Bullet: '1. '
@@ -1909,42 +1915,52 @@ msgstr ""
#. type: Plain text
#: text/chapter10.md:387
#, markdown-text
-msgid "and not:"
+msgid "And not:"
msgstr ""
#. type: Fenced code block (hs)
#: text/chapter10.md:388
#, no-wrap
-msgid "forall a. ( a -> Maybe a) -> Maybe a -> Array a -> Maybe a\n"
+msgid "forall a. (a -> Maybe a) -> Maybe a -> Array a -> Maybe a\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:394
+#: text/chapter10.md:393
#, markdown-text
msgid ""
"While both forms work, the latter is more vulnerable to unwanted inputs in "
-"place of `Just` and `Nothing`. For example, in the more vulnerable case we "
-"could call it as follows:"
+"place of `Just` and `Nothing`."
msgstr ""
-#. type: Fenced code block (hs)
+#. type: Plain text
#: text/chapter10.md:395
+#, markdown-text
+msgid "For example, in the more vulnerable case, we could call it as follows:"
+msgstr ""
+
+#. type: Fenced code block (hs)
+#: text/chapter10.md:396
#, no-wrap
msgid "maybeHeadImpl (\\_ -> Just 1000) (Just 1000) [1,2,3]\n"
msgstr ""
#. type: Plain text
#: text/chapter10.md:401
+#, markdown-text
+msgid "Which returns `Just 1000` for any array input."
+msgstr ""
+
+#. type: Plain text
+#: text/chapter10.md:403
#, markdown-text, no-wrap
msgid ""
-"which returns `Just 1000` for any array input.\n"
"This vulnerability is allowed because `(\\_ -> Just 1000)` and `Just 1000` "
-"match the signatures of `(a -> Maybe a)` and `Maybe a` respectively when `a` "
-"is `Int` (based on input array).\n"
+"match the signatures of `(a -> Maybe a)` and `Maybe a`, respectively, when "
+"`a` is `Int` (based on input array).\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:404
+#: text/chapter10.md:406
#, markdown-text, no-wrap
msgid ""
"In the more secure type signature, even when `a` is determined to be `Int` "
@@ -1958,22 +1974,22 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter10.md:405
+#: text/chapter10.md:407
#, markdown-text, no-wrap
msgid "Defining Foreign Types"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:408
+#: text/chapter10.md:410
#, markdown-text
msgid ""
-"Suppose instead of returning a `Maybe a`, we want to actually return "
-"`arr[0]`. We want a type that represents a value either of type `a` or the "
-"`undefined` value (but not `null`). We'll call this type `Undefined a`."
+"Suppose instead of returning a `Maybe a`, we want to return `arr[0]`. We "
+"want a type that represents a value either of type `a` or the `undefined` "
+"value (but not `null`). We'll call this type `Undefined a`."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:410
+#: text/chapter10.md:412
#, markdown-text
msgid ""
"We can define a _foreign type_ using a _foreign type declaration_. The "
@@ -1981,13 +1997,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:411
+#: text/chapter10.md:413
#, no-wrap
msgid "foreign import data Undefined :: Type -> Type\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:416
+#: text/chapter10.md:418
#, markdown-text, no-wrap
msgid ""
"The `data` keyword here indicates that we are defining a _type_, not a "
@@ -1997,13 +2013,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:418
+#: text/chapter10.md:420
#, markdown-text
-msgid "We can now simply reuse our original definition for `head`:"
+msgid "We can now reuse our original definition for `head`:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:419
+#: text/chapter10.md:421
#, no-wrap
msgid ""
"export const undefinedHead = arr =>\n"
@@ -2011,36 +2027,36 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:425
+#: text/chapter10.md:427
#, markdown-text
msgid "And in the PureScript module:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:426
+#: text/chapter10.md:428
#, no-wrap
msgid "foreign import undefinedHead :: forall a. Array a -> Undefined a\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:431
+#: text/chapter10.md:433
#, markdown-text
msgid ""
-"The body of the `undefinedHead` function returns `arr[0]` which may be "
+"The body of the `undefinedHead` function returns `arr[0]`, which may be "
"`undefined`, and the type signature correctly reflects that fact."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:433
+#: text/chapter10.md:435
#, markdown-text
msgid ""
-"This function has the correct runtime representation for its type, but is "
+"This function has the correct runtime representation for its type, but it's "
"quite useless since we have no way to use a value of type `Undefined "
"a`. Well, not exactly. We can use this type in another FFI!"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:435
+#: text/chapter10.md:437
#, markdown-text
msgid ""
"We can write a function that will tell us whether a value is undefined or "
@@ -2048,19 +2064,19 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:436
+#: text/chapter10.md:438
#, no-wrap
msgid "foreign import isUndefined :: forall a. Undefined a -> Boolean\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:441
+#: text/chapter10.md:443
#, markdown-text
msgid "This is defined in our foreign JavaScript module as follows:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:442
+#: text/chapter10.md:444
#, no-wrap
msgid ""
"export const isUndefined = value =>\n"
@@ -2068,7 +2084,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:448
+#: text/chapter10.md:450
#, markdown-text
msgid ""
"We can now use `isUndefined` and `undefinedHead` together from PureScript to "
@@ -2076,7 +2092,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:449
+#: text/chapter10.md:451
#, no-wrap
msgid ""
"isEmpty :: forall a. Array a -> Boolean\n"
@@ -2084,24 +2100,24 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:455
+#: text/chapter10.md:457
#, markdown-text
msgid ""
"Here, the foreign function we defined is very simple, which means we can "
-"benefit from the use of PureScript's typechecker as much as possible. This "
-"is good practice in general: foreign functions should be kept as small as "
+"benefit from using PureScript's typechecker as much as possible. This is "
+"good practice in general: foreign functions should be kept as small as "
"possible, and application logic moved into PureScript code wherever "
"possible."
msgstr ""
#. type: Title ##
-#: text/chapter10.md:456 text/chapter8.md:365 text/chapter8.md:443
+#: text/chapter10.md:458 text/chapter8.md:365 text/chapter8.md:444
#, markdown-text, no-wrap
msgid "Exceptions"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:459
+#: text/chapter10.md:461
#, markdown-text
msgid ""
"Another option is to simply throw an exception in the case of an empty "
@@ -2111,19 +2127,19 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:460
+#: text/chapter10.md:462
#, no-wrap
msgid "foreign import unsafeHead :: forall a. Array a -> a\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:465
+#: text/chapter10.md:467
#, markdown-text
msgid "In our foreign JavaScript module, we can define `unsafeHead` as follows:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:466
+#: text/chapter10.md:468
#, no-wrap
msgid ""
"export const unsafeHead = arr => {\n"
@@ -2136,7 +2152,7 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter10.md:479
+#: text/chapter10.md:481
#, markdown-text
msgid ""
"(Medium) Given a record that represents a quadratic polynomial `a*x^2 + b*x "
@@ -2144,7 +2160,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:487
+#: text/chapter10.md:489
#, markdown-text, no-wrap
msgid ""
" ```hs\n"
@@ -2157,7 +2173,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:489
+#: text/chapter10.md:491
#, markdown-text, no-wrap
msgid ""
" Write a JavaScript function `quadraticRootsImpl` and a wrapper "
@@ -2168,7 +2184,7 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter10.md:491
+#: text/chapter10.md:493
#, markdown-text
msgid ""
"(Medium) Write the function `toMaybe :: forall a. Undefined a -> Maybe "
@@ -2177,13 +2193,13 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter10.md:493
+#: text/chapter10.md:495
#, markdown-text
msgid "(Difficult) With `toMaybe` in place, we can rewrite `maybeHead` as"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:498
+#: text/chapter10.md:500
#, markdown-text, no-wrap
msgid ""
" ```hs\n"
@@ -2193,7 +2209,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:500
+#: text/chapter10.md:502
#, markdown-text, no-wrap
msgid ""
" Is this a better approach than our previous implementation? _Note:_ "
@@ -2201,31 +2217,31 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter10.md:501
+#: text/chapter10.md:503
#, markdown-text, no-wrap
msgid "Using Type Class Member Functions"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:504
+#: text/chapter10.md:506
#, markdown-text
msgid ""
-"Just like our earlier guide on passing the `Maybe` constructor over FFI, "
-"this is another case of writing PureScript that calls JavaScript, which in "
-"turn calls PureScript functions again. Here we will explore how to pass type "
-"class member functions over the FFI."
+"Like our earlier guide on passing the `Maybe` constructor over FFI, this is "
+"another case of writing PureScript that calls JavaScript, which calls "
+"PureScript functions again. Here we will explore how to pass type class "
+"member functions over the FFI."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:506
+#: text/chapter10.md:508
#, markdown-text
msgid ""
-"We start with writing a foreign JavaScript function which expects the "
+"We start with writing a foreign JavaScript function that expects the "
"appropriate instance of `show` to match the type of `x`."
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:507
+#: text/chapter10.md:509
#, no-wrap
msgid ""
"export const boldImpl = show => x =>\n"
@@ -2233,25 +2249,25 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:513
+#: text/chapter10.md:515
#, markdown-text
msgid "Then we write the matching signature:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:514
+#: text/chapter10.md:516
#, no-wrap
msgid "foreign import boldImpl :: forall a. (a -> String) -> a -> String\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:519
+#: text/chapter10.md:521
#, markdown-text
-msgid "and a wrapper function that passes the correct instance of `show`:"
+msgid "And a wrapper function that passes the correct instance of `show`:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:520
+#: text/chapter10.md:522
#, no-wrap
msgid ""
"bold :: forall a. Show a => a -> String\n"
@@ -2259,13 +2275,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:526
+#: text/chapter10.md:528
#, markdown-text
-msgid "Alternatively in point-free form:"
+msgid "Alternatively, in point-free form:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:527
+#: text/chapter10.md:529
#, no-wrap
msgid ""
"bold :: forall a. Show a => a -> String\n"
@@ -2273,13 +2289,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:533
+#: text/chapter10.md:535
#, markdown-text
msgid "We can then call the wrapper:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:534
+#: text/chapter10.md:536
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -2291,7 +2307,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:544
+#: text/chapter10.md:546
#, markdown-text
msgid ""
"Here's another example demonstrating passing multiple functions, including a "
@@ -2299,7 +2315,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:545
+#: text/chapter10.md:547
#, no-wrap
msgid ""
"export const showEqualityImpl = eq => show => a => b => {\n"
@@ -2312,7 +2328,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:555
+#: text/chapter10.md:557
#, no-wrap
msgid ""
"foreign import showEqualityImpl :: forall a. (a -> a -> Boolean) -> (a -> "
@@ -2323,7 +2339,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:562
+#: text/chapter10.md:564
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -2335,13 +2351,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter10.md:571
+#: text/chapter10.md:573
#, markdown-text, no-wrap
msgid "Effectful Functions"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:574
+#: text/chapter10.md:576
#, markdown-text
msgid ""
"Let's extend our `bold` function to log to the console. Logging is an "
@@ -2350,7 +2366,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:575
+#: text/chapter10.md:577
#, no-wrap
msgid ""
"export const yellImpl = show => x => () =>\n"
@@ -2358,7 +2374,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:581
+#: text/chapter10.md:583
#, markdown-text
msgid ""
"The new foreign import is the same as before, except that the return type "
@@ -2366,7 +2382,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:582
+#: text/chapter10.md:584
#, no-wrap
msgid ""
"foreign import yellImpl :: forall a. (a -> String) -> a -> Effect Unit\n"
@@ -2376,15 +2392,15 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:590
+#: text/chapter10.md:592
#, markdown-text
msgid ""
"When testing this in the repl, notice that the string is printed directly to "
-"the console (instead of being quoted) and a `unit` value is returned."
+"the console (instead of being quoted), and a `unit` value is returned."
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:591
+#: text/chapter10.md:593
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -2397,7 +2413,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:602
+#: text/chapter10.md:604
#, markdown-text
msgid ""
"There are also `EffectFn` wrappers from `Effect.Uncurried`. These are "
@@ -2407,18 +2423,18 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:604
+#: text/chapter10.md:606
#, markdown-text
msgid ""
"You'd generally only use these if you want to call existing JavaScript "
-"library APIs directly, rather than wrapping those APIs in curried "
+"library APIs directly rather than wrapping those APIs in curried "
"functions. So it doesn't make much sense to present an example of uncurried "
-"`yell`, where the JavaScript relies on PureScript type class members, since "
+"`yell`, where the JavaScript relies on PureScript type class members since "
"you wouldn't find that in the existing JavaScript ecosystem."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:606
+#: text/chapter10.md:608
#, markdown-text
msgid ""
"Instead, we'll modify our previous `diagonal` example to include logging in "
@@ -2426,7 +2442,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:607
+#: text/chapter10.md:609
#, no-wrap
msgid ""
"export const diagonalLog = function(w, h) {\n"
@@ -2437,13 +2453,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:615
+#: text/chapter10.md:617
#, no-wrap
msgid "foreign import diagonalLog :: EffectFn2 Number Number Number\n"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:619
+#: text/chapter10.md:621
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -2456,13 +2472,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter10.md:629
+#: text/chapter10.md:631
#, markdown-text, no-wrap
msgid "Asynchronous Functions"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:632
+#: text/chapter10.md:634
#, markdown-text
msgid ""
"Promises in JavaScript translate directly to asynchronous effects in "
@@ -2472,7 +2488,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:634
+#: text/chapter10.md:636
#, markdown-text
msgid ""
"Suppose we want to use this JavaScript `wait` promise (or asynchronous "
@@ -2481,13 +2497,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:635
+#: text/chapter10.md:637
#, no-wrap
msgid "const wait = ms => new Promise(resolve => setTimeout(resolve, ms));\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:640
+#: text/chapter10.md:642
#, markdown-text
msgid ""
"We just need to export it wrapped as an `Effect` (function of zero "
@@ -2495,7 +2511,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:641
+#: text/chapter10.md:643
#, no-wrap
msgid ""
"export const sleepImpl = ms => () =>\n"
@@ -2503,13 +2519,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:647
+#: text/chapter10.md:649
#, markdown-text
msgid "Then import it as follows:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:648
+#: text/chapter10.md:650
#, no-wrap
msgid ""
"foreign import sleepImpl :: Int -> Effect (Promise Unit)\n"
@@ -2519,13 +2535,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:656
+#: text/chapter10.md:658
#, markdown-text
msgid "We can then run this `Promise` in an `Aff` block like so:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:657
+#: text/chapter10.md:659
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -2546,16 +2562,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:676
+#: text/chapter10.md:678
#, markdown-text
msgid ""
-"Note that asynchronous logging in the repl just waits to print until the "
-"entire block has finished executing. This code behaves more predictably when "
-"run with `spago test` where there is a slight delay _between_ prints."
+"Note that asynchronous logging in the repl waits to print until the entire "
+"block has finished executing. This code behaves more predictably when run "
+"with `spago test` where there is a slight delay _between_ prints."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:678
+#: text/chapter10.md:680
#, markdown-text
msgid ""
"Let's look at another example where we return a value from a promise. This "
@@ -2564,7 +2580,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:679
+#: text/chapter10.md:681
#, no-wrap
msgid ""
"async function diagonalWait(delay, w, h) {\n"
@@ -2577,7 +2593,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:690
+#: text/chapter10.md:692
#, markdown-text
msgid ""
"Since we're returning a `Number`, we represent this type in the `Promise` "
@@ -2585,7 +2601,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:691
+#: text/chapter10.md:693
#, no-wrap
msgid ""
"foreign import diagonalAsyncImpl :: Int -> Number -> Number -> Effect "
@@ -2596,7 +2612,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:698
+#: text/chapter10.md:700
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -2615,7 +2631,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:717
+#: text/chapter10.md:719
#, markdown-text
msgid ""
"Exercises for the above sections are still on the ToDo list. If you have any "
@@ -2623,23 +2639,23 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter10.md:718
+#: text/chapter10.md:720
#, markdown-text, no-wrap
msgid "JSON"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:721
+#: text/chapter10.md:723
#, markdown-text
msgid ""
-"There are many reasons to use JSON in an application, for example, it's a "
+"There are many reasons to use JSON in an application; for example, it's a "
"common means of communicating with web APIs. This section will discuss other "
-"use-cases too, beginning with a technique to improve type safety when "
+"use-cases, too, beginning with a technique to improve type safety when "
"passing structural data over the FFI."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:723
+#: text/chapter10.md:725
#, markdown-text
msgid ""
"Let's revisit our earlier FFI functions `cumulativeSums` and `addComplex` "
@@ -2647,7 +2663,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:724
+#: text/chapter10.md:726
#, no-wrap
msgid ""
"export const cumulativeSumsBroken = arr => {\n"
@@ -2670,15 +2686,15 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:745
+#: text/chapter10.md:747
#, markdown-text
msgid ""
"We can use the original type signatures, and the code will still compile, "
-"despite the fact that the return types are incorrect."
+"despite the incorrect return types."
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:746
+#: text/chapter10.md:748
#, no-wrap
msgid ""
"foreign import cumulativeSumsBroken :: Array Int -> Array Int\n"
@@ -2687,7 +2703,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:753
+#: text/chapter10.md:755
#, markdown-text
msgid ""
"We can even execute the code, which might either produce unexpected results "
@@ -2695,7 +2711,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:754
+#: text/chapter10.md:756
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -2722,7 +2738,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:778
+#: text/chapter10.md:780
#, markdown-text
msgid ""
"For example, our resulting `sums` is no-longer a valid `Array Int`, now that "
@@ -2732,7 +2748,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:780
+#: text/chapter10.md:782
#, markdown-text
msgid ""
"Likewise, there are no errors when calling `addComplexBroken`; however, "
@@ -2742,7 +2758,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:782
+#: text/chapter10.md:784
#, markdown-text
msgid ""
"Let's use JSON to make our PureScript code more impervious to bugs in "
@@ -2750,7 +2766,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:784
+#: text/chapter10.md:786
#, markdown-text
msgid ""
"The `argonaut` library contains the JSON decoding and encoding capabilities "
@@ -2760,7 +2776,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:786
+#: text/chapter10.md:788
#, markdown-text
msgid ""
"If we create an alternate foreign import that defines the return type as "
@@ -2768,7 +2784,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:787
+#: text/chapter10.md:789
#, no-wrap
msgid ""
"foreign import cumulativeSumsJson :: Array Int -> Json\n"
@@ -2776,13 +2792,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:793
+#: text/chapter10.md:795
#, markdown-text
msgid "Note that we're simply pointing to our existing broken functions:"
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:794
+#: text/chapter10.md:796
#, no-wrap
msgid ""
"export const cumulativeSumsJson = cumulativeSumsBroken\n"
@@ -2790,13 +2806,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:800
+#: text/chapter10.md:802
#, markdown-text
msgid "And then write a wrapper to decode the returned foreign `Json` value:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:801
+#: text/chapter10.md:803
#, no-wrap
msgid ""
"{{#include "
@@ -2806,7 +2822,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:808
+#: text/chapter10.md:810
#, markdown-text
msgid ""
"Then any values that can't be successfully decoded to our return type appear "
@@ -2814,7 +2830,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:809
+#: text/chapter10.md:811
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -2830,13 +2846,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:822
+#: text/chapter10.md:824
#, markdown-text
msgid "If we call the working versions, a `Right` value is returned."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:824
+#: text/chapter10.md:826
#, markdown-text
msgid ""
"Try this yourself by modifying `test/Examples.js` with the following change "
@@ -2844,7 +2860,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:825
+#: text/chapter10.md:827
#, no-wrap
msgid ""
"export const cumulativeSumsJson = cumulativeSums\n"
@@ -2852,7 +2868,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:830
+#: text/chapter10.md:832
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -2867,7 +2883,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:843
+#: text/chapter10.md:845
#, markdown-text
msgid ""
"Using JSON is also the easiest way to pass other structural types, such as "
@@ -2879,7 +2895,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:845
+#: text/chapter10.md:847
#, markdown-text
msgid ""
"Here's an example of a foreign function signature that modifies a `Map` of "
@@ -2888,13 +2904,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:846
+#: text/chapter10.md:848
#, no-wrap
msgid "{{#include ../exercises/chapter10/test/Examples.purs:mapSetFooJson}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:851
+#: text/chapter10.md:853
#, markdown-text
msgid ""
"Note that this is a prime use case for function composition. Both of these "
@@ -2902,7 +2918,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:852
+#: text/chapter10.md:854
#, no-wrap
msgid ""
"mapSetFoo :: Map String Int -> Either JsonDecodeError (Map String Int)\n"
@@ -2913,7 +2929,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:861
+#: text/chapter10.md:863
#, markdown-text
msgid ""
"Here is the JavaScript implementation. Note the `Array.from` step, which is "
@@ -2922,7 +2938,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:862
+#: text/chapter10.md:864
#, no-wrap
msgid ""
"export const mapSetFooJson = j => {\n"
@@ -2933,13 +2949,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:871
+#: text/chapter10.md:873
#, markdown-text
msgid "Now we can send and receive a `Map` over the FFI:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:872
+#: text/chapter10.md:874
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -2962,7 +2978,7 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter10.md:900
+#: text/chapter10.md:902
#, markdown-text
msgid ""
"(Medium) Write a JavaScript function and PureScript wrapper `valuesOfMap :: "
@@ -2972,7 +2988,7 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter10.md:900
+#: text/chapter10.md:902
#, markdown-text
msgid ""
"(Easy) Write a new wrapper for the previous JavaScript function with the "
@@ -2983,20 +2999,20 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter10.md:900
+#: text/chapter10.md:902
#, markdown-text
msgid ""
"(Medium) Rewrite the earlier `quadraticRoots` function as "
-"`quadraticRootsSet` which returns the `Complex` roots as a `Set` via JSON "
+"`quadraticRootsSet` that returns the `Complex` roots as a `Set` via JSON "
"(instead of as a `Pair`)."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:900
+#: text/chapter10.md:902
#, markdown-text, no-wrap
msgid ""
"1. (Difficult) Rewrite the earlier `quadraticRoots` function as "
-"`quadraticRootsSafe` which uses JSON to pass the `Pair` of `Complex` roots "
+"`quadraticRootsSafe` that uses JSON to pass the `Pair` of `Complex` roots "
"over FFI. Don't use the `Pair` constructor in JavaScript, but instead, just "
"return the pair in a decoder-compatible format.\n"
"_Hint_: You'll need to write a `DecodeJson` instance for `Pair`. Consult the "
@@ -3015,7 +3031,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:906
+#: text/chapter10.md:908
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -3026,7 +3042,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:911
+#: text/chapter10.md:913
#, markdown-text, no-wrap
msgid ""
" Derive generic `EncodeJson` and `DecodeJson` instances for the `Tree` "
@@ -3042,7 +3058,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:917
+#: text/chapter10.md:919
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -3053,7 +3069,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:919
+#: text/chapter10.md:921
#, markdown-text, no-wrap
msgid ""
" Write instances of `EncodeJson` and `DecodeJson` for the `IntOrString` "
@@ -3062,22 +3078,22 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter10.md:920
+#: text/chapter10.md:922
#, markdown-text, no-wrap
msgid "Address book"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:923
+#: text/chapter10.md:925
#, markdown-text
msgid ""
-"In this section we will apply our newly-acquired FFI and JSON knowledge to "
-"build on our address book example from chapter 8. We will add the following "
+"In this section, we will apply our newly-acquired FFI and JSON knowledge to "
+"build on our address book example from Chapter 8. We will add the following "
"features:"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter10.md:927
+#: text/chapter10.md:929
#, markdown-text
msgid ""
"A Save button at the bottom of the form that, when clicked, serializes the "
@@ -3085,7 +3101,7 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter10.md:927
+#: text/chapter10.md:929
#, markdown-text
msgid ""
"Automatic retrieval of the JSON document from local storage upon page "
@@ -3093,13 +3109,13 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter10.md:927
+#: text/chapter10.md:929
#, markdown-text
msgid "A pop-up alert if there is an issue saving or loading the form state."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:929
+#: text/chapter10.md:931
#, markdown-text
msgid ""
"We'll start by creating FFI wrappers for the following Web Storage APIs in "
@@ -3107,7 +3123,7 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter10.md:932
+#: text/chapter10.md:934
#, markdown-text
msgid ""
"`setItem` takes a key and a value (both strings), and returns a computation "
@@ -3115,7 +3131,7 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter10.md:932
+#: text/chapter10.md:934
#, markdown-text
msgid ""
"`getItem` takes a key, and attempts to retrieve the associated value from "
@@ -3124,7 +3140,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:933
+#: text/chapter10.md:935
#, no-wrap
msgid ""
"foreign import setItem :: String -> String -> Effect Unit\n"
@@ -3133,7 +3149,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:940
+#: text/chapter10.md:942
#, markdown-text
msgid ""
"Here is the corresponding JavaScript implementation of these functions in "
@@ -3141,7 +3157,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:941
+#: text/chapter10.md:943
#, no-wrap
msgid ""
"export const setItem = key => value => () =>\n"
@@ -3152,13 +3168,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:950
+#: text/chapter10.md:952
#, markdown-text
msgid "We'll create a save button like so:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:951
+#: text/chapter10.md:953
#, no-wrap
msgid ""
"saveButton :: R.JSX\n"
@@ -3176,7 +3192,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:967
+#: text/chapter10.md:969
#, markdown-text
msgid ""
"And write our validated `person` as a JSON string with `setItem` in the "
@@ -3184,7 +3200,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:968
+#: text/chapter10.md:970
#, no-wrap
msgid ""
"validateAndSave :: Effect Unit\n"
@@ -3199,7 +3215,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:980
+#: text/chapter10.md:982
#, markdown-text
msgid ""
"Note that if we attempt to compile at this stage, we'll encounter the "
@@ -3207,7 +3223,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter10.md:981
+#: text/chapter10.md:983
#, no-wrap
msgid ""
" No type class instance was found for\n"
@@ -3215,17 +3231,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:987
+#: text/chapter10.md:989
#, markdown-text
msgid ""
"This is because `PhoneType` in the `Person` record needs an `EncodeJson` "
-"instance. We'll just derive a generic encode instance, and a decode instance "
-"too while we're at it. More information how this works is available in the "
+"instance. We'll also derive a generic encode instance and a decode instance "
+"while we're at it. More information on how this works is available in the "
"argonaut docs:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:988
+#: text/chapter10.md:990
#, no-wrap
msgid ""
"{{#include ../exercises/chapter10/src/Data/AddressBook.purs:import}}\n"
@@ -3235,7 +3251,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:995
+#: text/chapter10.md:997
#, markdown-text
msgid ""
"Now we can save our `person` to local storage, but this isn't very useful "
@@ -3243,31 +3259,31 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:997
+#: text/chapter10.md:999
#, markdown-text
msgid "We'll start with retrieving the \"person\" string from local storage:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:998
+#: text/chapter10.md:1000
#, no-wrap
msgid "item <- getItem \"person\"\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1003
+#: text/chapter10.md:1005
#, markdown-text
msgid ""
-"Then we'll create a helper function to handle converting the string from "
-"local storage to our `Person` record. Note that this string in storage may "
-"be `null`, so we represent it as a foreign `Json` until it is successfully "
-"decoded as a `String`. There are a number of other conversion steps along "
-"the way - each of which return an `Either` value, so it makes sense to "
-"organize these together in a `do` block."
+"Then we'll create a helper function to convert the string from local storage "
+"to our `Person` record. Note that this string in storage may be `null`, so "
+"we represent it as a foreign `Json` until it is successfully decoded as a "
+"`String`. There are a number of other conversion steps along the way – each "
+"of which returns an `Either` value, so it makes sense to organize these "
+"together in a `do` block."
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:1004
+#: text/chapter10.md:1006
#, no-wrap
msgid ""
"processItem :: Json -> Either String Person\n"
@@ -3278,16 +3294,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1013
+#: text/chapter10.md:1015
#, markdown-text
msgid ""
-"Then we inspect this result to see if it succeeded. If it failed, we'll log "
-"the errors and use our default `examplePerson`, otherwise we'll use the "
+"Then we inspect this result to see if it succeeded. If it fails, we'll log "
+"the errors and use our default `examplePerson`, otherwise, we'll use the "
"person retrieved from local storage."
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:1014
+#: text/chapter10.md:1016
#, no-wrap
msgid ""
"initialPerson <- case processItem item of\n"
@@ -3298,7 +3314,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1023
+#: text/chapter10.md:1025
#, markdown-text
msgid ""
"Finally, we'll pass this `initialPerson` to our component via the `props` "
@@ -3306,7 +3322,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:1024
+#: text/chapter10.md:1026
#, no-wrap
msgid ""
"-- Create JSX node from react component.\n"
@@ -3314,13 +3330,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1030
+#: text/chapter10.md:1032
#, markdown-text
msgid "And pick it up on the other side to use in our state hook:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:1031
+#: text/chapter10.md:1033
#, no-wrap
msgid ""
"mkAddressBookApp :: Effect (ReactComponent { initialPerson :: Person })\n"
@@ -3330,7 +3346,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1039
+#: text/chapter10.md:1041
#, markdown-text
msgid ""
"As a finishing touch, we'll improve the quality of our error messages by "
@@ -3338,7 +3354,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:1040
+#: text/chapter10.md:1042
#, no-wrap
msgid ""
"processItem :: Json -> Either String Person\n"
@@ -3352,10 +3368,10 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1049
+#: text/chapter10.md:1051
#, markdown-text
msgid ""
-"Only the first error should ever occur during normal operation of this "
+"Only the first error should ever occur during the normal operation of this "
"app. You can trigger the other errors by opening your web browser's dev "
"tools, editing the saved \"person\" string in local storage, and refreshing "
"the page. How you modify the JSON string determines which error is "
@@ -3363,25 +3379,24 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1051
+#: text/chapter10.md:1053
#, markdown-text
msgid ""
-"That covers local storage. Next we'll implement the `alert` action, which is "
-"very similar to the `log` action from the `Effect.Console` module. The only "
-"difference is that the `alert` action uses the `window.alert` method, "
-"whereas the `log` action uses the `console.log` method. As such, `alert` can "
-"only be used in environments where `window.alert` is defined, such as a web "
-"browser."
+"That covers local storage. Next, we'll implement the `alert` action, similar "
+"to the `log` action from the `Effect.Console` module. The only difference is "
+"that the `alert` action uses the `window.alert` method, whereas the `log` "
+"action uses the `console.log` method. As such, `alert` can only be used in "
+"environments where `window.alert` is defined, such as a web browser."
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:1052
+#: text/chapter10.md:1054
#, no-wrap
msgid "foreign import alert :: String -> Effect Unit\n"
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter10.md:1056
+#: text/chapter10.md:1058
#, no-wrap
msgid ""
"export const alert = msg => () =>\n"
@@ -3389,31 +3404,31 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1062
+#: text/chapter10.md:1064
#, markdown-text
msgid "We want this alert to appear when either:"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter10.md:1065
+#: text/chapter10.md:1067
#, markdown-text
msgid "A user attempts to save a form with validation errors."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter10.md:1065
+#: text/chapter10.md:1067
#, markdown-text
msgid "The state cannot be retrieved from local storage."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1067
+#: text/chapter10.md:1069
#, markdown-text
msgid "That is accomplished by simply replacing `log` with `alert` on these lines:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter10.md:1068
+#: text/chapter10.md:1070
#, no-wrap
msgid ""
"Left errs -> alert $ \"There are \" <> show (length errs) <> \" validation "
@@ -3423,7 +3438,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter10.md:1080
+#: text/chapter10.md:1082
#, markdown-text
msgid ""
"(Easy) Write a wrapper for the `removeItem` method on the `localStorage` "
@@ -3431,7 +3446,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter10.md:1080
+#: text/chapter10.md:1082
#, markdown-text
msgid ""
"(Medium) Add a \"Reset\" button that, when clicked, calls the newly-created "
@@ -3439,7 +3454,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter10.md:1080
+#: text/chapter10.md:1082
#, markdown-text
msgid ""
"(Easy) Write a wrapper for the `confirm` method on the JavaScript `Window` "
@@ -3447,7 +3462,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter10.md:1080
+#: text/chapter10.md:1082
#, markdown-text
msgid ""
"(Medium) Call this `confirm` function when a users clicks the \"Reset\" "
@@ -3455,26 +3470,26 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter10.md:1081 text/chapter11.md:964 text/chapter12.md:594
-#: text/chapter13.md:401 text/chapter14.md:721 text/chapter2.md:129
-#: text/chapter3.md:722 text/chapter4.md:631 text/chapter5.md:529
-#: text/chapter6.md:758 text/chapter7.md:691 text/chapter8.md:1000
-#: text/chapter9.md:237
+#: text/chapter10.md:1083 text/chapter11.md:963 text/chapter12.md:594
+#: text/chapter13.md:401 text/chapter14.md:720 text/chapter2.md:129
+#: text/chapter3.md:782 text/chapter4.md:631 text/chapter5.md:529
+#: text/chapter6.md:757 text/chapter7.md:691 text/chapter8.md:1001
+#: text/chapter9.md:238
#, markdown-text, no-wrap
msgid "Conclusion"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1084
+#: text/chapter10.md:1086
#, markdown-text
msgid ""
"In this chapter, we've learned how to work with foreign JavaScript code from "
-"PureScript and we've seen the issues involved with writing trustworthy code "
+"PureScript, and we've seen the issues involved with writing trustworthy code "
"using the FFI:"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter10.md:1088
+#: text/chapter10.md:1090
#, markdown-text
msgid ""
"We've seen the importance of ensuring that foreign functions have correct "
@@ -3482,43 +3497,43 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter10.md:1088
+#: text/chapter10.md:1090
#, markdown-text
msgid ""
"We learned how to deal with corner cases like null values and other types of "
-"JavaScript data, by using foreign types, or the `Json` data type."
+"JavaScript data by using foreign types or the `Json` data type."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter10.md:1088
+#: text/chapter10.md:1090
#, markdown-text
msgid "We saw how to safely serialize and deserialize JSON data."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1090
+#: text/chapter10.md:1092
#, markdown-text
msgid ""
-"For more examples, the `purescript`, `purescript-contrib` and "
+"For more examples, the `purescript`, `purescript-contrib`, and "
"`purescript-node` GitHub organizations provide plenty of examples of "
-"libraries which use the FFI. In the remaining chapters, we will see some of "
+"libraries that use the FFI. In the remaining chapters, we will see some of "
"these libraries put to use to solve real-world problems in a type-safe way."
msgstr ""
#. type: Title ##
-#: text/chapter10.md:1091
+#: text/chapter10.md:1093
#, markdown-text, no-wrap
msgid "Addendum"
msgstr ""
#. type: Title ###
-#: text/chapter10.md:1093
+#: text/chapter10.md:1095
#, markdown-text, no-wrap
msgid "Calling PureScript from JavaScript"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1096
+#: text/chapter10.md:1098
#, markdown-text
msgid ""
"Calling a PureScript function from JavaScript is very simple, at least for "
@@ -3526,13 +3541,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1098
+#: text/chapter10.md:1100
#, markdown-text
msgid "Let's take the following simple module as an example:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1099
+#: text/chapter10.md:1101
#, no-wrap
msgid ""
"module Test where\n"
@@ -3542,11 +3557,11 @@ msgid ""
"gcd n 0 = n\n"
"gcd n m\n"
" | n > m = gcd (n - m) m\n"
-" | otherwise = gcd (m - n) n\n"
+" | otherwise = gcd (m – n) n\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1111
+#: text/chapter10.md:1113
#, markdown-text
msgid ""
"This function finds the greatest common divisor of two numbers by repeated "
@@ -3558,7 +3573,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1113
+#: text/chapter10.md:1115
#, markdown-text
msgid ""
"To understand how this function can be called from JavaScript, it is "
@@ -3568,7 +3583,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1114
+#: text/chapter10.md:1116
#, no-wrap
msgid ""
"import Test from 'Test.js';\n"
@@ -3576,17 +3591,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1120
+#: text/chapter10.md:1122
#, markdown-text
msgid ""
-"Here, I am assuming that the code was compiled with `spago build`, which "
-"compiles PureScript modules to ES modules. For that reason, I was able to "
-"reference the `gcd` function on the `Test` object, after importing the "
-"`Test` module using `import`."
+"Here, I assume the code was compiled with `spago build`, which compiles "
+"PureScript modules to ES modules. For that reason, I could reference the "
+"`gcd` function on the `Test` object, after importing the `Test` module using "
+"`import`."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1122
+#: text/chapter10.md:1124
#, markdown-text
msgid ""
"You can also use the `spago bundle-app` and `spago bundle-module` commands "
@@ -3596,23 +3611,23 @@ msgid ""
msgstr ""
#. type: Title ###
-#: text/chapter10.md:1123
+#: text/chapter10.md:1125
#, markdown-text, no-wrap
msgid "Understanding Name Generation"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1126
+#: text/chapter10.md:1128
#, markdown-text
msgid ""
"PureScript aims to preserve names during code generation as much as "
-"possible. In particular, most identifiers which are neither PureScript nor "
+"possible. In particular, most identifiers that are neither PureScript nor "
"JavaScript keywords can be expected to be preserved, at least for names of "
"top-level declarations."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1128
+#: text/chapter10.md:1130
#, markdown-text
msgid ""
"If you decide to use a JavaScript keyword as an identifier, the name will be "
@@ -3620,25 +3635,25 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1129
+#: text/chapter10.md:1131
#, no-wrap
msgid "null = []\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1134 text/chapter10.md:1146
+#: text/chapter10.md:1136 text/chapter10.md:1148
#, markdown-text
-msgid "generates the following JavaScript:"
+msgid "Generates the following JavaScript:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1135
+#: text/chapter10.md:1137
#, no-wrap
msgid "var $$null = [];\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1140
+#: text/chapter10.md:1142
#, markdown-text
msgid ""
"In addition, if you would like to use special characters in your identifier "
@@ -3646,46 +3661,46 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1141
+#: text/chapter10.md:1143
#, no-wrap
msgid "example' = 100\n"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1147
+#: text/chapter10.md:1149
#, no-wrap
msgid "var example$prime = 100;\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1152
+#: text/chapter10.md:1154
#, markdown-text
msgid ""
"Where compiled PureScript code is intended to be called from JavaScript, it "
-"is recommended that identifiers only use alphanumeric characters, and avoid "
+"is recommended that identifiers only use alphanumeric characters and avoid "
"JavaScript keywords. If user-defined operators are provided for use in "
"PureScript code, it is good practice to provide an alternative function with "
"an alphanumeric name for use in JavaScript."
msgstr ""
#. type: Title ###
-#: text/chapter10.md:1153
+#: text/chapter10.md:1155
#, markdown-text, no-wrap
msgid "Runtime Data Representation"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1156
+#: text/chapter10.md:1158
#, markdown-text
msgid ""
"Types allow us to reason at compile-time that our programs are \"correct\" "
-"in some sense - that is, they will not break at runtime. But what does that "
+"in some sense – that is, they will not break at runtime. But what does that "
"mean? In PureScript, it means that the type of an expression should be "
"compatible with its representation at runtime."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1158
+#: text/chapter10.md:1160
#, markdown-text
msgid ""
"For that reason, it is important to understand the representation of data at "
@@ -3696,7 +3711,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1160
+#: text/chapter10.md:1162
#, markdown-text
msgid ""
"The good news is that PureScript expressions have particularly simple "
@@ -3705,7 +3720,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1162
+#: text/chapter10.md:1164
#, markdown-text
msgid ""
"For simple types, the correspondence is almost trivial. For example, if an "
@@ -3717,10 +3732,10 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1164
+#: text/chapter10.md:1166
#, markdown-text
msgid ""
-"A similar law holds for expressions of type `Int`, `Number`, and `String` - "
+"A similar law holds for expressions of type `Int`, `Number`, and `String` – "
"expressions of type `Int` or `Number` evaluate to non-null JavaScript "
"numbers, and expressions of type `String` evaluate to non-null JavaScript "
"strings. Expressions of type `Int` will evaluate to integers at runtime, "
@@ -3729,45 +3744,44 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1166
+#: text/chapter10.md:1168
#, markdown-text
msgid ""
"What about `Unit`? Well, since `Unit` has only one inhabitant (`unit`) and "
-"its value is not observable, it doesn't actually matter what it's "
-"represented with at runtime. Old code tends to represent it using "
-"`{}`. Newer code, however, tends to use `undefined`. So, although it doesn't "
-"really matter what you use to represent `Unit`, it is recommended to use "
-"`undefined` (not returning anything from a function also returns "
-"`undefined`)."
+"its value is not observable, it doesn't matter what it's represented with at "
+"runtime. Old code tends to represent it using `{}`. Newer code, however, "
+"tends to use `undefined`. So, although it doesn't matter what you use to "
+"represent `Unit`, it is recommended to use `undefined` (not returning "
+"anything from a function also returns `undefined`)."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1168
+#: text/chapter10.md:1170
#, markdown-text
msgid "What about some more complex types?"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1170
+#: text/chapter10.md:1172
#, markdown-text, no-wrap
msgid ""
"As we have already seen, PureScript functions correspond to JavaScript "
"functions of a single argument. More precisely, if an expression `f` has "
"type `a -> b` for some types `a` and `b`, and an expression `x` evaluates to "
"a value with the correct runtime representation for type `a`, then `f` "
-"evaluates to a JavaScript function, which when applied to the result of "
+"evaluates to a JavaScript function, which, when applied to the result of "
"evaluating `x`, has the correct runtime representation for type `b`. As a "
"simple example, an expression of type `String -> String` evaluates to a "
-"function which takes non-null JavaScript strings to non-null JavaScript "
+"function that takes non-null JavaScript strings to non-null JavaScript "
"strings.\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1172
+#: text/chapter10.md:1174
#, markdown-text
msgid ""
"As you might expect, PureScript's arrays correspond to JavaScript "
-"arrays. But remember - PureScript arrays are homogeneous, so every element "
+"arrays. But remember – PureScript arrays are homogeneous, so every element "
"has the same type. Concretely, if a PureScript expression `e` has type "
"`Array a` for some type `a`, then `e` evaluates to a (non-null) JavaScript "
"array, all of whose elements have the correct runtime representation for "
@@ -3775,52 +3789,52 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1174
+#: text/chapter10.md:1176
#, markdown-text
msgid ""
"We've already seen that PureScript's records evaluate to JavaScript "
-"objects. Just as for functions and arrays, we can reason about the runtime "
+"objects. As for functions and arrays, we can reason about the runtime "
"representation of data in a record's fields by considering the types "
"associated with its labels. Of course, the fields of a record are not "
"required to be of the same type."
msgstr ""
#. type: Title ###
-#: text/chapter10.md:1175
+#: text/chapter10.md:1177
#, markdown-text, no-wrap
msgid "Representing ADTs"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1178
+#: text/chapter10.md:1180
#, markdown-text
msgid ""
"For every constructor of an algebraic data type, the PureScript compiler "
"creates a new JavaScript object type by defining a function. Its "
-"constructors correspond to functions which create new JavaScript objects "
+"constructors correspond to functions that create new JavaScript objects "
"based on those prototypes."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1180
+#: text/chapter10.md:1182
#, markdown-text
msgid "For example, consider the following simple ADT:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1181
+#: text/chapter10.md:1183
#, no-wrap
msgid "data ZeroOrOne a = Zero | One a\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1186
+#: text/chapter10.md:1188
#, markdown-text
msgid "The PureScript compiler generates the following code:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1187
+#: text/chapter10.md:1189
#, no-wrap
msgid ""
"function One(value0) {\n"
@@ -3838,7 +3852,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1203
+#: text/chapter10.md:1205
#, markdown-text
msgid ""
"Here, we see two JavaScript object types: `Zero` and `One`. It is possible "
@@ -3848,7 +3862,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1205
+#: text/chapter10.md:1207
#, markdown-text
msgid ""
"The PureScript compiler also generates helper functions. For constructors "
@@ -3860,29 +3874,29 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1207
+#: text/chapter10.md:1209
#, markdown-text
msgid ""
"What about constructors with more than one argument? In that case, the "
"PureScript compiler also creates a new object type, and a helper "
-"function. This time, however, the helper function is curried function of two "
-"arguments. For example, this algebraic data type:"
+"function. This time, however, the helper function is a curried function of "
+"two arguments. For example, this algebraic data type:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1208
+#: text/chapter10.md:1210
#, no-wrap
msgid "data Two a b = Two a b\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1213
+#: text/chapter10.md:1215
#, markdown-text
-msgid "generates this JavaScript code:"
+msgid "Generates this JavaScript code:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1214
+#: text/chapter10.md:1216
#, no-wrap
msgid ""
"function Two(value0, value1) {\n"
@@ -3898,52 +3912,53 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1228
+#: text/chapter10.md:1230
#, markdown-text
msgid ""
-"Here, values of the object type `Two` can be created using the `new` "
-"keyword, or by using the `Two.create` function."
+"Here, values of the object type `Two` can be created using the `new` keyword "
+"or by using the `Two.create` function."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1230
+#: text/chapter10.md:1232
#, markdown-text
msgid ""
"The case of newtypes is slightly different. Recall that a newtype is like an "
"algebraic data type, restricted to having a single constructor taking a "
"single argument. In this case, the runtime representation of the newtype is "
-"actually the same as the type of its argument."
+"the same as its argument type."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1232
+#: text/chapter10.md:1234
#, markdown-text
-msgid "For example, this newtype representing telephone numbers:"
+msgid ""
+"For example, this newtype represents telephone numbers is represented as a "
+"JavaScript string at runtime:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1233
+#: text/chapter10.md:1235
#, no-wrap
msgid "newtype PhoneNumber = PhoneNumber String\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1238
+#: text/chapter10.md:1240
#, markdown-text
msgid ""
-"is actually represented as a JavaScript string at runtime. This is useful "
-"for designing libraries, since newtypes provide an additional layer of type "
-"safety, but without the runtime overhead of another function call."
+"This is useful for designing libraries since newtypes provide an additional "
+"layer of type safety without the runtime overhead of another function call."
msgstr ""
#. type: Title ###
-#: text/chapter10.md:1239
+#: text/chapter10.md:1241
#, markdown-text, no-wrap
msgid "Representing Quantified Types"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1242
+#: text/chapter10.md:1244
#, markdown-text
msgid ""
"Expressions with quantified (polymorphic) types have restrictive "
@@ -3953,19 +3968,19 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1244
+#: text/chapter10.md:1246
#, markdown-text
msgid "Consider this polymorphic type, for example:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1245
+#: text/chapter10.md:1247
#, no-wrap
msgid "forall a. a -> a\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1250
+#: text/chapter10.md:1252
#, markdown-text
msgid ""
"What sort of functions have this type? Well, there is certainly one function "
@@ -3973,7 +3988,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1251
+#: text/chapter10.md:1253
#, no-wrap
msgid ""
"identity :: forall a. a -> a\n"
@@ -3981,7 +3996,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1257
+#: text/chapter10.md:1259
#, markdown-text, no-wrap
msgid ""
"> Note that the actual "
@@ -3990,7 +4005,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1259
+#: text/chapter10.md:1261
#, markdown-text
msgid ""
"In fact, the `identity` function is the _only_ (total) function with this "
@@ -4001,7 +4016,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1261
+#: text/chapter10.md:1263
#, markdown-text, no-wrap
msgid ""
"What is the runtime representation of a quantified type `forall a. t`? Well, "
@@ -4014,19 +4029,19 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1263
+#: text/chapter10.md:1265
#, markdown-text
msgid ""
-"But that is not enough - the runtime representation of a quantified type is "
+"But that is not enough – the runtime representation of a quantified type is "
"more strict than this. We require any expression to be _parametrically "
-"polymorphic_ - that is, it cannot use any information about the type of its "
+"polymorphic_ – that is, it cannot use any information about the type of its "
"argument in its implementation. This additional condition prevents "
"problematic implementations such as the following JavaScript function from "
"inhabiting a polymorphic type:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1264
+#: text/chapter10.md:1266
#, no-wrap
msgid ""
"function invalid(a) {\n"
@@ -4039,62 +4054,62 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1275
+#: text/chapter10.md:1277
#, markdown-text, no-wrap
msgid ""
"Certainly, this function takes strings to strings, numbers to numbers, "
-"etc. but it does not meet the additional condition, since it inspects the "
+"etc. But it does not meet the additional condition, since it inspects the "
"(runtime) type of its argument, so this function would not be a valid "
"inhabitant of the type `forall a. a -> a`.\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1277
+#: text/chapter10.md:1279
#, markdown-text, no-wrap
msgid ""
"Without being able to inspect the runtime type of our function argument, our "
-"only option is to return the argument unchanged, and so `identity` is indeed "
-"the only inhabitant of the type `forall a. a -> a`.\n"
+"only option is to return the argument unchanged. So `identity` is indeed the "
+"only inhabitant of the type `forall a. a -> a`.\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1279
+#: text/chapter10.md:1281
#, markdown-text
msgid ""
"A full discussion of _parametric polymorphism_ and _parametricity_ is beyond "
-"the scope of this book. Note however, that since PureScript's types are "
+"the scope of this book. Note, however, that since PureScript's types are "
"_erased_ at runtime, a polymorphic function in PureScript _cannot_ inspect "
-"the runtime representation of its arguments (without using the FFI), and so "
-"this representation of polymorphic data is appropriate."
+"the runtime representation of its arguments (without using the FFI), so this "
+"representation of polymorphic data is appropriate."
msgstr ""
#. type: Title ###
-#: text/chapter10.md:1280
+#: text/chapter10.md:1282
#, markdown-text, no-wrap
msgid "Representing Constrained Types"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1283
+#: text/chapter10.md:1285
#, markdown-text
msgid ""
"Functions with a type class constraint have an interesting representation at "
-"runtime. Because the behavior of the function might depend on the type class "
+"runtime. Because the function's behavior might depend on the type class "
"instance chosen by the compiler, the function is given an additional "
"argument, called a _type class dictionary_, which contains the "
"implementation of the type class functions provided by the chosen instance."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1285
+#: text/chapter10.md:1287
#, markdown-text
msgid ""
"For example, here is a simple PureScript function with a constrained type "
-"which uses the `Show` type class:"
+"that uses the `Show` type class:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1286
+#: text/chapter10.md:1288
#, no-wrap
msgid ""
"shout :: forall a. Show a => a -> String\n"
@@ -4102,13 +4117,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1292
+#: text/chapter10.md:1294
#, markdown-text
msgid "The generated JavaScript looks like this:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1293
+#: text/chapter10.md:1295
#, no-wrap
msgid ""
"var shout = function (dict) {\n"
@@ -4119,7 +4134,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1302
+#: text/chapter10.md:1304
#, markdown-text
msgid ""
"Notice that `shout` is compiled to a (curried) function of two arguments, "
@@ -4129,7 +4144,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1304
+#: text/chapter10.md:1306
#, markdown-text
msgid ""
"We can call this function from JavaScript by passing an explicit type class "
@@ -4137,7 +4152,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1305
+#: text/chapter10.md:1307
#, no-wrap
msgid ""
"import { showNumber } from 'Data.Show'\n"
@@ -4146,13 +4161,13 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter10.md:1314
+#: text/chapter10.md:1316
#, markdown-text
msgid "(Easy) What are the runtime representations of these types?"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1320
+#: text/chapter10.md:1322
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -4163,7 +4178,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1323
+#: text/chapter10.md:1325
#, markdown-text, no-wrap
msgid ""
" What can you say about the expressions which have these types?\n"
@@ -4175,24 +4190,24 @@ msgid ""
msgstr ""
#. type: Title ###
-#: text/chapter10.md:1324
+#: text/chapter10.md:1326
#, markdown-text, no-wrap
msgid "Representing Side Effects"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1327
+#: text/chapter10.md:1329
#, markdown-text
msgid ""
"The `Effect` monad is also defined as a foreign type. Its runtime "
-"representation is quite simple - an expression of type `Effect a` should "
+"representation is quite simple – an expression of type `Effect a` should "
"evaluate to a JavaScript function of **no arguments**, which performs any "
"side-effects and returns a value with the correct runtime representation for "
"type `a`."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1329
+#: text/chapter10.md:1331
#, markdown-text
msgid ""
"The definition of the `Effect` type constructor is given in the `Effect` "
@@ -4200,13 +4215,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1330
+#: text/chapter10.md:1332
#, no-wrap
msgid "foreign import data Effect :: Type -> Type\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1335
+#: text/chapter10.md:1337
#, markdown-text
msgid ""
"As a simple example, consider the `random` function defined in the `random` "
@@ -4214,35 +4229,35 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1336
+#: text/chapter10.md:1338
#, no-wrap
msgid "foreign import random :: Effect Number\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1341
+#: text/chapter10.md:1343
#, markdown-text
msgid "The definition of the `random` function is given here:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1342
+#: text/chapter10.md:1344
#, no-wrap
msgid "export const random = Math.random;\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1347
+#: text/chapter10.md:1349
#, markdown-text
msgid ""
"Notice that the `random` function is represented at runtime as a function of "
-"no arguments. It performs the side effect of generating a random number, and "
+"no arguments. It performs the side effect of generating a random number, "
"returns it, and the return value matches the runtime representation of the "
"`Number` type: it is a non-null JavaScript number."
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1349
+#: text/chapter10.md:1351
#, markdown-text
msgid ""
"As a slightly more interesting example, consider the `log` function defined "
@@ -4251,19 +4266,19 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter10.md:1350
+#: text/chapter10.md:1352
#, no-wrap
msgid "foreign import log :: String -> Effect Unit\n"
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1355
+#: text/chapter10.md:1357
#, markdown-text
msgid "And here is its definition:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1356
+#: text/chapter10.md:1358
#, no-wrap
msgid ""
"export const log = function (s) {\n"
@@ -4274,7 +4289,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1365
+#: text/chapter10.md:1367
#, markdown-text
msgid ""
"The representation of `log` at runtime is a JavaScript function of a single "
@@ -4283,7 +4298,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1367
+#: text/chapter10.md:1369
#, markdown-text
msgid ""
"Expressions of type `Effect a` can be invoked from JavaScript like regular "
@@ -4292,7 +4307,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter10.md:1368
+#: text/chapter10.md:1370
#, no-wrap
msgid ""
"import { main } from 'Main'\n"
@@ -4301,11 +4316,11 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter10.md:1374
+#: text/chapter10.md:1376
#, markdown-text
msgid ""
"When using `spago bundle-app --to` or `spago run`, this call to `main` is "
-"generated automatically, whenever the `Main` module is defined."
+"generated automatically whenever the `Main` module is defined."
msgstr ""
#. type: Title #
@@ -4320,7 +4335,7 @@ msgstr ""
msgid ""
"The goal of this chapter will be to learn about _monad transformers_, which "
"provide a way to combine side-effects provided by different monads. The "
-"motivating example will be a text adventure game which can be played on the "
+"motivating example will be a text adventure game that can be played on the "
"console in NodeJS. The various side-effects of the game (logging, state, and "
"configuration) will all be provided by a monad transformer stack."
msgstr ""
@@ -4358,7 +4373,7 @@ msgstr ""
#: text/chapter11.md:15
#, markdown-text
msgid ""
-"`optparse`, which provides applicative parsers for processing command line "
+"`optparse`, which provides applicative parsers for processing command-line "
"arguments"
msgstr ""
@@ -4377,7 +4392,7 @@ msgstr ""
#. type: Plain text
#: text/chapter11.md:21
#, markdown-text
-msgid "By default you will see a usage message:"
+msgid "By default, you will see a usage message:"
msgstr ""
#. type: Fenced code block (text)
@@ -4397,23 +4412,23 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:35
-#, markdown-text, no-wrap
+#: text/chapter11.md:36
+#, markdown-text
msgid ""
"To provide command line arguments, you can either call `spago run` with the "
-"`-a` option to pass additional arguments directly to your application, or "
-"you can call `spago bundle-app`, which will create an index.js file that can "
-"be run directly with `node`. \n"
+"`-a` option to pass additional arguments directly to your application or "
+"call `spago bundle-app`, which will create an index.js file that can be run "
+"directly with `node`."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:37
+#: text/chapter11.md:38
#, markdown-text
msgid "For example, to provide the player name using the `-p` option:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:38
+#: text/chapter11.md:39
#, no-wrap
msgid ""
"$ spago run -a \"-p Phil\"\n"
@@ -4421,7 +4436,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:43
+#: text/chapter11.md:44
#, no-wrap
msgid ""
"$ spago bundle-app \n"
@@ -4430,34 +4445,34 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:50
+#: text/chapter11.md:51
#, markdown-text
msgid ""
"From the prompt, you can enter commands like `look`, `inventory`, `take`, "
"`use`, `north`, `south`, `east`, and `west`. There is also a `debug` "
-"command, which can be used to print the game state when the `--debug` "
-"command line option is provided."
+"command, which can print the game state when the `--debug` command line "
+"option is provided."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:52
+#: text/chapter11.md:53
#, markdown-text
msgid ""
"The game is played on a two-dimensional grid, and the player moves by "
"issuing commands `north`, `south`, `east`, and `west`. The game contains a "
-"collection of items which can either be in the player's possession (in the "
-"user's _inventory_), or on the game grid at some location. Items can be "
-"picked up by the player, using the `take` command."
+"collection of items that can either be in the player's possession (in the "
+"user's _inventory_) or on the game grid at some location. Items can be "
+"picked up by the player using the `take` command."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:54
+#: text/chapter11.md:55
#, markdown-text
msgid "For reference, here is a complete walkthrough of the game:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:55
+#: text/chapter11.md:56
#, no-wrap
msgid ""
"$ spago run -a \"-p Phil\"\n"
@@ -4490,22 +4505,22 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:86
+#: text/chapter11.md:87
#, markdown-text
msgid ""
"The game is very simple, but the aim of the chapter is to use the "
-"`transformers` package to build a library which will enable rapid "
-"development of this type of game."
+"`transformers` package to build a library that will enable rapid development "
+"of this type of game."
msgstr ""
#. type: Title ##
-#: text/chapter11.md:87
+#: text/chapter11.md:88
#, markdown-text, no-wrap
msgid "The State Monad"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:90
+#: text/chapter11.md:91
#, markdown-text
msgid ""
"We will start by looking at some of the monads provided by the "
@@ -4513,32 +4528,32 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:92
+#: text/chapter11.md:93
#, markdown-text
msgid ""
"The first example is the `State` monad, which provides a way to model "
-"_mutable state_ in pure code. We have already seen an approach to mutable "
+"_mutable state_ in pure code. We have already seen an approach to a mutable "
"state provided by the `Effect` monad. `State` provides an alternative."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:94
+#: text/chapter11.md:95
#, markdown-text
msgid ""
"The `State` type constructor takes two type parameters: the type `s` of the "
-"state, and the return type `a`. Even though we speak of the \"`State` "
+"state and the return type `a`. Even though we speak of the \"`State` "
"monad\", the instance of the `Monad` type class is actually provided for the "
-"`State s` type constructor, for any type `s`."
+"`State s` type constructor for any type `s`."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:96
+#: text/chapter11.md:97
#, markdown-text
msgid "The `Control.Monad.State` module provides the following API:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:97
+#: text/chapter11.md:98
#, no-wrap
msgid ""
"get :: forall s. State s s\n"
@@ -4549,27 +4564,28 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:106
+#: text/chapter11.md:107
#, markdown-text
msgid ""
"Note that these API signatures are presented in a simplified form using the "
-"`State` type constructor for now. The actual API involves `MonadState` which "
-"we'll cover in the later \"Type Classes\" section of this chapter, so don't "
-"worry if you see different signatures in your IDE tooltips or on Pursuit."
+"`State` type constructor for now. The actual API involves `MonadState`, "
+"which we'll cover in the later \"Type Classes\" section of this chapter, so "
+"don't worry if you see different signatures in your IDE tooltips or on "
+"Pursuit."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:108
+#: text/chapter11.md:109
#, markdown-text
msgid ""
"Let's see an example. One use of the `State` monad might be to add the "
"values in an array of integers to the current state. We could do that by "
-"choosing `Int` as the state type `s`, and using `traverse_` to traverse the "
+"choosing `Int` as the state type `s` and using `traverse_` to traverse the "
"array, with a call to `modify` for each array element:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:109
+#: text/chapter11.md:110
#, no-wrap
msgid ""
"import Data.Foldable (traverse_)\n"
@@ -4581,7 +4597,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:119
+#: text/chapter11.md:120
#, markdown-text
msgid ""
"The `Control.Monad.State` module provides three functions for running a "
@@ -4589,7 +4605,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:120
+#: text/chapter11.md:121
#, no-wrap
msgid ""
"evalState :: forall s a. State s a -> s -> a\n"
@@ -4598,17 +4614,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:127
+#: text/chapter11.md:128
#, markdown-text
msgid ""
-"Each of these functions takes an initial state of type `s` and a computation "
-"of type `State s a`. `evalState` only returns the return value, `execState` "
-"only returns the final state, and `runState` returns both, expressed as a "
-"value of type `Tuple a s`."
+"Each function takes an initial state of type `s` and a computation of type "
+"`State s a`. `evalState` only returns the return value, `execState` only "
+"returns the final state, and `runState` returns both, expressed as a value "
+"of type `Tuple a s`."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:129
+#: text/chapter11.md:130
#, markdown-text
msgid ""
"Given the `sumArray` function above, we could use `execState` in PSCi to sum "
@@ -4616,7 +4632,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:130
+#: text/chapter11.md:131
#, no-wrap
msgid ""
"> :paste\n"
@@ -4636,14 +4652,13 @@ msgid ""
"`evalState` in our example above?"
msgstr ""
-#. type: Plain text
+#. type: Bullet: ' 1. '
#: text/chapter11.md:145
-#, markdown-text, no-wrap
+#, markdown-text
msgid ""
-" 1. (Medium) A string of parentheses is _balanced_ if it is obtained by "
-"either concatenating zero-or-more shorter balanced\n"
-" strings, or by wrapping a shorter balanced string in a pair of "
-"parentheses.\n"
+"(Medium) A string of parentheses is _balanced_ if it is obtained by either "
+"concatenating zero-or-more shorter balanced strings or wrapping a shorter "
+"balanced string in a pair of parentheses."
msgstr ""
#. type: Plain text
@@ -4664,16 +4679,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:154
+#: text/chapter11.md:153
#, markdown-text, no-wrap
msgid ""
-" which tests whether or not a `String` of parentheses is balanced, by "
-"keeping track of the number of opening parentheses\n"
-" which have not been closed. Your function should work as follows:\n"
+" which tests whether or not a `String` of parentheses is balanced by "
+"keeping track of the number of opening parentheses that have not been "
+"closed. Your function should work as follows:\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:158
+#: text/chapter11.md:157
#, markdown-text, no-wrap
msgid ""
" ```text\n"
@@ -4682,7 +4697,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:161
+#: text/chapter11.md:160
#, markdown-text, no-wrap
msgid ""
" > testParens \"(()(())())\"\n"
@@ -4690,7 +4705,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:164
+#: text/chapter11.md:163
#, markdown-text, no-wrap
msgid ""
" > testParens \")\"\n"
@@ -4698,7 +4713,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:168
+#: text/chapter11.md:167
#, markdown-text, no-wrap
msgid ""
" > testParens \"(()()\"\n"
@@ -4707,7 +4722,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:170
+#: text/chapter11.md:169
#, markdown-text, no-wrap
msgid ""
" _Hint_: you may like to use the `toCharArray` function from the "
@@ -4716,13 +4731,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter11.md:171
+#: text/chapter11.md:170
#, markdown-text, no-wrap
msgid "The Reader Monad"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:174
+#: text/chapter11.md:173
#, markdown-text
msgid ""
"Another monad provided by the `transformers` package is the `Reader` "
@@ -4733,7 +4748,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:176
+#: text/chapter11.md:175
#, markdown-text
msgid ""
"The `Reader` type constructor takes two type arguments: a type `r` which "
@@ -4741,13 +4756,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:178
+#: text/chapter11.md:177
#, markdown-text
msgid "The `Control.Monad.Reader` module provides the following API:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:179
+#: text/chapter11.md:178
#, no-wrap
msgid ""
"ask :: forall r. Reader r r\n"
@@ -4755,7 +4770,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:185
+#: text/chapter11.md:184
#, markdown-text
msgid ""
"The `ask` action can be used to read the current configuration, and the "
@@ -4764,7 +4779,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:187
+#: text/chapter11.md:186
#, markdown-text
msgid ""
"For example, suppose we were developing an application controlled by "
@@ -4774,7 +4789,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:188
+#: text/chapter11.md:187
#, no-wrap
msgid ""
"hasPermission :: String -> Permissions -> Boolean\n"
@@ -4782,7 +4797,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:194
+#: text/chapter11.md:193
#, markdown-text
msgid ""
"Whenever we wanted to check if the user had a particular permission, we "
@@ -4791,7 +4806,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:195
+#: text/chapter11.md:194
#, no-wrap
msgid ""
"createUser :: Reader Permissions (Maybe User)\n"
@@ -4803,7 +4818,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:205
+#: text/chapter11.md:204
#, markdown-text
msgid ""
"To elevate the user's permissions, we might use the `local` action to modify "
@@ -4811,7 +4826,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:206
+#: text/chapter11.md:205
#, no-wrap
msgid ""
"runAsAdmin :: forall a. Reader Permissions a -> Reader Permissions a\n"
@@ -4819,7 +4834,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:212
+#: text/chapter11.md:211
#, markdown-text
msgid ""
"Then we could write a function to create a new user, even if the user did "
@@ -4827,7 +4842,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:213
+#: text/chapter11.md:212
#, no-wrap
msgid ""
"createUserAsAdmin :: Reader Permissions (Maybe User)\n"
@@ -4835,7 +4850,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:219
+#: text/chapter11.md:218
#, markdown-text
msgid ""
"To run a computation in the `Reader` monad, the `runReader` function can be "
@@ -4843,13 +4858,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:220
+#: text/chapter11.md:219
#, no-wrap
msgid "runReader :: forall r a. Reader r a -> r -> a\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:227
+#: text/chapter11.md:226
#, markdown-text, no-wrap
msgid ""
" In these exercises, we will use the `Reader` monad to build a small library "
@@ -4858,7 +4873,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:228
+#: text/chapter11.md:227
#, no-wrap
msgid ""
"type Level = Int\n"
@@ -4867,15 +4882,15 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:235
+#: text/chapter11.md:234
#, markdown-text
msgid ""
-"(Easy) Write a function `line` which renders a function at the current "
+"(Easy) Write a function `line` that renders a function at the current "
"indentation level. Your function should have the following type:"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:239
+#: text/chapter11.md:238
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -4884,7 +4899,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:242
+#: text/chapter11.md:241
#, markdown-text, no-wrap
msgid ""
" _Hint_: use the `ask` function to read the current indentation "
@@ -4893,7 +4908,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:246
+#: text/chapter11.md:245
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -4902,7 +4917,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:249
+#: text/chapter11.md:248
#, markdown-text, no-wrap
msgid ""
" which increases the indentation level for a block of code.\n"
@@ -4911,7 +4926,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:253
+#: text/chapter11.md:252
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -4920,7 +4935,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:256
+#: text/chapter11.md:255
#, markdown-text, no-wrap
msgid ""
" which concatenates a collection of documents, separating them with new "
@@ -4929,7 +4944,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:260
+#: text/chapter11.md:259
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -4938,21 +4953,21 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:262
+#: text/chapter11.md:261
#, markdown-text, no-wrap
msgid " which renders a document as a String.\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:264
+#: text/chapter11.md:263
#, markdown-text, no-wrap
msgid ""
-" You should now be able to use your library to write simple documents, as "
+" You should now be able to use your library to write simple documents as "
"follows:\n"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:265
+#: text/chapter11.md:264
#, no-wrap
msgid ""
" render $ cat\n"
@@ -4966,53 +4981,52 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter11.md:276
+#: text/chapter11.md:275
#, markdown-text, no-wrap
msgid "The Writer Monad"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:279
+#: text/chapter11.md:278
#, markdown-text
msgid ""
-"The `Writer` monad provides the ability to accumulate a secondary value in "
-"addition to the return value of a computation."
+"The `Writer` monad allows accumulating a secondary value in addition to the "
+"return value of a computation."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:281
+#: text/chapter11.md:280
#, markdown-text
msgid ""
"A common use case is to accumulate a log of type `String` or `Array String`, "
-"but the `Writer` monad is more general than this. It can actually be used to "
-"accumulate a value in any monoid, so it might be used to keep track of an "
-"integer total using the `Additive Int` monoid, or to track whether any of "
-"several intermediate `Boolean` values were true, using the `Disj Boolean` "
-"monoid."
+"but the `Writer` monad is more general than this. It can accumulate a value "
+"in any monoid, so it might be used to keep track of an integer total using "
+"the `Additive Int` monoid or to track whether any of several intermediate "
+"`Boolean` values were true using the `Disj Boolean` monoid."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:283
+#: text/chapter11.md:282
#, markdown-text
msgid ""
-"The `Writer` type constructor takes two type arguments: a type `w` which "
+"The `Writer` type constructor takes two type arguments: a type `w` that "
"should be an instance of the `Monoid` type class, and the return type `a`."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:285
+#: text/chapter11.md:284
#, markdown-text
msgid "The key element of the `Writer` API is the `tell` function:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:286
+#: text/chapter11.md:285
#, no-wrap
msgid "tell :: forall w a. Monoid w => w -> Writer w Unit\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:291
+#: text/chapter11.md:290
#, markdown-text
msgid ""
"The `tell` action appends the provided value to the current accumulated "
@@ -5020,16 +5034,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:293
+#: text/chapter11.md:292
#, markdown-text
msgid ""
-"As an example, let's add a log to an existing function by using the `Array "
+"As an example, let's add a log to an existing function using the `Array "
"String` monoid. Consider our previous implementation of the _greatest common "
"divisor_ function:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:294
+#: text/chapter11.md:293
#, no-wrap
msgid ""
"gcd :: Int -> Int -> Int\n"
@@ -5041,7 +5055,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:304
+#: text/chapter11.md:303
#, markdown-text
msgid ""
"We could add a log to this function by changing the return type to `Writer "
@@ -5049,7 +5063,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:305
+#: text/chapter11.md:304
#, no-wrap
msgid ""
"import Control.Monad.Writer\n"
@@ -5059,7 +5073,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:313
+#: text/chapter11.md:312
#, markdown-text
msgid ""
"We only have to change our function slightly to log the two inputs at each "
@@ -5067,7 +5081,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:314
+#: text/chapter11.md:313
#, no-wrap
msgid ""
" gcdLog n 0 = pure n\n"
@@ -5080,7 +5094,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:325
+#: text/chapter11.md:324
#, markdown-text
msgid ""
"We can run a computation in the `Writer` monad by using either of the "
@@ -5088,7 +5102,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:326
+#: text/chapter11.md:325
#, no-wrap
msgid ""
"execWriter :: forall w a. Writer w a -> w\n"
@@ -5096,7 +5110,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:332
+#: text/chapter11.md:331
#, markdown-text
msgid ""
"Just like in the case of the `State` monad, `execWriter` only returns the "
@@ -5104,13 +5118,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:334
+#: text/chapter11.md:333
#, markdown-text
msgid "We can test our modified function in PSCi:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:335
+#: text/chapter11.md:334
#, no-wrap
msgid ""
"> import Control.Monad.Writer\n"
@@ -5122,7 +5136,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:347
+#: text/chapter11.md:346
#, markdown-text
msgid ""
"(Medium) Rewrite the `sumArray` function above using the `Writer` monad and "
@@ -5130,16 +5144,16 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:347
+#: text/chapter11.md:346
#, markdown-text
msgid ""
"(Medium) The _Collatz_ function is defined on natural numbers `n` as `n / 2` "
-"when `n` is even, and `3 * n + 1` when `n` is odd. For example, the iterated "
+"when `n` is even and `3 * n + 1` when `n` is odd. For example, the iterated "
"Collatz sequence starting at `10` is as follows:"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:351
+#: text/chapter11.md:350
#, markdown-text, no-wrap
msgid ""
" ```text\n"
@@ -5148,7 +5162,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:353
+#: text/chapter11.md:352
#, markdown-text, no-wrap
msgid ""
" It is conjectured that the iterated Collatz sequence always reaches `1` "
@@ -5156,15 +5170,15 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:355
+#: text/chapter11.md:354
#, markdown-text, no-wrap
msgid ""
-" Write a function which uses recursion to calculate how many iterations "
+" Write a function that uses recursion to calculate how many iterations "
"of the Collatz function are required before the sequence reaches `1`.\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:357
+#: text/chapter11.md:356
#, markdown-text, no-wrap
msgid ""
" Modify your function to use the `Writer` monad to log each application "
@@ -5172,22 +5186,22 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter11.md:358
+#: text/chapter11.md:357
#, markdown-text, no-wrap
msgid "Monad Transformers"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:361
+#: text/chapter11.md:360
#, markdown-text
msgid ""
-"Each of the three monads above: `State`, `Reader` and `Writer`, are also "
+"Each of the three monads above: `State`, `Reader`, and `Writer`, are also "
"examples of so-called _monad transformers_. The equivalent monad "
-"transformers are called `StateT`, `ReaderT`, and `WriterT` respectively."
+"transformers are called `StateT`, `ReaderT`, and `WriterT`, respectively."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:363
+#: text/chapter11.md:362
#, markdown-text
msgid ""
"What is a monad transformer? Well, as we have seen, a monad augments "
@@ -5203,25 +5217,25 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:365
+#: text/chapter11.md:364
#, markdown-text
msgid ""
-"Note that we have already seen that the `Effect` monad provides a partial "
-"solution to this problem. Monad transformers provide another solution, and "
-"each approach has its own benefits and limitations."
+"Note that we have already seen that the `Effect` monad partially solves this "
+"problem. Monad transformers provide another solution, and each approach has "
+"its own benefits and limitations."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:367
+#: text/chapter11.md:366
#, markdown-text
msgid ""
-"A monad transformer is a type constructor which is parameterized not only by "
-"a type, but by another type constructor. It takes one monad and turns it "
-"into another monad, adding its own variety of side-effects."
+"A monad transformer is a type constructor parameterized by a type and "
+"another type constructor. It takes one monad and turns it into another "
+"monad, adding its own variety of side-effects."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:369
+#: text/chapter11.md:368
#, markdown-text
msgid ""
"Let's see an example. The monad transformer version of the `State` monad is "
@@ -5230,7 +5244,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:370
+#: text/chapter11.md:369
#, no-wrap
msgid ""
"> import Control.Monad.State.Trans\n"
@@ -5239,7 +5253,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:377
+#: text/chapter11.md:376
#, markdown-text
msgid ""
"This looks quite confusing, but we can apply `StateT` one argument at a time "
@@ -5247,7 +5261,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:379
+#: text/chapter11.md:378
#, markdown-text
msgid ""
"The first type argument is the type of the state we wish to use, as was the "
@@ -5255,7 +5269,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:380
+#: text/chapter11.md:379
#, no-wrap
msgid ""
"> :kind StateT String\n"
@@ -5263,7 +5277,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:386
+#: text/chapter11.md:385
#, markdown-text, no-wrap
msgid ""
"The next argument is a type constructor of kind `Type -> Type`. It "
@@ -5273,7 +5287,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:387
+#: text/chapter11.md:386
#, no-wrap
msgid ""
"> :kind StateT String (Either String)\n"
@@ -5281,7 +5295,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:393
+#: text/chapter11.md:392
#, markdown-text
msgid ""
"We are left with a type constructor. The final argument represents the "
@@ -5289,7 +5303,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:394
+#: text/chapter11.md:393
#, no-wrap
msgid ""
"> :kind StateT String (Either String) Number\n"
@@ -5297,34 +5311,34 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:400
+#: text/chapter11.md:399
#, markdown-text
msgid ""
-"Finally we are left with something of kind `Type`, which means we can try to "
-"find values of this type."
+"Finally, we are left with something of kind `Type`, which means we can try "
+"to find values of this type."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:402
+#: text/chapter11.md:401
#, markdown-text
msgid ""
-"The monad we have constructed - `StateT String (Either String)` - represents "
-"computations which can fail with an error, and which can use mutable state."
+"The monad we have constructed – `StateT String (Either String)` – represents "
+"computations that can fail with an error and use mutable state."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:404
+#: text/chapter11.md:403
#, markdown-text
msgid ""
"We can use the actions of the outer `StateT String` monad (`get`, `put`, and "
-"`modify`) directly, but in order to use the effects of the wrapped monad "
-"(`Either String`), we need to \"lift\" them over the monad transformer. The "
+"`modify`) directly, but to use the effects of the wrapped monad (`Either "
+"String`), we need to \"lift\" them over the monad transformer. The "
"`Control.Monad.Trans` module defines the `MonadTrans` type class, which "
"captures those type constructors which are monad transformers, as follows:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:405
+#: text/chapter11.md:404
#, no-wrap
msgid ""
"class MonadTrans t where\n"
@@ -5332,12 +5346,12 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:411
+#: text/chapter11.md:410
#, markdown-text
msgid ""
"This class contains a single member, `lift`, which takes computations in any "
"underlying monad `m` and lifts them into the wrapped monad `t m`. In our "
-"case, the type constructor `t` is `StateT String`, and `m` is the `Either "
+"case, the type constructor `t` is `StateT String`, `m` is the `Either "
"String` monad, so `lift` provides a way to lift computations of type `Either "
"String a` to computations of type `StateT String (Either String) a`. This "
"means that we can use the effects of `StateT String` and `Either String` "
@@ -5346,15 +5360,15 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:413
+#: text/chapter11.md:412
#, markdown-text
msgid ""
-"For example, the following computation reads the underlying state, and then "
+"For example, the following computation reads the underlying state and then "
"throws an error if the state is the empty string:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:414
+#: text/chapter11.md:413
#, no-wrap
msgid ""
"import Data.String (drop, take)\n"
@@ -5370,22 +5384,22 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:428
+#: text/chapter11.md:427
#, markdown-text
msgid ""
"If the state is not empty, the computation uses `put` to update the state to "
-"`drop 1 s` (that is, `s` with the first character removed), and returns "
-"`take 1 s` (that is, the first character of `s`)."
+"`drop 1 s` (that is, `s` with the first character removed) and returns `take "
+"1 s` (that is, the first character of `s`)."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:430
+#: text/chapter11.md:429
#, markdown-text
msgid "Let's try this in PSCi:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:431
+#: text/chapter11.md:430
#, no-wrap
msgid ""
"> runStateT split \"test\"\n"
@@ -5396,10 +5410,10 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:440
+#: text/chapter11.md:439
#, markdown-text
msgid ""
-"This is not very remarkable, since we could have implemented this without "
+"This is not very remarkable since we could have implemented this without "
"`StateT`. However, since we are working in a monad, we can use do notation "
"or applicative combinators to build larger computations from smaller "
"ones. For example, we can apply `split` twice to read the first two "
@@ -5407,7 +5421,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:441
+#: text/chapter11.md:440
#, no-wrap
msgid ""
"> runStateT ((<>) <$> split <*> split) \"test\"\n"
@@ -5415,34 +5429,32 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:447
+#: text/chapter11.md:446
#, markdown-text
msgid ""
"We can use the `split` function with a handful of other actions to build a "
"basic parsing library. In fact, this is the approach taken by the `parsing` "
-"library. This is the power of monad transformers - we can create "
-"custom-built monads for a variety of problems, choosing the side-effects "
-"that we need, and keeping the expressiveness of do notation and applicative "
-"combinators."
+"library. This is the power of monad transformers – we can create "
+"custom-built monads for various problems, choose the side-effects we need, "
+"and keep the expressiveness of do notation and applicative combinators."
msgstr ""
#. type: Title ##
-#: text/chapter11.md:448
+#: text/chapter11.md:447
#, markdown-text, no-wrap
msgid "The ExceptT Monad Transformer"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:451
+#: text/chapter11.md:450
#, markdown-text
msgid ""
"The `transformers` package also defines the `ExceptT e` monad transformer, "
-"which is the transformer corresponding to the `Either e` monad. It provides "
-"the following API:"
+"corresponding to the `Either e` monad. It provides the following API:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:452
+#: text/chapter11.md:451
#, no-wrap
msgid ""
"class MonadError e m where\n"
@@ -5455,18 +5467,18 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:463
+#: text/chapter11.md:462
#, markdown-text
msgid ""
-"The `MonadError` class captures those monads which support throwing and "
-"catching of errors of some type `e`, and an instance is provided for the "
-"`ExceptT e` monad transformer. The `throwError` action can be used to "
-"indicate failure, just like `Left` in the `Either e` monad. The `catchError` "
-"action allows us to continue after an error is thrown using `throwError`."
+"The `MonadError` class captures those monads that support throwing and "
+"catching errors of some type `e`, and an instance is provided for the "
+"`ExceptT e` monad transformer. The `throwError` action can indicate failure, "
+"like `Left` in the `Either e` monad. The `catchError` action allows us to "
+"continue after an error is thrown using `throwError`."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:465
+#: text/chapter11.md:464
#, markdown-text
msgid ""
"The `runExceptT` handler is used to run a computation of type `ExceptT e m "
@@ -5474,7 +5486,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:467
+#: text/chapter11.md:466
#, markdown-text
msgid ""
"This API is similar to that provided by the `exceptions` package and the "
@@ -5482,7 +5494,7 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter11.md:470
+#: text/chapter11.md:469
#, markdown-text
msgid ""
"`Exception` uses actual JavaScript exceptions, whereas `ExceptT` models "
@@ -5490,7 +5502,7 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter11.md:470
+#: text/chapter11.md:469
#, markdown-text
msgid ""
"The `Exception` effect only supports exceptions of one type, namely "
@@ -5499,7 +5511,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:472
+#: text/chapter11.md:471
#, markdown-text
msgid ""
"Let's try out `ExceptT` by using it to wrap the `Writer` monad. Again, we "
@@ -5508,7 +5520,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:473
+#: text/chapter11.md:472
#, no-wrap
msgid ""
"import Control.Monad.Except\n"
@@ -5523,7 +5535,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:486
+#: text/chapter11.md:485
#, markdown-text
msgid ""
"If we test this function in PSCi, we can see how the two effects of "
@@ -5534,7 +5546,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:487
+#: text/chapter11.md:486
#, no-wrap
msgid ""
"> runWriter $ runExceptT writerAndExceptT\n"
@@ -5542,56 +5554,56 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:493
+#: text/chapter11.md:492
#, markdown-text
msgid ""
-"Note that only those log messages which were written before the error was "
-"thrown actually get appended to the log."
+"Note that only those log messages that were written before the error was "
+"thrown get appended to the log."
msgstr ""
#. type: Title ##
-#: text/chapter11.md:494
+#: text/chapter11.md:493
#, markdown-text, no-wrap
msgid "Monad Transformer Stacks"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:497
+#: text/chapter11.md:496
#, markdown-text
msgid ""
"As we have seen, monad transformers can be used to build new monads on top "
"of existing monads. For some monad transformer `t1` and some monad `m`, the "
-"application `t1 m` is also a monad. That means that we can apply a _second_ "
-"monad transformer `t2` to the result `t1 m` to construct a third monad `t2 "
-"(t1 m)`. In this way, we can construct a _stack_ of monad transformers, "
-"which combine the side-effects provided by their constituent monads."
+"application `t1 m` is also a monad. That means we can apply a _second_ monad "
+"transformer `t2` to the result `t1 m` to construct a third monad `t2 (t1 "
+"m)`. In this way, we can construct a _stack_ of monad transformers, which "
+"combine the side-effects provided by their constituent monads."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:499
+#: text/chapter11.md:498
#, markdown-text
msgid ""
"In practice, the underlying monad `m` is either the `Effect` monad, if "
"native side-effects are required, or the `Identity` monad, defined in the "
"`Data.Identity` module. The `Identity` monad adds no new side-effects, so "
"transforming the `Identity` monad only provides the effects of the monad "
-"transformer. In fact, the `State`, `Reader` and `Writer` monads are "
-"implemented by transforming the `Identity` monad with `StateT`, `ReaderT` "
-"and `WriterT` respectively."
+"transformer. The `State`, `Reader`, and `Writer` monads are implemented by "
+"transforming the `Identity` monad with `StateT`, `ReaderT`, and `WriterT`, "
+"respectively."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:501
+#: text/chapter11.md:500
#, markdown-text
msgid ""
"Let's see an example in which three side effects are combined. We will use "
-"the `StateT`, `WriterT` and `ExceptT` effects, with the `Identity` monad on "
+"the `StateT`, `WriterT`, and `ExceptT` effects, with the `Identity` monad on "
"the bottom of the stack. This monad transformer stack will provide the side "
"effects of mutable state, accumulating a log, and pure errors."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:503
+#: text/chapter11.md:502
#, markdown-text
msgid ""
"We can use this monad transformer stack to reproduce our `split` action with "
@@ -5599,7 +5611,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:504
+#: text/chapter11.md:503
#, no-wrap
msgid ""
"type Errors = Array String\n"
@@ -5620,7 +5632,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:523
+#: text/chapter11.md:522
#, markdown-text
msgid ""
"If we test this computation in PSCi, we see that the state is appended to "
@@ -5628,18 +5640,18 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:525
+#: text/chapter11.md:524
#, markdown-text
msgid ""
"Note that we have to remove the side-effects in the order in which they "
-"appear in the monad transformer stack: first we use `runStateT` to remove "
+"appear in the monad transformer stack: first, we use `runStateT` to remove "
"the `StateT` type constructor, then `runWriterT`, then "
"`runExceptT`. Finally, we run the computation in the `Identity` monad by "
"using `unwrap`."
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:526
+#: text/chapter11.md:525
#, no-wrap
msgid ""
"> runParser p s = unwrap $ runExceptT $ runWriterT $ runStateT p s\n"
@@ -5653,7 +5665,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:537
+#: text/chapter11.md:536
#, markdown-text
msgid ""
"However, if the parse is unsuccessful because the state is empty, then no "
@@ -5661,7 +5673,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:538
+#: text/chapter11.md:537
#, no-wrap
msgid ""
"> runParser split \"\"\n"
@@ -5669,31 +5681,31 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:544
+#: text/chapter11.md:543
#, markdown-text
msgid ""
-"This is because of the way in which the side-effects provided by the "
-"`ExceptT` monad transformer interact with the side-effects provided by the "
-"`WriterT` monad transformer. We can address this by changing the order in "
-"which the monad transformer stack is composed. If we move the `ExceptT` "
-"transformer to the top of the stack, then the log will contain all messages "
-"written up until the first error, as we saw earlier when we transformed "
-"`Writer` with `ExceptT`."
+"This is because of how the side-effects provided by the `ExceptT` monad "
+"transformer interact with the side-effects provided by the `WriterT` monad "
+"transformer. We can address this by changing the order in which the monad "
+"transformer stack is composed. If we move the `ExceptT` transformer to the "
+"top of the stack, then the log will contain all messages written up until "
+"the first error, as we saw earlier when we transformed `Writer` with "
+"`ExceptT`."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:546
+#: text/chapter11.md:545
#, markdown-text
msgid ""
"One problem with this code is that we have to use the `lift` function "
-"multiple times to lift computations over multiple monad transformers: for "
+"multiple times to lift computations over multiple monad transformers; for "
"example, the call to `throwError` has to be lifted twice, once over "
"`WriterT` and a second time over `StateT`. This is fine for small monad "
-"transformer stacks, but quickly becomes inconvenient."
+"transformer stacks but quickly becomes inconvenient."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:548
+#: text/chapter11.md:547
#, markdown-text
msgid ""
"Fortunately, as we will see, we can use the automatic code generation "
@@ -5702,7 +5714,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:553
+#: text/chapter11.md:552
#, markdown-text
msgid ""
"(Easy) Use the `ExceptT` monad transformer over the `Identity` functor to "
@@ -5711,13 +5723,13 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:553
+#: text/chapter11.md:552
#, markdown-text
msgid "(Medium) Write a parser"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:557
+#: text/chapter11.md:556
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -5726,21 +5738,21 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:559
+#: text/chapter11.md:558
#, markdown-text, no-wrap
msgid ""
-" which matches a string as a prefix of the current state, or fails with "
+" which matches a string as a prefix of the current state or fails with "
"an error message.\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:561
+#: text/chapter11.md:560
#, markdown-text, no-wrap
msgid " Your parser should work as follows:\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:566
+#: text/chapter11.md:565
#, markdown-text, no-wrap
msgid ""
" ```text\n"
@@ -5750,18 +5762,18 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:569
+#: text/chapter11.md:568
#, markdown-text, no-wrap
msgid ""
" _Hint_: you can use the implementation of `split` as a starting "
"point. You might find the `stripPrefix` function useful.\n"
" 1. (Difficult) Use the `ReaderT` and `WriterT` monad transformers to "
-"reimplement the document printing library which we wrote earlier using the "
+"reimplement the document printing library, which we wrote earlier using the "
"`Reader` monad.\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:571
+#: text/chapter11.md:570
#, markdown-text, no-wrap
msgid ""
" Instead of using `line` to emit strings and `cat` to concatenate "
@@ -5771,13 +5783,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter11.md:572
+#: text/chapter11.md:571
#, markdown-text, no-wrap
msgid "Type Classes to the Rescue!"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:575
+#: text/chapter11.md:574
#, markdown-text
msgid ""
"When we looked at the `State` monad at the start of this chapter, I gave the "
@@ -5785,7 +5797,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:576
+#: text/chapter11.md:575
#, no-wrap
msgid ""
"get :: forall s. State s s\n"
@@ -5794,7 +5806,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:583
+#: text/chapter11.md:582
#, markdown-text
msgid ""
"In reality, the types given in the `Control.Monad.State.Class` module are "
@@ -5802,7 +5814,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:584
+#: text/chapter11.md:583
#, no-wrap
msgid ""
"get :: forall m s. MonadState s m => m s\n"
@@ -5811,7 +5823,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:591
+#: text/chapter11.md:590
#, markdown-text
msgid ""
"The `Control.Monad.State.Class` module defines the `MonadState` "
@@ -5822,71 +5834,71 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:593
+#: text/chapter11.md:592
#, markdown-text
msgid ""
"In particular, there are instances of `MonadState` for the `WriterT`, "
-"`ReaderT` and `ExceptT` monad transformers, provided in the `transformers` "
-"package. Each of these monad transformers has an instance for `MonadState` "
-"whenever the underlying `Monad` does. In practice, this means that as long "
-"as `StateT` appears _somewhere_ in the monad transformer stack, and "
-"everything above `StateT` is an instance of `MonadState`, then we are free "
-"to use `get`, `put` and `modify` directly, without the need to use `lift`."
+"`ReaderT`, and `ExceptT` monad transformers provided in the `transformers` "
+"package. Each has an instance for `MonadState` whenever the underlying "
+"`Monad` does. In practice, this means that as long as `StateT` appears "
+"_somewhere_ in the monad transformer stack, and everything above `StateT` is "
+"an instance of `MonadState`, then we are free to use `get`, `put`, and "
+"`modify` directly without the need to use `lift`."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:595
+#: text/chapter11.md:594
#, markdown-text
msgid ""
"Indeed, the same is true of the actions we covered for the `ReaderT`, "
"`WriterT`, and `ExceptT` transformers. `transformers` defines a type class "
-"for each of the major transformers, allowing us to abstract over monads "
-"which support their operations."
+"for each of the major transformers, allowing us to abstract over monads that "
+"support their operations."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:597
+#: text/chapter11.md:596
#, markdown-text
msgid ""
"In the case of the `split` function above, the monad stack we constructed is "
-"an instance of each of the `MonadState`, `MonadWriter` and `MonadError` type "
-"classes. This means that we don't need to call `lift` at all! We can just "
-"use the actions `get`, `put`, `tell` and `throwError` as if they were "
+"an instance of each of the `MonadState`, `MonadWriter`, and `MonadError` "
+"type classes. This means that we don't need to call `lift` at all! We can "
+"just use the actions `get`, `put`, `tell`, and `throwError` as if they were "
"defined on the monad stack itself:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:598
+#: text/chapter11.md:597
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Split.purs:split}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:603
+#: text/chapter11.md:602
#, markdown-text
msgid ""
-"This computation really looks like we have extended our programming language "
-"to support the three new side-effects of mutable state, logging and error "
+"This computation looks like we have extended our programming language to "
+"support the three new side-effects of mutable state, logging, and error "
"handling. However, everything is still implemented using pure functions and "
"immutable data under the hood."
msgstr ""
#. type: Title ##
-#: text/chapter11.md:604
+#: text/chapter11.md:603
#, markdown-text, no-wrap
msgid "Alternatives"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:607
+#: text/chapter11.md:606
#, markdown-text
msgid ""
"The `control` package defines a number of abstractions for working with "
-"computations which can fail. One of these is the `Alternative` type class:"
+"computations that can fail. One of these is the `Alternative` type class:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:608
+#: text/chapter11.md:607
#, no-wrap
msgid ""
"class Functor f <= Alt f where\n"
@@ -5899,17 +5911,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:619
+#: text/chapter11.md:618
#, markdown-text, no-wrap
msgid ""
"`Alternative` provides two new combinators: the `empty` value, which "
"provides a prototype for a failing computation, and the `alt` function (and "
-"its alias, `<|>`) which provides the ability to fall back to an "
+"its alias, `<|>`), which provides the ability to fall back to an "
"_alternative_ computation in the case of an error.\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:621
+#: text/chapter11.md:620
#, markdown-text
msgid ""
"The `Data.Array` module provides two useful functions for working with type "
@@ -5917,7 +5929,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:622
+#: text/chapter11.md:621
#, no-wrap
msgid ""
"many :: forall f a. Alternative f => Lazy (f (Array a)) => f a -> f (Array "
@@ -5927,22 +5939,22 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:628
+#: text/chapter11.md:627
#, markdown-text
msgid "There is also an equivalent `many` and `some` for `Data.List`"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:630
+#: text/chapter11.md:629
#, markdown-text
msgid ""
"The `many` combinator uses the `Alternative` type class to repeatedly run a "
-"computation _zero-or-more_ times. The `some` combinator is similar, but "
+"computation _zero-or-more_ times. The `some` combinator is similar but "
"requires at least the first computation to succeed."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:632
+#: text/chapter11.md:631
#, markdown-text
msgid ""
"In the case of our `Parser` monad transformer stack, there is an instance of "
@@ -5953,7 +5965,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:633
+#: text/chapter11.md:632
#, no-wrap
msgid ""
"> import Data.Array (many)\n"
@@ -5968,7 +5980,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:646
+#: text/chapter11.md:645
#, markdown-text
msgid ""
"Here, the input string `\"test\"` has been repeatedly split to return an "
@@ -5977,13 +5989,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter11.md:647
+#: text/chapter11.md:646
#, markdown-text, no-wrap
msgid "Monad Comprehensions"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:650
+#: text/chapter11.md:649
#, markdown-text
msgid ""
"The `Control.MonadPlus` module defines a subclass of the `Alternative` type "
@@ -5992,35 +6004,35 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:651
+#: text/chapter11.md:650
#, no-wrap
msgid "class (Monad m, Alternative m) <= MonadPlus m\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:656
+#: text/chapter11.md:655
#, markdown-text
msgid "In particular, our `Parser` monad is an instance of `MonadPlus`."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:658
+#: text/chapter11.md:657
#, markdown-text
msgid ""
"When we covered array comprehensions earlier in the book, we introduced the "
"`guard` function, which could be used to filter out unwanted results. In "
-"fact, the `guard` function is more general, and can be used for any monad "
+"fact, the `guard` function is more general and can be used for any monad, "
"which is an instance of `MonadPlus`:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:659
+#: text/chapter11.md:658
#, no-wrap
msgid "guard :: forall m. Alternative m => Boolean -> m Unit\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:664
+#: text/chapter11.md:663
#, markdown-text, no-wrap
msgid ""
"The `<|>` operator allows us to backtrack in case of failure. To see how "
@@ -6029,29 +6041,29 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:665
+#: text/chapter11.md:664
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Split.purs:upper}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:670
+#: text/chapter11.md:669
#, markdown-text
msgid ""
"Here, we use a `guard` to fail if the string is not upper case. Note that "
-"this code looks very similar to the array comprehensions we saw earlier - "
+"this code looks very similar to the array comprehensions we saw earlier – "
"using `MonadPlus` in this way, we sometimes refer to constructing _monad "
"comprehensions_."
msgstr ""
#. type: Title ##
-#: text/chapter11.md:671
+#: text/chapter11.md:670
#, markdown-text, no-wrap
msgid "Backtracking"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:674
+#: text/chapter11.md:673
#, markdown-text, no-wrap
msgid ""
"We can use the `<|>` operator to backtrack to another alternative in case of "
@@ -6060,13 +6072,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:675
+#: text/chapter11.md:674
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Split.purs:lower}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:680
+#: text/chapter11.md:679
#, markdown-text
msgid ""
"With this, we can define a parser which eagerly matches many upper case "
@@ -6075,19 +6087,19 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:681
+#: text/chapter11.md:680
#, no-wrap
msgid "> upperOrLower = some upper <|> some lower\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:686
+#: text/chapter11.md:685
#, markdown-text
msgid "This parser will match characters until the case changes:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:687
+#: text/chapter11.md:686
#, no-wrap
msgid ""
"> runParser upperOrLower \"abcDEF\"\n"
@@ -6099,7 +6111,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:697
+#: text/chapter11.md:696
#, markdown-text
msgid ""
"We can even use `many` to fully split a string into its lower and upper case "
@@ -6107,7 +6119,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter11.md:698
+#: text/chapter11.md:697
#, no-wrap
msgid ""
"> components = many upperOrLower\n"
@@ -6127,49 +6139,48 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:715
+#: text/chapter11.md:714
#, markdown-text
msgid ""
"Again, this illustrates the power of reusability that monad transformers "
-"bring - we were able to write a backtracking parser in a declarative style "
+"bring – we were able to write a backtracking parser in a declarative style "
"with only a few lines of code, by reusing standard abstractions!"
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:722
+#: text/chapter11.md:721
#, markdown-text
msgid ""
"(Easy) Remove the calls to the `lift` function from your implementation of "
"the `string` parser. Verify that the new implementation type checks, and "
-"convince yourself that it should."
+"convince yourself it should."
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:722
+#: text/chapter11.md:721
#, markdown-text
msgid ""
"(Medium) Use your `string` parser with the `some` combinator to write a "
-"parser `asFollowedByBs` which recognizes strings consisting of several "
-"copies of the string `\"a\"` followed by several copies of the string "
-"`\"b\"`."
+"parser `asFollowedByBs` that recognizes strings consisting of several copies "
+"of the string `\"a\"` followed by several copies of the string `\"b\"`."
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:722
+#: text/chapter11.md:721
#, markdown-text
msgid ""
-"(Medium) Use the `<|>` operator to write a parser `asOrBs` which recognizes "
+"(Medium) Use the `<|>` operator to write a parser `asOrBs` that recognizes "
"strings of the letters `a` or `b` in any order."
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:722
+#: text/chapter11.md:721
#, markdown-text
msgid "(Difficult) The `Parser` monad might also be defined as follows:"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:726
+#: text/chapter11.md:725
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -6178,30 +6189,30 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:728
+#: text/chapter11.md:727
#, markdown-text, no-wrap
msgid " What effect does this change have on our parsing functions?\n"
msgstr ""
#. type: Title ##
-#: text/chapter11.md:729
+#: text/chapter11.md:728
#, markdown-text, no-wrap
msgid "The RWS Monad"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:732
+#: text/chapter11.md:731
#, markdown-text
msgid ""
"One particular combination of monad transformers is so common that it is "
"provided as a single monad transformer in the `transformers` package. The "
-"`Reader`, `Writer` and `State` monads are combined into the "
-"_reader-writer-state_ monad, or more simply the `RWS` monad. This monad has "
-"a corresponding monad transformer called the `RWST` monad transformer."
+"`Reader`, `Writer`, and `State` monads are combined into the "
+"_reader-writer-state_ or simply `RWS` monad. This monad has a corresponding "
+"monad transformer called the `RWST` monad transformer."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:734
+#: text/chapter11.md:733
#, markdown-text
msgid ""
"We will use the `RWS` monad to model the game logic for our text adventure "
@@ -6209,7 +6220,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:736
+#: text/chapter11.md:735
#, markdown-text
msgid ""
"The `RWS` monad is defined in terms of three type parameters (in addition to "
@@ -6217,31 +6228,30 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:737
+#: text/chapter11.md:736
#, no-wrap
msgid "type RWS r w s = RWST r w s Identity\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:742
+#: text/chapter11.md:741
#, markdown-text
msgid ""
-"Notice that the `RWS` monad is defined in terms of its own monad "
-"transformer, by setting the base monad to `Identity` which provides no "
-"side-effects."
+"Notice that the `RWS` monad is defined as its own monad transformer by "
+"setting the base monad to `Identity`, which provides no side-effects."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:744
+#: text/chapter11.md:743
#, markdown-text
msgid ""
"The first type parameter, `r`, represents the global configuration type. The "
-"second, `w`, represents the monoid which we will use to accumulate a log, "
-"and the third, `s` is the type of our mutable state."
+"second, `w`, represents the monoid, which we will use to accumulate a log, "
+"and the third, `s`, is the type of our mutable state."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:746
+#: text/chapter11.md:745
#, markdown-text
msgid ""
"In the case of our game, our global configuration is defined in a type "
@@ -6249,22 +6259,22 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:747
+#: text/chapter11.md:746
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Data/GameEnvironment.purs:env}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:752
+#: text/chapter11.md:751
#, markdown-text
msgid ""
-"It defines the player name, and a flag which indicates whether or not the "
-"game is running in debug mode. These options will be set from the command "
-"line when we come to run our monad transformer."
+"It defines the player name and a flag that indicates whether or not the game "
+"is running in debug mode. These options will be set from the command line "
+"when we come to run our monad transformer."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:754
+#: text/chapter11.md:753
#, markdown-text
msgid ""
"The mutable state is defined in a type called `GameState` in the "
@@ -6272,7 +6282,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:755
+#: text/chapter11.md:754
#, no-wrap
msgid ""
"{{#include ../exercises/chapter11/src/Data/GameState.purs:imports}}\n"
@@ -6281,7 +6291,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:762
+#: text/chapter11.md:761
#, markdown-text
msgid ""
"The `Coords` data type represents points on a two-dimensional grid, and the "
@@ -6289,17 +6299,17 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:763
+#: text/chapter11.md:762
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Data/GameItem.purs:GameItem}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:768
+#: text/chapter11.md:767
#, markdown-text
msgid ""
"The `GameState` type uses two new data structures: `Map` and `Set`, which "
-"represent sorted maps and sorted sets respectively. The `items` property is "
+"represent sorted maps and sorted sets, respectively. The `items` property is "
"a mapping from coordinates of the game grid to sets of game items at that "
"location. The `player` property stores the current coordinates of the "
"player, and the `inventory` property stores a set of game items currently "
@@ -6307,16 +6317,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:770
+#: text/chapter11.md:769
#, markdown-text
msgid ""
-"The `Map` and `Set` data structures are sorted by their keys, can be used "
-"with any key type in the `Ord` type class. This means that the keys in our "
-"data structures should be totally ordered."
+"The `Map` and `Set` data structures are sorted by their keys, and can be "
+"used with any key type in the `Ord` type class. This means that the keys in "
+"our data structures should be totally ordered."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:772
+#: text/chapter11.md:771
#, markdown-text
msgid ""
"We will see how the `Map` and `Set` structures are used as we write the "
@@ -6324,7 +6334,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:774
+#: text/chapter11.md:773
#, markdown-text
msgid ""
"For our log, we will use the `List String` monoid. We can define a type "
@@ -6332,30 +6342,30 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:775
+#: text/chapter11.md:774
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Game.purs:Game}}\n"
msgstr ""
#. type: Title ##
-#: text/chapter11.md:779
+#: text/chapter11.md:778
#, markdown-text, no-wrap
msgid "Implementing Game Logic"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:782
+#: text/chapter11.md:781
#, markdown-text
msgid ""
-"Our game is going to be built from simple actions defined in the `Game` "
-"monad, by reusing the actions from the `Reader`, `Writer` and `State` "
-"monads. At the top level of our application, we will run the pure "
-"computations in the `Game` monad, and use the `Effect` monad to turn the "
-"results into observable side-effects, such as printing text to the console."
+"Our game will be built from simple actions defined in the `Game` monad by "
+"reusing the actions from the `Reader`, `Writer`, and `State` monads. At the "
+"top level of our application, we will run the pure computations in the "
+"`Game` monad and use the `Effect` monad to turn the results into observable "
+"side-effects, such as printing text to the console."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:784
+#: text/chapter11.md:783
#, markdown-text
msgid ""
"One of the simplest actions in our game is the `has` action. This action "
@@ -6364,23 +6374,23 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:785
+#: text/chapter11.md:784
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Game.purs:has}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:790
+#: text/chapter11.md:789
#, markdown-text
msgid ""
"This function uses the `get` action defined in the `MonadState` type class "
-"to read the current game state, and then uses the `member` function defined "
+"to read the current game state and then uses the `member` function defined "
"in `Data.Set` to test whether the specified `GameItem` appears in the `Set` "
"of inventory items."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:792
+#: text/chapter11.md:791
#, markdown-text
msgid ""
"Another action is the `pickUp` action. It adds a game item to the player's "
@@ -6390,13 +6400,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:793
+#: text/chapter11.md:792
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Game.purs:pickup_start}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:798
+#: text/chapter11.md:797
#, markdown-text
msgid ""
"Next, `pickUp` looks up the set of items in the current room. It does this "
@@ -6404,23 +6414,23 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:799
+#: text/chapter11.md:798
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Game.purs:pickup_case}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:804
+#: text/chapter11.md:803
#, markdown-text
msgid ""
"The `lookup` function returns an optional result indicated by the `Maybe` "
"type constructor. If the key does not appear in the map, the `lookup` "
-"function returns `Nothing`, otherwise it returns the corresponding value in "
+"function returns `Nothing`; otherwise, it returns the corresponding value in "
"the `Just` constructor."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:806
+#: text/chapter11.md:805
#, markdown-text
msgid ""
"We are interested in the case where the corresponding item set contains the "
@@ -6428,62 +6438,62 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:807
+#: text/chapter11.md:806
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Game.purs:pickup_Just}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:812
+#: text/chapter11.md:811
#, markdown-text
msgid ""
-"In this case, we can use `put` to update the game state, and `tell` to add a "
+"In this case, we can use `put` to update the game state and `tell` to add a "
"message to the log:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:813
+#: text/chapter11.md:812
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Game.purs:pickup_body}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:818
+#: text/chapter11.md:817
#, markdown-text
msgid ""
-"Note that there is no need to `lift` either of the two computations here, "
+"Note that there is no need to `lift` either of the two computations here "
"because there are appropriate instances for both `MonadState` and "
"`MonadWriter` for our `Game` monad transformer stack."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:820
+#: text/chapter11.md:819
#, markdown-text
msgid ""
"The argument to `put` uses a record update to modify the game state's "
-"`items` and `inventory` fields. We use the `update` function from `Data.Map` "
-"which modifies a value at a particular key. In this case, we modify the set "
-"of items at the player's current location, using the `delete` function to "
-"remove the specified item from the set. `inventory` is also updated, using "
-"`insert` to add the new item to the player's inventory set."
+"`items` and `inventory` fields. We use the `update` function from "
+"`Data.Map`, which modifies a value at a particular key. In this case, we "
+"modify the set of items at the player's current location, using the `delete` "
+"function to remove the specified item from the set. `inventory` is also "
+"updated, using `insert` to add the new item to the player's inventory set."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:822
+#: text/chapter11.md:821
#, markdown-text
msgid ""
-"Finally, the `pickUp` function handles the remaining cases, by notifying the "
+"Finally, the `pickUp` function handles the remaining cases by notifying the "
"user using `tell`:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:823
+#: text/chapter11.md:822
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Game.purs:pickup_err}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:828
+#: text/chapter11.md:827
#, markdown-text
msgid ""
"As an example of using the `Reader` monad, we can look at the code for the "
@@ -6492,54 +6502,54 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:829
+#: text/chapter11.md:828
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Game.purs:debug}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:834
+#: text/chapter11.md:833
#, markdown-text
msgid ""
"Here, we use the `ask` action to read the game configuration. Again, note "
"that we don't need to `lift` any computation, and we can use actions defined "
-"in the `MonadState`, `MonadReader` and `MonadWriter` type classes in the "
+"in the `MonadState`, `MonadReader`, and `MonadWriter` type classes in the "
"same do notation block."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:836
+#: text/chapter11.md:835
#, markdown-text
msgid ""
-"If the `debugMode` flag is set, then the `tell` action is used to write the "
-"state to the log. Otherwise, an error message is added."
+"If the `debugMode` flag is set, the `tell` action is used to write the state "
+"to the log. Otherwise, an error message is added."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:838
+#: text/chapter11.md:837
#, markdown-text
msgid ""
"The remainder of the `Game` module defines a set of similar actions, each "
-"using only the actions defined by the `MonadState`, `MonadReader` and "
+"using only the actions defined by the `MonadState`, `MonadReader`, and "
"`MonadWriter` type classes."
msgstr ""
#. type: Title ##
-#: text/chapter11.md:839
+#: text/chapter11.md:838
#, markdown-text, no-wrap
msgid "Running the Computation"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:842
+#: text/chapter11.md:841
#, markdown-text
msgid ""
"Since our game logic runs in the `RWS` monad, it is necessary to run the "
-"computation in order to respond to the user's commands."
+"computation to respond to the user's commands."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:844
+#: text/chapter11.md:843
#, markdown-text
msgid ""
"The front-end of our game is built using two packages: `optparse`, which "
@@ -6549,7 +6559,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:846
+#: text/chapter11.md:845
#, markdown-text
msgid ""
"The interface to our game logic is provided by the function `game` in the "
@@ -6557,21 +6567,21 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:847
+#: text/chapter11.md:846
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Game.purs:game_sig}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:852
+#: text/chapter11.md:851
#, markdown-text
msgid ""
"To run this computation, we pass a list of words entered by the user as an "
-"array of strings, and run the resulting `RWS` computation using `runRWS`:"
+"array of strings and run the resulting `RWS` computation using `runRWS`:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:853
+#: text/chapter11.md:852
#, no-wrap
msgid ""
"data RWSResult state result writer = RWSResult state result writer\n"
@@ -6580,17 +6590,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:860
+#: text/chapter11.md:859
#, markdown-text
msgid ""
-"`runRWS` looks like a combination of `runReader`, `runWriter` and "
+"`runRWS` looks like a combination of `runReader`, `runWriter`, and "
"`runState`. It takes a global configuration and an initial state as an "
-"argument, and returns a data structure containing the log, the result and "
+"argument and returns a data structure containing the log, the result, and "
"the final state."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:862
+#: text/chapter11.md:861
#, markdown-text
msgid ""
"The front-end of our application is defined by a function `runGame`, with "
@@ -6598,13 +6608,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:863
+#: text/chapter11.md:862
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Main.purs:runGame_sig}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:868
+#: text/chapter11.md:867
#, markdown-text
msgid ""
"This function interacts with the user via the console (using the "
@@ -6613,16 +6623,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:870
+#: text/chapter11.md:869
#, markdown-text
msgid ""
"The `node-readline` package provides the `LineHandler` type, which "
-"represents actions in the `Effect` monad which handle user input from the "
+"represents actions in the `Effect` monad, which handle user input from the "
"terminal. Here is the corresponding API:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:871
+#: text/chapter11.md:870
#, no-wrap
msgid ""
"type LineHandler a = String -> Effect a\n"
@@ -6635,16 +6645,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:882
+#: text/chapter11.md:881
#, markdown-text
msgid ""
-"The `Interface` type represents a handle for the console, and is passed as "
-"an argument to the functions which interact with it. An `Interface` can be "
+"The `Interface` type represents a handle for the console and is passed as an "
+"argument to the functions which interact with it. An `Interface` can be "
"created using the `createConsoleInterface` function:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:883
+#: text/chapter11.md:882
#, no-wrap
msgid ""
"{{#include ../exercises/chapter11/src/Main.purs:import_RL}}\n"
@@ -6653,7 +6663,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:890
+#: text/chapter11.md:889
#, markdown-text
msgid ""
"The first step is to set the prompt at the console. We pass the `interface` "
@@ -6661,13 +6671,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:891
+#: text/chapter11.md:890
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Main.purs:runGame_prompt}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:896
+#: text/chapter11.md:895
#, markdown-text
msgid ""
"In our case, we are interested in implementing the line handler "
@@ -6676,13 +6686,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:897
+#: text/chapter11.md:896
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Main.purs:runGame_lineHandler}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:902
+#: text/chapter11.md:901
#, markdown-text
msgid ""
"The `let` binding is closed over both the game configuration, named `env`, "
@@ -6690,7 +6700,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:904
+#: text/chapter11.md:903
#, markdown-text
msgid ""
"Our handler takes an additional first argument, the game state. This is "
@@ -6699,7 +6709,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:906
+#: text/chapter11.md:905
#, markdown-text
msgid ""
"The first thing this action does is to break the user input into words using "
@@ -6709,7 +6719,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:908
+#: text/chapter11.md:907
#, markdown-text
msgid ""
"Having run the game logic, which is a pure computation, we need to print any "
@@ -6721,21 +6731,21 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:910
+#: text/chapter11.md:909
#, markdown-text
msgid ""
"The `runGame` function finally attaches the initial line handler to the "
-"console interface, and displays the initial prompt:"
+"console interface and displays the initial prompt:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:911
+#: text/chapter11.md:910
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Main.purs:runGame_attach_handler}}\n"
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:919
+#: text/chapter11.md:918
#, markdown-text
msgid ""
"(Medium) Implement a new command `cheat`, which moves all game items from "
@@ -6744,7 +6754,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:919
+#: text/chapter11.md:918
#, markdown-text
msgid ""
"(Difficult) The `Writer` component of the `RWS` monad is currently used for "
@@ -6753,22 +6763,22 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:921
+#: text/chapter11.md:920
#, markdown-text, no-wrap
msgid ""
" Refactor the code to use the `ExceptT` monad transformer to handle the "
-"error messages, and `RWS` to handle informational messages. _Note:_ There "
-"are no tests for this exercise.\n"
+"error messages and `RWS` to handle informational messages. _Note:_ There are "
+"no tests for this exercise.\n"
msgstr ""
#. type: Title ##
-#: text/chapter11.md:922
+#: text/chapter11.md:921
#, markdown-text, no-wrap
msgid "Handling Command Line Options"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:925
+#: text/chapter11.md:924
#, markdown-text
msgid ""
"The final piece of the application is responsible for parsing command line "
@@ -6777,7 +6787,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:927
+#: text/chapter11.md:926
#, markdown-text
msgid ""
"`optparse` is an example of _applicative command line option "
@@ -6791,13 +6801,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:928
+#: text/chapter11.md:927
#, no-wrap
msgid "customExecParser :: forall a. ParserPrefs → ParserInfo a → Effect a\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:933
+#: text/chapter11.md:932
#, markdown-text
msgid ""
"This is best illustrated by example. The application's `main` function is "
@@ -6805,13 +6815,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:934
+#: text/chapter11.md:933
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Main.purs:main}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:939
+#: text/chapter11.md:938
#, markdown-text
msgid ""
"The first argument is used to configure the `optparse` library. In our case, "
@@ -6822,13 +6832,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:941
+#: text/chapter11.md:940
#, markdown-text
msgid "The second argument is the complete description of our parser program:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:942
+#: text/chapter11.md:941
#, no-wrap
msgid ""
"{{#include ../exercises/chapter11/src/Main.purs:argParser}}\n"
@@ -6837,75 +6847,75 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:949
+#: text/chapter11.md:948
#, markdown-text, no-wrap
msgid ""
"Here `OP.info` combines a `Parser` with a set of options for how the help "
"message is formatted. `env <**> OP.helper` takes any command line argument "
-"`Parser` named `env` and adds a `--help` option to it automatically. Options "
-"for the help message are of type `InfoMod`, which is a monoid, so we can use "
-"the `fold` function to add several options together.\n"
+"`Parser` named `env` and automatically adds a `--help` option. Options for "
+"the help message are of type `InfoMod`, which is a monoid, so we can use the "
+"`fold` function to add several options together.\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:951
+#: text/chapter11.md:950
#, markdown-text
msgid "The interesting part of our parser is constructing the `GameEnvironment`:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter11.md:952
+#: text/chapter11.md:951
#, no-wrap
msgid "{{#include ../exercises/chapter11/src/Main.purs:env}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:957
+#: text/chapter11.md:956
#, markdown-text, no-wrap
msgid ""
"`player` and `debug` are both `Parser`s, so we can use our applicative "
"operators `<$>` and `<*>` to lift our `gameEnvironment` function, which has "
"the type `PlayerName -> Boolean -> GameEnvironment` over "
"`Parser`. `OP.strOption` constructs a command line option that expects a "
-"string value, and is configured via a collection of `Mod`s folded "
-"together. `OP.flag` works similarly, but doesn't expect an associated "
+"string value and is configured via a collection of `Mod`s folded "
+"together. `OP.flag` works similarly but doesn't expect an associated "
"value. `optparse` offers extensive "
"[documentation](https://pursuit.purescript.org/packages/purescript-optparse) "
"on different modifiers available to build various command line parsers.\n"
msgstr ""
#. type: Plain text
-#: text/chapter11.md:959
+#: text/chapter11.md:958
#, markdown-text, no-wrap
msgid ""
-"Notice how we were able to use the notation afforded by the applicative "
-"operators to give a compact, declarative specification of our command line "
-"interface. In addition, it is simple to add new command line arguments, "
-"simply by adding a new function argument to `runGame`, and then using `<*>` "
-"to lift `runGame` over an additional argument in the definition of `env`.\n"
+"Notice how we used the notation afforded by the applicative operators to "
+"give a compact, declarative specification of our command line interface. In "
+"addition, it is simple to add new command line arguments by adding a new "
+"function argument to `runGame` and then using `<*>` to lift `runGame` over "
+"an additional argument in the definition of `env`.\n"
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter11.md:963
+#: text/chapter11.md:962
#, markdown-text
msgid ""
"(Medium) Add a new Boolean-valued property `cheatMode` to the "
"`GameEnvironment` record. Add a new command line flag `-c` to the `optparse` "
-"configuration which enables cheat mode. The `cheat` command from the "
-"previous exercise should be disallowed if cheat mode is not enabled."
+"configuration, enabling cheat mode. The `cheat` command from the previous "
+"exercise should be disallowed if cheat mode is not enabled."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:967
+#: text/chapter11.md:966
#, markdown-text
msgid ""
"This chapter was a practical demonstration of the techniques we've learned "
-"so far, using monad transformers to build a pure specification of our game, "
+"so far, using monad transformers to build a pure specification of our game "
"and the `Effect` monad to build a front-end using the console."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:969
+#: text/chapter11.md:968
#, markdown-text
msgid ""
"Because we separated our implementation from the user interface, it would be "
@@ -6915,23 +6925,23 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter11.md:971
+#: text/chapter11.md:970
#, markdown-text
msgid ""
"We have seen how monad transformers allow us to write safe code in an "
-"imperative style, where effects are tracked by the type system. In addition, "
-"type classes provide a powerful way to abstract over the actions provided by "
-"a monad, enabling code reuse. We were able to use standard abstractions like "
-"`Alternative` and `MonadPlus` to build useful monads by combining standard "
-"monad transformers."
+"imperative style, where the type system tracks effects. In addition, type "
+"classes provide a powerful way to abstract over the actions provided by a "
+"monad, enabling code reuse. We used standard abstractions like `Alternative` "
+"and `MonadPlus` to build useful monads by combining standard monad "
+"transformers."
msgstr ""
#. type: Plain text
-#: text/chapter11.md:972
+#: text/chapter11.md:971
#, markdown-text
msgid ""
-"Monad transformers are an excellent demonstration of the sort of expressive "
-"code that can be written by relying on advanced type system features such as "
+"Monad transformers are an excellent demonstration of expressive code that "
+"can be written by relying on advanced type system features such as "
"higher-kinded polymorphism and multi-parameter type classes."
msgstr ""
@@ -6980,8 +6990,8 @@ msgid ""
"The HTML file `html/index.html` contains a single `canvas` element which "
"will be used in each example, and a `script` element to load the compiled "
"PureScript code. To test the code for each section, open the HTML file in "
-"your browser. Because most exercises target the browser, there are no unit "
-"tests for this chapter."
+"your browser. Because most exercises target the browser, this chapter has no "
+"unit tests."
msgstr ""
#. type: Title ##
@@ -7006,7 +7016,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `main` action starts, like in the other modules, by using the "
-"`getCanvasElementById` action to get a reference to the canvas object, and "
+"`getCanvasElementById` action to get a reference to the canvas object and "
"the `getContext2D` action to access the 2D rendering context for the canvas:"
msgstr ""
@@ -7015,7 +7025,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `void` function takes a functor and replaces its value with `Unit`. In "
-"the example it is used to make `main` conform with its signature."
+"the example, it is used to make `main` conform with its signature."
msgstr ""
#. type: Fenced code block (haskell)
@@ -7065,7 +7075,7 @@ msgstr ""
#: text/chapter12.md:43
#, markdown-text
msgid ""
-"The graphics context `ctx` manages the state of the canvas, and provides "
+"The graphics context `ctx` manages the state of the canvas and provides "
"methods to render primitive shapes, set styles and colors, and apply "
"transformations."
msgstr ""
@@ -7113,9 +7123,9 @@ msgstr ""
#: text/chapter12.md:59
#, markdown-text
msgid ""
-"`fillPath` takes a graphics context and another action which builds the path "
+"`fillPath` takes a graphics context and another action that builds the path "
"to render. To build a path, we can use the `rect` action. `rect` takes a "
-"graphics context, and a record which provides the position and size of the "
+"graphics context and a record that provides the position and size of the "
"rectangle:"
msgstr ""
@@ -7158,16 +7168,16 @@ msgstr ""
#, markdown-text
msgid ""
"There are other ways to render paths. The `arc` function renders an arc "
-"segment, and the `moveTo`, `lineTo` and `closePath` functions can be used to "
-"render piecewise-linear paths."
+"segment, and the `moveTo`, `lineTo`, and `closePath` functions can render "
+"piecewise-linear paths."
msgstr ""
#. type: Plain text
#: text/chapter12.md:77
#, markdown-text
msgid ""
-"The `Shapes.purs` file renders three shapes: a rectangle, an arc segment and "
-"a triangle."
+"The `Shapes.purs` file renders three shapes: a rectangle, an arc segment, "
+"and a triangle."
msgstr ""
#. type: Plain text
@@ -7195,7 +7205,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `x` and `y` properties represent the location of the top-left corner, "
-"while the `w` and `h` properties represent the width and height "
+"while the `w` and `h` properties represent the width and height, "
"respectively."
msgstr ""
@@ -7225,7 +7235,7 @@ msgstr ""
#, markdown-text
msgid ""
"Here, the `x` and `y` properties represent the center point, `r` is the "
-"radius, and `start` and `end` represent the endpoints of the arc in radians."
+"radius, `start` and `end` represent the endpoints of the arc in radians."
msgstr ""
#. type: Plain text
@@ -7234,7 +7244,7 @@ msgstr ""
msgid ""
"For example, this code fills an arc segment centered at `(300, 300)` with "
"radius `50`. The arc completes 2/3rds of a rotation. Note that the unit "
-"circle is flipped vertically, since the y-axis increases towards the bottom "
+"circle is flipped vertically since the y-axis increases towards the bottom "
"of the canvas:"
msgstr ""
@@ -7257,15 +7267,15 @@ msgstr ""
msgid ""
"Notice that both the `Rectangle` and `Arc` record types contain `x` and `y` "
"properties of type `Number`. In both cases, this pair represents a "
-"point. This means that we can write row-polymorphic functions which can act "
-"on either type of record."
+"point. This means we can write row-polymorphic functions acting on either "
+"type of record."
msgstr ""
#. type: Plain text
#: text/chapter12.md:120
#, markdown-text
msgid ""
-"For example, the `Shapes` module defines a `translate` function which "
+"For example, the `Shapes` module defines a `translate` function that "
"translates a shape by modifying its `x` and `y` properties:"
msgstr ""
@@ -7301,7 +7311,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `translate` function can be used with both the `Rectangle` and `Arc` "
-"records, as can be seen in the `Shapes` example."
+"records, as seen in the `Shapes` example."
msgstr ""
#. type: Plain text
@@ -7379,19 +7389,18 @@ msgstr ""
#, markdown-text
msgid ""
"(Easy) Experiment with the `strokePath` and `setStrokeStyle` functions in "
-"each of the examples so far."
+"each example so far."
msgstr ""
#. type: Bullet: ' 1. '
#: text/chapter12.md:158
#, markdown-text
msgid ""
-"(Easy) The `fillPath` and `strokePath` functions can be used to render "
-"complex paths with a common style by using a do notation block inside the "
-"function argument. Try changing the `Rectangle` example to render two "
-"rectangles side-by-side using the same call to `fillPath`. Try rendering a "
-"sector of a circle by using a combination of a piecewise-linear path and an "
-"arc segment."
+"(Easy) The `fillPath` and `strokePath` functions can render complex paths "
+"with a common style using a do notation block inside the function "
+"argument. Try changing the `Rectangle` example to render two rectangles "
+"side-by-side using the same call to `fillPath`. Try rendering a sector of a "
+"circle by using a combination of a piecewise-linear path and an arc segment."
msgstr ""
#. type: Bullet: ' 1. '
@@ -7449,7 +7458,7 @@ msgstr ""
#, markdown-text, no-wrap
msgid ""
" which takes a `Number` between `0` and `1` as its argument and returns "
-"a `Point`, write an action which plots `f` by using your `renderPath` "
+"a `Point`, write an action that plots `f` by using your `renderPath` "
"function. Your action should approximate the path by sampling `f` at a "
"finite set of points.\n"
msgstr ""
@@ -7470,17 +7479,17 @@ msgstr ""
#: text/chapter12.md:185
#, markdown-text
msgid ""
-"The `Example/Random.purs` file contains an example which uses the `Effect` "
-"monad to interleave two different types of side-effect: random number "
-"generation, and canvas manipulation. The example renders one hundred "
-"randomly generated circles onto the canvas."
+"The `Example/Random.purs` file contains an example that uses the `Effect` "
+"monad to interleave two types of side-effect: random number generation and "
+"canvas manipulation. The example renders one hundred randomly generated "
+"circles onto the canvas."
msgstr ""
#. type: Plain text
#: text/chapter12.md:187
#, markdown-text
msgid ""
-"The `main` action obtains a reference to the graphics context as before, and "
+"The `main` action obtains a reference to the graphics context as before and "
"then sets the stroke and fill styles:"
msgstr ""
@@ -7510,7 +7519,7 @@ msgstr ""
msgid ""
"On each iteration, the do notation block starts by generating three random "
"numbers distributed between `0` and `1`. These numbers represent the `x` and "
-"`y` coordinates, and the radius of a circle:"
+"`y` coordinates and the radius of a circle:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -7564,7 +7573,7 @@ msgstr ""
#, markdown-text
msgid ""
"There is more to the canvas than just rendering simple shapes. Every canvas "
-"maintains a transformation which is used to transform shapes before "
+"maintains a transformation that is used to transform shapes before "
"rendering. Shapes can be translated, rotated, scaled, and skewed."
msgstr ""
@@ -7609,8 +7618,8 @@ msgstr ""
#: text/chapter12.md:245
#, markdown-text
msgid ""
-"The `rotate` action performs a rotation around the origin, through some "
-"number of radians specified by the first argument."
+"The `rotate` action rotates around the origin through some number of radians "
+"specified by the first argument."
msgstr ""
#. type: Plain text
@@ -7679,7 +7688,7 @@ msgstr ""
#, markdown-text
msgid ""
"A common use case is to render some subset of the scene using a "
-"transformation, and then to reset the transformation afterwards."
+"transformation and then reset the transformation."
msgstr ""
#. type: Plain text
@@ -7720,7 +7729,7 @@ msgid ""
"This allows us to save the current state, apply some styles and "
"transformations, render some primitives, and finally restore the original "
"transformation and state. For example, the following function performs some "
-"canvas action, but applies a rotation before doing so, and restores the "
+"canvas action but applies a rotation before doing so and restores the "
"transformation afterwards:"
msgstr ""
@@ -7791,7 +7800,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `Effect.Ref` module provides a type constructor for global mutable "
-"references, and an associated effect:"
+"references and an associated effect:"
msgstr ""
#. type: Fenced code block (text)
@@ -7817,7 +7826,7 @@ msgstr ""
#: text/chapter12.md:327
#, markdown-text
msgid ""
-"The `Example/Refs.purs` file contains an example which uses a `Ref` to track "
+"The `Example/Refs.purs` file contains an example that uses a `Ref` to track "
"mouse clicks on the `canvas` element."
msgstr ""
@@ -7825,7 +7834,7 @@ msgstr ""
#: text/chapter12.md:329
#, markdown-text
msgid ""
-"The code starts by creating a new reference containing the value `0`, by "
+"The code starts by creating a new reference containing the value `0` by "
"using the `new` action:"
msgstr ""
@@ -7867,7 +7876,7 @@ msgstr ""
#: text/chapter12.md:347
#, markdown-text
msgid ""
-"This action uses `withContext` to preserve the original transformation, and "
+"This action uses `withContext` to preserve the original transformation and "
"then applies the following sequence of transformations (remember that "
"transformations are applied bottom-to-top):"
msgstr ""
@@ -7876,8 +7885,8 @@ msgstr ""
#: text/chapter12.md:352
#, markdown-text
msgid ""
-"The rectangle is translated through `(-100, -100)` so that its center lies "
-"at the origin."
+"The rectangle is translated through `(-100, -100)`, so its center lies at "
+"the origin."
msgstr ""
#. type: Bullet: '- '
@@ -7898,8 +7907,8 @@ msgstr ""
#: text/chapter12.md:352
#, markdown-text
msgid ""
-"The rectangle is translated through `(300, 300)` so that it center lies at "
-"the center of the canvas."
+"The rectangle is translated through `(300, 300)`, so its center lies at the "
+"center of the canvas."
msgstr ""
#. type: Plain text
@@ -7926,16 +7935,16 @@ msgstr ""
#: text/chapter12.md:366
#, markdown-text
msgid ""
-"(Easy) Write a higher-order function which strokes and fills a path "
-"simultaneously. Rewrite the `Random.purs` example using your function."
+"(Easy) Write a higher-order function that simultaneously strokes and fills a "
+"path. Rewrite the `Random.purs` example using your function."
msgstr ""
#. type: Bullet: ' 1. '
#: text/chapter12.md:366
#, markdown-text
msgid ""
-"(Medium) Use `Random` and `Dom` to create an application which renders a "
-"circle with random position, color and radius to the canvas when the mouse "
+"(Medium) Use `Random` and `Dom` to create an application that renders a "
+"circle with random position, color, and radius to the canvas when the mouse "
"is clicked."
msgstr ""
@@ -7943,7 +7952,7 @@ msgstr ""
#: text/chapter12.md:366
#, markdown-text
msgid ""
-"(Medium) Write a function which transforms the scene by rotating it around a "
+"(Medium) Write a function that transforms the scene by rotating it around a "
"point with specified coordinates. _Hint_: use a translation to first "
"translate the scene to the origin."
msgstr ""
@@ -7969,7 +7978,7 @@ msgid ""
"An L-system is defined by an _alphabet_, an initial sequence of letters from "
"the alphabet, and a set of _production rules_. Each production rule takes a "
"letter of the alphabet and returns a sequence of replacement letters. This "
-"process is iterated some number of times starting with the initial sequence "
+"process is iterated some number of times, starting with the initial sequence "
"of letters."
msgstr ""
@@ -7987,7 +7996,7 @@ msgstr ""
#, markdown-text
msgid ""
"For example, suppose the alphabet consists of the letters `L` (turn left), "
-"`R` (turn right) and `F` (move forward). We might define the following "
+"`R` (turn right), and `F` (move forward). We might define the following "
"production rules:"
msgstr ""
@@ -8022,8 +8031,8 @@ msgstr ""
#, markdown-text
msgid ""
"and so on. Plotting a piecewise-linear path corresponding to this set of "
-"instruction approximates a curve called the _Koch curve_. Increasing the "
-"number of iterations increases the resolution of the curve."
+"instructions approximates the _Koch curve_. Increasing the number of "
+"iterations increases the resolution of the curve."
msgstr ""
#. type: Plain text
@@ -8091,10 +8100,10 @@ msgstr ""
#: text/chapter12.md:420
#, markdown-text
msgid ""
-"Now we can implement a function `lsystem` which will take a specification in "
-"this form, and render it to the canvas. What type should `lsystem` have? "
+"Now we can implement a function `lsystem` that will take a specification in "
+"this form and render it to the canvas. What type should `lsystem` have? "
"Well, it needs to take values like `initial` and `productions` as arguments, "
-"as well as a function which can render a letter of the alphabet to the "
+"as well as a function that can render a letter of the alphabet to the "
"canvas."
msgstr ""
@@ -8127,11 +8136,11 @@ msgstr ""
#: text/chapter12.md:434
#, markdown-text
msgid ""
-"The third argument represents a function which takes a letter of the "
-"alphabet and _interprets_ it by performing some actions on the canvas. In "
-"our example, this would mean turning left in the case of the letter `L`, "
-"turning right in the case of the letter `R`, and moving forward in the case "
-"of a letter `F`."
+"The third argument represents a function that takes a letter of the alphabet "
+"and _interprets_ it by performing some actions on the canvas. In our "
+"example, this would mean turning left in the case of the letter `L`, turning "
+"right in the case of the letter `R`, and moving forward in the case of a "
+"letter `F`."
msgstr ""
#. type: Plain text
@@ -8167,12 +8176,12 @@ msgstr ""
#: text/chapter12.md:448
#, markdown-text
msgid ""
-"The second observation is that, in order to implement instructions like "
-"\"turn left\" and \"turn right\", we will need to maintain some state, "
-"namely the direction in which the path is moving at any time. We need to "
-"modify our function to pass the state through the computation. Again, the "
-"`lsystem` function should work for any type of state, so we will represent "
-"it using the type variable `s`."
+"The second observation is that, to implement instructions like \"turn left\" "
+"and \"turn right\", we will need to maintain some state, namely the "
+"direction in which the path is moving at any time. We need to modify our "
+"function to pass the state through the computation. Again, the `lsystem` "
+"function should work for any type of state, so we will represent it using "
+"the type variable `s`."
msgstr ""
#. type: Plain text
@@ -8284,19 +8293,18 @@ msgstr ""
#, markdown-text
msgid ""
"The `go` function works by recursion on its second argument. There are two "
-"cases: when `n` is zero, and when `n` is non-zero."
+"cases: when `n` is zero and `n` is non-zero."
msgstr ""
#. type: Plain text
#: text/chapter12.md:496
#, markdown-text, no-wrap
msgid ""
-"In the first case, the recursion is complete, and we simply need to "
-"interpret the current sentence according to the interpretation function. We "
-"have a sentence of type `Array a`, a state of type `s`, and a function of "
-"type `s -> a -> Effect s`. This sounds like a job for the `foldM` function "
-"which we defined earlier, and which is available from the `control` "
-"package:\n"
+"In the first case, the recursion is complete, and we need to interpret the "
+"current sentence according to the interpretation function. We have a "
+"sentence of type `Array a`, a state of type `s`, and a function of type `s "
+"-> a -> Effect s`. This sounds like a job for the `foldM` function which we "
+"defined earlier, and which is available from the `control` package:\n"
msgstr ""
#. type: Fenced code block (haskell)
@@ -8328,7 +8336,7 @@ msgstr ""
#: text/chapter12.md:508
#, markdown-text
msgid ""
-"That's it! Note how the use of higher order functions like `foldM` and "
+"That's it! Note how using higher-order functions like `foldM` and "
"`concatMap` allowed us to communicate our ideas concisely."
msgstr ""
@@ -8362,9 +8370,9 @@ msgstr ""
msgid ""
"We can understand this type as saying that our interpretation function is "
"free to have any side-effects at all, captured by the monad `m`. It might "
-"render to the canvas, or print information to the console, or support "
-"failure or multiple return values. The reader is encouraged to try writing "
-"L-systems which use these various types of side-effect."
+"render to the canvas, print information to the console, or support failure "
+"or multiple return values. The reader is encouraged to try writing L-systems "
+"that use these various types of side-effect."
msgstr ""
#. type: Plain text
@@ -8372,11 +8380,11 @@ msgstr ""
#, markdown-text
msgid ""
"This function is a good example of the power of separating data from "
-"implementation. The advantage of this approach is that we gain the freedom "
-"to interpret our data in multiple different ways. We might even factor "
-"`lsystem` into two smaller functions: the first would build the sentence "
-"using repeated application of `concatMap`, and the second would interpret "
-"the sentence using `foldM`. This is also left as an exercise for the reader."
+"implementation. The advantage of this approach is that we can interpret our "
+"data in multiple ways. We might even factor `lsystem` into two smaller "
+"functions: the first would build the sentence using repeated application of "
+"`concatMap`, and the second would interpret the sentence using `foldM`. This "
+"is also left as an exercise for the reader."
msgstr ""
#. type: Plain text
@@ -8419,7 +8427,7 @@ msgstr ""
#, markdown-text
msgid ""
"To interpret the letter `F` (move forward), we can calculate the new "
-"position of the path, render a line segment, and update the state, as "
+"position of the path, render a line segment, and update the state as "
"follows:"
msgstr ""
@@ -8485,7 +8493,7 @@ msgstr ""
#: text/chapter12.md:562
#, markdown-text
msgid ""
-"(Easy) Try changing the various numerical constants in the code, to "
+"(Easy) Try changing the various numerical constants in the code to "
"understand their effect on the rendered system."
msgstr ""
@@ -8502,9 +8510,9 @@ msgstr ""
#: text/chapter12.md:562
#, markdown-text
msgid ""
-"(Medium) Add a drop shadow to the filled shape, by using the "
-"`setShadowOffsetX`, `setShadowOffsetY`, `setShadowBlur` and `setShadowColor` "
-"actions. _Hint_: use PSCi to find the types of these functions."
+"(Medium) Add a drop shadow to the filled shape using the `setShadowOffsetX`, "
+"`setShadowOffsetY`, `setShadowBlur`, and `setShadowColor` actions. _Hint_: "
+"use PSCi to find the types of these functions."
msgstr ""
#. type: Bullet: ' 1. '
@@ -8540,7 +8548,7 @@ msgid ""
"interesting shapes?\n"
" 1. (Difficult) An L-system is given by an alphabet with four letters: `L` "
"(turn left through 60 degrees), `R` (turn right through 60 degrees), `F` "
-"(move forward) and `M` (also move forward).\n"
+"(move forward), and `M` (also move forward).\n"
msgstr ""
#. type: Plain text
@@ -8572,7 +8580,7 @@ msgstr ""
#, markdown-text, no-wrap
msgid ""
" Render this L-system. _Note_: you will need to decrease the number of "
-"iterations of the production rules, since the size of the final sentence "
+"iterations of the production rules since the size of the final sentence "
"grows exponentially with the number of iterations.\n"
msgstr ""
@@ -8612,7 +8620,7 @@ msgstr ""
msgid ""
"In this chapter, we learned how to use the HTML5 Canvas API from PureScript "
"by using the `canvas` library. We also saw a practical demonstration of many "
-"of the techniques we have learned already: maps and folds, records and row "
+"techniques we have learned already: maps and folds, records and row "
"polymorphism, and the `Effect` monad for handling side-effects."
msgstr ""
@@ -8644,8 +8652,8 @@ msgstr ""
#, markdown-text
msgid ""
"This approach is taken in the `drawing` package, and it brings the "
-"flexibility of being able to manipulate the scene as data in various ways "
-"before rendering."
+"flexibility of manipulating the scene as data in various ways before "
+"rendering."
msgstr ""
#. type: Plain text
@@ -8728,7 +8736,7 @@ msgstr ""
#: text/chapter13.md:24
#, markdown-text
msgid ""
-"`merge` takes two sorted arrays of integers, and merges their elements so "
+"`merge` takes two sorted arrays of integers and merges their elements so "
"that the result is also sorted. For example:"
msgstr ""
@@ -8747,7 +8755,7 @@ msgstr ""
#, markdown-text
msgid ""
"In a typical test suite, we might test `merge` by generating a few small "
-"test cases like this by hand, and asserting that the results were equal to "
+"test cases like this by hand and asserting that the results were equal to "
"the appropriate values. However, everything we need to know about the "
"`merge` function can be summarized by this property:"
msgstr ""
@@ -8764,8 +8772,8 @@ msgstr ""
#: text/chapter13.md:37
#, markdown-text
msgid ""
-"`quickcheck` allows us to test this property directly, by generating random "
-"test cases. We simply state the properties that we want our code to have, as "
+"`quickcheck` allows us to test this property directly by generating random "
+"test cases. We state the properties we want our code to have as "
"functions. In this case, we have a single property:"
msgstr ""
@@ -8783,11 +8791,10 @@ msgstr ""
#, markdown-text
msgid ""
"When we run this code, `quickcheck` will attempt to disprove the properties "
-"we claimed, by generating random inputs `xs` and `ys`, and passing them to "
-"our functions. If our function returns `false` for any inputs, the property "
-"will be incorrect, and the library will raise an error. Fortunately, the "
-"library is unable to disprove our properties after generating 100 random "
-"test cases:"
+"we claimed by generating random inputs `xs` and `ys` and passing them to our "
+"functions. If our function returns `false` for any inputs, the property will "
+"be incorrect, and the library will raise an error. Fortunately, the library "
+"is unable to disprove our properties after generating 100 random test cases:"
msgstr ""
#. type: Fenced code block (text)
@@ -8887,11 +8894,10 @@ msgstr ""
#: text/chapter13.md:94
#, markdown-text
msgid ""
-"(Easy) Write a property which asserts that merging an array with the empty "
-"array does not modify the original array. _Note_: This new property is "
-"redundant, since this situation is already covered by our existing "
-"property. We're just trying to give you readers a simple way to practice "
-"using quickCheck."
+"(Easy) Write a property that asserts that merging an array with an empty one "
+"does not modify the original array. _Note_: This new property is redundant "
+"since this situation is already covered by our existing property. We're just "
+"trying to give readers a simple way to practice using quickCheck."
msgstr ""
#. type: Bullet: ' 1. '
@@ -8948,7 +8954,7 @@ msgstr ""
#, markdown-text
msgid ""
"This error message indicates that the compiler could not generate random "
-"test cases, because it did not know what type of elements we wanted our "
+"test cases because it did not know what type of elements we wanted our "
"arrays to have. In these sorts of cases, we can use type annotations to "
"force the compiler to infer a particular type, such as `Array Int`:"
msgstr ""
@@ -8965,9 +8971,9 @@ msgstr ""
#: text/chapter13.md:122
#, markdown-text
msgid ""
-"We can alternatively use a helper function to specify type, which may result "
-"in cleaner code. For example, if we define a function `ints` as a synonym "
-"for the identity function:"
+"We can alternatively use a helper function to specify the type, which may "
+"result in cleaner code. For example, if we define a function `ints` as a "
+"synonym for the identity function:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -8998,17 +9004,17 @@ msgstr ""
#: text/chapter13.md:136
#, markdown-text
msgid ""
-"Here, `xs` and `ys` both have type `Array Int`, since the `ints` function "
-"has been used to disambiguate the unknown type."
+"Here, `xs` and `ys` have type `Array Int` since the `ints` function has been "
+"used to disambiguate the unknown type."
msgstr ""
#. type: Bullet: ' 1. '
#: text/chapter13.md:141
#, markdown-text
msgid ""
-"(Easy) Write a function `bools` which forces the types of `xs` and `ys` to "
-"be `Array Boolean`, and add additional properties which test `mergePoly` at "
-"that type."
+"(Easy) Write a function `bools` that forces the types of `xs` and `ys` to be "
+"`Array Boolean`, and add additional properties that test `mergePoly` at that "
+"type."
msgstr ""
#. type: Bullet: ' 1. '
@@ -9031,8 +9037,8 @@ msgstr ""
#: text/chapter13.md:145
#, markdown-text
msgid ""
-"Now we will see how the `quickcheck` library is able to randomly generate "
-"test cases for our properties."
+"Now we will see how the `quickcheck` library can randomly generate test "
+"cases for our properties."
msgstr ""
#. type: Plain text
@@ -9076,9 +9082,9 @@ msgstr ""
#, markdown-text
msgid ""
"For example, we can use the `Arbitrary` instance for the `Int` type, "
-"provided in the `quickcheck` library, to create a distribution on the 256 "
-"byte values, using the `Functor` instance for `Gen` to map a function from "
-"integers to bytes over arbitrary integer values:"
+"provided in the `quickcheck` library, to create a distribution on the "
+"256-byte values, using the `Functor` instance for `Gen` to map a function "
+"from integers to bytes over arbitrary integer values:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -9124,8 +9130,8 @@ msgstr ""
msgid ""
"In this test, we generated arbitrary arrays `xs` and `ys`, but had to sort "
"them, since `merge` expects sorted input. On the other hand, we could create "
-"a newtype representing sorted arrays, and write an `Arbitrary` instance "
-"which generates sorted data:"
+"a newtype representing sorted arrays and write an `Arbitrary` instance that "
+"generates sorted data:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -9161,8 +9167,8 @@ msgstr ""
#, markdown-text
msgid ""
"This may look like a small change, but the types of `xs` and `ys` have "
-"changed to `Sorted Int`, instead of just `Array Int`. This communicates our "
-"_intent_ in a clearer way - the `mergePoly` function takes sorted "
+"changed to `Sorted Int` instead of just `Array Int`. This communicates our "
+"_intent_ in a clearer way – the `mergePoly` function takes sorted "
"input. Ideally, the type of the `mergePoly` function itself would be updated "
"to use the `Sorted` type constructor."
msgstr ""
@@ -9204,9 +9210,8 @@ msgstr ""
#: text/chapter13.md:217
#, markdown-text
msgid ""
-"The `insert` function is used to insert a new element into a sorted tree, "
-"and the `member` function can be used to query a tree for a particular "
-"value. For example:"
+"The `insert` function inserts a new element into a sorted tree, and the "
+"`member` function can query a tree for a particular value. For example:"
msgstr ""
#. type: Fenced code block (text)
@@ -9226,9 +9231,8 @@ msgstr ""
#: text/chapter13.md:229
#, markdown-text
msgid ""
-"The `toArray` and `fromArray` functions can be used to convert sorted trees "
-"to and from arrays. We can use `fromArray` to write an `Arbitrary` instance "
-"for trees:"
+"The `toArray` and `fromArray` functions can convert sorted trees to and from "
+"arrays. We can use `fromArray` to write an `Arbitrary` instance for trees:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -9243,7 +9247,7 @@ msgstr ""
#: text/chapter13.md:236
#, markdown-text
msgid ""
-"We can now use `Tree a` as the type of an argument to our test properties, "
+"We can now use `Tree a` as the type of an argument to our test properties "
"whenever there is an `Arbitrary` instance available for the type `a`. For "
"example, we can test that the `member` test always returns `true` after "
"inserting a value:"
@@ -9279,7 +9283,7 @@ msgstr ""
#: text/chapter13.md:248
#, markdown-text
msgid ""
-"(Difficult) Write a property which asserts that a value inserted into a tree "
+"(Difficult) Write a property that asserts that a value inserted into a tree "
"is still a member of that tree after arbitrarily many more insertions."
msgstr ""
@@ -9293,19 +9297,19 @@ msgstr ""
#: text/chapter13.md:252
#, markdown-text
msgid ""
-"The `Merge` module defines another generalization of the `merge` function - "
-"the `mergeWith` function takes an additional function as an argument which "
-"is used to determine the order in which elements should be merged. That is, "
-"`mergeWith` is a higher-order function."
+"The `Merge` module defines another generalization of the `merge` function – "
+"the `mergeWith` function takes an additional function as an argument to "
+"determine the order in which elements should be merged. That is, `mergeWith` "
+"is a higher-order function."
msgstr ""
#. type: Plain text
#: text/chapter13.md:254
#, markdown-text
msgid ""
-"For example, we can pass the `length` function as the first argument, to "
-"merge two arrays which are already in length-increasing order. The result "
-"should also be in length-increasing order:"
+"For example, we can pass the `length` function as the first argument to "
+"merge two arrays already in length-increasing order. The result should also "
+"be in length-increasing order:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -9326,14 +9330,14 @@ msgstr ""
#, markdown-text
msgid ""
"How might we test such a function? Ideally, we would like to generate values "
-"for all three arguments, including the first argument which is a function."
+"for all three arguments, including the first argument, which is a function."
msgstr ""
#. type: Plain text
#: text/chapter13.md:268
#, markdown-text
msgid ""
-"There is a second type class which allows us to create randomly-generated "
+"There is a second type class that allows us to create randomly-generated "
"functions. It is called `Coarbitrary`, and it is defined as follows:"
msgstr ""
@@ -9349,8 +9353,8 @@ msgstr ""
#: text/chapter13.md:275
#, markdown-text
msgid ""
-"The `coarbitrary` function takes a function argument of type `t`, and a "
-"random generator for a function result of type `r`, and uses the function "
+"The `coarbitrary` function takes a function argument of type `t` and a "
+"random generator for a function result of type `r`. It uses the function "
"argument to _perturb_ the random generator. That is, it uses the function "
"argument to modify the random output of the random generator for the result."
msgstr ""
@@ -9359,7 +9363,7 @@ msgstr ""
#: text/chapter13.md:277
#, markdown-text
msgid ""
-"In addition, there is a type class instance which gives us `Arbitrary` "
+"In addition, there is a type class instance that gives us `Arbitrary` "
"functions if the function domain is `Coarbitrary` and the function codomain "
"is `Arbitrary`:"
msgstr ""
@@ -9374,21 +9378,20 @@ msgstr ""
#: text/chapter13.md:283
#, markdown-text
msgid ""
-"In practice, this means that we can write properties which take functions as "
-"arguments. In the case of the `mergeWith` function, we can generate the "
-"first argument randomly, modifying our tests to take account of the new "
-"argument."
+"In practice, we can write properties that take functions as arguments. In "
+"the case of the `mergeWith` function, we can generate the first argument "
+"randomly, modifying our tests to take account of the new argument."
msgstr ""
#. type: Plain text
#: text/chapter13.md:285
#, markdown-text
msgid ""
-"We cannot guarantee that the result will be sorted - we do not even "
-"necessarily have an `Ord` instance - but we can expect that the result be "
+"We cannot guarantee that the result will be sorted – we do not even "
+"necessarily have an `Ord` instance – but we can expect that the result be "
"sorted with respect to the function `f` that we pass in as an argument. In "
-"addition, we need the two input arrays to be sorted with respect to `f`, so "
-"we use the `sortBy` function to sort `xs` and `ys` based on comparison after "
+"addition, we need the two input arrays to be sorted concerning `f`, so we "
+"use the `sortBy` function to sort `xs` and `ys` based on comparison after "
"the function `f` has been applied:"
msgstr ""
@@ -9442,7 +9445,7 @@ msgstr ""
#: text/chapter13.md:315
#, markdown-text
msgid ""
-"This means that we are not limited to just values and functions - we can "
+"This means that we are not limited to just values and functions – we can "
"also randomly generate _higher-order functions_, or functions whose "
"arguments are higher-order functions, and so on."
msgstr ""
@@ -9481,9 +9484,9 @@ msgstr ""
#: text/chapter13.md:327
#, markdown-text
msgid ""
-"We have to write a function which perturbs a random generator given a value "
-"of type `Tree a`. If the input value is a `Leaf`, then we will just return "
-"the generator unchanged:"
+"We have to write a function that perturbs a random generator given a value "
+"of type `Tree a`. If the input value is a `Leaf`, then we will return the "
+"generator unchanged:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -9515,9 +9518,9 @@ msgstr ""
#: text/chapter13.md:342
#, markdown-text
msgid ""
-"Now we are free to write properties whose arguments include functions taking "
-"trees as arguments. For example, the `Tree` module defines a function "
-"`anywhere`, which tests if a predicate holds on any subtree of its argument:"
+"Now we can write properties whose arguments include functions taking trees "
+"as arguments. For example, the `Tree` module defines a function `anywhere`, "
+"which tests if a predicate holds on any subtree of its argument:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -9530,8 +9533,8 @@ msgstr ""
#: text/chapter13.md:348
#, markdown-text
msgid ""
-"Now we are able to generate the predicate function randomly. For example, we "
-"expect the `anywhere` function to _respect disjunction_:"
+"Now we can generate the predicate function randomly. For example, we expect "
+"the `anywhere` function to _respect disjunction_:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -9572,8 +9575,8 @@ msgid ""
"For the purposes of testing, we usually include calls to the `quickCheck` "
"function in the `main` action of our test suite. However, there is a variant "
"of the `quickCheck` function, called `quickCheckPure` which does not use "
-"side-effects. Instead, it is a pure function which takes a random seed as an "
-"input, and returns an array of test results."
+"side-effects. Instead, it is a pure function that takes a random seed as an "
+"input and returns an array of test results."
msgstr ""
#. type: Plain text
@@ -9616,8 +9619,8 @@ msgstr ""
#, markdown-text
msgid ""
"`quickCheckPure` might be useful in other situations, such as generating "
-"random input data for performance benchmarks, or generating sample form data "
-"for web applications."
+"random input data for performance benchmarks or sample form data for web "
+"applications."
msgstr ""
#. type: Bullet: ' 1. '
@@ -9661,8 +9664,8 @@ msgid ""
" _Hint_: Use the `oneOf` function defined in `Test.QuickCheck.Gen` to "
"define your `Arbitrary` instance.\n"
" 1. (Medium) Use `all` to simplify the result of the `quickCheckPure` "
-"function - your new function should have type `List Result -> Boolean` and "
-"should return `true` if every test passes and `false` otherwise.\n"
+"function – your new function should have the type `List Result -> Boolean` "
+"and should return `true` if every test passes and `false` otherwise.\n"
" 1. (Medium) As another approach to simplifying the result of "
"`quickCheckPure`, try writing a function `squashResults :: List Result -> "
"Result`. Consider using the `First` monoid from `Data.Maybe.First` with the "
@@ -9688,7 +9691,7 @@ msgstr ""
#: text/chapter13.md:408
#, markdown-text
msgid ""
-"We saw how to write properties as functions, and how to use the `>` "
+"We saw how to write properties as functions and how to use the `>` "
"operator to improve error messages."
msgstr ""
@@ -9697,7 +9700,7 @@ msgstr ""
#, markdown-text
msgid ""
"We saw how the `Arbitrary` and `Coarbitrary` type classes enable generation "
-"of boilerplate testing code, and how they allow us to test higher-order "
+"of boilerplate testing code and how they allow us to test higher-order "
"properties."
msgstr ""
@@ -9727,10 +9730,11 @@ msgstr ""
#: text/chapter14.md:8
#, markdown-text
msgid ""
-"A domain-specific language is a language which is well-suited to development "
+"A domain-specific language is a language that is well-suited to development "
"in a particular problem domain. Its syntax and functions are chosen to "
-"maximize readability of code used to express ideas in that domain. We have "
-"already seen a number of examples of domain-specific languages in this book:"
+"maximize the readability of code used to express ideas in that domain. We "
+"have already seen several examples of domain-specific languages in this "
+"book:"
msgstr ""
#. type: Bullet: '- '
@@ -9746,7 +9750,7 @@ msgstr ""
#: text/chapter14.md:11
#, markdown-text
msgid ""
-"The `quickcheck` package, covered in chapter 13, is a domain-specific "
+"The `quickcheck` package, covered in Chapter 13, is a domain-specific "
"language for the domain of _generative testing_. Its combinators enable a "
"particularly expressive notation for test properties."
msgstr ""
@@ -9755,10 +9759,10 @@ msgstr ""
#: text/chapter14.md:13
#, markdown-text
msgid ""
-"This chapter will take a more structured approach to some of standard "
-"techniques in the implementation of domain-specific languages. It is by no "
-"means a complete exposition of the subject, but should provide you with "
-"enough knowledge to build some practical DSLs for your own tasks."
+"This chapter will take a more structured approach to some standard "
+"techniques in implementing domain-specific languages. It is by no means a "
+"complete exposition of the subject, but should provide you with enough "
+"knowledge to build some practical DSLs for your own tasks."
msgstr ""
#. type: Plain text
@@ -9766,7 +9770,7 @@ msgstr ""
#, markdown-text
msgid ""
"Our running example will be a domain-specific language for creating HTML "
-"documents. Our aim will be to develop a type-safe language for describing "
+"documents. We will aim to develop a type-safe language for describing "
"correct HTML documents, and we will work by improving a naive implementation "
"in small steps."
msgstr ""
@@ -9775,9 +9779,8 @@ msgstr ""
#: text/chapter14.md:19
#, markdown-text
msgid ""
-"The project accompanying this chapter adds one new dependency - the `free` "
-"library, which defines the _free monad_, one of the tools which we will be "
-"using."
+"The project accompanying this chapter adds one new dependency – the `free` "
+"library, which defines the _free monad_, one of the tools we will use."
msgstr ""
#. type: Plain text
@@ -9789,7 +9792,7 @@ msgstr ""
#. type: Title ##
#: text/chapter14.md:22
#, markdown-text, no-wrap
-msgid "A HTML Data Type"
+msgid "An HTML Data Type"
msgstr ""
#. type: Plain text
@@ -9826,7 +9829,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `Element` type represents HTML elements. Each element consists of an "
-"element name, an array of attribute pairs and some content. The content "
+"element name, an array of attribute pairs, and some content. The content "
"property uses the `Maybe` type to indicate that an element might be open "
"(containing other elements and text) or closed."
msgstr ""
@@ -9892,7 +9895,7 @@ msgstr ""
#: text/chapter14.md:87
#, markdown-text
msgid ""
-"Creating HTML documents is difficult - every new element requires at least "
+"Creating HTML documents is difficult – every new element requires at least "
"one record and one data constructor."
msgstr ""
@@ -9942,8 +9945,8 @@ msgid ""
"The first technique we will apply is simple but can be very "
"effective. Instead of exposing the representation of the data to the "
"module's users, we can use the module exports list to hide the `Element`, "
-"`Content` and `Attribute` data constructors, and only export so-called "
-"_smart constructors_, which construct data which is known to be correct."
+"`Content`, and `Attribute` data constructors, and only export so-called "
+"_smart constructors_, which construct data known to be correct."
msgstr ""
#. type: Plain text
@@ -9971,7 +9974,7 @@ msgstr ""
#, markdown-text
msgid ""
"Next, we create smart constructors for those HTML elements we want our users "
-"to be able to create, by applying the `element` function:"
+"to be able to create by applying the `element` function:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -10037,7 +10040,7 @@ msgstr ""
#: text/chapter14.md:139
#, markdown-text
msgid ""
-"A type constructor and any associated data constructors, indicated by the "
+"A type constructor and any associated data constructors indicated by the "
"name of the type followed by a parenthesized list of exported data "
"constructors."
msgstr ""
@@ -10047,8 +10050,7 @@ msgstr ""
#, markdown-text
msgid ""
"Here, we export the `Element` _type_, but we do not export its data "
-"constructors. If we did, the user would be able to construct invalid HTML "
-"elements."
+"constructors. If we did, the user could construct invalid HTML elements."
msgstr ""
#. type: Plain text
@@ -10085,7 +10087,7 @@ msgstr ""
#, markdown-text
msgid ""
"We can apply this technique to the `Content` type very easily. We simply "
-"remove the data constructors for the `Content` type from the exports list, "
+"remove the data constructors for the `Content` type from the exports list "
"and provide the following smart constructors:"
msgstr ""
@@ -10126,9 +10128,9 @@ msgstr ""
#, markdown-text
msgid ""
"This representation suffers from the same problem as the original `Element` "
-"type - it is possible to represent attributes which do not exist or whose "
+"type – it is possible to represent attributes that do not exist or whose "
"names were entered incorrectly. To solve this problem, we can create a "
-"newtype which represents attribute names:"
+"newtype that represents attribute names:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -10248,9 +10250,9 @@ msgstr ""
msgid ""
"Note, however, that no changes had to be made to the `render` function, "
"because the underlying data representation never changed. This is one of the "
-"benefits of the smart constructors approach - it allows us to separate the "
-"internal data representation for a module from the representation which is "
-"perceived by users of its external API."
+"benefits of the smart constructors approach – it allows us to separate the "
+"internal data representation for a module from the representation perceived "
+"by users of its external API."
msgstr ""
#. type: Bullet: ' 1. '
@@ -10265,8 +10267,8 @@ msgstr ""
#: text/chapter14.md:253
#, markdown-text
msgid ""
-"(Medium) Some HTML attributes such as `checked` and `disabled` do not "
-"require values, and may be rendered as _empty attributes_:"
+"(Medium) Some HTML attributes, such as `checked` and `disabled`, do not "
+"require values and may be rendered as _empty attributes_:"
msgstr ""
#. type: Plain text
@@ -10472,7 +10474,7 @@ msgstr ""
#: text/chapter14.md:356
#, markdown-text
msgid ""
-"(Easy) Create a data type which represents either pixel or percentage "
+"(Easy) Create a data type representing either pixel or percentage "
"lengths. Write an instance of `IsValue` for your type. Modify the `width` "
"and `height` attributes to use your new type."
msgstr ""
@@ -10483,7 +10485,7 @@ msgstr ""
msgid ""
"(Difficult) By defining type-level representatives for the Boolean values "
"`true` and `false`, we can use a phantom type to encode whether an "
-"`AttributeKey` represents an _empty attribute_ such as `disabled` or "
+"`AttributeKey` represents an _empty attribute_, such as `disabled` or "
"`checked`."
msgstr ""
@@ -10519,7 +10521,7 @@ msgid ""
"In our final set of modifications to our API, we will use a construction "
"called the _free monad_ to turn our `Content` type into a monad, enabling do "
"notation. This will allow us to structure our HTML documents in a form in "
-"which the nesting of elements becomes clearer - instead of this:"
+"which the nesting of elements becomes clearer – instead of this:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -10561,7 +10563,7 @@ msgstr ""
msgid ""
"However, do notation is not the only benefit of a free monad. The free monad "
"allows us to separate the _representation_ of our monadic actions from their "
-"_interpretation_, and even support _multiple interpretations_ of the same "
+"_interpretation_ and even support _multiple interpretations_ of the same "
"actions."
msgstr ""
@@ -10569,7 +10571,7 @@ msgstr ""
#: text/chapter14.md:394
#, markdown-text
msgid ""
-"The `Free` monad is defined in the `free` library, in the "
+"The `Free` monad is defined in the `free` library in the "
"`Control.Monad.Free` module. We can find out some basic information about it "
"using PSCi, as follows:"
msgstr ""
@@ -10588,9 +10590,9 @@ msgstr ""
#: text/chapter14.md:403
#, markdown-text
msgid ""
-"The kind of `Free` indicates that it takes a type constructor as an "
-"argument, and returns another type constructor. In fact, the `Free` monad "
-"can be used to turn any `Functor` into a `Monad`!"
+"The kind of `Free` indicates that it takes a type constructor as an argument "
+"and returns another type constructor. In fact, the `Free` monad can be used "
+"to turn any `Functor` into a `Monad`!"
msgstr ""
#. type: Plain text
@@ -10600,8 +10602,7 @@ msgid ""
"We begin by defining the _representation_ of our monadic actions. To do "
"this, we need to create a `Functor` with one data constructor for each "
"monadic action we wish to support. In our case, our two monadic actions will "
-"be `elem` and `text`. In fact, we can simply modify our `Content` type as "
-"follows:"
+"be `elem` and `text`. We can simply modify our `Content` type as follows:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -10622,7 +10623,7 @@ msgstr ""
#, markdown-text
msgid ""
"Here, the `ContentF` type constructor looks just like our old `Content` data "
-"type - however, it now takes a type argument `a`, and each data constructor "
+"type – however, it now takes a type argument `a`, and each data constructor "
"has been modified to take a value of type `a` as an additional argument. The "
"`Functor` instance simply applies the function `f` to the value of type `a` "
"in each data constructor."
@@ -10648,7 +10649,7 @@ msgstr ""
#, markdown-text
msgid ""
"Instead of a type synonym, we might use a `newtype` to avoid exposing the "
-"internal representation of our library to our users - by hiding the "
+"internal representation of our library to our users – by hiding the "
"`Content` data constructor, we restrict our users to only using the monadic "
"actions we provide."
msgstr ""
@@ -10687,7 +10688,7 @@ msgstr ""
msgid ""
"In addition, we have to modify our `elem` and `text` functions, which become "
"our new monadic actions for the `Content` monad. To do this, we can use the "
-"`liftF` function, provided by the `Control.Monad.Free` module. Here is its "
+"`liftF` function provided by the `Control.Monad.Free` module. Here is its "
"type:"
msgstr ""
@@ -10702,8 +10703,8 @@ msgstr ""
#, markdown-text
msgid ""
"`liftF` allows us to construct an action in our free monad from a value of "
-"type `f a` for some type `a`. In our case, we can simply use the data "
-"constructors of our `ContentF` type constructor directly:"
+"type `f a` for some type `a`. In our case, we can use the data constructors "
+"of our `ContentF` type constructor directly:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -10772,10 +10773,9 @@ msgstr ""
#: text/chapter14.md:479
#, markdown-text
msgid ""
-"_Note_: Technically, we are restricted to using monads `m` which satisfy the "
-"stronger `MonadRec` constraint. In practice, this means that we don't need "
-"to worry about stack overflow, since `m` supports safe _monadic tail "
-"recursion_."
+"_Note_: Technically, we are restricted to monads `m` that satisfy the "
+"stronger `MonadRec` constraint. In practice, we don't need to worry about "
+"stack overflow since `m` supports safe _monadic tail recursion_."
msgstr ""
#. type: Plain text
@@ -10783,7 +10783,7 @@ msgstr ""
#, markdown-text
msgid ""
"First, we have to choose a monad in which we can interpret our actions. We "
-"will use the `Writer String` monad to accumulate a HTML string as our "
+"will use the `Writer String` monad to accumulate an HTML string as our "
"result."
msgstr ""
@@ -10904,7 +10904,7 @@ msgstr ""
#: text/chapter14.md:540
#, markdown-text
msgid ""
-"We can implement this function by simply pattern matching on the two data "
+"We can implement this function by pattern matching on the two data "
"constructors of `ContentF`:"
msgstr ""
@@ -10924,7 +10924,7 @@ msgstr ""
#: text/chapter14.md:551
#, markdown-text
msgid ""
-"In each case, the expression `rest` has the type `Content Unit`, and "
+"In each case, the expression `rest` has the type `Content Unit` and "
"represents the remainder of the interpreted computation. We can complete "
"each case by returning the `rest` action."
msgstr ""
@@ -10983,18 +10983,18 @@ msgstr ""
#, markdown-text
msgid ""
"Let's illustrate the power of the free monad construction by extending our "
-"language with a new monadic action which returns a non-trivial result."
+"language with a new monadic action that returns a non-trivial result."
msgstr ""
#. type: Plain text
#: text/chapter14.md:580
#, markdown-text
msgid ""
-"Suppose we want to generate HTML documents which contain hyperlinks to "
+"Suppose we want to generate HTML documents that contain hyperlinks to "
"different sections of the document using _anchors_. We can accomplish this "
-"already, by generating anchor names by hand and including them at least "
-"twice in the document: once at the definition of the anchor itself, and once "
-"in each hyperlink. However, this approach has some basic issues:"
+"by generating anchor names by hand and including them at least twice in the "
+"document: once at the anchor's definition and once in each "
+"hyperlink. However, this approach has some basic issues:"
msgstr ""
#. type: Bullet: '- '
@@ -11013,9 +11013,9 @@ msgstr ""
#: text/chapter14.md:585
#, markdown-text
msgid ""
-"In the interest of protecting the developer from their own mistakes, we can "
-"introduce a new type which represents anchor names, and provide a monadic "
-"action for generating new unique names."
+"To protect the developer from their mistakes, we can introduce a new type "
+"that represents anchor names and provide a monadic action for generating new "
+"unique names."
msgstr ""
#. type: Plain text
@@ -11046,8 +11046,8 @@ msgstr ""
#: text/chapter14.md:598
#, markdown-text
msgid ""
-"Next, we define an instance for the `IsValue` type class for our new type, "
-"so that we are able to use names in attribute values:"
+"Next, we define an instance for the `IsValue` type class for our new type so "
+"that we can use names in attribute values:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -11166,21 +11166,20 @@ msgstr ""
#: text/chapter14.md:654
#, markdown-text
msgid ""
-"Notice that we provide the `id` function as our continuation, meaning that "
-"we return the result of type `Name` unchanged."
+"Notice that we provide the `id` function as our continuation, meaning we "
+"return the result of type `Name` unchanged."
msgstr ""
#. type: Plain text
#: text/chapter14.md:656
#, markdown-text
msgid ""
-"Finally, we need to update our interpretation function, to interpret the new "
+"Finally, we need to update our interpretation function to interpret the new "
"action. We previously used the `Writer String` monad to interpret our "
-"computations, but that monad does not have the ability to generate new "
-"names, so we must switch to something else. The `WriterT` monad transformer "
-"can be used with the `State` monad to combine the effects we need. We can "
-"define our interpretation monad as a type synonym to keep our type "
-"signatures short:"
+"computations, but that monad cannot generate new names, so we must switch to "
+"something else. The `WriterT` monad transformer can be used with the `State` "
+"monad to combine the effects we need. We can define our interpretation monad "
+"as a type synonym to keep our type signatures short:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -11202,8 +11201,8 @@ msgstr ""
#, markdown-text
msgid ""
"Because the `Writer` and `WriterT` monads use the same type class members to "
-"abstract their actions, we do not need to change any actions - we only need "
-"to replace every reference to `Writer String` with `Interp`. We do, however, "
+"abstract their actions, we do not need to change any actions – we only need "
+"to replace every reference to `Writer String` with `Interp`. However, we "
"need to modify the handler used to run our computation. Instead of just "
"`execWriter`, we now need to use `evalState` as well:"
msgstr ""
@@ -11251,7 +11250,7 @@ msgstr ""
#, markdown-text
msgid ""
"With that, we can try out our new functionality in PSCi, by generating a "
-"unique name inside the `Content` monad, and using it as both the name of an "
+"unique name inside the `Content` monad and using it as both the name of an "
"element and the target of a hyperlink:"
msgstr ""
@@ -11280,12 +11279,12 @@ msgstr ""
#: text/chapter14.md:703
#, markdown-text
msgid ""
-"You can verify that multiple calls to `newName` do in fact result in unique "
-"names."
+"You can verify that multiple calls to `newName` do, in fact, result in "
+"unique names."
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter14.md:712
+#: text/chapter14.md:711
#, markdown-text
msgid ""
"(Medium) We can simplify the API further by hiding the `Element` type from "
@@ -11293,7 +11292,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' - '
-#: text/chapter14.md:712
+#: text/chapter14.md:711
#, markdown-text
msgid ""
"Combine functions like `p` and `img` (with return type `Element`) with the "
@@ -11301,25 +11300,30 @@ msgid ""
msgstr ""
#. type: Bullet: ' - '
-#: text/chapter14.md:712
+#: text/chapter14.md:711
#, markdown-text
msgid ""
"Change the `render` function to accept an argument of type `Content Unit` "
"instead of `Element`."
msgstr ""
-#. type: Plain text
-#: text/chapter14.md:712
-#, markdown-text, no-wrap
+#. type: Bullet: ' 1. '
+#: text/chapter14.md:711
+#, markdown-text
msgid ""
-" 1. (Medium) Hide the implementation of the `Content` monad by using a "
-"`newtype` instead of a type synonym. You should not export the data\n"
-" constructor for your `newtype`.\n"
-" 1. (Difficult) Modify the `ContentF` type to support a new action\n"
+"(Medium) Hide the implementation of the `Content` monad using a `newtype` "
+"instead of a type synonym. You should not export the data constructor for "
+"your `newtype`."
+msgstr ""
+
+#. type: Bullet: ' 1. '
+#: text/chapter14.md:711
+#, markdown-text
+msgid "(Difficult) Modify the `ContentF` type to support a new action"
msgstr ""
#. type: Plain text
-#: text/chapter14.md:716
+#: text/chapter14.md:715
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -11328,7 +11332,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter14.md:718
+#: text/chapter14.md:717
#, markdown-text, no-wrap
msgid ""
" which returns a boolean value indicating whether or not the document is "
@@ -11336,7 +11340,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter14.md:720
+#: text/chapter14.md:719
#, markdown-text, no-wrap
msgid ""
" _Hint_: use the `ask` action and the `ReaderT` monad transformer to "
@@ -11345,33 +11349,33 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter14.md:724
+#: text/chapter14.md:723
#, markdown-text
msgid ""
"In this chapter, we developed a domain-specific language for creating HTML "
-"documents, by incrementally improving a naive implementation using some "
+"documents by incrementally improving a naive implementation using some "
"standard techniques:"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter14.md:729
+#: text/chapter14.md:728
#, markdown-text
msgid ""
"We used _smart constructors_ to hide the details of our data representation, "
-"only permitting the user to create documents which were "
+"only permitting the user to create documents that were "
"_correct-by-construction_."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter14.md:729
+#: text/chapter14.md:728
#, markdown-text
msgid ""
-"We used an _user-defined infix binary operator_ to improve the syntax of the "
+"We used a _user-defined infix binary operator_ to improve the syntax of the "
"language."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter14.md:729
+#: text/chapter14.md:728
#, markdown-text
msgid ""
"We used _phantom types_ to encode additional information in the types of our "
@@ -11379,17 +11383,17 @@ msgid ""
msgstr ""
#. type: Bullet: '- '
-#: text/chapter14.md:729
+#: text/chapter14.md:728
#, markdown-text
msgid ""
"We used the _free monad_ to turn our array representation of a collection of "
"content into a monadic representation supporting do notation. We then "
-"extended this representation to support a new monadic action, and "
-"interpreted the monadic computations using standard monad transformers."
+"extended this representation to support a new monadic action and interpreted "
+"the monadic computations using standard monad transformers."
msgstr ""
#. type: Plain text
-#: text/chapter14.md:731
+#: text/chapter14.md:730
#, markdown-text
msgid ""
"These techniques all leverage PureScript's module and type systems, either "
@@ -11398,13 +11402,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter14.md:732
+#: text/chapter14.md:731
#, markdown-text
msgid ""
-"The implementation of domain-specific languages in functional programming "
-"languages is an area of active research, but hopefully this provides a "
-"useful introduction some simple techniques, and illustrates the power of "
-"working in a language with expressive types."
+"Implementing domain-specific languages in functional programming languages "
+"is an area of active research. Still, hopefully, this provides a useful "
+"introduction to some simple techniques and illustrates the power of working "
+"in a language with expressive types."
msgstr ""
#. type: Title #
@@ -11497,11 +11501,11 @@ msgid ""
"The book repo contains PureScript example code and unit tests for the "
"exercises that accompany each chapter. There's some initial setup required "
"to reset the exercise solutions so they are ready to be solved by you. Use "
-"the `resetSolutions.sh` script to simplify this process. While you're at it, "
-"you should also strip out all the anchor comments with the "
-"`removeAnchors.sh` script (these anchors are used for copying code snippets "
-"into the book's rendered markdown, and you probably don't need this clutter "
-"in your local repo):"
+"the `resetSolutions.sh` script to simplify this process. While at it, you "
+"should also strip out all the anchor comments with the `removeAnchors.sh` "
+"script (these anchors are used for copying code snippets into the book's "
+"rendered markdown, and you probably don't need this clutter in your local "
+"repo):"
msgstr ""
#. type: Fenced code block (sh)
@@ -11552,10 +11556,10 @@ msgstr ""
msgid ""
"Note that the `answer` function (found in `src/Euler.purs`) has been "
"modified to find the multiples of 3 and 5 below any integer. The test suite "
-"(found in `test/Main.purs`) for this `answer` function is more comprehensive "
-"than the test in the earlier getting-started guide. Don't worry about "
-"understanding how this test framework code works while reading these early "
-"chapters."
+"(located in `test/Main.purs`) for this `answer` function is more "
+"comprehensive than the test in the earlier getting-started guide. Don't "
+"worry about understanding how this test framework code works while reading "
+"these early chapters."
msgstr ""
#. type: Plain text
@@ -11571,7 +11575,7 @@ msgstr ""
#: text/chapter2.md:59
#, markdown-text
msgid ""
-"Let's work through this next exercise together in test-driven-development "
+"Let's work through this next exercise together in a test-driven-development "
"style."
msgstr ""
@@ -11601,7 +11605,7 @@ msgstr ""
#, markdown-text
msgid ""
"We'll start by enabling the tests for this exercise. Move the start of the "
-"block-comment down a few lines as shown below. Block comments start with "
+"block-comment down a few lines, as shown below. Block comments start with "
"`{-` and end with `-}`:"
msgstr ""
@@ -11638,8 +11642,8 @@ msgstr ""
#: text/chapter2.md:86
#, markdown-text
msgid ""
-"Let's first take a look at what happens with a faulty version of this "
-"function. Add the following code to `test/MySolutions.purs`:"
+"Let's first look at what happens with a faulty version of this function. Add "
+"the following code to `test/MySolutions.purs`:"
msgstr ""
#. type: Fenced code block (hs)
@@ -11744,10 +11748,10 @@ msgstr ""
#, markdown-text
msgid ""
"There will be many more exercises in the chapters ahead, and working through "
-"those really helps with learning the material. If you're stumped by any of "
-"the exercises, please reach out to any of the community resources listed in "
-"the [Getting Help](https://book.purescript.org/chapter1.html#getting-help) "
-"section of this book, or even file an issue in this [book's "
+"those helps with learning the material. If any of the exercises stumps you, "
+"please reach out to any of the community resources listed in the [Getting "
+"Help](https://book.purescript.org/chapter1.html#getting-help) section of "
+"this book, or even file an issue in this [book's "
"repo](https://github.com/purescript-contrib/purescript-book/issues). This "
"reader feedback on which exercises could be made more approachable helps us "
"improve the book."
@@ -11758,11 +11762,11 @@ msgstr ""
#, markdown-text
msgid ""
"Once you solve all the exercises in a chapter, you may compare your answers "
-"against those in the `no-peeking/Solutions.purs`. No peeking please without "
-"putting in an honest effort to solve these yourself though. And even if you "
+"against those in the `no-peeking/Solutions.purs`. No peeking, please, "
+"without putting in an honest effort to solve these yourself. And even if you "
"are stuck, try asking a community member for help first, as we would prefer "
"to give you a small hint rather than spoil the exercise. If you found a more "
-"elegant solution (that still only requires knowledge of covered content), "
+"elegant solution (that only requires knowledge of the covered content), "
"please send us a PR."
msgstr ""
@@ -11830,22 +11834,31 @@ msgid "Here, we import several modules:"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter3.md:24
+#: text/chapter3.md:25
+#, markdown-text
+msgid ""
+"The `Prelude` module, which contains a small set of standard definitions and "
+"functions. It re-exports many foundational modules from "
+"the `purescript-prelude` library."
+msgstr ""
+
+#. type: Bullet: '- '
+#: text/chapter3.md:25
#, markdown-text
msgid "The `Control.Plus` module, which defines the `empty` value."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter3.md:24
+#: text/chapter3.md:25
#, markdown-text
msgid ""
-"The `Data.List` module, which is provided by the `lists` package which can "
-"be installed using Spago. It contains a few functions which we will need for "
+"The `Data.List` module, provided by the `lists` package, which can be "
+"installed using Spago. It contains a few functions that we will need for "
"working with linked lists."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter3.md:24
+#: text/chapter3.md:25
#, markdown-text
msgid ""
"The `Data.Maybe` module, which defines data types and functions for working "
@@ -11853,16 +11866,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:26
+#: text/chapter3.md:27
#, markdown-text
msgid ""
"Notice that the imports for these modules are listed explicitly in "
-"parentheses. This is generally a good practice, as it helps to avoid "
-"conflicting imports."
+"parentheses (except for `Prelude`, which is typically imported as an open "
+"import). This is generally a good practice, as it helps to avoid conflicting "
+"imports."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:28
+#: text/chapter3.md:29
#, markdown-text
msgid ""
"Assuming you have cloned the book's source code repository, the project for "
@@ -11870,7 +11884,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:29
+#: text/chapter3.md:30
#, no-wrap
msgid ""
"$ cd chapter3\n"
@@ -11878,24 +11892,24 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter3.md:34
+#: text/chapter3.md:35
#, markdown-text, no-wrap
msgid "Simple Types"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:37
+#: text/chapter3.md:38
#, markdown-text
msgid ""
-"PureScript defines three built-in types which correspond to JavaScript's "
-"primitive types: numbers, strings and booleans. These are defined in the "
+"PureScript defines three built-in types corresponding to JavaScript's "
+"primitive types: numbers, strings, and booleans. These are defined in the "
"`Prim` module, which is implicitly imported by every module. They are called "
"`Number`, `String`, and `Boolean`, respectively, and you can see them in "
"PSCi by using the `:type` command to print the types of some simple values:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:38
+#: text/chapter3.md:39
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -11911,15 +11925,15 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:52
+#: text/chapter3.md:53
#, markdown-text
msgid ""
-"PureScript defines some other built-in types: integers, characters, arrays, "
+"PureScript defines other built-in types: integers, characters, arrays, "
"records, and functions."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:54
+#: text/chapter3.md:55
#, markdown-text
msgid ""
"Integers are differentiated from floating point values of type `Number` by "
@@ -11927,7 +11941,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:55
+#: text/chapter3.md:56
#, no-wrap
msgid ""
"> :type 1\n"
@@ -11935,7 +11949,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:61
+#: text/chapter3.md:62
#, markdown-text
msgid ""
"Character literals are wrapped in single quotes, unlike string literals "
@@ -11943,7 +11957,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:62
+#: text/chapter3.md:63
#, no-wrap
msgid ""
"> :type 'a'\n"
@@ -11951,7 +11965,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:68
+#: text/chapter3.md:69
#, markdown-text
msgid ""
"Arrays correspond to JavaScript arrays, but unlike in JavaScript, all "
@@ -11959,7 +11973,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:69
+#: text/chapter3.md:70
#, no-wrap
msgid ""
"> :type [1, 2, 3]\n"
@@ -11973,16 +11987,15 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:81
+#: text/chapter3.md:82
#, markdown-text
msgid ""
-"The error in the last example is an error from the type checker, which "
-"unsuccessfully attempted to _unify_ (i.e. make equal) the types of the two "
-"elements."
+"The last example shows an error from the type checker, which failed to "
+"_unify_ (i.e., make equal) the types of the two elements."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:83
+#: text/chapter3.md:84
#, markdown-text
msgid ""
"Records correspond to JavaScript's objects, and record literals have the "
@@ -11990,7 +12003,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:84
+#: text/chapter3.md:85
#, no-wrap
msgid ""
"> author = { name: \"Phil\", interests: [\"Functional Programming\", "
@@ -12003,16 +12016,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:94
+#: text/chapter3.md:95
#, markdown-text
msgid ""
-"This type indicates that the specified object has two _fields_, a `name` "
-"field which has type `String`, and an `interests` field, which has type "
-"`Array String`, i.e. an array of `String`s."
+"This type indicates that the specified object has two _fields_: a `name` "
+"field with the type `String` and an `interests` field with the type `Array "
+"String`, i.e., an array of `String`s."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:96
+#: text/chapter3.md:97
#, markdown-text
msgid ""
"Fields of records can be accessed using a dot, followed by the label of the "
@@ -12020,7 +12033,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:97
+#: text/chapter3.md:98
#, no-wrap
msgid ""
"> author.name\n"
@@ -12031,57 +12044,40 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:106
-#, markdown-text
-msgid ""
-"PureScript's functions correspond to JavaScript's functions. The PureScript "
-"standard libraries provide plenty of examples of functions, and we will see "
-"more in this chapter:"
-msgstr ""
-
-#. type: Fenced code block (text)
#: text/chapter3.md:107
-#, no-wrap
-msgid ""
-"> import Prelude\n"
-"> :type flip\n"
-"forall a b c. (a -> b -> c) -> b -> a -> c\n"
-"\n"
-"> :type const\n"
-"forall a b. a -> b -> a\n"
-msgstr ""
-
-#. type: Plain text
-#: text/chapter3.md:117
#, markdown-text
msgid ""
-"Functions can be defined at the top-level of a file by specifying arguments "
-"before the equals sign:"
+"PureScript's functions correspond to JavaScript's functions. Functions can "
+"be defined at the top-level of a file by specifying arguments before the "
+"equals sign:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:118
+#: text/chapter3.md:108
#, no-wrap
msgid ""
+"import Prelude -- bring the (+) operator into scope\n"
+"\n"
"add :: Int -> Int -> Int\n"
"add x y = x + y\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:124
+#: text/chapter3.md:116
#, markdown-text
msgid ""
-"Alternatively, functions can be defined inline, by using a backslash "
-"character followed by a space-delimited list of argument names. To enter a "
-"multi-line declaration in PSCi, we can enter \"paste mode\" by using the "
-"`:paste` command. In this mode, declarations are terminated using the "
-"_Control-D_ key sequence:"
+"Alternatively, functions can be defined inline using a backslash character "
+"followed by a space-delimited list of argument names. To enter a multi-line "
+"declaration in PSCi, we can enter \"paste mode\" using the `:paste` "
+"command. In this mode, declarations are terminated using the _Control-D_ key "
+"sequence:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:125
+#: text/chapter3.md:117
#, no-wrap
msgid ""
+"> import Prelude\n"
"> :paste\n"
"… add :: Int -> Int -> Int\n"
"… add = \\x y -> x + y\n"
@@ -12089,7 +12085,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:133
+#: text/chapter3.md:126
#, markdown-text
msgid ""
"Having defined this function in PSCi, we can _apply_ it to its arguments by "
@@ -12097,7 +12093,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:134
+#: text/chapter3.md:127
#, no-wrap
msgid ""
"> add 10 20\n"
@@ -12105,97 +12101,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter3.md:139
-#, markdown-text, no-wrap
-msgid "Quantified Types"
-msgstr ""
-
-#. type: Plain text
-#: text/chapter3.md:142
-#, markdown-text
-msgid ""
-"In the previous section, we saw the types of some functions defined in the "
-"Prelude. For example, the `flip` function had the following type:"
-msgstr ""
-
-#. type: Fenced code block (text)
-#: text/chapter3.md:143
-#, no-wrap
-msgid ""
-"> :type flip\n"
-"forall a b c. (a -> b -> c) -> b -> a -> c\n"
-msgstr ""
-
-#. type: Plain text
-#: text/chapter3.md:149
-#, markdown-text
-msgid ""
-"The keyword `forall` here indicates that `flip` has a _universally "
-"quantified type_. It means that we can substitute any types for `a`, `b` and "
-"`c`, and `flip` will work with those types."
-msgstr ""
-
-#. type: Plain text
-#: text/chapter3.md:151
-#, markdown-text
-msgid ""
-"For example, we might choose the type `a` to be `Int`, `b` to be `String` "
-"and `c` to be `String`. In that case we could _specialize_ the type of "
-"`flip` to"
-msgstr ""
-
-#. type: Fenced code block (text)
-#: text/chapter3.md:152
-#, no-wrap
-msgid "(Int -> String -> String) -> String -> Int -> String\n"
-msgstr ""
-
-#. type: Plain text
-#: text/chapter3.md:157
-#, markdown-text
-msgid ""
-"We don't have to indicate in code that we want to specialize a quantified "
-"type - it happens automatically. For example, we can just use `flip` as if "
-"it had this type already:"
-msgstr ""
-
-#. type: Fenced code block (text)
-#: text/chapter3.md:158
-#, no-wrap
-msgid ""
-"> flip (\\n s -> show n <> s) \"Ten\" 10\n"
-"\n"
-"\"10Ten\"\n"
-msgstr ""
-
-#. type: Plain text
-#: text/chapter3.md:165
-#, markdown-text
-msgid ""
-"While we can choose any types for `a`, `b` and `c`, we have to be "
-"consistent. The type of the function we passed to `flip` had to be "
-"consistent with the types of the other arguments. That is why we passed the "
-"string `\"Ten\"` as the second argument, and the number `10` as the "
-"third. It would not work if the arguments were reversed:"
-msgstr ""
-
-#. type: Fenced code block (text)
-#: text/chapter3.md:166
-#, no-wrap
-msgid ""
-"> flip (\\n s -> show n <> s) 10 \"Ten\"\n"
-"\n"
-"Could not match type Int with type String\n"
-msgstr ""
-
-#. type: Title ##
-#: text/chapter3.md:172
+#: text/chapter3.md:132
#, markdown-text, no-wrap
msgid "Notes On Indentation"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:175
+#: text/chapter3.md:135
#, markdown-text
msgid ""
"PureScript code is _indentation-sensitive_, just like Haskell, but unlike "
@@ -12205,21 +12117,21 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:177
+#: text/chapter3.md:137
#, markdown-text
msgid ""
-"If a declaration spans multiple lines, then any lines except the first must "
-"be indented past the indentation level of the first line."
+"If a declaration spans multiple lines, any lines except the first must be "
+"indented past the indentation level of the first line."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:179
+#: text/chapter3.md:139
#, markdown-text
-msgid "Therefore, the following is valid PureScript code:"
+msgid "Therefore, the following is a valid PureScript code:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:180
+#: text/chapter3.md:140
#, no-wrap
msgid ""
"add x y z = x +\n"
@@ -12227,13 +12139,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:186
+#: text/chapter3.md:146
#, markdown-text
-msgid "But this is not valid code:"
+msgid "But this is not a valid code:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:187
+#: text/chapter3.md:147
#, no-wrap
msgid ""
"add x y z = x +\n"
@@ -12241,7 +12153,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:193
+#: text/chapter3.md:153
#, markdown-text
msgid ""
"In the second case, the PureScript compiler will try to parse _two_ "
@@ -12249,7 +12161,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:195
+#: text/chapter3.md:155
#, markdown-text
msgid ""
"Generally, any declarations defined in the same block should be indented at "
@@ -12258,7 +12170,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:196
+#: text/chapter3.md:156
#, no-wrap
msgid ""
"> :paste\n"
@@ -12268,13 +12180,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:204
+#: text/chapter3.md:164
#, markdown-text
-msgid "but this is not:"
+msgid "But this is not:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:205
+#: text/chapter3.md:165
#, no-wrap
msgid ""
"> :paste\n"
@@ -12284,47 +12196,60 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:213
+#: text/chapter3.md:173
#, markdown-text
msgid ""
-"Certain PureScript keywords (such as `where`, `of` and `let`) introduce a "
-"new block of code, in which declarations must be further-indented:"
+"Certain PureScript keywords introduce a new block of code, in which "
+"declarations must be further-indented:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:214
+#: text/chapter3.md:174
#, no-wrap
msgid ""
-"example x y z = foo + bar\n"
-" where\n"
+"example x y z =\n"
+" let\n"
" foo = x * y\n"
" bar = y * z\n"
+" in\n"
+" foo + bar\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:222
+#: text/chapter3.md:184
#, markdown-text
+msgid "This doesn't compile:"
+msgstr ""
+
+#. type: Fenced code block (haskell)
+#: text/chapter3.md:185
+#, no-wrap
msgid ""
-"Note how the declarations for `foo` and `bar` are indented past the "
-"declaration of `example`."
+"example x y z =\n"
+" let\n"
+" foo = x * y\n"
+" bar = y * z\n"
+" in\n"
+" foo + bar\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:224
+#: text/chapter3.md:195
#, markdown-text
msgid ""
-"The only exception to this rule is the `where` keyword in the initial "
-"`module` declaration at the top of a source file."
+"If you want to learn more (or encounter any problems), see the "
+"[Syntax](https://github.com/purescript/documentation/blob/master/language/Syntax.md#syntax) "
+"documentation."
msgstr ""
#. type: Title ##
-#: text/chapter3.md:225
+#: text/chapter3.md:196
#, markdown-text, no-wrap
msgid "Defining Our Types"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:228
+#: text/chapter3.md:199
#, markdown-text
msgid ""
"A good first step when tackling a new problem in PureScript is to write out "
@@ -12333,64 +12258,64 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:229
+#: text/chapter3.md:200
#, no-wrap
msgid "{{#include ../exercises/chapter3/src/Data/AddressBook.purs:Entry}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:234
+#: text/chapter3.md:205
#, markdown-text
msgid ""
-"This defines a _type synonym_ called `Entry` - the type `Entry` is "
+"This defines a _type synonym_ called `Entry` – the type `Entry` is "
"equivalent to the type on the right of the equals symbol: a record type with "
-"three fields - `firstName`, `lastName` and `address`. The two name fields "
-"will have type `String`, and the `address` field will have type `Address`, "
-"defined as follows:"
+"three fields – `firstName`, `lastName`, and `address`. The two name fields "
+"will have the type `String`, and the `address` field will have the type "
+"`Address`, defined as follows:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:235
+#: text/chapter3.md:206
#, no-wrap
msgid "{{#include ../exercises/chapter3/src/Data/AddressBook.purs:Address}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:240
+#: text/chapter3.md:211
#, markdown-text
msgid "Note that records can contain other records."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:242
+#: text/chapter3.md:213
#, markdown-text
msgid ""
-"Now let's define a third type synonym, for our address book data structure, "
+"Now let's define a third type synonym for our address book data structure, "
"which will be represented simply as a linked list of entries:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:243
+#: text/chapter3.md:214
#, no-wrap
msgid "{{#include ../exercises/chapter3/src/Data/AddressBook.purs:AddressBook}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:248
+#: text/chapter3.md:219
#, markdown-text
msgid ""
-"Note that `List Entry` is not the same as `Array Entry`, which represents an "
+"Note that `List Entry` differs from `Array Entry`, which represents an "
"_array_ of entries."
msgstr ""
#. type: Title ##
-#: text/chapter3.md:249
+#: text/chapter3.md:220
#, markdown-text, no-wrap
msgid "Type Constructors and Kinds"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:252
+#: text/chapter3.md:223
#, markdown-text
msgid ""
"`List` is an example of a _type constructor_. Values do not have the type "
@@ -12399,17 +12324,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:254
+#: text/chapter3.md:225
#, markdown-text
msgid ""
"Note that just like function application, type constructors are applied to "
-"other types simply by juxtaposition: the type `List Entry` is in fact the "
-"type constructor `List` _applied_ to the type `Entry` - it represents a list "
+"other types simply by juxtaposition: the type `List Entry` is, in fact, the "
+"type constructor `List` _applied_ to the type `Entry` – it represents a list "
"of entries."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:256
+#: text/chapter3.md:227
#, markdown-text
msgid ""
"If we try to incorrectly define a value of type `List` (by using the type "
@@ -12417,7 +12342,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:257
+#: text/chapter3.md:228
#, no-wrap
msgid ""
"> import Data.List\n"
@@ -12426,7 +12351,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:264
+#: text/chapter3.md:235
#, markdown-text
msgid ""
"This is a _kind error_. Just like values are distinguished by their _types_, "
@@ -12435,7 +12360,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:266
+#: text/chapter3.md:237
#, markdown-text
msgid ""
"There is a special kind called `Type` which represents the kind of all types "
@@ -12443,7 +12368,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:268
+#: text/chapter3.md:239
#, markdown-text, no-wrap
msgid ""
"There are also kinds for type constructors. For example, the kind `Type -> "
@@ -12453,7 +12378,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:270
+#: text/chapter3.md:241
#, markdown-text
msgid ""
"To find out the kind of a type, use the `:kind` command in PSCi. For "
@@ -12461,7 +12386,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:271
+#: text/chapter3.md:242
#, no-wrap
msgid ""
"> :kind Number\n"
@@ -12476,7 +12401,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:284
+#: text/chapter3.md:255
#, markdown-text
msgid ""
"PureScript's _kind system_ supports other interesting kinds, which we will "
@@ -12484,13 +12409,114 @@ msgid ""
msgstr ""
#. type: Title ##
+#: text/chapter3.md:256
+#, markdown-text, no-wrap
+msgid "Quantified Types"
+msgstr ""
+
+#. type: Plain text
+#: text/chapter3.md:259
+#, markdown-text
+msgid ""
+"For illustration purposes, let's define a primitive function that takes any "
+"two arguments and returns the first one:"
+msgstr ""
+
+#. type: Fenced code block (text)
+#: text/chapter3.md:260
+#, no-wrap
+msgid ""
+"> :paste\n"
+"… constantlyFirst :: forall a b. a -> b -> a\n"
+"… constantlyFirst = \\a b -> a\n"
+"… ^D\n"
+msgstr ""
+
+#. type: Plain text
+#: text/chapter3.md:275
+#, markdown-text, no-wrap
+msgid ""
+"> Note that if you use `:type` to ask about the type of `constantlyFirst`, "
+"it will be more verbose:\n"
+">\n"
+"> ```text\n"
+"> : type constantlyFirst\n"
+"> forall (a :: Type) (b :: Type). a -> b -> a\n"
+"> ```\n"
+">\n"
+"> The type signature contains additional kind information, which explicitly "
+"notes that `a` and `b` should be concrete types.\n"
+msgstr ""
+
+#. type: Plain text
+#: text/chapter3.md:277
+#, markdown-text
+msgid ""
+"The keyword `forall` indicates that `constantlyFirst` has a _universally "
+"quantified type_. It means we can substitute any types for `a` and `b` – "
+"`constantlyFirst` will work with these types."
+msgstr ""
+
+#. type: Plain text
+#: text/chapter3.md:279
+#, markdown-text
+msgid ""
+"For example, we might choose the type `a` to be `Int` and `b` – `String`. In "
+"that case, we can _specialize_ the type of `constantlyFirst` to"
+msgstr ""
+
+#. type: Fenced code block (text)
+#: text/chapter3.md:280
+#, no-wrap
+msgid "Int -> String -> Int\n"
+msgstr ""
+
+#. type: Plain text
#: text/chapter3.md:285
+#, markdown-text
+msgid ""
+"We don't have to indicate in code that we want to specialize a quantified "
+"type – it happens automatically. For example, we can use `constantlyFirst` "
+"as if it had this type already:"
+msgstr ""
+
+#. type: Fenced code block (text)
+#: text/chapter3.md:286
+#, no-wrap
+msgid ""
+"> constantlyFirst 3 \"ignored\"\n"
+"\n"
+"3\n"
+msgstr ""
+
+#. type: Plain text
+#: text/chapter3.md:293
+#, markdown-text
+msgid ""
+"While we can choose any types for `a` and `b`, the return type of "
+"`constantlyFirst` has to be the same as the type of the first argument "
+"(because both of them are \"tied\" to the same `a`):"
+msgstr ""
+
+#. type: Fenced code block (text)
+#: text/chapter3.md:294
+#, no-wrap
+msgid ""
+":type constantlyFirst true \"ignored\"\n"
+"Boolean\n"
+"\n"
+":type constantlyFirst \"keep\" 3\n"
+"String\n"
+msgstr ""
+
+#. type: Title ##
+#: text/chapter3.md:302
#, markdown-text, no-wrap
msgid "Displaying Address Book Entries"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:288
+#: text/chapter3.md:305
#, markdown-text
msgid ""
"Let's write our first function, which will render an address book entry as a "
@@ -12502,7 +12528,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:289
+#: text/chapter3.md:306
#, no-wrap
msgid ""
"{{#include "
@@ -12510,16 +12536,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:294
+#: text/chapter3.md:311
#, markdown-text
msgid ""
-"This type signature says that `showEntry` is a function, which takes an "
+"This type signature says that `showEntry` is a function that takes an "
"`Entry` as an argument and returns a `String`. Here is the code for "
"`showEntry`:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:295
+#: text/chapter3.md:312
#, no-wrap
msgid ""
"{{#include "
@@ -12527,7 +12553,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:300
+#: text/chapter3.md:317
#, markdown-text
msgid ""
"This function concatenates the three fields of the `Entry` record into a "
@@ -12536,13 +12562,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:301
+#: text/chapter3.md:318
#, no-wrap
msgid "{{#include ../exercises/chapter3/src/Data/AddressBook.purs:showAddress}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:306
+#: text/chapter3.md:323
#, markdown-text, no-wrap
msgid ""
"A function definition begins with the name of the function, followed by a "
@@ -12553,13 +12579,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter3.md:307
+#: text/chapter3.md:324
#, markdown-text, no-wrap
msgid "Test Early, Test Often"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:310
+#: text/chapter3.md:327
#, markdown-text
msgid ""
"The PSCi interactive mode allows for rapid prototyping with immediate "
@@ -12568,19 +12594,19 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:312
+#: text/chapter3.md:329
#, markdown-text
msgid "First, build the code you've written:"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:318
+#: text/chapter3.md:335
#, markdown-text
msgid "Next, load PSCi, and use the `import` command to import your new module:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:319
+#: text/chapter3.md:336
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -12589,7 +12615,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:326
+#: text/chapter3.md:343
#, markdown-text
msgid ""
"We can create an entry by using a record literal, which looks just like an "
@@ -12597,7 +12623,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:327
+#: text/chapter3.md:344
#, no-wrap
msgid ""
"> address = { street: \"123 Fake St.\", city: \"Faketown\", state: \"CA\" "
@@ -12605,13 +12631,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:332
+#: text/chapter3.md:349
#, markdown-text
msgid "Now, try applying our function to the example:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:333
+#: text/chapter3.md:350
#, no-wrap
msgid ""
"> showAddress address\n"
@@ -12620,7 +12646,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:340
+#: text/chapter3.md:357
#, markdown-text
msgid ""
"Let's also test `showEntry` by creating an address book entry record "
@@ -12628,7 +12654,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:341
+#: text/chapter3.md:358
#, no-wrap
msgid ""
"> entry = { firstName: \"John\", lastName: \"Smith\", address: address }\n"
@@ -12638,27 +12664,27 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter3.md:348
+#: text/chapter3.md:365
#, markdown-text, no-wrap
msgid "Creating Address Books"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:351
+#: text/chapter3.md:368
#, markdown-text
msgid ""
"Now let's write some utility functions for working with address books. We "
-"will need a value which represents an empty address book: an empty list."
+"will need a value representing an empty address book: an empty list."
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:352
+#: text/chapter3.md:369
#, no-wrap
msgid "{{#include ../exercises/chapter3/src/Data/AddressBook.purs:emptyBook}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:357
+#: text/chapter3.md:374
#, markdown-text
msgid ""
"We will also need a function for inserting a value into an existing address "
@@ -12666,7 +12692,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:358
+#: text/chapter3.md:375 text/chapter3.md:459
#, no-wrap
msgid ""
"{{#include "
@@ -12674,28 +12700,28 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:363
+#: text/chapter3.md:380
#, markdown-text
msgid ""
"This type signature says that `insertEntry` takes an `Entry` as its first "
-"argument, and an `AddressBook` as a second argument, and returns a new "
+"argument, an `AddressBook` as a second argument, and returns a new "
"`AddressBook`."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:365
+#: text/chapter3.md:382
#, markdown-text
msgid ""
"We don't modify the existing `AddressBook` directly. Instead, we return a "
-"new `AddressBook` which contains the same data. As such, `AddressBook` is an "
-"example of an _immutable data structure_. This is an important idea in "
-"PureScript - mutation is a side-effect of code, and inhibits our ability to "
+"new `AddressBook`, which contains the same data. As such, `AddressBook` is "
+"an example of an _immutable data structure_. This is an important idea in "
+"PureScript – mutation is a side-effect of code and inhibits our ability to "
"reason effectively about its behavior, so we prefer pure functions and "
"immutable data where possible."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:367
+#: text/chapter3.md:384
#, markdown-text
msgid ""
"To implement `insertEntry`, we can use the `Cons` function from "
@@ -12703,7 +12729,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:368
+#: text/chapter3.md:385
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -12711,38 +12737,38 @@ msgid ""
"> import Data.List\n"
"> :type Cons\n"
"\n"
-"forall a. a -> List a -> List a\n"
+"forall (a :: Type). a -> List a -> List a\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:378
+#: text/chapter3.md:395
#, markdown-text
msgid ""
-"This type signature says that `Cons` takes a value of some type `a`, and a "
+"This type signature says that `Cons` takes a value of some type `a`, takes a "
"list of elements of type `a`, and returns a new list with entries of the "
"same type. Let's specialize this with `a` as our `Entry` type:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:379
+#: text/chapter3.md:396
#, no-wrap
msgid "Entry -> List Entry -> List Entry\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:384
+#: text/chapter3.md:401
#, markdown-text
msgid "But `List Entry` is the same as `AddressBook`, so this is equivalent to"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:385
+#: text/chapter3.md:402
#, no-wrap
msgid "Entry -> AddressBook -> AddressBook\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:390
+#: text/chapter3.md:407
#, markdown-text
msgid ""
"In our case, we already have the appropriate inputs: an `Entry`, and an "
@@ -12751,74 +12777,152 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:392
+#: text/chapter3.md:409
#, markdown-text
msgid "Here is our implementation of `insertEntry`:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:393
+#: text/chapter3.md:410
#, no-wrap
msgid "insertEntry entry book = Cons entry book\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:398
+#: text/chapter3.md:415
#, markdown-text
msgid ""
-"This brings the two arguments `entry` and `book` into scope, on the left "
-"hand side of the equals symbol, and then applies the `Cons` function to "
-"create the result."
+"This brings the two arguments `entry` and `book` into scope – on the "
+"left-hand side of the equals symbol – and then applies the `Cons` function "
+"to create the result."
msgstr ""
#. type: Title ##
-#: text/chapter3.md:399
+#: text/chapter3.md:416
#, markdown-text, no-wrap
msgid "Curried Functions"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:402
+#: text/chapter3.md:419
#, markdown-text
msgid ""
"Functions in PureScript take exactly one argument. While it looks like the "
-"`insertEntry` function takes two arguments, it is in fact an example of a "
-"_curried function_."
+"`insertEntry` function takes two arguments, it is an example of a _curried "
+"function_. In PureScript, all functions are considered curried."
+msgstr ""
+
+#. type: Plain text
+#: text/chapter3.md:421
+#, markdown-text
+msgid ""
+"Currying means converting a function that takes multiple arguments into a "
+"function that takes them one at a time. When we call a function, we pass it "
+"one argument, and it returns another function that also takes one argument "
+"until all arguments are passed."
+msgstr ""
+
+#. type: Plain text
+#: text/chapter3.md:423
+#, markdown-text
+msgid ""
+"For example, when we pass `5` to `add`, we get another function, which takes "
+"an int, adds 5 to it, and returns the sum as a result:"
+msgstr ""
+
+#. type: Fenced code block (haskell)
+#: text/chapter3.md:424
+#, no-wrap
+msgid ""
+"add :: Int -> Int -> Int\n"
+"add x y = x + y\n"
+"\n"
+"addFive :: Int -> Int\n"
+"addFive = add 5\n"
+msgstr ""
+
+#. type: Plain text
+#: text/chapter3.md:433
+#, markdown-text
+msgid ""
+"`addFive` is the result of _partial application_, which means we pass less "
+"than the total number of arguments to a function that takes multiple "
+"arguments. Let's give it a try:"
+msgstr ""
+
+#. type: Plain text
+#: text/chapter3.md:443
+#, markdown-text, no-wrap
+msgid ""
+"> Note that you must define the `add` function if you haven't already:\n"
+">\n"
+"> ```text\n"
+"> > import Prelude\n"
+"> > :paste\n"
+">… add :: Int -> Int -> Int\n"
+">… add x y = x + y\n"
+">… ^D\n"
+"> ```\n"
+msgstr ""
+
+#. type: Fenced code block (text)
+#: text/chapter3.md:444
+#, no-wrap
+msgid ""
+"> :paste\n"
+"… addFive :: Int -> Int\n"
+"… addFive = add 5\n"
+"… ^D\n"
+"\n"
+"> addFive 1\n"
+"6\n"
+"\n"
+"> add 5 1\n"
+"6\n"
+msgstr ""
+
+#. type: Plain text
+#: text/chapter3.md:458
+#, markdown-text
+msgid ""
+"To better understand currying and partial application, try making a few "
+"other functions, for example, out of `add`. And when you're done, let's "
+"return to the `insertEntry`."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:404
+#: text/chapter3.md:464
#, markdown-text, no-wrap
msgid ""
-"The `->` operator in the type of `insertEntry` associates to the right, "
-"which means that the compiler parses the type as\n"
+"The `->` operator (in the type signature) associates to the right, which "
+"means that the compiler parses the type as\n"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:405
+#: text/chapter3.md:465
#, no-wrap
msgid "Entry -> (AddressBook -> AddressBook)\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:410
+#: text/chapter3.md:470
#, markdown-text
msgid ""
-"That is, `insertEntry` is a function which returns a function! It takes a "
-"single argument, an `Entry`, and returns a new function, which in turn takes "
-"a single `AddressBook` argument and returns a new `AddressBook`."
+"`insertEntry` takes a single argument, an `Entry`, and returns a new "
+"function, which in turn takes a single `AddressBook` argument and returns a "
+"new `AddressBook`."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:412
+#: text/chapter3.md:472
#, markdown-text
msgid ""
-"This means that we can _partially apply_ `insertEntry` by specifying only "
-"its first argument, for example. In PSCi, we can see the result type:"
+"This means we can _partially apply_ `insertEntry` by specifying only its "
+"first argument, for example. In PSCi, we can see the result type:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:413
+#: text/chapter3.md:473
#, no-wrap
msgid ""
"> :type insertEntry entry\n"
@@ -12827,7 +12931,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:420
+#: text/chapter3.md:480
#, markdown-text
msgid ""
"As expected, the return type was a function. We can apply the resulting "
@@ -12835,7 +12939,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:421
+#: text/chapter3.md:481
#, no-wrap
msgid ""
"> :type (insertEntry entry) emptyBook\n"
@@ -12843,15 +12947,15 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:427
+#: text/chapter3.md:487
#, markdown-text
msgid ""
-"Note though that the parentheses here are unnecessary - the following is "
+"Note though, that the parentheses here are unnecessary – the following is "
"equivalent:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:428
+#: text/chapter3.md:488
#, no-wrap
msgid ""
"> :type insertEntry entry emptyBook\n"
@@ -12859,25 +12963,25 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:434
+#: text/chapter3.md:494
#, markdown-text
msgid ""
-"This is because function application associates to the left, and this "
-"explains why we can just specify function arguments one after the other, "
-"separated by whitespace."
+"This is because function application associates to the left, which explains "
+"why we can specify function arguments one after the other, separated by "
+"whitespace."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:436
+#: text/chapter3.md:496
#, markdown-text, no-wrap
msgid ""
"The `->` operator in function types is a _type constructor_ for "
-"functions. It takes two type arguments, the function's argument type and the "
-"return type. The left and right operands respectively.\n"
+"functions. It takes two type arguments: the function's argument type and the "
+"return type – the left and right operands, respectively.\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:438
+#: text/chapter3.md:498
#, markdown-text
msgid ""
"Note that in the rest of the book, I will talk about things like \"functions "
@@ -12887,13 +12991,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:440
+#: text/chapter3.md:500
#, markdown-text
msgid "Now consider the definition of `insertEntry`:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:441
+#: text/chapter3.md:501
#, no-wrap
msgid ""
"insertEntry :: Entry -> AddressBook -> AddressBook\n"
@@ -12901,18 +13005,18 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:447
+#: text/chapter3.md:507
#, markdown-text
msgid ""
"If we explicitly parenthesize the right-hand side, we get `(Cons entry) "
"book`. That is, `insertEntry entry` is a function whose argument is just "
"passed along to the `(Cons entry)` function. But if two functions have the "
-"same result for every input, then they are the same function! So we can "
-"remove the argument `book` from both sides:"
+"same result for every input, then they are the same! So we can remove the "
+"argument `book` from both sides:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:448
+#: text/chapter3.md:508
#, no-wrap
msgid ""
"insertEntry :: Entry -> AddressBook -> AddressBook\n"
@@ -12920,44 +13024,44 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:454
+#: text/chapter3.md:514
#, markdown-text
msgid "But now, by the same argument, we can remove `entry` from both sides:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:455
+#: text/chapter3.md:515
#, no-wrap
msgid "{{#include ../exercises/chapter3/src/Data/AddressBook.purs:insertEntry}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:460
+#: text/chapter3.md:520
#, markdown-text
msgid ""
-"This process is called _eta conversion_, and can be used (along with some "
-"other techniques) to rewrite functions in _point-free form_, which means "
-"functions defined without reference to their arguments."
+"This process, called _eta conversion_, can be used (along with other "
+"techniques) to rewrite functions in _point-free form_, which means functions "
+"defined without reference to their arguments."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:462
+#: text/chapter3.md:522
#, markdown-text
msgid ""
"In the case of `insertEntry`, _eta conversion_ has resulted in a very clear "
-"definition of our function - \"`insertEntry` is just cons on "
-"lists\". However, it is arguable whether point-free form is better in "
+"definition of our function – \"`insertEntry` is just cons on "
+"lists\". However, it is arguable whether the point-free form is better in "
"general."
msgstr ""
#. type: Title ##
-#: text/chapter3.md:463
+#: text/chapter3.md:523
#, markdown-text, no-wrap
msgid "Property Accessors"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:466
+#: text/chapter3.md:526
#, markdown-text
msgid ""
"One common pattern is to use a function to access individual fields (or "
@@ -12966,13 +13070,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:467
+#: text/chapter3.md:527
#, no-wrap
msgid "\\entry -> entry.address\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:472
+#: text/chapter3.md:532
#, markdown-text
msgid ""
"PureScript also allows [_property "
@@ -12982,13 +13086,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:473
+#: text/chapter3.md:533
#, no-wrap
msgid "_.address\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:478
+#: text/chapter3.md:538
#, markdown-text
msgid ""
"This works with any number of levels or properties, so a function to extract "
@@ -12996,19 +13100,19 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:479
+#: text/chapter3.md:539
#, no-wrap
msgid "_.address.city\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:484 text/chapter5.md:247
+#: text/chapter3.md:544 text/chapter5.md:247
#, markdown-text
msgid "For example:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:485
+#: text/chapter3.md:545
#, no-wrap
msgid ""
"> address = { street: \"123 Fake St.\", city: \"Faketown\", state: \"CA\" "
@@ -13022,41 +13126,41 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter3.md:495
+#: text/chapter3.md:555
#, markdown-text, no-wrap
msgid "Querying the Address Book"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:498
+#: text/chapter3.md:558
#, markdown-text
msgid ""
"The last function we need to implement for our minimal address book "
"application will look up a person by name and return the correct "
"`Entry`. This will be a nice application of building programs by composing "
-"small functions - a key idea from functional programming."
+"small functions – a key idea from functional programming."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:500
+#: text/chapter3.md:560
#, markdown-text
msgid ""
-"We can first filter the address book, keeping only those entries with the "
-"correct first and last names. Then we can simply return the head "
-"(i.e. first) element of the resulting list."
+"We can filter the address book, keeping only those entries with the correct "
+"first and last names. Then we can return the head (i.e., first) element of "
+"the resulting list."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:502
+#: text/chapter3.md:562
#, markdown-text
msgid ""
"With this high-level specification of our approach, we can calculate the "
-"type of our function. First open PSCi, and find the types of the `filter` "
+"type of our function. First, open PSCi, and find the types of the `filter` "
"and `head` functions:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:503
+#: text/chapter3.md:563
#, no-wrap
msgid ""
"$ spago repl\n"
@@ -13064,34 +13168,34 @@ msgid ""
"> import Data.List\n"
"> :type filter\n"
"\n"
-"forall a. (a -> Boolean) -> List a -> List a\n"
+"forall (a :: Type). (a -> Boolean) -> List a -> List a\n"
"\n"
"> :type head\n"
"\n"
-"forall a. List a -> Maybe a\n"
+"forall (a :: Type). List a -> Maybe a\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:517
+#: text/chapter3.md:577
#, markdown-text
msgid "Let's pick apart these two types to understand their meaning."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:519
+#: text/chapter3.md:579
#, markdown-text
msgid ""
"`filter` is a curried function of two arguments. Its first argument is a "
-"function, which takes an element of the list and returns a `Boolean` value "
-"as a result. Its second argument is a list of elements, and the return value "
-"is another list."
+"function, which takes an element of the list and returns a `Boolean` "
+"value. Its second argument is a list of elements, and the return value is "
+"another list."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:521
+#: text/chapter3.md:581
#, markdown-text
msgid ""
-"`head` takes a list as its argument, and returns a type we haven't seen "
+"`head` takes a list as its argument and returns a type we haven't seen "
"before: `Maybe a`. `Maybe a` represents an optional value of type `a`, and "
"provides a type-safe alternative to using `null` to indicate a missing value "
"in languages like JavaScript. We will see it again in more detail in later "
@@ -13099,7 +13203,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:523
+#: text/chapter3.md:583
#, markdown-text
msgid ""
"The universally quantified types of `filter` and `head` can be _specialized_ "
@@ -13107,7 +13211,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:524
+#: text/chapter3.md:584
#, no-wrap
msgid ""
"filter :: (Entry -> Boolean) -> AddressBook -> AddressBook\n"
@@ -13116,15 +13220,15 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:531
+#: text/chapter3.md:591
#, markdown-text
msgid ""
"We know that we will need to pass the first and last names that we want to "
-"search for, as arguments to our function."
+"search for as arguments to our function."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:533
+#: text/chapter3.md:593
#, markdown-text, no-wrap
msgid ""
"We also know that we will need a function to pass to `filter`. Let's call "
@@ -13135,7 +13239,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:535
+#: text/chapter3.md:595
#, markdown-text
msgid ""
"Putting these facts together, a reasonable type signature for our function, "
@@ -13143,7 +13247,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:536
+#: text/chapter3.md:596
#, no-wrap
msgid ""
"{{#include "
@@ -13151,23 +13255,23 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:541
+#: text/chapter3.md:601
#, markdown-text
msgid ""
-"This type signature says that `findEntry` takes two strings, the first and "
-"last names, and a `AddressBook`, and returns an optional `Entry`. The "
+"This type signature says that `findEntry` takes two strings: the first and "
+"last names, takes an `AddressBook`, and returns an optional `Entry`. The "
"optional result will contain a value only if the name is found in the "
"address book."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:543
+#: text/chapter3.md:603
#, markdown-text
msgid "And here is the definition of `findEntry`:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:544
+#: text/chapter3.md:604
#, no-wrap
msgid ""
"findEntry firstName lastName book = head (filter filterEntry book)\n"
@@ -13178,13 +13282,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:552
+#: text/chapter3.md:612
#, markdown-text
msgid "Let's go over this code step by step."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:554
+#: text/chapter3.md:614
#, markdown-text
msgid ""
"`findEntry` brings three names into scope: `firstName` and `lastName`, both "
@@ -13192,16 +13296,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:556
+#: text/chapter3.md:616
#, markdown-text
msgid ""
-"The right hand side of the definition combines the `filter` and `head` "
+"The right-hand side of the definition combines the `filter` and `head` "
"functions: first, the list of entries is filtered, and the `head` function "
"is applied to the result."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:558
+#: text/chapter3.md:618
#, markdown-text
msgid ""
"The predicate function `filterEntry` is defined as an auxiliary declaration "
@@ -13213,74 +13317,74 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:560
+#: text/chapter3.md:620
#, markdown-text
msgid ""
-"Note that, just like for top-level declarations, it was not necessary to "
+"Note that, just like for top-level declarations, it was unnecessary to "
"specify a type signature for `filterEntry`. However, doing so is recommended "
"as a form of documentation."
msgstr ""
#. type: Title ##
-#: text/chapter3.md:561
+#: text/chapter3.md:621
#, markdown-text, no-wrap
msgid "Infix Function Application"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:564
+#: text/chapter3.md:624
#, markdown-text
msgid ""
-"Most of the functions discussed so far used _prefix_ function application, "
-"where the function name was put _before_ the arguments. For example, when "
-"using the `insertEntry` function to add an `Entry` (`john`) to an empty "
+"Most functions discussed so far used _prefix_ function application, where "
+"the function name was put _before_ the arguments. For example, when using "
+"the `insertEntry` function to add an `Entry` (`john`) to an empty "
"`AddressBook`, we might write:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:565
+#: text/chapter3.md:625
#, no-wrap
msgid "> book1 = insertEntry john emptyBook\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:570
+#: text/chapter3.md:630
#, markdown-text
msgid ""
"However, this chapter has also included examples of _infix_ [binary "
"operators](https://github.com/purescript/documentation/blob/master/language/Syntax.md#binary-operators), "
"such as the `==` operator in the definition of `filterEntry`, where the "
"operator is put _between_ the two arguments. These infix operators are "
-"actually defined in the PureScript source as infix aliases for their "
-"underlying _prefix_ implementations. For example, `==` is defined as an "
-"infix alias for the prefix `eq` function with the line:"
+"defined in the PureScript source as infix aliases for their underlying "
+"_prefix_ implementations. For example, `==` is defined as an infix alias for "
+"the prefix `eq` function with the line:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:571
+#: text/chapter3.md:631
#, no-wrap
msgid "infix 4 eq as ==\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:576
+#: text/chapter3.md:636
#, markdown-text
msgid ""
-"and therefore `entry.firstName == firstName` in `filterEntry` could be "
-"replaced with the `eq entry.firstName firstName`. We'll cover a few more "
-"examples of defining infix operators later in this section."
+"Therefore `entry.firstName == firstName` in `filterEntry` could be replaced "
+"with the `eq entry.firstName firstName`. We'll cover a few more examples of "
+"defining infix operators later in this section."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:578
+#: text/chapter3.md:638
#, markdown-text
msgid ""
-"There are situations where putting a prefix function in an infix position as "
-"an operator leads to more readable code. One example is the `mod` function:"
+"In some situations, putting a prefix function in an infix position as an "
+"operator leads to more readable code. One example is the `mod` function:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:579
+#: text/chapter3.md:639
#, no-wrap
msgid ""
"> mod 8 3\n"
@@ -13288,16 +13392,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:585
+#: text/chapter3.md:645
#, markdown-text
msgid ""
-"The above usage works fine, but is awkward to read. A more familiar phrasing "
+"The above usage works fine but is awkward to read. A more familiar phrasing "
"is \"eight mod three\", which you can achieve by wrapping a prefix function "
"in backticks (\\`):"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:586
+#: text/chapter3.md:646
#, no-wrap
msgid ""
"> 8 `mod` 3\n"
@@ -13305,7 +13409,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:592
+#: text/chapter3.md:652
#, markdown-text
msgid ""
"In the same way, wrapping `insertEntry` in backticks turns it into an infix "
@@ -13313,7 +13417,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:593
+#: text/chapter3.md:653
#, no-wrap
msgid ""
"book1 = insertEntry john emptyBook\n"
@@ -13321,7 +13425,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:599
+#: text/chapter3.md:659
#, markdown-text
msgid ""
"We can make an `AddressBook` with multiple entries by using multiple "
@@ -13330,7 +13434,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:600
+#: text/chapter3.md:660
#, no-wrap
msgid ""
"book3 = insertEntry john (insertEntry peggy (insertEntry ned emptyBook))\n"
@@ -13339,7 +13443,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:606
+#: text/chapter3.md:666
#, markdown-text
msgid ""
"We can also define an infix operator alias (or synonym) for `insertEntry.` "
@@ -13351,39 +13455,39 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:607
+#: text/chapter3.md:667
#, no-wrap
msgid "infixr 5 insertEntry as ++\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:612
+#: text/chapter3.md:672
#, markdown-text
msgid "This new operator lets us rewrite the above `book4` example as:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:613
+#: text/chapter3.md:673
#, no-wrap
msgid "book5 = john ++ (peggy ++ (ned ++ emptyBook))\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:618
+#: text/chapter3.md:678
#, markdown-text
msgid ""
-"and the right associativity of our new `++` operator lets us get rid of the "
+"The right associativity of our new `++` operator lets us get rid of the "
"parentheses without changing the meaning:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:619
+#: text/chapter3.md:679
#, no-wrap
msgid "book6 = john ++ peggy ++ ned ++ emptyBook\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:624
+#: text/chapter3.md:684
#, markdown-text
msgid ""
"Another common technique for eliminating parens is to use `apply`'s infix "
@@ -13391,19 +13495,19 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:626
+#: text/chapter3.md:686
#, markdown-text
msgid "For example, the earlier `book3` example could be rewritten as:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:627
+#: text/chapter3.md:687
#, no-wrap
msgid "book7 = insertEntry john $ insertEntry peggy $ insertEntry ned emptyBook\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:632
+#: text/chapter3.md:692
#, markdown-text
msgid ""
"Substituting `$` for parens is usually easier to type and (arguably) easier "
@@ -13413,16 +13517,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:634
+#: text/chapter3.md:694
#, markdown-text
msgid ""
-"Note that `$` isn't special syntax that's hardcoded into the language. It's "
+"Note that `$` isn't a special syntax hardcoded into the language. It's "
"simply the infix operator for a regular function called `apply`, which is "
"defined in `Data.Function` as follows:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:635
+#: text/chapter3.md:695
#, no-wrap
msgid ""
"apply :: forall a b. (a -> b) -> a -> b\n"
@@ -13432,7 +13536,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:643
+#: text/chapter3.md:703
#, markdown-text, no-wrap
msgid ""
"The `apply` function takes another function (of type `(a -> b)`) as its "
@@ -13448,7 +13552,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:645
+#: text/chapter3.md:705
#, markdown-text
msgid ""
"Another parens-busting opportunity for the `$` operator is in our earlier "
@@ -13456,13 +13560,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:646
+#: text/chapter3.md:706
#, no-wrap
msgid "findEntry firstName lastName book = head $ filter filterEntry book\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:651
+#: text/chapter3.md:711
#, markdown-text
msgid ""
"We'll see an even more elegant way to rewrite this line with \"function "
@@ -13470,7 +13574,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:653
+#: text/chapter3.md:713
#, markdown-text
msgid ""
"If you'd like to use a concise infix operator alias as a prefix function, "
@@ -13478,7 +13582,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:654
+#: text/chapter3.md:714
#, no-wrap
msgid ""
"> 8 + 3\n"
@@ -13489,7 +13593,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:663
+#: text/chapter3.md:723
#, markdown-text
msgid ""
"Alternatively, operators can be partially applied by surrounding the "
@@ -13501,7 +13605,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter3.md:664
+#: text/chapter3.md:724
#, no-wrap
msgid ""
"> add3 = (3 + _)\n"
@@ -13510,7 +13614,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:671
+#: text/chapter3.md:731
#, markdown-text
msgid ""
"To summarize, the following are equivalent definitions of a function that "
@@ -13518,7 +13622,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:672
+#: text/chapter3.md:732
#, no-wrap
msgid ""
"add5 x = 5 + x\n"
@@ -13533,13 +13637,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter3.md:683
+#: text/chapter3.md:743
#, markdown-text, no-wrap
msgid "Function Composition"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:686
+#: text/chapter3.md:746
#, markdown-text
msgid ""
"Just like we were able to simplify the `insertEntry` function by using eta "
@@ -13548,7 +13652,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:688
+#: text/chapter3.md:748
#, markdown-text
msgid ""
"Note that the `book` argument is passed to the `filter filterEntry` "
@@ -13558,7 +13662,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:690
+#: text/chapter3.md:750
#, markdown-text, no-wrap
msgid ""
"In PureScript, the function composition operators are `<<<` and `>>>`. The "
@@ -13567,7 +13671,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:692
+#: text/chapter3.md:752
#, markdown-text
msgid ""
"We can rewrite the right-hand side of `findEntry` using either "
@@ -13575,13 +13679,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:693
+#: text/chapter3.md:753
#, no-wrap
msgid "(head <<< filter filterEntry) book\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:698
+#: text/chapter3.md:758
#, markdown-text
msgid ""
"In this form, we can apply the eta conversion trick from earlier, to arrive "
@@ -13589,7 +13693,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:699
+#: text/chapter3.md:759
#, no-wrap
msgid ""
"{{#include "
@@ -13598,19 +13702,19 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:705
+#: text/chapter3.md:765
#, markdown-text
msgid "An equally valid right-hand side would be:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter3.md:706
+#: text/chapter3.md:766
#, no-wrap
msgid "filter filterEntry >>> head\n"
msgstr ""
#. type: Plain text
-#: text/chapter3.md:711
+#: text/chapter3.md:771
#, markdown-text
msgid ""
"Either way, this gives a clear definition of the `findEntry` function: "
@@ -13619,17 +13723,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter3.md:713
+#: text/chapter3.md:773
#, markdown-text
msgid ""
-"I will let you make your own decision which definition is easier to "
-"understand, but it is often useful to think of functions as building blocks "
-"in this way - each function executing a single task, and solutions assembled "
-"using function composition."
+"I will let you decide which definition is easier to understand, but it is "
+"often useful to think of functions as building blocks in this way: each "
+"function executes a single task, and solutions are assembled using function "
+"composition."
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter3.md:721
+#: text/chapter3.md:781
#, markdown-text
msgid ""
"(Easy) Test your understanding of the `findEntry` function by writing down "
@@ -13639,7 +13743,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter3.md:721
+#: text/chapter3.md:781
#, markdown-text
msgid ""
"(Medium) Write a function `findEntryByStreet :: String -> AddressBook -> "
@@ -13649,7 +13753,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter3.md:721
+#: text/chapter3.md:781
#, markdown-text
msgid ""
"(Medium) Rewrite `findEntryByStreet` to replace `filterEntry` with the "
@@ -13659,17 +13763,17 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter3.md:721
+#: text/chapter3.md:781
#, markdown-text
msgid ""
-"(Medium) Write a function `isInBook` which tests whether a name appears in a "
+"(Medium) Write a function `isInBook` that tests whether a name appears in a "
"`AddressBook`, returning a Boolean value. _Hint_: Use PSCi to find the type "
"of the `Data.List.null` function, which tests whether a list is empty or "
"not."
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter3.md:721
+#: text/chapter3.md:781
#, markdown-text
msgid ""
"(Difficult) Write a function `removeDuplicates` which removes \"duplicate\" "
@@ -13677,64 +13781,64 @@ msgid ""
"same first and last names, while ignoring `address` fields. _Hint_: Use PSCi "
"to find the type of the `Data.List.nubByEq` function, which removes "
"duplicate elements from a list based on an equality predicate. Note that the "
-"first element in each set of duplicates (closest to list head) is the one "
-"that is kept."
+"first element in each set of duplicates (closest to the list head) is the "
+"one that is kept."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:725
+#: text/chapter3.md:785
#, markdown-text
-msgid "In this chapter, we covered several new functional programming concepts:"
+msgid ""
+"In this chapter, we covered several new functional programming concepts and "
+"learned how to:"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter3.md:733
+#: text/chapter3.md:793
#, markdown-text
-msgid ""
-"How to use the interactive mode PSCi to experiment with functions and test "
-"ideas."
+msgid "Use the interactive mode PSCi to experiment with functions and test ideas."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter3.md:733
+#: text/chapter3.md:793
#, markdown-text
-msgid "The role of types as both a correctness tool, and an implementation tool."
+msgid "Use types as both a correctness tool and an implementation tool."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter3.md:733
+#: text/chapter3.md:793
#, markdown-text
-msgid "The use of curried functions to represent functions of multiple arguments."
+msgid "Use curried functions to represent functions of multiple arguments."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter3.md:733
+#: text/chapter3.md:793
#, markdown-text
-msgid "Creating programs from smaller components by composition."
+msgid "Create programs from smaller components by composition."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter3.md:733
+#: text/chapter3.md:793
#, markdown-text
-msgid "Structuring code neatly using `where` expressions."
+msgid "Structure code neatly using `where` expressions."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter3.md:733
+#: text/chapter3.md:793
#, markdown-text
-msgid "How to avoid null values by using the `Maybe` type."
+msgid "Avoid null values by using the `Maybe` type."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter3.md:733
+#: text/chapter3.md:793
#, markdown-text
msgid ""
-"Using techniques like eta conversion and function composition to refactor "
-"code into a clear specification."
+"Use techniques like eta conversion and function composition to refactor code "
+"into a clear specification."
msgstr ""
#. type: Plain text
-#: text/chapter3.md:734
+#: text/chapter3.md:794
#, markdown-text
msgid "In the following chapters, we'll build on these ideas."
msgstr ""
@@ -13742,7 +13846,7 @@ msgstr ""
#. type: Title #
#: text/chapter4.md:1
#, markdown-text, no-wrap
-msgid "Recursion, Maps And Folds"
+msgid "Recursion, Maps, And Folds"
msgstr ""
#. type: Plain text
@@ -13759,8 +13863,8 @@ msgstr ""
#, markdown-text
msgid ""
"We will also cover some standard functions from PureScript's standard "
-"libraries. We will see the `map` and `fold` functions, as well as some "
-"useful special cases, like `filter` and `concatMap`."
+"libraries. We will `map`, `fold`, and some useful special cases, like "
+"`filter` and `concatMap`."
msgstr ""
#. type: Plain text
@@ -13769,7 +13873,7 @@ msgstr ""
msgid ""
"The motivating example for this chapter is a library of functions for "
"working with a virtual filesystem. We will apply the techniques learned in "
-"this chapter to write functions which compute properties of the files "
+"this chapter to write functions that compute properties of the files "
"represented by a model of a filesystem."
msgstr ""
@@ -13866,14 +13970,14 @@ msgstr ""
#, markdown-text
msgid ""
"Here, we can see how the factorial function is computed by reducing the "
-"problem to a subproblem - that of computing the factorial of a smaller "
-"integer. When we reach zero, the answer is immediate."
+"problem to a subproblem – computing the factorial of a smaller integer. When "
+"we reach zero, the answer is immediate."
msgstr ""
#. type: Plain text
#: text/chapter4.md:40
#, markdown-text
-msgid "Here is another common example, which computes the _Fibonacci function_:"
+msgid "Here is another common example that computes the _Fibonacci function_:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -13894,12 +13998,12 @@ msgstr ""
#. type: Plain text
#: text/chapter4.md:48
-#, markdown-text
+#, markdown-text, no-wrap
msgid ""
-"Note that, while the above examples of `factorial` and `fib` work as "
+"> Note that, while the above examples of `factorial` and `fib` work as "
"intended, a more idiomatic implementation would use pattern matching instead "
-"of `if`/`then`/`else`. Pattern matching techniques are discussed in a later "
-"chapter."
+"of `if`/`then`/`else`. Pattern-matching techniques are discussed in a later "
+"chapter.\n"
msgstr ""
#. type: Title ##
@@ -13945,7 +14049,7 @@ msgstr ""
msgid ""
"In this function, we use an `if .. then .. else` expression to branch based "
"on the emptiness of the array. The `null` function returns `true` on an "
-"empty array. Empty arrays have length zero, and a non-empty array has a "
+"empty array. Empty arrays have a length of zero, and a non-empty array has a "
"length that is one more than the length of its tail."
msgstr ""
@@ -13954,26 +14058,26 @@ msgstr ""
#, markdown-text
msgid ""
"The `tail` function returns a `Maybe` wrapping the given array without its "
-"first element. If the array is empty (i.e. it doesn't have a tail) `Nothing` "
-"is returned. The `fromMaybe` function takes a default value and a `Maybe` "
-"value. If the latter is `Nothing` it returns the default, in the other case "
-"it returns the value wrapped by `Just`."
+"first element. If the array is empty (i.e., it doesn't have a tail), "
+"`Nothing` is returned. The `fromMaybe` function takes a default value and a "
+"`Maybe` value. If the latter is `Nothing` it returns the default; in the "
+"other case, it returns the value wrapped by `Just`."
msgstr ""
#. type: Plain text
#: text/chapter4.md:69
#, markdown-text
msgid ""
-"This example is obviously a very impractical way to find the length of an "
-"array in JavaScript, but should provide enough help to allow you to complete "
-"the following exercises:"
+"This example is a very impractical way to find the length of an array in "
+"JavaScript, but it should provide enough help to allow you to complete the "
+"following exercises:"
msgstr ""
#. type: Bullet: ' 1. '
#: text/chapter4.md:74
#, markdown-text
msgid ""
-"(Easy) Write a recursive function `isEven` which returns `true` if and only "
+"(Easy) Write a recursive function `isEven` that returns `true` if and only "
"if its input is an even integer."
msgstr ""
@@ -13981,7 +14085,7 @@ msgstr ""
#: text/chapter4.md:74
#, markdown-text
msgid ""
-"(Medium) Write a recursive function `countEven` which counts the number of "
+"(Medium) Write a recursive function `countEven` that counts the number of "
"even integers in an array. _Hint_: the function `head` (also available in "
"`Data.Array`) can be used to find the first element in a non-empty array."
msgstr ""
@@ -13998,15 +14102,15 @@ msgstr ""
msgid ""
"The `map` function is an example of a recursive function on arrays. It is "
"used to transform the elements of an array by applying a function to each "
-"element in turn. Therefore, it changes the _contents_ of the array, but "
-"preserves its _shape_ (i.e. its length)."
+"element in turn. Therefore, it changes the _contents_ of the array but "
+"preserves its _shape_ (i.e., its length)."
msgstr ""
#. type: Plain text
#: text/chapter4.md:80
#, markdown-text
msgid ""
-"When we cover _type classes_ later in the book we will see that the `map` "
+"When we cover _type classes_ later in the book, we will see that the `map` "
"function is an example of a more general pattern of shape-preserving "
"functions which transform a class of type constructors called _functors_."
msgstr ""
@@ -14032,7 +14136,7 @@ msgstr ""
#: text/chapter4.md:92
#, markdown-text
msgid ""
-"Notice how `map` is used - we provide a function which should be \"mapped "
+"Notice how `map` is used – we provide a function that should be \"mapped "
"over\" the array in the first argument, and the array itself in its second."
msgstr ""
@@ -14072,8 +14176,7 @@ msgstr ""
#, markdown-text, no-wrap
msgid ""
"There is an operator which is equivalent to the `map` function when used "
-"with arrays, called `<$>`. This operator can be used infix like any other "
-"binary operator:\n"
+"with arrays, called `<$>`.\n"
msgstr ""
#. type: Fenced code block (text)
@@ -14095,7 +14198,8 @@ msgstr ""
#, no-wrap
msgid ""
"> :type map\n"
-"forall a b f. Functor f => (a -> b) -> f a -> f b\n"
+"forall (f :: Type -> Type) (a :: Type) (b :: Type). Functor f => (a -> b) -> "
+"f a -> f b\n"
msgstr ""
#. type: Plain text
@@ -14110,7 +14214,7 @@ msgstr ""
#. type: Fenced code block (text)
#: text/chapter4.md:120
#, no-wrap
-msgid "forall a b. (a -> b) -> Array a -> Array b\n"
+msgid "forall (a :: Type) (b :: Type). (a -> b) -> Array a -> Array b\n"
msgstr ""
#. type: Plain text
@@ -14141,7 +14245,7 @@ msgid ""
"fact just an alias for a regular PureScript function. The function is simply "
"_applied_ using infix syntax. In fact, the function can be used like a "
"regular function by enclosing its name in parentheses. This means that we "
-"can used the parenthesized name `(<$>)` in place of `map` on arrays:\n"
+"can use the parenthesized name `(<$>)` in place of `map` on arrays:\n"
msgstr ""
#. type: Fenced code block (text)
@@ -14330,7 +14434,7 @@ msgid ""
"> import Data.Array\n"
"\n"
"> :type concat\n"
-"forall a. Array (Array a) -> Array a\n"
+"forall (a :: Type). Array (Array a) -> Array a\n"
"\n"
"> concat [[1, 2, 3], [4, 5], [6]]\n"
"[1, 2, 3, 4, 5, 6]\n"
@@ -14340,10 +14444,10 @@ msgstr ""
#: text/chapter4.md:205
#, markdown-text
msgid ""
-"There is a related function called `concatMap` which is like a combination "
-"of the `concat` and `map` functions. Where `map` takes a function from "
-"values to values (possibly of a different type), `concatMap` takes a "
-"function from values to arrays of values."
+"There is a related function called `concatMap` which is a combination of the "
+"`concat` and `map` functions. Where `map` takes a function from values to "
+"values (possibly of a different type), `concatMap` takes a function from "
+"values to arrays of values."
msgstr ""
#. type: Plain text
@@ -14359,7 +14463,7 @@ msgid ""
"> import Data.Array\n"
"\n"
"> :type concatMap\n"
-"forall a b. (a -> Array b) -> Array a -> Array b\n"
+"forall (a :: Type) (b :: Type). (a -> Array b) -> Array a -> Array b\n"
"\n"
"> concatMap (\\n -> [n, n * n]) (1 .. 5)\n"
"[1,1,2,4,3,9,4,16,5,25]\n"
@@ -14413,8 +14517,8 @@ msgstr ""
#: text/chapter4.md:229
#, markdown-text
msgid ""
-"We can perform this computation using an array comprehension. We will do so "
-"in steps, using PSCi as our interactive development environment."
+"We can perform this computation using array comprehension. We will do so in "
+"steps, using PSCi as our interactive development environment."
msgstr ""
#. type: Plain text
@@ -14551,13 +14655,13 @@ msgstr ""
#. type: Plain text
#: text/chapter4.md:292
-#, markdown-text
+#, markdown-text, no-wrap
msgid ""
-"_Note_: Just like `map` and `concatMap` allowed us to write _array "
+"> _Note_: Just like `map` and `concatMap` allowed us to write _array "
"comprehensions_, the more general operators `map` and `bind` allow us to "
"write so-called _monad comprehensions_. We'll see plenty more examples of "
"_monads_ later in the book, but in this chapter, we will only consider "
-"arrays."
+"arrays.\n"
msgstr ""
#. type: Plain text
@@ -14576,15 +14680,15 @@ msgstr ""
#: text/chapter4.md:300
#, markdown-text
msgid ""
-"The keyword `do` introduces a block of code which uses do notation. The "
-"block consists of expressions of a few types:"
+"The keyword `do` introduces a block of code that uses do notation. The block "
+"consists of expressions of a few types:"
msgstr ""
#. type: Bullet: '- '
#: text/chapter4.md:304
#, markdown-text
msgid ""
-"Expressions which bind elements of an array to a name. These are indicated "
+"Expressions that bind elements of an array to a name. These are indicated "
"with the backwards-facing arrow `<-`, with a name on the left, and an "
"expression on the right whose type is an array."
msgstr ""
@@ -14593,7 +14697,7 @@ msgstr ""
#: text/chapter4.md:304
#, markdown-text
msgid ""
-"Expressions which do not bind elements of the array to names. The `do` "
+"Expressions that do not bind elements of the array to names. The `do` "
"_result_ is an example of this kind of expression and is illustrated in the "
"last line, `pure [i, j]`."
msgstr ""
@@ -14601,7 +14705,7 @@ msgstr ""
#. type: Bullet: '- '
#: text/chapter4.md:304
#, markdown-text
-msgid "Expressions which give names to expressions, using the `let` keyword."
+msgid "Expressions that give names to expressions, using the `let` keyword."
msgstr ""
#. type: Plain text
@@ -14634,9 +14738,8 @@ msgstr ""
#: text/chapter4.md:315
#, markdown-text
msgid ""
-"In the case of arrays, `pure` simply constructs a singleton array. In fact, "
-"we could modify our `factors` function to use this form, instead of using "
-"`pure`:"
+"In the case of arrays, `pure` simply constructs a singleton array. We can "
+"modify our `factors` function to use this form, instead of using `pure`:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -14691,7 +14794,7 @@ msgid ""
"> import Control.Alternative\n"
"\n"
"> :type guard\n"
-"forall m. Alternative m => Boolean -> m Unit\n"
+"forall (m :: Type -> Type). Alternative m => Boolean -> m Unit\n"
msgstr ""
#. type: Plain text
@@ -14731,8 +14834,8 @@ msgstr ""
#: text/chapter4.md:360
#, markdown-text
msgid ""
-"That is, if `guard` is passed an expression which evaluates to `true`, then "
-"it returns an array with a single element. If the expression evaluates to "
+"If we pass an expression to `guard` that evaluates to `true`, then it "
+"returns an array with a single element. If the expression evaluates to "
"`false`, then its result is empty."
msgstr ""
@@ -14752,8 +14855,8 @@ msgstr ""
#: text/chapter4.md:369
#, markdown-text
msgid ""
-"(Easy) Write a function `isPrime` which tests if its integer argument is "
-"prime or not. _Hint_: Use the `factors` function."
+"(Easy) Write a function `isPrime`, which tests whether its integer argument "
+"is prime. _Hint_: Use the `factors` function."
msgstr ""
#. type: Bullet: ' 1. '
@@ -14761,18 +14864,18 @@ msgstr ""
#, markdown-text
msgid ""
"(Medium) Write a function `cartesianProduct` which uses do notation to find "
-"the _cartesian product_ of two arrays, i.e. the set of all pairs of elements "
-"`a`, `b`, where `a` is an element of the first array, and `b` is an element "
-"of the second."
+"the _cartesian product_ of two arrays, i.e., the set of all pairs of "
+"elements `a`, `b`, where `a` is an element of the first array, and `b` is an "
+"element of the second."
msgstr ""
#. type: Bullet: ' 1. '
#: text/chapter4.md:369
#, markdown-text
msgid ""
-"(Medium) Write a function `triples :: Int -> Array (Array Int)` which takes "
+"(Medium) Write a function `triples :: Int -> Array (Array Int)`, which takes "
"a number `n` and returns all Pythagorean triples whose components (the `a`, "
-"`b` and `c` values) are each less than or equal to `n`. A _Pythagorean "
+"`b`, and `c` values) are each less than or equal to `n`. A _Pythagorean "
"triple_ is an array of numbers `[a, b, c]` such that `a² + b² = c²`. _Hint_: "
"Use the `guard` function in an array comprehension."
msgstr ""
@@ -14783,9 +14886,9 @@ msgstr ""
msgid ""
"(Difficult) Write a function `primeFactors` which produces the [prime "
"factorization](https://www.mathsisfun.com/prime-factorization.html) of `n`, "
-"i.e. the array of prime integers whose product is `n`. _Hint_: for an "
-"integer greater than 1, break the problem down into two subproblems: finding "
-"the first factor, and finding the remaining factors."
+"i.e., the array of prime integers whose product is `n`. _Hint_: for an "
+"integer greater than 1, break the problem into two subproblems: finding the "
+"first factor and the remaining factors."
msgstr ""
#. type: Title ##
@@ -14799,14 +14902,14 @@ msgstr ""
#, markdown-text
msgid ""
"Left and right folds over arrays provide another class of interesting "
-"functions which can be implemented using recursion."
+"functions that can be implemented using recursion."
msgstr ""
#. type: Plain text
#: text/chapter4.md:375
#, markdown-text
msgid ""
-"Start by importing the `Data.Foldable` module, and inspecting the types of "
+"Start by importing the `Data.Foldable` module and inspecting the types of "
"the `foldl` and `foldr` functions using PSCi:"
msgstr ""
@@ -14817,29 +14920,31 @@ msgid ""
"> import Data.Foldable\n"
"\n"
"> :type foldl\n"
-"forall a b f. Foldable f => (b -> a -> b) -> b -> f a -> b\n"
+"forall (f :: Type -> Type) (a :: Type) (b :: Type). Foldable f => (b -> a -> "
+"b) -> b -> f a -> b\n"
"\n"
"> :type foldr\n"
-"forall a b f. Foldable f => (a -> b -> b) -> b -> f a -> b\n"
+"forall (f :: Type -> Type) (a :: Type) (b :: Type). Foldable f => (a -> b -> "
+"b) -> b -> f a -> b\n"
msgstr ""
#. type: Plain text
#: text/chapter4.md:387
#, markdown-text
msgid ""
-"These types are actually more general than we are interested in right "
-"now. For the purposes of this chapter, we can assume that PSCi had given the "
-"following (more specific) answer:"
+"These types are more general than we are interested in right now. For this "
+"chapter, we can simplify and assume the following (more specific) type "
+"signatures:"
msgstr ""
#. type: Fenced code block (text)
#: text/chapter4.md:388
#, no-wrap
msgid ""
-"> :type foldl\n"
+"-- foldl\n"
"forall a b. (b -> a -> b) -> b -> Array a -> b\n"
"\n"
-"> :type foldr\n"
+"-- foldr\n"
"forall a b. (a -> b -> b) -> b -> Array a -> b\n"
msgstr ""
@@ -14847,8 +14952,8 @@ msgstr ""
#: text/chapter4.md:397
#, markdown-text
msgid ""
-"In both of these cases, the type `a` corresponds to the type of elements of "
-"our array. The type `b` can be thought of as the type of an \"accumulator\", "
+"In both cases, the type `a` corresponds to the type of elements of our "
+"array. The type `b` can be thought of as the type of an \"accumulator\", "
"which will accumulate a result as we traverse the array."
msgstr ""
@@ -14870,8 +14975,8 @@ msgid ""
"`b` to be `Int`. We need to provide three arguments: a function `Int -> Int "
"-> Int`, which will add the next element to the accumulator, an initial "
"value for the accumulator of type `Int`, and an array of `Int`s to add. For "
-"the first argument, we can just use the addition operator, and the initial "
-"value of the accumulator will be zero:\n"
+"the first argument, we can use the addition operator, and the initial value "
+"of the accumulator will be zero:\n"
msgstr ""
#. type: Fenced code block (text)
@@ -14902,9 +15007,9 @@ msgstr ""
#: text/chapter4.md:415
#, markdown-text
msgid ""
-"Let's write an example where the choice of folding function does matter, in "
-"order to illustrate the difference. Instead of the addition function, let's "
-"use string concatenation to build a string:"
+"Let's write an example where the choice of folding function matters to "
+"illustrate the difference. Instead of the addition function, let's use "
+"string concatenation to build a string:"
msgstr ""
#. type: Fenced code block (text)
@@ -14935,7 +15040,7 @@ msgstr ""
#. type: Plain text
#: text/chapter4.md:431
#, markdown-text
-msgid "whereas the right fold is equivalent to this:"
+msgid "Whereas the right fold is equivalent to this:"
msgstr ""
#. type: Fenced code block (text)
@@ -14954,15 +15059,15 @@ msgstr ""
#: text/chapter4.md:439
#, markdown-text
msgid ""
-"Recursion is a powerful technique for specifying algorithms, but comes with "
-"a problem: evaluating recursive functions in JavaScript can lead to stack "
+"Recursion is a powerful technique for specifying algorithms but comes with a "
+"problem: evaluating recursive functions in JavaScript can lead to stack "
"overflow errors if our inputs are too large."
msgstr ""
#. type: Plain text
#: text/chapter4.md:441
#, markdown-text
-msgid "It is easy to verify this problem, with the following code in PSCi:"
+msgid "It is easy to verify this problem with the following code in PSCi:"
msgstr ""
#. type: Fenced code block (text)
@@ -14987,41 +15092,41 @@ msgstr ""
#: text/chapter4.md:458
#, markdown-text
msgid ""
-"This is a problem. If we are going to adopt recursion as a standard "
-"technique from functional programming, then we need a way to deal with "
-"possibly unbounded recursion."
+"This is a problem. If we adopt recursion as a standard technique from "
+"functional programming, we need a way to deal with possibly unbounded "
+"recursion."
msgstr ""
#. type: Plain text
#: text/chapter4.md:460
#, markdown-text
msgid ""
-"PureScript provides a partial solution to this problem in the form of _tail "
+"PureScript provides a partial solution to this problem through _tail "
"recursion optimization_."
msgstr ""
#. type: Plain text
#: text/chapter4.md:462
-#, markdown-text
+#, markdown-text, no-wrap
msgid ""
-"_Note_: more complete solutions to the problem can be implemented in "
+"> _Note_: more complete solutions to the problem can be implemented in "
"libraries using so-called _trampolining_, but that is beyond the scope of "
"this chapter. The interested reader can consult the documentation for the "
"[`free`](https://pursuit.purescript.org/packages/purescript-free) and "
"[`tailrec`](https://pursuit.purescript.org/packages/purescript-tailrec) "
-"packages."
+"packages.\n"
msgstr ""
#. type: Plain text
#: text/chapter4.md:464
#, markdown-text
msgid ""
-"The key observation which enables tail recursion optimization is the "
-"following: a recursive call in _tail position_ to a function can be replaced "
-"with a _jump_, which does not allocate a stack frame. A call is in _tail "
-"position_ when it is the last call made before a function returns. This is "
-"the reason why we observed a stack overflow in the example - the recursive "
-"call to `f` was _not_ in tail position."
+"The key observation that enables tail recursion optimization: a recursive "
+"call in _tail position_ to a function can be replaced with a _jump_, which "
+"does not allocate a stack frame. A call is in _tail position_ when it is the "
+"last call made before a function returns. This is why we observed a stack "
+"overflow in the example – the recursive call to `f` was _not_ in tail "
+"position."
msgstr ""
#. type: Plain text
@@ -15051,8 +15156,8 @@ msgstr ""
#: text/chapter4.md:474
#, markdown-text
msgid ""
-"Notice that the recursive call to `factorialTailRec` is the last thing that "
-"happens in this function - it is in tail position."
+"Notice that the recursive call to `factorialTailRec` is the last thing in "
+"this function – it is in tail position."
msgstr ""
#. type: Title ##
@@ -15065,18 +15170,17 @@ msgstr ""
#: text/chapter4.md:478
#, markdown-text
msgid ""
-"One common way to turn a function which is not tail recursive into a tail "
-"recursive function is to use an _accumulator parameter_. An accumulator "
-"parameter is an additional parameter which is added to a function which "
-"_accumulates_ a return value, as opposed to using the return value to "
-"accumulate the result."
+"One common way to turn a not tail recursive function into a tail recursive "
+"is to use an _accumulator parameter_. An accumulator parameter is an "
+"additional parameter added to a function that _accumulates_ a return value, "
+"as opposed to using the return value to accumulate the result."
msgstr ""
#. type: Plain text
#: text/chapter4.md:480
#, markdown-text
msgid ""
-"For example, consider again the `length` function presented in the beginning "
+"For example, consider again the `length` function presented at the beginning "
"of the chapter:"
msgstr ""
@@ -15112,9 +15216,9 @@ msgstr ""
#, markdown-text
msgid ""
"In this case, we delegate to the helper function `length'`, which is tail "
-"recursive - its only recursive call is in the last case, and is in tail "
-"position. This means that the generated code will be a _while loop_, and "
-"will not blow the stack for large inputs."
+"recursive – its only recursive call is in the last case, in tail "
+"position. This means that the generated code will be a _while loop_ and not "
+"blow the stack for large inputs."
msgstr ""
#. type: Plain text
@@ -15123,16 +15227,16 @@ msgstr ""
msgid ""
"To understand the implementation of `lengthTailRec`, note that the helper "
"function `length'` essentially uses the accumulator parameter to maintain an "
-"additional piece of state - the partial result. It starts out at 0, and "
-"grows by adding 1 for every element in the input array."
+"additional piece of state – the partial result. It starts at 0 and grows by "
+"adding 1 for every element in the input array."
msgstr ""
#. type: Plain text
#: text/chapter4.md:500
#, markdown-text
msgid ""
-"Note also that while we might think of the accumulator as \"state\", there "
-"is no direct mutation going on."
+"Note also that while we might think of the accumulator as a \"state\", there "
+"is no direct mutation."
msgstr ""
#. type: Title ##
@@ -15145,14 +15249,14 @@ msgstr ""
#: text/chapter4.md:504
#, markdown-text
msgid ""
-"If we can write our recursive functions using tail recursion, then we can "
-"benefit from tail recursion optimization, so it becomes tempting to try to "
-"write all of our functions in this form. However, it is often easy to forget "
-"that many functions can be written directly as a fold over an array or "
-"similar data structure. Writing algorithms directly in terms of combinators "
-"such as `map` and `fold` has the added advantage of code simplicity - these "
-"combinators are well-understood, and as such, communicate the _intent_ of "
-"the algorithm much better than explicit recursion."
+"If we can write our recursive functions using tail recursion, we can benefit "
+"from tail recursion optimization, so it becomes tempting to try to write all "
+"of our functions in this form. However, it is often easy to forget that many "
+"functions can be written directly as a fold over an array or similar data "
+"structure. Writing algorithms directly in terms of combinators such as `map` "
+"and `fold` has the added advantage of code simplicity – these combinators "
+"are well-understood, and as such, communicate the _intent_ of the algorithm "
+"much better than explicit recursion."
msgstr ""
#. type: Plain text
@@ -15225,15 +15329,15 @@ msgstr ""
#: text/chapter4.md:531
#, markdown-text
msgid ""
-"In this section, we're going to apply what we've learned, writing functions "
-"which will work with a model of a filesystem. We will use maps, folds and "
-"filters to work with a predefined API."
+"In this section, we'll apply what we've learned, writing functions that will "
+"work with a model of a filesystem. We will use maps, folds, and filters to "
+"work with a predefined API."
msgstr ""
#. type: Plain text
#: text/chapter4.md:533
#, markdown-text
-msgid "The `Data.Path` module defines an API for a virtual filesystem, as follows:"
+msgid "The `Data.Path` module defines an API for a virtual filesystem as follows:"
msgstr ""
#. type: Bullet: '- '
@@ -15263,9 +15367,7 @@ msgstr ""
#. type: Bullet: '- '
#: text/chapter4.md:540
#, markdown-text
-msgid ""
-"The `size` function returns the file size for a `Path` which represents a "
-"file."
+msgid "The `size` function returns the file size for a `Path` representing a file."
msgstr ""
#. type: Bullet: '- '
@@ -15323,7 +15425,7 @@ msgstr ""
#: text/chapter4.md:573
#, markdown-text
msgid ""
-"The `Test.Examples` module defines functions which use the `Data.Path` "
+"The `Test.Examples` module defines functions that use the `Data.Path` "
"API. You do not need to modify the `Data.Path` module, or understand its "
"implementation. We will work entirely in the `Test.Examples` module."
msgstr ""
@@ -15338,7 +15440,7 @@ msgstr ""
#: text/chapter4.md:577
#, markdown-text
msgid ""
-"Let's write a function which performs a deep enumeration of all files inside "
+"Let's write a function that performs a deep enumeration of all files inside "
"a directory. This function will have the following type:"
msgstr ""
@@ -15356,7 +15458,7 @@ msgid ""
"enumerate the immediate children of the directory. For each child, we can "
"recursively apply `allFiles`, which will return an array of "
"paths. `concatMap` will allow us to apply `allFiles` and flatten the results "
-"at the same time."
+"simultaneously."
msgstr ""
#. type: Plain text
@@ -15375,11 +15477,11 @@ msgstr ""
#. type: Plain text
#: text/chapter4.md:591
-#, markdown-text
+#, markdown-text, no-wrap
msgid ""
-"_Note_: the cons operator `:` actually has poor performance on immutable "
-"arrays, so it is not recommended in general. Performance can be improved by "
-"using other data structures, such as linked lists and sequences."
+"> _Note_: the cons operator `:` has poor performance on immutable arrays, so "
+"it is not generally recommended. Performance can be improved by using other "
+"data structures, such as linked lists and sequences.\n"
msgstr ""
#. type: Plain text
@@ -15414,9 +15516,9 @@ msgstr ""
msgid ""
"Recall that a backwards arrow corresponds to choosing an element from an "
"array. The first step is to choose an element from the immediate children of "
-"the argument. Then we simply call the function recursively for that "
-"file. Since we are using do notation, there is an implicit call to "
-"`concatMap` which concatenates all of the recursive results."
+"the argument. Then we call the function recursively for that file. Since we "
+"use do notation, there is an implicit call to `concatMap`, which "
+"concatenates all of the recursive results."
msgstr ""
#. type: Plain text
@@ -15435,7 +15537,7 @@ msgstr ""
#: text/chapter4.md:614
#, markdown-text
msgid ""
-"Try out the new version in PSCi - you should get the same result. I'll let "
+"Try out the new version in PSCi – you should get the same result. I'll let "
"you decide which version you find clearer."
msgstr ""
@@ -15483,21 +15585,19 @@ msgid ""
" 3. (Difficult) Write a function `largestSmallest` which takes a `Path` and "
"returns an array containing the single largest and single smallest files in "
"the `Path`. _Note_: consider the cases where there are zero or one files in "
-"the `Path` by returning an empty array or a one-element array "
-"respectively.\n"
+"the `Path` by returning an empty or one-element array, respectively.\n"
msgstr ""
#. type: Plain text
#: text/chapter4.md:633
#, markdown-text
msgid ""
-"In this chapter, we covered the basics of recursion in PureScript, as a "
-"means of expressing algorithms concisely. We also introduced user-defined "
-"infix operators, standard functions on arrays such as maps, filters and "
-"folds, and array comprehensions which combine these ideas. Finally, we "
-"showed the importance of using tail recursion in order to avoid stack "
-"overflow errors, and how to use accumulator parameters to convert functions "
-"to tail recursive form."
+"In this chapter, we covered the basics of recursion in PureScript to express "
+"algorithms concisely. We also introduced user-defined infix operators, "
+"standard functions on arrays such as maps, filters, and folds, and array "
+"comprehensions that combine these ideas. Finally, we showed the importance "
+"of using tail recursion to avoid stack overflow errors and how to use "
+"accumulator parameters to convert functions to tail recursive form."
msgstr ""
#. type: Title #
@@ -15510,7 +15610,7 @@ msgstr ""
#: text/chapter5.md:6
#, markdown-text
msgid ""
-"This chapter will introduce two new concepts: algebraic data types, and "
+"This chapter will introduce two new concepts: algebraic data types and "
"pattern matching. We will also briefly cover an interesting feature of the "
"PureScript type system: row polymorphism."
msgstr ""
@@ -15520,26 +15620,25 @@ msgstr ""
#, markdown-text
msgid ""
"Pattern matching is a common technique in functional programming and allows "
-"the developer to write compact functions which express potentially complex "
-"ideas, by breaking their implementation down into multiple cases."
+"the developer to write compact functions, which express potentially complex "
+"ideas by breaking their implementation down into multiple cases."
msgstr ""
#. type: Plain text
#: text/chapter5.md:10
#, markdown-text
msgid ""
-"Algebraic data types are a feature of the PureScript type system which "
-"enable a similar level of expressiveness in the language of types - they are "
-"closely related to pattern matching."
+"Algebraic data types are a feature of the PureScript type system, which "
+"enables a similar level of expressiveness in the language of types – they "
+"are closely related to pattern matching."
msgstr ""
#. type: Plain text
#: text/chapter5.md:12
#, markdown-text
msgid ""
-"The goal of the chapter will be to write a library to describe and "
-"manipulate simple vector graphics using algebraic types and pattern "
-"matching."
+"The chapter's goal will be to write a library to describe and manipulate "
+"simple vector graphics using algebraic types and pattern matching."
msgstr ""
#. type: Plain text
@@ -15554,7 +15653,7 @@ msgstr ""
#: text/chapter5.md:18
#, markdown-text
msgid ""
-"The `Data.Picture` module defines a data type `Shape` for simple shapes, and "
+"The `Data.Picture` module defines a data type `Shape` for simple shapes and "
"a type `Picture` for collections of shapes, along with functions for working "
"with those types."
msgstr ""
@@ -15593,17 +15692,17 @@ msgstr ""
msgid ""
"This makes the types and functions in that module available for use, but "
"only by using the _qualified name_, like `Number.max`. This can be useful to "
-"avoid overlapping imports, or just to make it clearer which modules certain "
-"things are imported from."
+"avoid overlapping imports or clarify which modules certain things are "
+"imported from."
msgstr ""
#. type: Plain text
#: text/chapter5.md:34
-#, markdown-text
+#, markdown-text, no-wrap
msgid ""
-"_Note_: it is not necessary to use the same module name as the original "
-"module for a qualified import. Shorter qualified names like `import "
-"Data.Number as N` are possible, and quite common."
+"> _Note_: Using the same module name as the original module for a qualified "
+"import is unnecessary – shorter qualified names like `import Data.Number as "
+"N` are possible and quite common.\n"
msgstr ""
#. type: Title ##
@@ -15616,7 +15715,7 @@ msgstr ""
#: text/chapter5.md:38
#, markdown-text
msgid ""
-"Let's begin by looking at an example. Here is a function which computes the "
+"Let's begin by looking at an example. Here is a function that computes the "
"greatest common divisor of two integers using pattern matching:"
msgstr ""
@@ -15631,10 +15730,10 @@ msgstr ""
#, markdown-text
msgid ""
"This algorithm is called the Euclidean Algorithm. If you search for its "
-"definition online, you will likely find a set of mathematical equations "
-"which look a lot like the code above. This is one benefit of pattern "
-"matching: it allows you to define code by cases, writing simple, declarative "
-"code which looks like a specification of a mathematical function."
+"definition online, you will likely find a set of mathematical equations that "
+"look like the code above. One benefit of pattern matching is that it allows "
+"you to define code by cases, writing simple, declarative code that looks "
+"like a mathematical function specification."
msgstr ""
#. type: Plain text
@@ -15644,7 +15743,7 @@ msgid ""
"A function written using pattern matching works by pairing sets of "
"conditions with their results. Each line is called an _alternative_ or a "
"_case_. The expressions on the left of the equals sign are called "
-"_patterns_, and each case consists of one or more patterns, separated by "
+"_patterns_, and each case consists of one or more patterns separated by "
"spaces. Cases describe which conditions the arguments must satisfy before "
"the expression on the right of the equals sign should be evaluated and "
"returned. Each case is tried in order, and the first case whose patterns "
@@ -15685,10 +15784,10 @@ msgstr ""
#: text/chapter5.md:54
#, markdown-text
msgid ""
-"Note that patterns can bind values to names - each line in the example binds "
+"Note that patterns can bind values to names – each line in the example binds "
"one or both of the names `n` and `m` to the input values. As we learn about "
-"different kinds of patterns, we will see that different types of patterns "
-"correspond to different ways to choose names from the input arguments."
+"different patterns, we will see that different patterns correspond to "
+"different ways to choose names from the input arguments."
msgstr ""
#. type: Title ##
@@ -15726,21 +15825,21 @@ msgstr ""
#. type: Bullet: '- '
#: text/chapter5.md:66
#, markdown-text
-msgid "`Number`, `String`, `Char` and `Boolean` literals"
+msgid "`Number`, `String`, `Char`, and `Boolean` literals"
msgstr ""
#. type: Bullet: '- '
#: text/chapter5.md:66
#, markdown-text
msgid ""
-"Wildcard patterns, indicated with an underscore (`_`), which match any "
-"argument, and which do not bind any names."
+"Wildcard patterns, indicated with an underscore (`_`), match any argument "
+"and do not bind any names."
msgstr ""
#. type: Plain text
#: text/chapter5.md:68
#, markdown-text
-msgid "Here are two more examples which demonstrate using these simple patterns:"
+msgid "Here are two more examples that demonstrate using these simple patterns:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -15764,15 +15863,15 @@ msgstr ""
msgid ""
"In the Euclidean algorithm example, we used an `if .. then .. else` "
"expression to switch between the two alternatives when `m > n` and `m <= "
-"n`. Another option in this case would be to use a _guard_.\n"
+"n`. Another option, in this case, would be to use a _guard_.\n"
msgstr ""
#. type: Plain text
#: text/chapter5.md:82
#, markdown-text
msgid ""
-"A guard is a boolean-valued expression which must be satisfied in addition "
-"to the constraints imposed by the patterns. Here is the Euclidean algorithm "
+"A guard is a boolean-valued expression that must be satisfied in addition to "
+"the constraints imposed by the patterns. Here is the Euclidean algorithm "
"rewritten to use a guard:"
msgstr ""
@@ -15788,8 +15887,8 @@ msgstr ""
msgid ""
"In this case, the third line uses a guard to impose the extra condition that "
"the first argument is strictly larger than the second. The guard in the "
-"final line uses the expression `otherwise`, which might seem like a keyword, "
-"but is in fact just a regular binding in `Prelude`:"
+"final line uses the expression `otherwise`, which might seem like a keyword "
+"but is, in fact, just a regular binding in `Prelude`:"
msgstr ""
#. type: Fenced code block (text)
@@ -15807,7 +15906,7 @@ msgstr ""
#: text/chapter5.md:98
#, markdown-text
msgid ""
-"As this example demonstrates, guards appear on the left of the equals "
+"This example demonstrates that guards appear on the left of the equals "
"symbol, separated from the list of patterns by a pipe character (`|`)."
msgstr ""
@@ -15869,8 +15968,8 @@ msgstr ""
#: text/chapter5.md:114
#, markdown-text
msgid ""
-"Here is another function which matches arrays of length five, binding each "
-"of its five elements in a different way:"
+"Here is another function that matches arrays of length five, binding each of "
+"its five elements differently:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -15884,9 +15983,9 @@ msgstr ""
#, markdown-text
msgid ""
"The first pattern only matches arrays with five elements, whose first and "
-"second elements are 0 and 1 respectively. In that case, the function returns "
-"the product of the third and fourth elements. In every other case, the "
-"function returns zero. For example, in PSCi:"
+"second elements are 0 and 1, respectively. In that case, the function "
+"returns the product of the third and fourth elements. In every other case, "
+"the function returns zero. For example, in PSCi:"
msgstr ""
#. type: Fenced code block (text)
@@ -15912,11 +16011,11 @@ msgstr ""
#: text/chapter5.md:138
#, markdown-text
msgid ""
-"Array literal patterns allow us to match arrays of a fixed length, but "
+"Array literal patterns allow us to match arrays of a fixed length. Still, "
"PureScript does _not_ provide any means of matching arrays of an unspecified "
-"length, since destructuring immutable arrays in these sorts of ways can lead "
-"to poor performance. If you need a data structure which supports this sort "
-"of matching, the recommended approach is to use `Data.List`. Other data "
+"length since destructuring immutable arrays in these sorts of ways can lead "
+"to poor performance. If you need a data structure that supports this sort of "
+"matching, the recommended approach is to use `Data.List`. Other data "
"structures exist which provide improved asymptotic performance for different "
"operations."
msgstr ""
@@ -15930,7 +16029,7 @@ msgstr ""
#. type: Plain text
#: text/chapter5.md:142
#, markdown-text
-msgid "_Record patterns_ are used to match - you guessed it - records."
+msgid "_Record patterns_ are used to match – you guessed it – records."
msgstr ""
#. type: Plain text
@@ -15945,8 +16044,8 @@ msgstr ""
#: text/chapter5.md:146
#, markdown-text
msgid ""
-"For example: this pattern matches any record which contains fields called "
-"`first` and `last`, and binds their values to the names `x` and `y` "
+"For example, this pattern matches any record which contains fields called "
+"`first` and `last`, and binds their values to the names `x` and `y`, "
"respectively:"
msgstr ""
@@ -15973,7 +16072,7 @@ msgid ""
"> showPerson { first: x, last: y } = y <> \", \" <> x\n"
"\n"
"> :type showPerson\n"
-"forall r. { first :: String, last :: String | r } -> String\n"
+"forall (r :: Row Type). { first :: String, last :: String | r } -> String\n"
msgstr ""
#. type: Plain text
@@ -16000,11 +16099,10 @@ msgstr ""
#: text/chapter5.md:171
#, markdown-text
msgid ""
-"We are able to append additional fields to the record, and the `showPerson` "
-"function will still work. As long as the record contains the `first` and "
-"`last` fields of type `String`, the function application is "
-"well-typed. However, it is _not_ valid to call `showPerson` with too _few_ "
-"fields:"
+"We can append additional fields to the record, and the `showPerson` function "
+"will still work. As long as the record contains the `first` and `last` "
+"fields of type `String`, the function application is well-typed. However, it "
+"is _not_ valid to call `showPerson` with too _few_ fields:"
msgstr ""
#. type: Fenced code block (text)
@@ -16044,7 +16142,7 @@ msgstr ""
#. type: Plain text
#: text/chapter5.md:187
#, markdown-text
-msgid "and PSCi would have inferred the same type."
+msgid "And PSCi would have inferred the same type."
msgstr ""
#. type: Title ##
@@ -16059,8 +16157,8 @@ msgstr ""
msgid ""
"Recall that the `showPerson` function matches a record inside its argument, "
"binding the `first` and `last` fields to values named `x` and `y`. We could "
-"alternatively just reuse the field names themselves, and simplify this sort "
-"of pattern match as follows:"
+"alternatively reuse the field names themselves and simplify this sort of "
+"pattern match as follows:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -16095,7 +16193,7 @@ msgstr ""
#. type: Plain text
#: text/chapter5.md:205
#, markdown-text
-msgid "This may improve readability of code in some circumstances."
+msgid "This may improve the readability of code in some circumstances."
msgstr ""
#. type: Title ##
@@ -16110,9 +16208,9 @@ msgstr ""
msgid ""
"Array patterns and record patterns both combine smaller patterns to build "
"larger patterns. For the most part, the examples above have only used simple "
-"patterns inside array patterns and record patterns, but it is important to "
-"note that patterns can be arbitrarily _nested_, which allows functions to be "
-"defined using conditions on potentially complex data types."
+"patterns inside array patterns and record patterns. Still, it is important "
+"to note that patterns can be arbitrarily _nested_, which allows functions to "
+"be defined using conditions on potentially complex data types."
msgstr ""
#. type: Plain text
@@ -16161,8 +16259,7 @@ msgstr ""
msgid ""
"This way, we save ourselves from allocating a new array if the pair is "
"already sorted. Note that if the input array does not contain _exactly_ two "
-"elements, then this function simply returns it unchanged, even if it's "
-"unsorted."
+"elements, then this function returns it unchanged, even if it's unsorted."
msgstr ""
#. type: Bullet: '1. '
@@ -16177,8 +16274,8 @@ msgstr ""
#: text/chapter5.md:233
#, markdown-text
msgid ""
-"(Medium) What is the most general type of the `sameCity` function, taking "
-"into account row polymorphism? What about the `livesInLA` function defined "
+"(Medium) What is the most general type of the `sameCity` function, "
+"considering row polymorphism? What about the `livesInLA` function defined "
"above? _Note_: There is no test for this exercise."
msgstr ""
@@ -16186,9 +16283,9 @@ msgstr ""
#: text/chapter5.md:233
#, markdown-text
msgid ""
-"(Medium) Write a function `fromSingleton` which uses an array literal "
-"pattern to extract the sole member of a singleton array. If the array is not "
-"a singleton, your function should return a provided default value. Your "
+"(Medium) Write a function `fromSingleton` that uses an array literal pattern "
+"to extract the sole member of a singleton array. If the array is not a "
+"singleton, your function should return a provided default value. Your "
"function should have type `forall a. a -> Array a -> a`"
msgstr ""
@@ -16203,7 +16300,7 @@ msgstr ""
#, markdown-text
msgid ""
"Patterns do not only appear in top-level function declarations. It is "
-"possible to use patterns to match on an intermediate value in a computation, "
+"possible to use patterns to match on an intermediate value in a computation "
"using a `case` expression. Case expressions provide a similar type of "
"utility to anonymous functions: it is not always desirable to give a name to "
"a function, and a `case` expression allows us to avoid naming a function "
@@ -16214,7 +16311,7 @@ msgstr ""
#: text/chapter5.md:239
#, markdown-text
msgid ""
-"Here is an example. This function computes \"longest zero suffix\" of an "
+"Here is an example. This function computes the \"longest zero suffix\" of an "
"array (the longest suffix which sums to zero):"
msgstr ""
@@ -16244,8 +16341,9 @@ msgstr ""
msgid ""
"This function works by case analysis. If the array is empty, our only option "
"is to return an empty array. If the array is non-empty, we first use a "
-"`case` expression to split into two cases. If the sum of the array is zero, "
-"we return the whole array. If not, we recurse on the tail of the array."
+"`case` expression to split it into two cases. If the sum of the array is "
+"zero, we return the whole array. If not, we recurse on the tail of the "
+"array."
msgstr ""
#. type: Title ##
@@ -16258,10 +16356,9 @@ msgstr ""
#: text/chapter5.md:261
#, markdown-text
msgid ""
-"If patterns in a case expression are tried in order, then what happens in "
-"the case when none of the patterns in a case alternatives match their "
-"inputs? In this case, the case expression will fail at runtime with a "
-"_pattern match failure_."
+"If patterns in a case expression are tried in order, what happens when none "
+"of the patterns in a case alternatives match their inputs? In this case, the "
+"case expression will fail at runtime with a _pattern match failure_."
msgstr ""
#. type: Plain text
@@ -16286,7 +16383,7 @@ msgstr ""
#, markdown-text
msgid ""
"This function contains only a single case, which only matches a single "
-"input, `true`. If we compile this file, and test in PSCi with any other "
+"input, `true`. If we compile this file and test in PSCi with any other "
"argument, we will see an error at runtime:"
msgstr ""
@@ -16303,8 +16400,8 @@ msgstr ""
#: text/chapter5.md:279
#, markdown-text
msgid ""
-"Functions which return a value for any combination of inputs are called "
-"_total_ functions, and functions which do not are called _partial_."
+"Functions that return a value for any combination of inputs are called "
+"_total_ functions, and functions that do not are called _partial_."
msgstr ""
#. type: Plain text
@@ -16326,7 +16423,7 @@ msgid ""
"The PureScript compiler will generate an error if it can detect that your "
"function is not total due to an incomplete pattern match. The "
"`unsafePartial` function can be used to silence these errors (if you are "
-"sure that your partial function is safe!) If we removed the call to the "
+"sure your partial function is safe!) If we removed the call to the "
"`unsafePartial` function above, then the compiler would generate the "
"following error:"
msgstr ""
@@ -16380,9 +16477,9 @@ msgstr ""
#: text/chapter5.md:308
#, markdown-text, no-wrap
msgid ""
-"We will see more types which involve the `=>` symbol later on in the book "
+"We will see more types that involve the `=>` symbol later on in the book "
"(they are related to _type classes_), but for now, it suffices to observe "
-"that PureScript keeps track of partial functions using the type system, and "
+"that PureScript keeps track of partial functions using the type system and "
"that we must explicitly tell the type checker when they are safe.\n"
msgstr ""
@@ -16392,7 +16489,7 @@ msgstr ""
msgid ""
"The compiler will also generate a warning in certain cases when it can "
"detect that cases are _redundant_ (that is, a case only matches values which "
-"would have been matched by a prior case):"
+"a prior case would have matched):"
msgstr ""
#. type: Fenced code block (haskell)
@@ -16422,10 +16519,11 @@ msgstr ""
#. type: Plain text
#: text/chapter5.md:327
-#, markdown-text
+#, markdown-text, no-wrap
msgid ""
-"_Note_: PSCi does not show warnings, so to reproduce this example, you will "
-"need to save this function as a file and compile it using `spago build`."
+"> _Note_: PSCi does not show warnings, so to reproduce this example, you "
+"will need to save this function as a file and compile it using `spago "
+"build`.\n"
msgstr ""
#. type: Title ##
@@ -16468,17 +16566,17 @@ msgstr ""
msgid ""
"However, this approach has one major drawback: to work with `Shape`s "
"abstractly, it is necessary to identify all of the operations one might wish "
-"to perform, and to define them on the `Shape` interface. It becomes "
-"difficult to add new operations without breaking modularity."
+"to perform and to define them on the `Shape` interface. It becomes difficult "
+"to add new operations without breaking modularity."
msgstr ""
#. type: Plain text
#: text/chapter5.md:339
#, markdown-text
msgid ""
-"Algebraic data types provide a type-safe way to solve this sort of problem, "
-"if the set of shapes is known in advance. It is possible to define new "
-"operations on `Shape` in a modular way, and still maintain type-safety."
+"Algebraic data types provide a type-safe way to solve this problem if the "
+"set of shapes is known in advance. It is possible to define new operations "
+"on `Shape` in a modular way and still maintain type-safety."
msgstr ""
#. type: Plain text
@@ -16501,8 +16599,8 @@ msgstr ""
#, markdown-text
msgid ""
"This declaration defines `Shape` as a sum of different constructors, and for "
-"each constructor identifies the data that is included. A `Shape` is either a "
-"`Circle` which contains a center `Point` and a radius (a number), or a "
+"each constructor identifies the included data. A `Shape` is either a "
+"`Circle` that contains a center `Point` and a radius (a number), or a "
"`Rectangle`, or a `Line`, or `Text`. There are no other ways to construct a "
"value of type `Shape`."
msgstr ""
@@ -16513,7 +16611,7 @@ msgstr ""
msgid ""
"An algebraic data type is introduced using the `data` keyword, followed by "
"the name of the new type and any type arguments. The type's constructors "
-"(i.e. its _data constructors_) are defined after the equals symbol, and are "
+"(i.e., its _data constructors_) are defined after the equals symbol and "
"separated by pipe characters (`|`). The data carried by an ADT's "
"constructors doesn't have to be restricted to primitive types: constructors "
"can include records, arrays, or even other ADTs."
@@ -16549,7 +16647,7 @@ msgstr ""
#, markdown-text
msgid ""
"Note that we don't use the syntax `forall a.` anywhere in our data "
-"definition. `forall` syntax is necessary for functions, but is not used when "
+"definition. `forall` syntax is necessary for functions but is not used when "
"defining ADTs with `data` or type aliases with `type`."
msgstr ""
@@ -16653,14 +16751,14 @@ msgstr ""
#, markdown-text
msgid ""
"(Easy) Write a function `circleAtOrigin` which constructs a `Circle` (of "
-"type `Shape`) centered at the origin with radius `10.0`."
+"type `Shape`) centered at the origin with a radius `10.0`."
msgstr ""
#. type: Bullet: '1. '
#: text/chapter5.md:397
#, markdown-text
msgid ""
-"(Medium) Write a function `doubleScaleAndCenter` which scales the size of a "
+"(Medium) Write a function `doubleScaleAndCenter` that scales the size of a "
"`Shape` by a factor of `2.0` and centers it at the origin."
msgstr ""
@@ -16801,7 +16899,7 @@ msgstr ""
#: text/chapter5.md:456
#, markdown-text
msgid ""
-"Also note that the constructor of a newtype often has the same name as the "
+"Also, note that the constructor of a newtype often has the same name as the "
"newtype itself, but this is not a requirement. For example, unique names are "
"also valid:"
msgstr ""
@@ -16816,13 +16914,13 @@ msgstr ""
#: text/chapter5.md:462
#, markdown-text
msgid ""
-"In this case, `Coulomb` is the _type constructor_ (of zero arguments) and "
+"In this case, `Coulomb` is the _type constructor_ (of zero arguments), and "
"`MakeCoulomb` is the _data constructor_. These constructors live in "
"different namespaces, even when the names are identical, such as with the "
"`Volt` example. This is true for all ADTs. Note that although the type "
-"constructor and data constructor can have different names, in practice it is "
-"idiomatic for them to share the same name. This is the case with `Amp` and "
-"`Volt` types above."
+"constructor and data constructor can have different names, in practice, it "
+"is idiomatic for them to share the same name. This is the case with `Amp` "
+"and `Volt` types above."
msgstr ""
#. type: Plain text
@@ -16874,7 +16972,7 @@ msgstr ""
#. type: Plain text
#: text/chapter5.md:480
#, markdown-text
-msgid "Define a type synonym for a `Picture` - just an array of `Shape`s:"
+msgid "Define a type synonym for a `Picture` – just an array of `Shape`s:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -17015,7 +17113,7 @@ msgstr ""
msgid ""
"This chapter also introduced algebraic data types, which are closely related "
"to pattern matching. We saw how algebraic data types allow concise "
-"descriptions of data structures, and provide a modular way to extend data "
+"descriptions of data structures and provide a modular way to extend data "
"types with new operations."
msgstr ""
@@ -17023,7 +17121,7 @@ msgstr ""
#: text/chapter5.md:536
#, markdown-text
msgid ""
-"Finally, we covered _row polymorphism_, a powerful type of abstraction which "
+"Finally, we covered _row polymorphism_, a powerful type of abstraction that "
"allows many idiomatic JavaScript functions to be given a type."
msgstr ""
@@ -17047,8 +17145,8 @@ msgstr ""
#: text/chapter6.md:6
#, markdown-text
msgid ""
-"This chapter will introduce a powerful form of abstraction which is enabled "
-"by PureScript's type system - type classes."
+"This chapter will introduce a powerful form of abstraction enabled by "
+"PureScript's type system – type classes."
msgstr ""
#. type: Plain text
@@ -17056,7 +17154,7 @@ msgstr ""
#, markdown-text
msgid ""
"This motivating example for this chapter will be a library for hashing data "
-"structures. We will see how the machinery of type classes allow us to hash "
+"structures. We will see how the machinery of type classes allows us to hash "
"complex data structures without having to think directly about the structure "
"of the data itself."
msgstr ""
@@ -17115,7 +17213,7 @@ msgstr ""
#. type: Bullet: '- '
#: text/chapter6.md:24
#, markdown-text
-msgid "`strings`, which defines functions which operate on strings."
+msgid "`strings`, which defines functions that operate on strings."
msgstr ""
#. type: Bullet: '- '
@@ -17202,7 +17300,7 @@ msgstr ""
#: text/chapter6.md:51
#, markdown-text
msgid ""
-"This code declares a type class instance called `showBoolean` - in "
+"This code declares a type class instance called `showBoolean` – in "
"PureScript, type class instances can be named to aid the readability of the "
"generated JavaScript. We say that the `Boolean` type _belongs to the `Show` "
"type class_."
@@ -17261,9 +17359,9 @@ msgstr ""
msgid ""
"The output of `show` should be a string that you can paste back into the "
"repl (or `.purs` file) to recreate the item being shown. Here we'll use "
-"`logShow`, which just calls `show` then `log`, to render the string without "
-"quotes. Ignore the `unit` print - that will covered in Chapter 8 when we "
-"examine `Effect`s, like `log`."
+"`logShow`, which just calls `show` and then `log`, to render the string "
+"without quotes. Ignore the `unit` print – that will be covered in Chapter 8 "
+"when we examine `Effect`s, like `log`."
msgstr ""
#. type: Fenced code block (text)
@@ -17309,15 +17407,15 @@ msgstr ""
#, markdown-text
msgid ""
"The problem here is not that there is no `Show` instance for the type we "
-"intended to `show`, but rather that PSCi was unable to infer the type. This "
-"is indicated by the _unknown type_ `a` in the inferred type."
+"intended to `show`, but rather that PSCi could not infer the type. This is "
+"indicated by the _unknown type_ `a` in the inferred type."
msgstr ""
#. type: Plain text
#: text/chapter6.md:111
#, markdown-text
msgid ""
-"We can annotate the expression with a type, using the `::` operator, so that "
+"We can annotate the expression with a type using the `::` operator, so that "
"PSCi can choose the correct type class instance:"
msgstr ""
@@ -17413,7 +17511,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `Eq` type class defines the `eq` function, which tests two values for "
-"equality. The `==` operator is actually just an alias for `eq`."
+"equality. The `==` operator is actually an alias for `eq`."
msgstr ""
#. type: Fenced code block (haskell)
@@ -17428,8 +17526,8 @@ msgstr ""
#: text/chapter6.md:152
#, markdown-text
msgid ""
-"Note that in either case, the two arguments must have the same type: it does "
-"not make sense to compare two values of different types for equality."
+"In either case, the two arguments must have the same type: it does not make "
+"sense to compare two values of different types for equality."
msgstr ""
#. type: Plain text
@@ -17460,7 +17558,7 @@ msgstr ""
#, markdown-text, no-wrap
msgid ""
"The `Ord` type class defines the `compare` function, which can be used to "
-"compare two values, for types which support ordering. The comparison "
+"compare two values, for types that support ordering. The comparison "
"operators `<` and `>` along with their non-strict companions `<=` and `>=`, "
"can be defined in terms of `compare`.\n"
msgstr ""
@@ -17489,26 +17587,26 @@ msgstr ""
#: text/chapter6.md:177
#, markdown-text
msgid ""
-"The `compare` function compares two values, and returns an `Ordering`, which "
+"The `compare` function compares two values and returns an `Ordering`, which "
"has three alternatives:"
msgstr ""
#. type: Bullet: '- '
#: text/chapter6.md:181
#, markdown-text
-msgid "`LT` - if the first argument is less than the second."
+msgid "`LT` – if the first argument is less than the second."
msgstr ""
#. type: Bullet: '- '
#: text/chapter6.md:181
#, markdown-text
-msgid "`EQ` - if the first argument is equal to the second."
+msgid "`EQ` – if the first argument is equal to the second."
msgstr ""
#. type: Bullet: '- '
#: text/chapter6.md:181
#, markdown-text
-msgid "`GT` - if the first argument is greater than the second."
+msgid "`GT` – if the first argument is greater than the second."
msgstr ""
#. type: Plain text
@@ -17539,19 +17637,19 @@ msgstr ""
#, markdown-text
msgid ""
"The `Field` type class identifies those types which support numeric "
-"operators such as addition, subtraction, multiplication and division. It is "
+"operators such as addition, subtraction, multiplication, and division. It is "
"provided to abstract over those operators, so that they can be reused where "
"appropriate."
msgstr ""
#. type: Plain text
#: text/chapter6.md:197
-#, markdown-text
+#, markdown-text, no-wrap
msgid ""
-"_Note_: Just like the `Eq` and `Ord` type classes, the `Field` type class "
+"> _Note_: Just like the `Eq` and `Ord` type classes, the `Field` type class "
"has special support in the PureScript compiler, so that simple expressions "
"such as `1 + 2 * 3` get translated into simple JavaScript, as opposed to "
-"function calls which dispatch based on a type class implementation."
+"function calls which dispatch based on a type class implementation.\n"
msgstr ""
#. type: Fenced code block (haskell)
@@ -17565,7 +17663,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `Field` type class is composed from several more general "
-"_superclasses_. This allows us to talk abstractly about types which support "
+"_superclasses_. This allows us to talk abstractly about types that support "
"some but not all of the `Field` operations. For example, a type of natural "
"numbers would be closed under addition and multiplication, but not "
"necessarily under subtraction, so that type might have an instance of the "
@@ -17612,8 +17710,7 @@ msgstr ""
#, markdown-text
msgid ""
"Strings form a semigroup under regular string concatenation, and so do "
-"arrays. Several other standard instances are provided by the `prelude` "
-"package."
+"arrays. The `prelude` package provides several other standard instances."
msgstr ""
#. type: Plain text
@@ -17651,9 +17748,9 @@ msgstr ""
#, markdown-text
msgid ""
"A `Monoid` type class instance for a type describes how to _accumulate_ a "
-"result with that type, by starting with an \"empty\" value, and combining "
-"new results. For example, we can write a function which concatenates an "
-"array of values in some monoid by using a fold. In PSCi:"
+"result with that type by starting with an \"empty\" value and combining new "
+"results. For example, we can write a function that concatenates an array of "
+"values in some monoid using a fold. In PSCi:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -17727,8 +17824,8 @@ msgstr ""
msgid ""
"It is instructive to specialize to the case where `f` is the array type "
"constructor. In this case, we can replace `f a` with `Array a` for any a, "
-"and we notice that the types of `foldl` and `foldr` become the types that we "
-"saw when we first encountered folds over arrays."
+"and we notice that the types of `foldl` and `foldr` become the types we saw "
+"when we first encountered folds over arrays."
msgstr ""
#. type: Plain text
@@ -17738,9 +17835,9 @@ msgid ""
"What about `foldMap`? Well, that becomes `forall a m. Monoid m => (a -> m) "
"-> Array a -> m`. This type signature says that we can choose any type `m` "
"for our result type, as long as that type is an instance of the `Monoid` "
-"type class. If we can provide a function which turns our array elements into "
+"type class. If we can provide a function that turns our array elements into "
"values in that monoid, then we can accumulate over our array using the "
-"structure of the monoid, and return a single value.\n"
+"structure of the monoid and return a single value.\n"
msgstr ""
#. type: Plain text
@@ -17764,7 +17861,7 @@ msgstr ""
#, markdown-text
msgid ""
"Here, we choose the monoid for strings, which concatenates strings together, "
-"and the `show` function which renders an `Int` as a `String`. Then, passing "
+"and the `show` function, which renders an `Int` as a `String`. Then, passing "
"in an array of integers, we see that the results of `show`ing each integer "
"have been concatenated into a single `String`."
msgstr ""
@@ -17773,7 +17870,7 @@ msgstr ""
#: text/chapter6.md:275
#, markdown-text
msgid ""
-"But arrays are not the only types which are foldable. `foldable-traversable` "
+"But arrays are not the only types that are foldable. `foldable-traversable` "
"also defines `Foldable` instances for types like `Maybe` and `Tuple`, and "
"other libraries like `lists` define `Foldable` instances for their own data "
"types. `Foldable` captures the notion of an _ordered container_."
@@ -17782,16 +17879,16 @@ msgstr ""
#. type: Title ###
#: text/chapter6.md:276
#, markdown-text, no-wrap
-msgid "Functor, and Type Class Laws"
+msgid "Functor and Type Class Laws"
msgstr ""
#. type: Plain text
#: text/chapter6.md:279
#, markdown-text
msgid ""
-"The Prelude also defines a collection of type classes which enable a "
+"The Prelude also defines a collection of type classes that enable a "
"functional style of programming with side-effects in PureScript: `Functor`, "
-"`Applicative` and `Monad`. We will cover these abstractions later in the "
+"`Applicative`, and `Monad`. We will cover these abstractions later in the "
"book, but for now, let's look at the definition of the `Functor` type class, "
"which we have seen already in the form of the `map` function:"
msgstr ""
@@ -17883,8 +17980,8 @@ msgstr ""
#, markdown-text
msgid ""
"The second law is the _composition law_. It states that mapping one function "
-"over a structure, and then mapping a second, is the same thing as mapping "
-"the composition of the two functions over the structure."
+"over a structure and then mapping a second is the same as mapping the "
+"composition of the two functions over the structure."
msgstr ""
#. type: Plain text
@@ -18011,7 +18108,7 @@ msgstr ""
#, markdown-text
msgid ""
"Types of functions can be constrained by using type classes. Here is an "
-"example: suppose we want to write a function which tests if three values are "
+"example: suppose we want to write a function that tests if three values are "
"equal, by using equality defined using an `Eq` type class instance."
msgstr ""
@@ -18110,17 +18207,16 @@ msgid ""
"> import Prelude\n"
"\n"
"> :type \\x -> x + x\n"
-"forall a. Semiring a => a -> a\n"
+"forall (a :: Type). Semiring a => a -> a\n"
msgstr ""
#. type: Plain text
#: text/chapter6.md:388
#, markdown-text, no-wrap
msgid ""
-"Here, we might have annotated this function as `Int -> Int`, or `Number -> "
+"Here, we might have annotated this function as `Int -> Int` or `Number -> "
"Number`, but PSCi shows us that the most general type works for any "
-"`Semiring`, allowing us to use our function with both `Int`s and "
-"`Number`s.\n"
+"`Semiring`, allowing us to use our function with both `Int`s and `Number.\n"
msgstr ""
#. type: Title ##
@@ -18158,16 +18254,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:402
+#: text/chapter6.md:401
#, markdown-text, no-wrap
msgid ""
"If a type class instance depends on multiple other instances, those "
-"instances should be grouped in parentheses and separated by\n"
-"commas on the left hand side of the `=>` symbol:\n"
+"instances should be grouped in parentheses and separated by commas on the "
+"left-hand side of the `=>` symbol:\n"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:403
+#: text/chapter6.md:402
#, no-wrap
msgid ""
"instance (Show a, Show b) => Show (Either a b) where\n"
@@ -18175,13 +18271,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:409
+#: text/chapter6.md:408
#, markdown-text
msgid "These two type class instances are provided in the `prelude` library."
msgstr ""
#. type: Plain text
-#: text/chapter6.md:411
+#: text/chapter6.md:410
#, markdown-text
msgid ""
"When the program is compiled, the correct type class instance for `Show` is "
@@ -18191,7 +18287,7 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:415
+#: text/chapter6.md:414
#, markdown-text
msgid ""
"(Easy) The following declaration defines a type of non-empty arrays of "
@@ -18199,7 +18295,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:419
+#: text/chapter6.md:418
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -18209,16 +18305,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:421
+#: text/chapter6.md:420
#, markdown-text, no-wrap
msgid ""
-" Write an `Eq` instance for the type `NonEmpty a` which reuses the "
+" Write an `Eq` instance for the type `NonEmpty a` that reuses the "
"instances for `Eq a` and `Eq (Array a)`. _Note:_ you may instead derive the "
"`Eq` instance.\n"
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:423
+#: text/chapter6.md:422
#, markdown-text
msgid ""
"(Medium) Write a `Semigroup` instance for `NonEmpty a` by reusing the "
@@ -18226,21 +18322,21 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:425
+#: text/chapter6.md:424
#, markdown-text
msgid "(Medium) Write a `Functor` instance for `NonEmpty`."
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:427
+#: text/chapter6.md:426
#, markdown-text
msgid ""
"(Medium) Given any type `a` with an instance of `Ord`, we can add a new "
-"\"infinite\" value which is greater than any other value:"
+"\"infinite\" value that is greater than any other value:"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:431
+#: text/chapter6.md:430
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -18250,15 +18346,15 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:433
+#: text/chapter6.md:432
#, markdown-text, no-wrap
msgid ""
-" Write an `Ord` instance for `Extended a` which reuses the `Ord` instance "
+" Write an `Ord` instance for `Extended a` that reuses the `Ord` instance "
"for `a`.\n"
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:435
+#: text/chapter6.md:434
#, markdown-text
msgid ""
"(Difficult) Write a `Foldable` instance for `NonEmpty`. _Hint_: reuse the "
@@ -18266,16 +18362,16 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:437
+#: text/chapter6.md:436
#, markdown-text
msgid ""
"(Difficult) Given a type constructor `f` which defines an ordered container "
-"(and so has a `Foldable` instance), we can create a new container type which "
+"(and so has a `Foldable` instance), we can create a new container type that "
"includes an extra element at the front:"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:441
+#: text/chapter6.md:440
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -18285,7 +18381,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:443
+#: text/chapter6.md:442
#, markdown-text, no-wrap
msgid ""
" The container `OneMore f` also has an ordering, where the new element "
@@ -18294,7 +18390,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:448
+#: text/chapter6.md:447
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -18305,15 +18401,15 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:450
+#: text/chapter6.md:449
#, markdown-text
msgid ""
-"(Medium) Write a `dedupShapes :: Array Shape -> Array Shape` function which "
+"(Medium) Write a `dedupShapes :: Array Shape -> Array Shape` function that "
"removes duplicate `Shape`s from an array using the `nubEq` function."
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:452
+#: text/chapter6.md:451
#, markdown-text
msgid ""
"(Medium) Write a `dedupShapesFast` function which is the same as "
@@ -18321,28 +18417,28 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter6.md:453
+#: text/chapter6.md:452
#, markdown-text, no-wrap
-msgid "Multi Parameter Type Classes"
+msgid "Multi-Parameter Type Classes"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:456
+#: text/chapter6.md:455
#, markdown-text
msgid ""
"It's not the case that a type class can only take a single type as an "
-"argument. This is the most common case, but in fact, a type class can be "
+"argument. This is the most common case, but a type class can be "
"parameterized by _zero or more_ type arguments."
msgstr ""
#. type: Plain text
-#: text/chapter6.md:458
+#: text/chapter6.md:457
#, markdown-text
msgid "Let's see an example of a type class with two type arguments."
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:459
+#: text/chapter6.md:458
#, no-wrap
msgid ""
"module Stream where\n"
@@ -18362,16 +18458,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:477
+#: text/chapter6.md:476
#, markdown-text
msgid ""
-"The `Stream` module defines a class `Stream` which identifies types which "
+"The `Stream` module defines a class `Stream` which identifies types that "
"look like streams of elements, where elements can be pulled from the front "
"of the stream using the `uncons` function."
msgstr ""
#. type: Plain text
-#: text/chapter6.md:479
+#: text/chapter6.md:478
#, markdown-text
msgid ""
"Note that the `Stream` type class is parameterized not only by the type of "
@@ -18380,7 +18476,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:481
+#: text/chapter6.md:480
#, markdown-text
msgid ""
"The module defines two type class instances: an instance for arrays, where "
@@ -18389,16 +18485,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:483
+#: text/chapter6.md:482
#, markdown-text
msgid ""
-"We can write functions which work over arbitrary streams. For example, here "
-"is a function which accumulates a result in some `Monoid` based on the "
+"We can write functions that work over arbitrary streams. For example, here "
+"is a function that accumulates a result in some `Monoid` based on the "
"elements of a stream:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:484
+#: text/chapter6.md:483
#, no-wrap
msgid ""
"import Prelude\n"
@@ -18412,7 +18508,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:496
+#: text/chapter6.md:495
#, markdown-text
msgid ""
"Try using `foldStream` in PSCi for different types of `Stream` and different "
@@ -18420,35 +18516,35 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter6.md:497
+#: text/chapter6.md:496
#, markdown-text, no-wrap
msgid "Functional Dependencies"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:500
+#: text/chapter6.md:499
#, markdown-text
msgid ""
-"Multi-parameter type classes can be very useful, but can easily lead to "
+"Multi-parameter type classes can be very useful but can easily lead to "
"confusing types and even issues with type inference. As a simple example, "
"consider writing a generic `tail` function on streams using the `Stream` "
"class given above:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:501
+#: text/chapter6.md:500
#, no-wrap
msgid "genericTail xs = map _.tail (uncons xs)\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:506
+#: text/chapter6.md:505
#, markdown-text
msgid "This gives a somewhat confusing error message:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter6.md:507
+#: text/chapter6.md:506
#, no-wrap
msgid ""
"The inferred type\n"
@@ -18460,7 +18556,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:516
+#: text/chapter6.md:515
#, markdown-text
msgid ""
"The problem is that the `genericTail` function does not use the `element` "
@@ -18469,7 +18565,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:518
+#: text/chapter6.md:517
#, markdown-text
msgid ""
"Worse still, we cannot even use `genericTail` by applying it to a specific "
@@ -18477,7 +18573,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter6.md:519
+#: text/chapter6.md:518
#, no-wrap
msgid ""
"> map _.tail (uncons \"testing\")\n"
@@ -18491,7 +18587,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:530
+#: text/chapter6.md:529
#, markdown-text
msgid ""
"Here, we might expect the compiler to choose the `streamString` "
@@ -18500,16 +18596,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:532
+#: text/chapter6.md:531
#, markdown-text
msgid ""
-"The compiler is unable to make that deduction automatically, and cannot "
-"commit to the `streamString` instance. However, we can help the compiler by "
-"adding a hint to the type class definition:"
+"The compiler cannot make that deduction automatically or commit to the "
+"`streamString` instance. However, we can help the compiler by adding a hint "
+"to the type class definition:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:533
+#: text/chapter6.md:532
#, no-wrap
msgid ""
"class Stream stream element | stream -> element where\n"
@@ -18517,7 +18613,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:539
+#: text/chapter6.md:538
#, markdown-text, no-wrap
msgid ""
"Here, `stream -> element` is called a _functional dependency_. A functional "
@@ -18529,7 +18625,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:541
+#: text/chapter6.md:540
#, markdown-text
msgid ""
"This hint is enough for the compiler to infer the correct type for our "
@@ -18537,52 +18633,53 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter6.md:542
+#: text/chapter6.md:541
#, no-wrap
msgid ""
"> :type genericTail\n"
-"forall stream element. Stream stream element => stream -> Maybe stream\n"
+"forall (stream :: Type) (element :: Type). Stream stream element => stream "
+"-> Maybe stream\n"
"\n"
"> genericTail \"testing\"\n"
"(Just \"esting\")\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:551
+#: text/chapter6.md:550
#, markdown-text
msgid ""
-"Functional dependencies can be quite useful when using multi-parameter type "
-"classes to design certain APIs."
+"Functional dependencies can be useful when designing certain APIs using "
+"multi-parameter type classes."
msgstr ""
#. type: Title ##
-#: text/chapter6.md:552
+#: text/chapter6.md:551
#, markdown-text, no-wrap
msgid "Nullary Type Classes"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:555
+#: text/chapter6.md:554
#, markdown-text
msgid ""
-"We can even define type classes with zero type arguments! These correspond "
-"to compile-time assertions about our functions, allowing us to track global "
-"properties of our code in the type system."
+"We can even define type classes with zero-type arguments! These correspond "
+"to compile-time assertions about our functions, allowing us to track the "
+"global properties of our code in the type system."
msgstr ""
#. type: Plain text
-#: text/chapter6.md:557
+#: text/chapter6.md:556
#, markdown-text
msgid ""
-"An important example is the `Partial` class which we saw earlier when "
-"discussing partial functions. Take for example the functions `head` and "
-"`tail` defined in `Data.Array.Partial` that allow us to get the head or tail "
-"of an array without wrapping them in a `Maybe`, so they can fail if the "
-"array is empty:"
+"An important example is the `Partial` class we saw earlier when discussing "
+"partial functions. Take, for example, the functions `head` and `tail` "
+"defined in `Data.Array.Partial` that allow us to get the head or tail of an "
+"array without wrapping them in a `Maybe`, so they can fail if the array is "
+"empty:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:558
+#: text/chapter6.md:557
#, no-wrap
msgid ""
"head :: forall a. Partial => Array a -> a\n"
@@ -18591,7 +18688,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:565
+#: text/chapter6.md:564
#, markdown-text
msgid ""
"Note that there is no instance defined for the `Partial` type class! Doing "
@@ -18600,7 +18697,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter6.md:566
+#: text/chapter6.md:565
#, no-wrap
msgid ""
"> head [1, 2, 3]\n"
@@ -18611,7 +18708,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:575
+#: text/chapter6.md:574
#, markdown-text
msgid ""
"Instead, we can republish the `Partial` constraint for any functions making "
@@ -18619,7 +18716,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:576
+#: text/chapter6.md:575
#, no-wrap
msgid ""
"secondElement :: forall a. Partial => Array a -> a\n"
@@ -18627,7 +18724,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:582
+#: text/chapter6.md:581
#, markdown-text
msgid ""
"We've already seen the `unsafePartial` function, which allows us to treat a "
@@ -18636,13 +18733,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:583
+#: text/chapter6.md:582
#, no-wrap
msgid "unsafePartial :: forall a. (Partial => a) -> a\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:588
+#: text/chapter6.md:587
#, markdown-text
msgid ""
"Note that the `Partial` constraint appears _inside the parentheses_ on the "
@@ -18651,7 +18748,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter6.md:589
+#: text/chapter6.md:588
#, no-wrap
msgid ""
"> unsafePartial head [1, 2, 3]\n"
@@ -18662,13 +18759,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter6.md:597
+#: text/chapter6.md:596
#, markdown-text, no-wrap
msgid "Superclasses"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:600
+#: text/chapter6.md:599
#, markdown-text
msgid ""
"Just as we can express relationships between type class instances by making "
@@ -18677,7 +18774,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:602
+#: text/chapter6.md:601
#, markdown-text, no-wrap
msgid ""
"We say that one type class is a superclass of another if every instance of "
@@ -18687,60 +18784,60 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:604
+#: text/chapter6.md:603
#, markdown-text
msgid ""
"We've [already seen an example of superclass relationships](#ord): the `Eq` "
"class is a superclass of `Ord`, and the `Semigroup` class is a superclass of "
"`Monoid`. For every type class instance of the `Ord` class, there must be a "
-"corresponding `Eq` instance for the same type. This makes sense, since in "
+"corresponding `Eq` instance for the same type. This makes sense since, in "
"many cases, when the `compare` function reports that two values are "
"incomparable, we often want to use the `Eq` class to determine if they are "
-"in fact equal."
+"equal."
msgstr ""
#. type: Plain text
-#: text/chapter6.md:606
+#: text/chapter6.md:605
#, markdown-text
msgid ""
"In general, it makes sense to define a superclass relationship when the laws "
-"for the subclass mention the members of the superclass. For example, it is "
-"reasonable to assume, for any pair of `Ord` and `Eq` instances, that if two "
-"values are equal under the `Eq` instance, then the `compare` function should "
-"return `EQ`. In other words, `a == b` should be true exactly when `compare a "
-"b` evaluates to `EQ`. This relationship on the level of laws justifies the "
+"for the subclass mention the superclass members. For example, for any pair "
+"of Ord and Eq instances, it is reasonable to assume that if two values are "
+"equal under the `Eq` instance, then the `compare` function should return "
+"`EQ`. In other words, `a == b` should be true exactly when `compare a b` "
+"evaluates to `EQ`. This relationship on the level of laws justifies the "
"superclass relationship between `Eq` and `Ord`."
msgstr ""
#. type: Plain text
-#: text/chapter6.md:608
+#: text/chapter6.md:607
#, markdown-text
msgid ""
-"Another reason to define a superclass relationship is in the case where "
-"there is a clear \"is-a\" relationship between the two classes. That is, "
-"every member of the subclass _is a_ member of the superclass as well."
+"Another reason to define a superclass relationship is when there is a clear "
+"\"is-a\" relationship between the two classes. That is, every member of the "
+"subclass _is a_ member of the superclass as well."
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:612
+#: text/chapter6.md:611
#, markdown-text
msgid ""
"(Medium) Define a partial function `unsafeMaximum :: Partial => Array Int -> "
-"Int` which finds the maximum of a non-empty array of integers. Test out your "
+"Int` that finds the maximum of a non-empty array of integers. Test out your "
"function in PSCi using `unsafePartial`. _Hint_: Use the `maximum` function "
"from `Data.Foldable`."
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:614
+#: text/chapter6.md:613
#, markdown-text
msgid ""
-"(Medium) The `Action` class is a multi-parameter type class which defines an "
+"(Medium) The `Action` class is a multi-parameter type class that defines an "
"action of one type on another:"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:618
+#: text/chapter6.md:617
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -18750,28 +18847,28 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:620
+#: text/chapter6.md:619
#, markdown-text, no-wrap
msgid ""
-" An _action_ is a function which describes how monoidal values are used "
-"to determine how to modify a value of another type. There are two laws for "
-"the `Action` type class:\n"
+" An _action_ is a function that describes how monoidal values are used to "
+"determine how to modify a value of another type. There are two laws for the "
+"`Action` type class:\n"
msgstr ""
#. type: Bullet: ' - '
-#: text/chapter6.md:623
+#: text/chapter6.md:622
#, markdown-text
msgid "`act mempty a = a`"
msgstr ""
#. type: Bullet: ' - '
-#: text/chapter6.md:623
+#: text/chapter6.md:622
#, markdown-text
msgid "`act (m1 <> m2) a = act m1 (act m2 a)`"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:625
+#: text/chapter6.md:624
#, markdown-text, no-wrap
msgid ""
" Applying an empty action is a no-op. And applying two actions in "
@@ -18780,13 +18877,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:627
+#: text/chapter6.md:626
#, markdown-text, no-wrap
msgid " For example, the natural numbers form a monoid under multiplication:\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:630
+#: text/chapter6.md:629
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -18795,7 +18892,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:632
+#: text/chapter6.md:631
#, markdown-text, no-wrap
msgid ""
" {{#include "
@@ -18803,7 +18900,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:635
+#: text/chapter6.md:634
#, markdown-text, no-wrap
msgid ""
" {{#include "
@@ -18812,13 +18909,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:637
+#: text/chapter6.md:636
#, markdown-text, no-wrap
-msgid " Write an instance which implements this action:\n"
+msgid " Write an instance that implements this action:\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:642
+#: text/chapter6.md:641
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -18829,31 +18926,31 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:644
+#: text/chapter6.md:643
#, markdown-text, no-wrap
msgid " Remember, your instance must satisfy the laws listed above.\n"
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:646
+#: text/chapter6.md:645
#, markdown-text
msgid ""
-"(Difficult) There are actually multiple ways to implement an instance of "
-"`Action Multiply Int`. How many can you think of? Purescript does not allow "
-"multiple implementations of a same instance, so you will have to replace "
-"your original implementation. _Note_: the tests cover 4 implementations."
+"(Difficult) There are multiple ways to implement an instance of `Action "
+"Multiply Int`. How many can you think of? Purescript does not allow multiple "
+"implementations of the same instance, so you will have to replace your "
+"original implementation. _Note_: the tests cover 4 implementations."
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:648
+#: text/chapter6.md:647
#, markdown-text
msgid ""
-"(Medium) Write an `Action` instance which repeats an input string some "
-"number of times:"
+"(Medium) Write an `Action` instance that repeats an input string some number "
+"of times:"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:653
+#: text/chapter6.md:652
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -18864,7 +18961,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:655
+#: text/chapter6.md:654
#, markdown-text, no-wrap
msgid ""
" _Hint_: Search Pursuit for a helper-function with the signature [`String "
@@ -18874,13 +18971,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:657
+#: text/chapter6.md:656
#, markdown-text, no-wrap
msgid " Does this instance satisfy the laws listed above?\n"
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:659
+#: text/chapter6.md:658
#, markdown-text
msgid ""
"(Medium) Write an instance `Action m a => Action m (Array a)`, where the "
@@ -18888,7 +18985,7 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:661
+#: text/chapter6.md:660
#, markdown-text
msgid ""
"(Difficult) Given the following newtype, write an instance for `Action m "
@@ -18896,7 +18993,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:665
+#: text/chapter6.md:664
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -18905,7 +19002,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:667
+#: text/chapter6.md:666
#, markdown-text, no-wrap
msgid ""
" _Note_: The testing framework requires `Show` and `Eq` instances for the "
@@ -18916,7 +19013,7 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter6.md:669
+#: text/chapter6.md:668
#, markdown-text
msgid ""
"(Difficult) Should the arguments of the multi-parameter type class `Action` "
@@ -18925,13 +19022,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter6.md:670
+#: text/chapter6.md:669
#, markdown-text, no-wrap
msgid "A Type Class for Hashes"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:673
+#: text/chapter6.md:672
#, markdown-text
msgid ""
"In the last section of this chapter, we will use the lessons from the rest "
@@ -18939,29 +19036,29 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:675
-#, markdown-text
+#: text/chapter6.md:674
+#, markdown-text, no-wrap
msgid ""
-"Note that this library is for demonstration purposes only, and is not "
-"intended to provide a robust hashing mechanism."
+"> Note that this library is for demonstration purposes only and is not "
+"intended to provide a robust hashing mechanism.\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:677
+#: text/chapter6.md:676
#, markdown-text
msgid "What properties might we expect of a hash function?"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter6.md:680
+#: text/chapter6.md:679
#, markdown-text
msgid ""
-"A hash function should be deterministic, and map equal values to equal hash "
+"A hash function should be deterministic and map equal values to equal hash "
"codes."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter6.md:680
+#: text/chapter6.md:679
#, markdown-text
msgid ""
"A hash function should distribute its results approximately uniformly over "
@@ -18969,29 +19066,29 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:682
+#: text/chapter6.md:681
#, markdown-text
msgid ""
"The first property looks a lot like a law for a type class, whereas the "
-"second property is more along the lines of an informal contract, and "
+"second property is more along the lines of an informal contract and "
"certainly would not be enforceable by PureScript's type system. However, "
"this should provide the intuition for the following type class:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:683
+#: text/chapter6.md:682
#, no-wrap
msgid "{{#include ../exercises/chapter6/src/Data/Hashable.purs:Hashable}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:688
+#: text/chapter6.md:687
#, markdown-text
msgid "with the associated law that `a == b` implies `hash a == hash b`."
msgstr ""
#. type: Plain text
-#: text/chapter6.md:690
+#: text/chapter6.md:689
#, markdown-text
msgid ""
"We'll spend the rest of this section building a library of instances and "
@@ -18999,19 +19096,19 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:692
+#: text/chapter6.md:691
#, markdown-text
msgid "We will need a way to combine hash codes in a deterministic way:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:693
+#: text/chapter6.md:692
#, no-wrap
msgid "{{#include ../exercises/chapter6/src/Data/Hashable.purs:combineHashes}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:698
+#: text/chapter6.md:697
#, markdown-text
msgid ""
"The `combineHashes` function will mix two hash codes and redistribute the "
@@ -19019,48 +19116,48 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:700
+#: text/chapter6.md:699
#, markdown-text
msgid ""
-"Let's write a function which uses the `Hashable` constraint to restrict the "
+"Let's write a function that uses the `Hashable` constraint to restrict the "
"types of its inputs. One common task which requires a hashing function is to "
"determine if two values hash to the same hash code. The `hashEqual` relation "
"provides such a capability:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:701
+#: text/chapter6.md:700
#, no-wrap
msgid "{{#include ../exercises/chapter6/src/Data/Hashable.purs:hashEqual}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:706
+#: text/chapter6.md:705
#, markdown-text
msgid ""
"This function uses the `on` function from `Data.Function` to define "
"hash-equality in terms of equality of hash codes, and should read like a "
"declarative definition of hash-equality: two values are \"hash-equal\" if "
-"they are equal after each value has been passed through the `hash` function."
+"they are equal after each value passed through the `hash` function."
msgstr ""
#. type: Plain text
-#: text/chapter6.md:708
+#: text/chapter6.md:707
#, markdown-text
msgid ""
"Let's write some `Hashable` instances for some primitive types. Let's start "
"with an instance for integers. Since a `HashCode` is really just a wrapped "
-"integer, this is simple - we can use the `hashCode` helper function:"
+"integer, this is simple – we can use the `hashCode` helper function:"
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:709
+#: text/chapter6.md:708
#, no-wrap
msgid "{{#include ../exercises/chapter6/src/Data/Hashable.purs:hashInt}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:714
+#: text/chapter6.md:713
#, markdown-text
msgid ""
"We can also define a simple instance for `Boolean` values using pattern "
@@ -19068,13 +19165,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:715
+#: text/chapter6.md:714
#, no-wrap
msgid "{{#include ../exercises/chapter6/src/Data/Hashable.purs:hashBoolean}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:720
+#: text/chapter6.md:719
#, markdown-text
msgid ""
"With an instance for hashing integers, we can create an instance for hashing "
@@ -19082,13 +19179,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:721
+#: text/chapter6.md:720
#, no-wrap
msgid "{{#include ../exercises/chapter6/src/Data/Hashable.purs:hashChar}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:726
+#: text/chapter6.md:725
#, markdown-text
msgid ""
"To define an instance for arrays, we can `map` the `hash` function over the "
@@ -19098,13 +19195,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:727
+#: text/chapter6.md:726
#, no-wrap
msgid "{{#include ../exercises/chapter6/src/Data/Hashable.purs:hashArray}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:732
+#: text/chapter6.md:731
#, markdown-text
msgid ""
"Notice how we build up instances using the simpler instances we have already "
@@ -19113,29 +19210,29 @@ msgid ""
msgstr ""
#. type: Fenced code block (haskell)
-#: text/chapter6.md:733
+#: text/chapter6.md:732
#, no-wrap
msgid "{{#include ../exercises/chapter6/src/Data/Hashable.purs:hashString}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter6.md:738
+#: text/chapter6.md:737
#, markdown-text
msgid ""
"How can we prove that these `Hashable` instances satisfy the type class law "
"that we stated above? We need to make sure that equal values have equal hash "
-"codes. In cases like `Int`, `Char`, `String` and `Boolean`, this is simple "
-"because there are no values of those types which are equal in the sense of "
+"codes. In cases like `Int`, `Char`, `String`, and `Boolean`, this is simple "
+"because there are no values of those types that are equal in the sense of "
"`Eq` but not equal identically."
msgstr ""
#. type: Plain text
-#: text/chapter6.md:740
+#: text/chapter6.md:739
#, markdown-text
msgid ""
"What about some more interesting types? To prove the type class law for the "
"`Array` instance, we can use induction on the length of the array. The only "
-"array with length zero is `[]`. Any two non-empty arrays are equal only if "
+"array with a length zero is `[]`. Any two non-empty arrays are equal only if "
"they have equal head elements and equal tails, by the definition of `Eq` on "
"arrays. By the inductive hypothesis, the tails have equal hashes, and we "
"know that the head elements have equal hashes if the `Hashable a` instance "
@@ -19144,7 +19241,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:742
+#: text/chapter6.md:741
#, markdown-text
msgid ""
"The source code for this chapter includes several other examples of "
@@ -19152,7 +19249,7 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter6.md:748
+#: text/chapter6.md:747
#, markdown-text
msgid ""
"(Easy) Use PSCi to test the hash functions for each of the defined "
@@ -19160,18 +19257,18 @@ msgid ""
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter6.md:748
+#: text/chapter6.md:747
#, markdown-text
msgid ""
-"(Medium) Write a function `arrayHasDuplicates` which tests if an array has "
-"any duplicate elements based on both hash and value equality. First check "
+"(Medium) Write a function `arrayHasDuplicates`, which tests if an array has "
+"any duplicate elements based on both hash and value equality. First, check "
"for hash equality with the `hashEqual` function, then check for value "
"equality with `==` if a duplicate pair of hashes is found. _Hint_: the "
"`nubByEq` function in `Data.Array` should make this task much simpler."
msgstr ""
#. type: Bullet: ' 1. '
-#: text/chapter6.md:748
+#: text/chapter6.md:747
#, markdown-text
msgid ""
"(Medium) Write a `Hashable` instance for the following newtype which "
@@ -19179,7 +19276,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:751
+#: text/chapter6.md:750
#, markdown-text, no-wrap
msgid ""
" ```haskell\n"
@@ -19187,7 +19284,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:754
+#: text/chapter6.md:753
#, markdown-text, no-wrap
msgid ""
" {{#include "
@@ -19196,7 +19293,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:757
+#: text/chapter6.md:756
#, markdown-text, no-wrap
msgid ""
" The newtype `Hour` and its `Eq` instance represent the type of integers "
@@ -19207,26 +19304,25 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter6.md:761
+#: text/chapter6.md:760
#, markdown-text
msgid ""
"In this chapter, we've been introduced to _type classes_, a type-oriented "
-"form of abstraction which enables powerful forms of code reuse. We've seen a "
-"collection of standard type classes from the PureScript standard libraries, "
+"form of abstraction that enables powerful forms of code reuse. We've seen a "
+"collection of standard type classes from the PureScript standard libraries "
"and defined our own library based on a type class for computing hash codes."
msgstr ""
#. type: Plain text
-#: text/chapter6.md:762
+#: text/chapter6.md:761
#, markdown-text
msgid ""
-"This chapter also gave an introduction to the notion of type class laws, a "
-"technique for proving properties about code which uses type classes for "
-"abstraction. Type class laws are part of a larger subject called _equational "
-"reasoning_, in which the properties of a programming language and its type "
-"system are used to enable logical reasoning about its programs. This is an "
-"important idea, and will be a theme which we will return to throughout the "
-"rest of the book."
+"This chapter also introduced type class laws, a technique for proving "
+"properties about code that uses type classes for abstraction. Type class "
+"laws are part of a larger subject called _equational reasoning_, in which "
+"the properties of a programming language and its type system are used to "
+"enable logical reasoning about its programs. This is an important idea and a "
+"theme that we will return to throughout the rest of the book."
msgstr ""
#. type: Title ##
@@ -19239,10 +19335,10 @@ msgstr ""
#: text/chapter7.md:6
#, markdown-text
msgid ""
-"In this chapter, we will meet an important new abstraction - the "
+"In this chapter, we will meet an important new abstraction – the "
"_applicative functor_, described by the `Applicative` type class. Don't "
-"worry if the name sounds confusing - we will motivate the concept with a "
-"practical example - validating form data. This technique allows us to "
+"worry if the name sounds confusing – we will motivate the concept with a "
+"practical example – validating form data. This technique allows us to "
"convert code which usually involves a lot of boilerplate checking into a "
"simple, declarative description of our form."
msgstr ""
@@ -19261,9 +19357,9 @@ msgstr ""
#, markdown-text
msgid ""
"The example code for this chapter will be a continuation of the address book "
-"example from chapter 3. This time, we will extend our address book data "
-"types, and write functions to validate values for those types. The "
-"understanding is that these functions could be used, for example in a web "
+"example from Chapter 3. This time, we will extend our address book data "
+"types and write functions to validate values for those types. The "
+"understanding is that these functions could be used, for example, in a web "
"user interface, to display errors to the user as part of a data entry form."
msgstr ""
@@ -19304,7 +19400,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `Data.AddressBook` module defines data types and `Show` instances for "
-"the types in our project, and the `Data.AddressBook.Validation` module "
+"the types in our project and the `Data.AddressBook.Validation` module "
"contains validation rules for those types."
msgstr ""
@@ -19326,7 +19422,7 @@ msgstr ""
#: text/chapter7.md:27
#, markdown-text
msgid ""
-"The source code for this module defines a function `address` which has the "
+"The source code for this module defines a function `address` that has the "
"following type:"
msgstr ""
@@ -19397,7 +19493,7 @@ msgstr ""
#: text/chapter7.md:61
#, markdown-text
msgid ""
-"Of course, this is an expected type error - `address` takes strings as "
+"Of course, this is an expected type error – `address` takes strings as "
"arguments, not values of type `Maybe String`."
msgstr ""
@@ -19473,8 +19569,8 @@ msgstr ""
#, no-wrap
msgid ""
"> :type lift3\n"
-"forall a b c d f. Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f "
-"d\n"
+"forall (a :: Type) (b :: Type) (c :: Type) (d :: Type) (f :: Type -> "
+"Type). Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d\n"
msgstr ""
#. type: Plain text
@@ -19497,7 +19593,7 @@ msgstr ""
#: text/chapter7.md:99
#, markdown-text
msgid ""
-"This type says that we can take any function with three arguments, and lift "
+"This type says that we can take any function with three arguments and lift "
"it to give a new function whose argument and result types are wrapped with "
"`Maybe`."
msgstr ""
@@ -19583,13 +19679,13 @@ msgstr ""
#, markdown-text
msgid ""
"Now we'll see how `map` and `apply` can be used together to lift functions "
-"of arbitrary number of arguments."
+"of an arbitrary number of arguments."
msgstr ""
#. type: Plain text
#: text/chapter7.md:131
#, markdown-text
-msgid "For functions of one argument, we can just use `map` directly."
+msgid "For functions of one argument, we can use `map` directly."
msgstr ""
#. type: Plain text
@@ -19641,10 +19737,10 @@ msgstr ""
#. type: Plain text
#: text/chapter7.md:150
-#, markdown-text
+#, markdown-text, no-wrap
msgid ""
-"It is left as an exercise for the reader to verify the types involved in "
-"this expression."
+"> It is left as an exercise for the reader to verify the types involved in "
+"this expression.\n"
msgstr ""
#. type: Plain text
@@ -19678,7 +19774,7 @@ msgstr ""
#: text/chapter7.md:164
#, markdown-text
msgid ""
-"Alternatively _applicative do notation_ can be used for the same purpose in "
+"Alternatively, _applicative do notation_ can be used for the same purpose in "
"a way that looks similar to the familiar _do notation_. Here is `lift3` "
"using _applicative do notation_. Note `ado` is used instead of `do`, and "
"`in` is used on the final line to denote the yielded value:"
@@ -19749,7 +19845,7 @@ msgstr ""
#: text/chapter7.md:199
#, markdown-text
msgid ""
-"If we think of applicative functors as functors which allow lifting of "
+"If we think of applicative functors as functors that allow lifting of "
"functions, then `pure` can be thought of as lifting functions of zero "
"arguments."
msgstr ""
@@ -19777,7 +19873,7 @@ msgid ""
"As an example, the functor `Maybe` represents the side effect of "
"possibly-missing values. Some other examples include `Either err`, which "
"represents the side effect of possible errors of type `err`, and the arrow "
-"functor `r ->` which represents the side-effect of reading from a global "
+"functor `r ->`, which represents the side-effect of reading from a global "
"configuration. For now, we'll only consider the `Maybe` functor.\n"
msgstr ""
@@ -19795,8 +19891,8 @@ msgstr ""
#: text/chapter7.md:209
#, markdown-text
msgid ""
-"`pure` lifts pure (side-effect free) values into the larger language, and "
-"for functions, we can use `map` and `apply` as described above."
+"`pure` lifts pure (side-effect free) values into the larger language; for "
+"functions, we can use `map` and `apply` as described above."
msgstr ""
#. type: Plain text
@@ -19859,12 +19955,12 @@ msgstr ""
#: text/chapter7.md:230
#, markdown-text
msgid ""
-"Now suppose that this function forms the implementation of a (very simple!) "
-"web service with the three arguments provided as query parameters. We want "
-"to make sure that the user provided each of the three parameters, so we "
-"might use the `Maybe` type to indicate the presence or otherwise absence of "
-"a parameter. We can lift `fullName` over `Maybe` to create an implementation "
-"of the web service which checks for missing parameters:"
+"Suppose that this function forms the implementation of a (very simple!) web "
+"service with the three arguments provided as query parameters. We want to "
+"ensure that the user provided each of the three parameters, so we might use "
+"the Maybe type to indicate the presence or absence of a parameter. We can "
+"lift `fullName` over `Maybe` to create an implementation of the web service "
+"which checks for missing parameters:"
msgstr ""
#. type: Fenced code block (text)
@@ -19882,9 +19978,9 @@ msgstr ""
#. type: Plain text
#: text/chapter7.md:242 text/chapter7.md:292 text/chapter7.md:444
-#: text/chapter7.md:479 text/chapter7.md:525 text/chapter7.md:557
+#: text/chapter7.md:479 text/chapter7.md:525
#, markdown-text
-msgid "or with _applicative do_"
+msgid "Or with _applicative do_:"
msgstr ""
#. type: Fenced code block (text)
@@ -19923,7 +20019,7 @@ msgstr ""
#: text/chapter7.md:267
#, markdown-text
msgid ""
-"This is good, because now we can send an error response back from our web "
+"This is good because now we can send an error response back from our web "
"service if the parameters are invalid. However, it would be better if we "
"could indicate which field was incorrect in the response."
msgstr ""
@@ -19997,8 +20093,8 @@ msgstr ""
#: text/chapter7.md:307
#, markdown-text
msgid ""
-"Now our function takes three optional arguments using `Maybe`, and returns "
-"either a `String` error message or a `String` result."
+"Now our function takes three optional arguments using `Maybe, and returns "
+"either a`String` error message or a `String` result."
msgstr ""
#. type: Plain text
@@ -20026,8 +20122,8 @@ msgstr ""
#, markdown-text
msgid ""
"In this case, we see the error message corresponding to the first missing "
-"field, or a successful result if every field was provided. However, if we "
-"are missing multiple inputs, we still only see the first error:"
+"field or a successful result if every field was provided. However, if we are "
+"missing multiple inputs, we still only see the first error:"
msgstr ""
#. type: Fenced code block (text)
@@ -20058,8 +20154,8 @@ msgstr ""
#, markdown-text
msgid ""
"As an example of working with applicative functors abstractly, this section "
-"will show how to write a function which will generically combine "
-"side-effects encoded by an applicative functor `f`."
+"will show how to write a function that generically combines side-effects "
+"encoded by an applicative functor `f`."
msgstr ""
#. type: Plain text
@@ -20080,7 +20176,7 @@ msgstr ""
#: text/chapter7.md:337
#, markdown-text, no-wrap
msgid ""
-"For any fixed list size `n`, there is a function of `n` arguments which "
+"For any fixed list size `n`, there is a function of `n` arguments that "
"builds a list of size `n` out of those arguments. For example, if `n` is "
"`3`, the function is `\\x y z -> x : y : z : Nil`. This function has type `a "
"-> a -> a -> List a`. We can use the `Applicative` instance for `List` to "
@@ -20183,9 +20279,9 @@ msgstr ""
#, markdown-text
msgid ""
"When specialized to `Maybe`, our function returns a `Just` only if every "
-"list element was `Just`, otherwise it returns `Nothing`. This is consistent "
+"list element is `Just`; otherwise, it returns `Nothing`. This is consistent "
"with our intuition of working in a larger language supporting optional "
-"values - a list of computations which return optional results only has a "
+"values – a list of computations that produce optional results only has a "
"result itself if every computation contained a result."
msgstr ""
@@ -20194,7 +20290,7 @@ msgstr ""
#, markdown-text, no-wrap
msgid ""
"But the `combineList` function works for any `Applicative`! We can use it to "
-"combine computations which possibly signal an error using `Either err`, or "
+"combine computations that possibly signal an error using `Either err`, or "
"which read from a global configuration using `r ->`.\n"
msgstr ""
@@ -20202,7 +20298,7 @@ msgstr ""
#: text/chapter7.md:380
#, markdown-text
msgid ""
-"We will see the `combineList` function again later, when we consider "
+"We will see the `combineList` function again later when we consider "
"`Traversable` functors."
msgstr ""
@@ -20210,10 +20306,10 @@ msgstr ""
#: text/chapter7.md:386
#, markdown-text
msgid ""
-"(Medium) Write versions of the numeric operators `+`, `-`, `*` and `/` which "
-"work with optional arguments (i.e. arguments wrapped in `Maybe`) and return "
-"a value wrapped in `Maybe`. Name these functions `addMaybe`, `subMaybe`, "
-"`mulMaybe`, and `divMaybe`. _Hint_: Use `lift2`."
+"(Medium) Write versions of the numeric operators `+`, `-`, `*`, and `/` "
+"which work with optional arguments (i.e., arguments wrapped in `Maybe`) and "
+"return a value wrapped in `Maybe`. Name these functions `addMaybe`, "
+"`subMaybe`, `mulMaybe`, and `divMaybe`. _Hint_: Use `lift2`."
msgstr ""
#. type: Bullet: ' 1. '
@@ -20231,8 +20327,8 @@ msgstr ""
msgid ""
"(Difficult) Write a function `combineMaybe` which has type `forall a "
"f. Applicative f => Maybe (f a) -> f (Maybe a)`. This function takes an "
-"optional computation with side-effects, and returns a side-effecting "
-"computation which has an optional result."
+"optional computation with side-effects and returns a side-effecting "
+"computation with an optional result."
msgstr ""
#. type: Plain text
@@ -20241,8 +20337,8 @@ msgstr ""
msgid ""
"The source code for this chapter defines several data types which might be "
"used in an address book application. The details are omitted here, but the "
-"key functions which are exported by the `Data.AddressBook` module have the "
-"following types:"
+"key functions exported by the `Data.AddressBook` module have the following "
+"types:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -20260,7 +20356,7 @@ msgstr ""
#. type: Plain text
#: text/chapter7.md:400
#, markdown-text
-msgid "where `PhoneType` is defined as an algebraic data type:"
+msgid "Where `PhoneType` is defined as an algebraic data type:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -20273,9 +20369,8 @@ msgstr ""
#: text/chapter7.md:406
#, markdown-text
msgid ""
-"These functions can be used to construct a `Person` representing an address "
-"book entry. For example, the following value is defined in "
-"`Data.AddressBook`:"
+"These functions can construct a `Person` representing an address book "
+"entry. For example, the following value is defined in `Data.AddressBook`:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -20350,7 +20445,7 @@ msgstr ""
msgid ""
"In the first two lines, we use the `nonEmpty1` function to validate a "
"non-empty string. `nonEmpty1` returns an error indicated with the `Left` "
-"constructor if its input is empty, otherwise it returns the value wrapped "
+"constructor if its input is empty. Otherwise, it returns the value wrapped "
"with the `Right` constructor."
msgstr ""
@@ -20367,7 +20462,7 @@ msgstr ""
#: text/chapter7.md:454
#, markdown-text
msgid ""
-"This function can be seen to work in PSCi, but has a limitation which we "
+"This function can be seen to work in PSCi, but it has a limitation that we "
"have seen before:"
msgstr ""
@@ -20384,19 +20479,18 @@ msgstr ""
#, markdown-text
msgid ""
"The `Either String` applicative functor only provides the first error "
-"encountered. Given the input here, we would prefer to see two errors - one "
-"for the missing first name, and a second for the missing last name."
+"encountered. Given the input here, we would prefer to see two errors – one "
+"for the missing first name and a second for the missing last name."
msgstr ""
#. type: Plain text
#: text/chapter7.md:463
#, markdown-text
msgid ""
-"There is another applicative functor which is provided by the `validation` "
-"library. This functor is called `V`, and it provides the ability to return "
-"errors in any _semigroup_. For example, we can use `V (Array String)` to "
-"return an array of `String`s as errors, concatenating new errors onto the "
-"end of the array."
+"There is another applicative functor that the `validation` library "
+"provides. This functor is called `V`, and it can return errors in any "
+"_semigroup_. For example, we can use `V (Array String)` to return an array "
+"of `String`s as errors, concatenating new errors onto the end of the array."
msgstr ""
#. type: Plain text
@@ -20446,8 +20540,8 @@ msgstr ""
#, markdown-text
msgid ""
"`validateAddress` validates an `Address` structure. It checks that the "
-"`street` and `city` fields are non-empty, and checks that the string in the "
-"`state` field has length 2."
+"`street` and `city` fields are non-empty and that the string in the `state` "
+"field has length 2."
msgstr ""
#. type: Plain text
@@ -20580,7 +20674,7 @@ msgstr ""
msgid ""
"(Medium) Write a regular expression `nonEmptyRegex :: Regex` to check that a "
"string is not entirely whitespace. _Hint_: If you need help developing this "
-"regex expression, check out [RegExr](https://regexr.com) which has a great "
+"regex expression, check out [RegExr](https://regexr.com), which has a great "
"cheatsheet and interactive test environment."
msgstr ""
@@ -20621,6 +20715,12 @@ msgid ""
"../exercises/chapter7/src/Data/AddressBook/Validation.purs:validatePerson}}\n"
msgstr ""
+#. type: Plain text
+#: text/chapter7.md:557
+#, markdown-text
+msgid "or with _applicative do_"
+msgstr ""
+
#. type: Fenced code block (haskell)
#: text/chapter7.md:558
#, no-wrap
@@ -20633,7 +20733,7 @@ msgstr ""
#: text/chapter7.md:563
#, markdown-text
msgid ""
-"`validatePhoneNumbers` uses a new function we haven't seen before - "
+"`validatePhoneNumbers` uses a new function we haven't seen before – "
"`traverse`."
msgstr ""
@@ -20668,10 +20768,9 @@ msgstr ""
#, markdown-text
msgid ""
"Every traversable functor is both a `Functor` and `Foldable` (recall that a "
-"_foldable functor_ was a type constructor which supported a fold operation, "
+"_foldable functor_ was a type constructor that supported a fold operation, "
"reducing a structure to a single value). In addition, a traversable functor "
-"provides the ability to combine a collection of side-effects which depend on "
-"its structure."
+"can combine a collection of side-effects that depend on its structure."
msgstr ""
#. type: Plain text
@@ -20724,7 +20823,7 @@ msgid ""
"type `a`, then `traverse m` is a validation function for arrays of type "
"`Array a`. But that's exactly what we need to be able to validate the "
"`phones` field of the `Person` data structure! We pass `validatePhoneNumber` "
-"to `traverse` to create a validation function which validates each element "
+"to `traverse` to create a validation function that validates each element "
"successively."
msgstr ""
@@ -20767,7 +20866,7 @@ msgid ""
"Traversable functors capture the idea of traversing a data structure, "
"collecting a set of effectful computations, and combining their effects. In "
"fact, `sequence` and `traverse` are equally important to the definition of "
-"`Traversable` - each can be implemented in terms of each other. This is left "
+"`Traversable` – each can be implemented in terms of the other. This is left "
"as an exercise for the interested reader."
msgstr ""
@@ -20801,11 +20900,11 @@ msgstr ""
#: text/chapter7.md:620
#, markdown-text
msgid ""
-"In the case of an empty list, we can simply return an empty list using "
-"`pure`. If the list is non-empty, we can use the function `f` to create a "
-"computation of type `f b` from the head element. We can also call `traverse` "
-"recursively on the tail. Finally, we can lift the `Cons` constructor over "
-"the applicative functor `m` to combine the two results."
+"In the case of an empty list, we can return an empty list using `pure`. If "
+"the list is non-empty, we can use the function `f` to create a computation "
+"of type `f b` from the head element. We can also call `traverse` recursively "
+"on the tail. Finally, we can lift the `Cons` constructor over the "
+"applicative functor `m` to combine the two results."
msgstr ""
#. type: Plain text
@@ -20842,7 +20941,7 @@ msgid ""
"These examples show that traversing the `Nothing` value returns `Nothing` "
"with no validation, and traversing `Just x` uses the validation function to "
"validate `x`. That is, `traverse` takes a validation function for type `a` "
-"and returns a validation function for `Maybe a`, i.e. a validation function "
+"and returns a validation function for `Maybe a`, i.e., a validation function "
"for optional values of type `a`."
msgstr ""
@@ -20850,7 +20949,7 @@ msgstr ""
#: text/chapter7.md:641
#, markdown-text
msgid ""
-"Other traversable functors include `Array`, and `Tuple a` and `Either a` for "
+"Other traversable functors include `Array`, `Tuple a`, and `Either a` for "
"any type `a`. Generally, most \"container\" data type constructors have "
"`Traversable` instances. As an example, the exercises will include writing a "
"`Traversable` instance for a type of binary trees."
@@ -20878,7 +20977,7 @@ msgstr ""
#, markdown-text, no-wrap
msgid ""
" Recall from the previous chapter that you may either write these "
-"instances manually or let the compiler derive them for you.\n"
+"instances manually or let the compiler derive them.\n"
msgstr ""
#. type: Plain text
@@ -20886,9 +20985,9 @@ msgstr ""
#, markdown-text, no-wrap
msgid ""
" There are many \"correct\" formatting options for `Show` output. The "
-"test for this exercise expects the following whitespace style. This happens "
-"to match the default formatting of generic show, so you only need to make "
-"note of this if you're planning on writing this instance manually.\n"
+"test for this exercise expects the following whitespace style. This matches "
+"the default formatting of the generic show, so you only need to note this if "
+"you're planning on writing this instance manually.\n"
msgstr ""
#. type: Plain text
@@ -20905,7 +21004,7 @@ msgstr ""
#, markdown-text
msgid ""
"(Medium) Write a `Traversable` instance for `Tree a`, which combines "
-"side-effects from left-to-right. _Hint_: There are some additional instance "
+"side-effects left-to-right. _Hint_: There are some additional instance "
"dependencies that need to be defined for `Traversable`."
msgstr ""
@@ -20918,8 +21017,8 @@ msgid ""
"of the tree. This means the order of effect execution is root-left-right, "
"instead of left-root-right as was done for the previous in-order traverse "
"exercise. _Hint_: No additional instances need to be defined, and you don't "
-"need to call any of the the functions defined earlier. Applicative do "
-"notation (`ado`) is the easiest way to write this function."
+"need to call any of the functions defined earlier. Applicative do notation "
+"(`ado`) is the easiest way to write this function."
msgstr ""
#. type: Bullet: ' 1. '
@@ -20981,8 +21080,8 @@ msgstr ""
msgid ""
"However, in general, applicative functors are more general than this. The "
"applicative functor laws do not impose any ordering on the side-effects that "
-"their computations perform. In fact, it would be valid for an applicative "
-"functor to perform its side-effects in parallel."
+"their computations perform. It would be valid for an applicative functor to "
+"perform its side-effects in parallel."
msgstr ""
#. type: Plain text
@@ -21001,8 +21100,8 @@ msgstr ""
msgid ""
"As a second example, the `parallel` package provides a type class `Parallel` "
"which supports _parallel computations_. `Parallel` provides a function "
-"`parallel` which uses some `Applicative` functor to compute the result of "
-"its input computation _in parallel_:"
+"`parallel` that uses some `Applicative` functor to compute the result of its "
+"input computation _in parallel_:"
msgstr ""
#. type: Fenced code block (haskell)
@@ -21034,7 +21133,7 @@ msgstr ""
#: text/chapter7.md:690
#, markdown-text
msgid ""
-"Applicative functors are a natural way to capture side-effects which can be "
+"Applicative functors are a natural way to capture side-effects that can be "
"combined in parallel."
msgstr ""
@@ -21049,7 +21148,7 @@ msgstr ""
#, markdown-text
msgid ""
"We introduced the concept of an _applicative functor_ which generalizes the "
-"idea of function application to type constructors which capture some notion "
+"idea of function application to type constructors that captures some notion "
"of side-effect."
msgstr ""
@@ -21057,9 +21156,9 @@ msgstr ""
#: text/chapter7.md:698
#, markdown-text
msgid ""
-"We saw how applicative functors gave a solution to the problem of validating "
-"data structures, and how by switching the applicative functor we could "
-"change from reporting a single error to reporting all errors across a data "
+"We saw how applicative functors solved the problem of validating data "
+"structures and how by switching the applicative functor, we could change "
+"from reporting a single error to reporting all errors across a data "
"structure."
msgstr ""
@@ -21076,13 +21175,13 @@ msgstr ""
#: text/chapter7.md:700
#, markdown-text
msgid ""
-"Applicative functors are an interesting abstraction which provide neat "
+"Applicative functors are an interesting abstraction that provides neat "
"solutions to a number of problems. We will see them a few more times "
"throughout the book. In this case, the validation applicative functor "
"provided a way to write validators in a declarative style, allowing us to "
"define _what_ our validators should validate and not _how_ they should "
"perform that validation. In general, we will see that applicative functors "
-"are a useful tool for the design of _domain specific languages_."
+"are a useful tool for the design of _domain specific languages."
msgstr ""
#. type: Plain text
@@ -21103,17 +21202,17 @@ msgstr ""
#: text/chapter8.md:6
#, markdown-text
msgid ""
-"In the last chapter, we introduced applicative functors, an abstraction "
-"which we used to deal with _side-effects_: optional values, error messages "
-"and validation. This chapter will introduce another abstraction for dealing "
-"with side-effects in a more expressive way: _monads_."
+"In the last chapter, we introduced applicative functors, an abstraction we "
+"used to deal with _side-effects_: optional values, error messages, and "
+"validation. This chapter will introduce another abstraction for dealing with "
+"side-effects more expressively: _monads_."
msgstr ""
#. type: Plain text
#: text/chapter8.md:8
#, markdown-text
msgid ""
-"The goal of this chapter is to explain why monads are a useful abstraction, "
+"The goal of this chapter is to explain why monads are a useful abstraction "
"and their connection with _do notation_."
msgstr ""
@@ -21127,18 +21226,16 @@ msgstr ""
#: text/chapter8.md:15
#, markdown-text
msgid ""
-"`effect` - defines the `Effect` monad, the subject of the second half of the "
+"`effect` – defines the `Effect` monad, the subject of the second half of the "
"chapter. This dependency is often listed in every starter project (it's been "
-"a dependency of every chapter so far), so you'll rarely have to explicitly "
-"install it."
+"a dependency of every chapter so far), so you'll rarely have to install it "
+"explicitly."
msgstr ""
#. type: Bullet: '- '
#: text/chapter8.md:15
#, markdown-text
-msgid ""
-"`react-basic-hooks` - a web framework that we will use for our Address Book "
-"app."
+msgid "`react-basic-hooks` – a web framework we will use for our Address Book app."
msgstr ""
#. type: Title ##
@@ -21180,15 +21277,15 @@ msgstr ""
#. type: Bullet: '- '
#: text/chapter8.md:25
#, markdown-text
-msgid "If the sum of `x` and `y` is `n` then return the pair `[x, y]`, else fail."
+msgid "If the sum of `x` and `y` is `n`, return the pair `[x, y]`, else fail."
msgstr ""
#. type: Plain text
#: text/chapter8.md:27
#, markdown-text
msgid ""
-"Array comprehensions allow us to write this non-deterministic algorithm in a "
-"natural way:"
+"Array comprehensions allow us to write this non-deterministic algorithm "
+"naturally:"
msgstr ""
#. type: Fenced code block (hs)
@@ -21237,13 +21334,13 @@ msgstr ""
#: text/chapter8.md:52
#, markdown-text
msgid ""
-"In general, a _monad_ for some type constructor `m` provides a way to use do "
+"Generally, a _monad_ for some type constructor `m` provides a way to use do "
"notation with values of type `m a`. Note that in the array comprehension "
"above, every line contains a computation of type `Array a` for some type "
"`a`. In general, every line of a do notation block will contain a "
"computation of type `m a` for some type `a` and our monad `m`. The monad `m` "
-"must be the same on every line (i.e. we fix the side-effect), but the types "
-"`a` can differ (i.e. individual computations can have different result "
+"must be the same on every line (i.e., we fix the side-effect), but the types "
+"`a` can differ (i.e., individual computations can have different result "
"types)."
msgstr ""
@@ -21266,7 +21363,7 @@ msgstr ""
#: text/chapter8.md:60
#, markdown-text
msgid ""
-"which looks for a child element of a node, and returns `Nothing` if no such "
+"Which looks for a child element of a node and returns `Nothing` if no such "
"element exists."
msgstr ""
@@ -21274,8 +21371,8 @@ msgstr ""
#: text/chapter8.md:62
#, markdown-text
msgid ""
-"In this case, we can look for a deeply-nested element by using do "
-"notation. Suppose we wanted to read a user's city from a user profile which "
+"In this case, we can look for a deeply-nested element using do "
+"notation. Suppose we wanted to read a user's city from a user profile that "
"had been encoded as an XML document:"
msgstr ""
@@ -21296,10 +21393,10 @@ msgstr ""
#, markdown-text
msgid ""
"The `userCity` function looks for a child element `profile`, an element "
-"`address` inside the `profile` element, and finally an element `city` inside "
-"the `address` element. If any of these elements are missing, the return "
-"value will be `Nothing`. Otherwise, the return value is constructed using "
-"`Just` from the `city` node."
+"`address` inside the `profile` element, and finally, an element `city` "
+"inside the `address` element. If any of these elements are missing, the "
+"return value will be `Nothing`. Otherwise, the return value is constructed "
+"using `Just` from the `city` node."
msgstr ""
#. type: Plain text
@@ -21348,7 +21445,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `Monad` type class extends `Bind` with the operations of the "
-"`Applicative` type class that we have already seen."
+"`Applicative` type class we've already seen."
msgstr ""
#. type: Plain text
@@ -21403,7 +21500,7 @@ msgstr ""
#, markdown-text
msgid ""
"Let's see how the `Bind` type class is related to do notation. Consider a "
-"simple do notation block which starts by binding a value from the result of "
+"simple do notation block that starts by binding a value from the result of "
"some computation:"
msgstr ""
@@ -21472,10 +21569,10 @@ msgstr ""
#: text/chapter8.md:143
#, markdown-text, no-wrap
msgid ""
-"It is worth noting that code expressed using do notation is often much "
-"clearer than the equivalent code using the `>>=` operator. However, writing "
-"binds explicitly using `>>=` can often lead to opportunities to write code "
-"in _point-free_ form - but the usual warnings about readability apply.\n"
+"Notably, code expressed using do notation is often much clearer than the "
+"equivalent code using the `>>=` operator. However, writing binds explicitly "
+"using `>>=` can often lead to opportunities to write code in _point-free_ "
+"form – but the usual warnings about readability apply.\n"
msgstr ""
#. type: Title ##
@@ -21593,9 +21690,9 @@ msgstr ""
#: text/chapter8.md:192
#, markdown-text
msgid ""
-"Each of these computations involves three monadic expression `m1`, `m2` and "
-"`m3`. In each case, the result of `m1` is eventually bound to the name `x`, "
-"and the result of `m2` is bound to the name `y`."
+"Each of these computations involves three monadic expressions `m1`, `m2`, "
+"and `m3`. In each case, the result of `m1` is eventually bound to the name "
+"`x`, and the result of `m2` is bound to the name `y`."
msgstr ""
#. type: Plain text
@@ -21610,7 +21707,7 @@ msgstr ""
#: text/chapter8.md:196
#, markdown-text
msgid ""
-"In `c2`, all three expressions `m1`, `m2` and `m3` appear in the same do "
+"In `c2`, all three expressions `m1`, `m2`, and `m3` appear in the same do "
"notation block."
msgstr ""
@@ -21652,10 +21749,10 @@ msgstr ""
#, markdown-text
msgid ""
"As an example of working with monads abstractly, this section will present a "
-"function which works with any type constructor in the `Monad` type "
-"class. This should serve to solidify the intuition that monadic code "
-"corresponds to programming \"in a larger language\" with side-effects, and "
-"also illustrate the generality which programming with monads brings."
+"function that works with any type constructor in the `Monad` type "
+"class. This should solidify the intuition that monadic code corresponds to "
+"programming \"in a larger language\" with side-effects, and also illustrate "
+"the generality which programming with monads brings."
msgstr ""
#. type: Plain text
@@ -21663,8 +21760,7 @@ msgstr ""
#, markdown-text
msgid ""
"The function we will write is called `foldM`. It generalizes the `foldl` "
-"function that we met earlier to a monadic context. Here is its type "
-"signature:"
+"function we met earlier to a monadic context. Here is its type signature:"
msgstr ""
#. type: Fenced code block (hs)
@@ -21696,7 +21792,7 @@ msgstr ""
#, markdown-text
msgid ""
"For example, if we picked `m` to be `Maybe`, then our fold would be allowed "
-"to fail by returning `Nothing` at any stage - every step returns an optional "
+"to fail by returning `Nothing` at any stage – every step returns an optional "
"result, and the result of the fold is therefore also optional."
msgstr ""
@@ -21706,7 +21802,7 @@ msgstr ""
msgid ""
"If we picked `m` to be the `Array` type constructor, then every step of the "
"fold would be allowed to return zero or more results, and the fold would "
-"proceed to the next step independently for each result. At the end, the set "
+"proceed to the next step independently for each result. In the end, the set "
"of results would consist of all folds over all possible paths. This "
"corresponds to a traversal of a graph!"
msgstr ""
@@ -21769,14 +21865,14 @@ msgstr ""
#, markdown-text
msgid ""
"Note that this implementation is almost identical to that of `foldl` on "
-"lists, with the exception of do notation."
+"lists, except for do notation."
msgstr ""
#. type: Plain text
#: text/chapter8.md:251
#, markdown-text
msgid ""
-"We can define and test this function in PSCi. Here is an example - suppose "
+"We can define and test this function in PSCi. Here is an example – suppose "
"we defined a \"safe division\" function on integers, which tested for "
"division by zero and used the `Maybe` type constructor to indicate failure:"
msgstr ""
@@ -21812,7 +21908,7 @@ msgstr ""
#, markdown-text
msgid ""
"The `foldM safeDivide` function returns `Nothing` if a division by zero was "
-"attempted at any point. Otherwise it returns the result of repeatedly "
+"attempted at any point. Otherwise, it returns the result of repeatedly "
"dividing the accumulator, wrapped in the `Just` constructor."
msgstr ""
@@ -21863,7 +21959,7 @@ msgstr ""
#, markdown-text
msgid ""
"The interested reader can check that `ap` agrees with `apply` for the monads "
-"we have already encountered: `Array`, `Maybe` and `Either e`."
+"we have already encountered: `Array`, `Maybe`, and `Either e`."
msgstr ""
#. type: Plain text
@@ -21885,7 +21981,7 @@ msgid ""
"But monads allow us to do more than we could do with just applicative "
"functors, and the key difference is highlighted by the syntax of do "
"notation. Consider the `userCity` example again, in which we looked for a "
-"user's city in an XML document which encoded their user profile:"
+"user's city in an XML document that encoded their user profile:"
msgstr ""
#. type: Plain text
@@ -21916,14 +22012,14 @@ msgid ""
"express parallelism. This was precisely because the function arguments being "
"lifted were independent of one another. Since the `Monad` type class allows "
"computations to depend on the results of previous computations, the same "
-"does not apply - a monad has to combine its side-effects in sequence."
+"does not apply – a monad has to combine its side-effects in sequence."
msgstr ""
#. type: Bullet: ' 1. '
#: text/chapter8.md:312
#, markdown-text
msgid ""
-"(Easy) Write a function `third` which returns the third element of an array "
+"(Easy) Write a function `third` that returns the third element of an array "
"with three or more elements. Your function should return an appropriate "
"`Maybe` type. _Hint:_ Look up the types of the `head` and `tail` functions "
"from the `Data.Array` module in the `arrays` package. Use do notation with "
@@ -21964,7 +22060,7 @@ msgstr ""
msgid ""
" _Hint_: This function can be written as a one-liner using `foldM`. You "
"might want to use the `nub` and `sort` functions to remove duplicates and "
-"sort the result respectively.\n"
+"sort the result.\n"
" 1. (Medium) Confirm that the `ap` function and the `apply` operator agree "
"for the `Maybe` monad. _Note:_ There are no tests for this exercise.\n"
" 1. (Medium) Verify that the monad laws hold for the `Monad` instance for "
@@ -22020,7 +22116,7 @@ msgstr ""
#: text/chapter8.md:345
#, markdown-text, no-wrap
msgid ""
-" where the `Apply` instance uses the `ap` function defined above. Recall "
+" Where the `Apply` instance uses the `ap` function defined above. Recall "
"that `lift2` was defined as follows:\n"
msgstr ""
@@ -22051,8 +22147,8 @@ msgstr ""
#: text/chapter8.md:356
#, markdown-text
msgid ""
-"We will now look at one particular monad which is of central importance in "
-"PureScript - the `Effect` monad."
+"We will now look at one particular monad of central importance in PureScript "
+"– the `Effect` monad."
msgstr ""
#. type: Plain text
@@ -22068,7 +22164,7 @@ msgstr ""
#: text/chapter8.md:360
#, markdown-text
msgid ""
-"What are native side-effects? They are the side-effects which distinguish "
+"What are native side-effects? They are the side-effects that distinguish "
"JavaScript expressions from idiomatic PureScript expressions, which "
"typically are free from side-effects. Some examples of native effects are:"
msgstr ""
@@ -22150,12 +22246,12 @@ msgstr ""
#, markdown-text
msgid ""
"Note that the distinction is subtle. It is true, for example, that an error "
-"message is a possible side-effect of a JavaScript expression, in the form of "
+"message is a possible side-effect of a JavaScript expression in the form of "
"an exception. In that sense, exceptions do represent native side-effects, "
"and it is possible to represent them using `Effect`. However, error messages "
"implemented using `Either` are not a side-effect of the JavaScript runtime, "
"and so it is not appropriate to implement error messages in that style using "
-"`Effect`. So it is not the effect itself which is native, but rather how it "
+"`Effect`. So it is not the effect itself, which is native, but rather how it "
"is implemented at runtime."
msgstr ""
@@ -22169,18 +22265,18 @@ msgstr ""
#: text/chapter8.md:384
#, markdown-text
msgid ""
-"In a pure language like PureScript, one question which presents itself is: "
-"without side-effects, how can one write useful real-world code?"
+"In a pure language like PureScript, one question presents itself: without "
+"side-effects, how can one write useful real-world code?"
msgstr ""
#. type: Plain text
#: text/chapter8.md:386
#, markdown-text
msgid ""
-"The answer is that PureScript does not aim to eliminate side-effects. It "
-"aims to represent side-effects in such a way that pure computations can be "
-"distinguished from computations with side-effects in the type system. In "
-"this sense, the language is still pure."
+"The answer is that PureScript does not aim to eliminate side-effects but to "
+"represent them in such a way that pure computations can be distinguished "
+"from computations with side-effects in the type system. In this sense, the "
+"language is still pure."
msgstr ""
#. type: Plain text
@@ -22188,23 +22284,23 @@ msgstr ""
#, markdown-text
msgid ""
"Values with side-effects have different types from pure values. As such, it "
-"is not possible to pass a side-effecting argument to a function, for "
-"example, and have side-effects performed unexpectedly."
+"is impossible to pass a side-effecting argument to a function, for example, "
+"and have side-effects performed unexpectedly."
msgstr ""
#. type: Plain text
#: text/chapter8.md:390
#, markdown-text
msgid ""
-"The only way in which side-effects managed by the `Effect` monad will be "
-"presented is to run a computation of type `Effect a` from JavaScript."
+"The only way side-effects managed by the `Effect` monad will be presented is "
+"to run a computation of type `Effect a` from JavaScript."
msgstr ""
#. type: Plain text
#: text/chapter8.md:392
#, markdown-text
msgid ""
-"The Spago build tool (and other tools) provide a shortcut, by generating "
+"The Spago build tool (and other tools) provide a shortcut by generating "
"additional JavaScript to invoke the `main` computation when the application "
"starts. `main` is required to be a computation in the `Effect` monad."
msgstr ""
@@ -22218,24 +22314,31 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:399
+#: text/chapter8.md:398
#, markdown-text
msgid ""
-"Let's take a closer look at the return type of the familiar `log` "
-"function. `Effect` indicates that this function produces a native effect, "
-"console IO in this case. `Unit` indicates that no _meaningful_ data is "
-"returned. You can think of `Unit` as being analogous to the `void` keyword "
-"in other languages, such as C, Java, etc."
+"Let's look at the return type of the familiar `log` function. `Effect` "
+"indicates that this function produces a native effect, console IO in this "
+"case."
msgstr ""
-#. type: Fenced code block (hs)
+#. type: Plain text
#: text/chapter8.md:400
+#, markdown-text
+msgid ""
+"`Unit` indicates that no _meaningful_ data is returned. You can think of "
+"`Unit` as analogous to the `void` keyword in other languages, such as C, "
+"Java, etc."
+msgstr ""
+
+#. type: Fenced code block (hs)
+#: text/chapter8.md:401
#, no-wrap
msgid "log :: String -> Effect Unit\n"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:409
+#: text/chapter8.md:410
#, markdown-text, no-wrap
msgid ""
"> _Aside:_ You may encounter IDE suggestions for the more general (and more "
@@ -22252,7 +22355,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:411
+#: text/chapter8.md:412
#, markdown-text
msgid ""
"Now let's consider an `Effect` that returns meaningful data. The `random` "
@@ -22260,13 +22363,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:412
+#: text/chapter8.md:413
#, no-wrap
msgid "random :: Effect Number\n"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:417
+#: text/chapter8.md:418
#, markdown-text
msgid ""
"Here's a full example program (found in `test/Random.purs` of this chapter's "
@@ -22274,13 +22377,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:418
+#: text/chapter8.md:419
#, no-wrap
msgid "{{#include ../exercises/chapter8/test/Random.purs}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:423
+#: text/chapter8.md:424
#, markdown-text
msgid ""
"Because `Effect` is a monad, we use do notation to _unwrap_ the data it "
@@ -22289,7 +22392,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:424
+#: text/chapter8.md:425
#, no-wrap
msgid ""
"main :: Effect Unit\n"
@@ -22297,19 +22400,19 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:430
+#: text/chapter8.md:431
#, markdown-text
msgid "Try running this yourself with:"
msgstr ""
#. type: Fenced code block (shell)
-#: text/chapter8.md:431
+#: text/chapter8.md:432
#, no-wrap
msgid "spago run --main Test.Random\n"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:436
+#: text/chapter8.md:437
#, markdown-text
msgid ""
"You should see a randomly chosen number between `0.0` and `1.0` printed to "
@@ -22317,17 +22420,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:438
+#: text/chapter8.md:439
#, markdown-text, no-wrap
msgid ""
"> _Aside:_ `spago run` defaults to searching in the `Main` module for a "
"`main` function. You may also specify an alternate module as an entry point "
-"with the `--main` flag, as is done in the above example. Just be sure that "
-"this alternate module also contains a `main` function.\n"
+"with the `--main` flag, as in the above example. Just be sure that this "
+"alternate module also contains a `main` function.\n"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:440
+#: text/chapter8.md:441
#, markdown-text
msgid ""
"Note that it's also possible to generate \"random\" (technically "
@@ -22336,42 +22439,42 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:442
+#: text/chapter8.md:443
#, markdown-text
msgid ""
"As mentioned previously, the `Effect` monad is of central importance to "
-"PureScript. The reason why it's central is because it is the conventional "
-"way to interoperate with PureScript's `Foreign Function Interface`, which "
+"PureScript. The reason why it's central is that it is the conventional way "
+"to interoperate with PureScript's `Foreign Function Interface`, which "
"provides the mechanism to execute a program and perform side effects. While "
-"it's desireable to avoid using the `Foreign Function Interface`, it's fairly "
+"it's desirable to avoid using the `Foreign Function Interface`, it's fairly "
"critical to understand how it works and how to use it, so I recommend "
"reading that chapter before doing any serious PureScript work. That said, "
-"the `Effect` monad is fairly simple. It has a few helper functions, but "
-"aside from that it doesn't do much except encapsulate side effects."
+"the `Effect` monad is fairly simple. It has a few helper functions but "
+"doesn't do much except encapsulate side effects."
msgstr ""
#. type: Plain text
-#: text/chapter8.md:446
+#: text/chapter8.md:447
#, markdown-text
msgid ""
"Let's examine a function from the `node-fs` package that involves two "
-"_native_ side effects: reading mutable state, and exceptions:"
+"_native_ side effects: reading mutable state and exceptions:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:447
+#: text/chapter8.md:448
#, no-wrap
msgid "readTextFile :: Encoding -> String -> Effect String\n"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:452
+#: text/chapter8.md:453
#, markdown-text
msgid "If we attempt to read a file that does not exist:"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:453
+#: text/chapter8.md:454
#, no-wrap
msgid ""
"import Node.Encoding (Encoding(..))\n"
@@ -22384,13 +22487,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:464
+#: text/chapter8.md:465
#, markdown-text
msgid "We encounter the following exception:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter8.md:465
+#: text/chapter8.md:466
#, no-wrap
msgid ""
" throw err;\n"
@@ -22404,7 +22507,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:477
+#: text/chapter8.md:478
#, markdown-text
msgid ""
"To manage this exception gracefully, we can wrap the potentially problematic "
@@ -22412,7 +22515,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:478
+#: text/chapter8.md:479
#, no-wrap
msgid ""
"main :: Effect Unit\n"
@@ -22425,7 +22528,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:488
+#: text/chapter8.md:489
#, markdown-text
msgid ""
"`try` runs an `Effect` and returns eventual exceptions as a `Left` value. If "
@@ -22433,22 +22536,22 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:489
+#: text/chapter8.md:490
#, no-wrap
msgid "try :: forall a. Effect a -> Effect (Either Error a)\n"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:494
+#: text/chapter8.md:495
#, markdown-text
msgid ""
"We can also generate our own exceptions. Here is an alternative "
-"implementation of `Data.List.head` which throws an exception if the list is "
-"empty, rather than returning a `Maybe` value of `Nothing`."
+"implementation of `Data.List.head` that throws an exception if the list is "
+"empty rather than returning a `Maybe` value of `Nothing`."
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:495
+#: text/chapter8.md:496
#, no-wrap
msgid ""
"exceptionHead :: List Int -> Effect Int\n"
@@ -22458,7 +22561,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:503
+#: text/chapter8.md:504
#, markdown-text
msgid ""
"Note that the `exceptionHead` function is a somewhat impractical example, as "
@@ -22468,19 +22571,19 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter8.md:504
+#: text/chapter8.md:505
#, markdown-text, no-wrap
msgid "Mutable State"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:507
+#: text/chapter8.md:508
#, markdown-text
msgid "There is another effect defined in the core libraries: the `ST` effect."
msgstr ""
#. type: Plain text
-#: text/chapter8.md:509
+#: text/chapter8.md:510
#, markdown-text
msgid ""
"The `ST` effect is used to manipulate mutable state. As pure functional "
@@ -22490,7 +22593,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:511
+#: text/chapter8.md:512
#, markdown-text
msgid ""
"The `ST` effect is defined in the `Control.Monad.ST` module. To see how it "
@@ -22498,7 +22601,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:512
+#: text/chapter8.md:513
#, no-wrap
msgid ""
"new :: forall a r. a -> ST r (STRef r a)\n"
@@ -22511,27 +22614,27 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:523
+#: text/chapter8.md:524
#, markdown-text
msgid ""
"`new` is used to create a new mutable reference cell of type `STRef r a`, "
-"which can be read using the `read` action, and modified using the `write` "
-"and `modify` actions. The type `a` is the type of the value stored in the "
-"cell, and the type `r` is used to indicate a _memory region_ (or _heap_) in "
-"the type system."
+"which can be read using the `read` action and modified using the `write` and "
+"`modify` actions. The type `a` is the type of the value stored in the cell, "
+"and the type `r` is used to indicate a _memory region_ (or _heap_) in the "
+"type system."
msgstr ""
#. type: Plain text
-#: text/chapter8.md:525
+#: text/chapter8.md:526
#, markdown-text
msgid ""
"Here is an example. Suppose we want to simulate the movement of a particle "
-"falling under gravity by iterating a simple update function over a large "
-"number of small time steps."
+"falling under gravity by iterating a simple update function over many small "
+"time steps."
msgstr ""
#. type: Plain text
-#: text/chapter8.md:527
+#: text/chapter8.md:528
#, markdown-text
msgid ""
"We can do this by creating a mutable reference cell to hold the position and "
@@ -22540,7 +22643,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:528
+#: text/chapter8.md:529
#, no-wrap
msgid ""
"import Prelude\n"
@@ -22564,25 +22667,25 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:550
+#: text/chapter8.md:551
#, markdown-text
msgid ""
-"At the end of the computation, we read the final value of the reference "
-"cell, and return the position of the particle."
+"At the end of the computation, we read the final value of the reference cell "
+"and return the position of the particle."
msgstr ""
#. type: Plain text
-#: text/chapter8.md:552
+#: text/chapter8.md:553
#, markdown-text
msgid ""
-"Note that even though this function uses mutable state, it is still a pure "
+"Note that even though this function uses a mutable state, it is still a pure "
"function, so long as the reference cell `ref` is not allowed to be used by "
-"other parts of the program. We will see that this is exactly what the `ST` "
-"effect disallows."
+"other program parts. We will see that this is exactly what the `ST` effect "
+"disallows."
msgstr ""
#. type: Plain text
-#: text/chapter8.md:554
+#: text/chapter8.md:555
#, markdown-text
msgid ""
"To run a computation with the `ST` effect, we have to use the `run` "
@@ -22590,13 +22693,13 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:555
+#: text/chapter8.md:556
#, no-wrap
msgid "run :: forall a. (forall r. ST r a) -> a\n"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:560
+#: text/chapter8.md:561
#, markdown-text
msgid ""
"The thing to notice here is that the region type `r` is quantified _inside "
@@ -22605,17 +22708,17 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:562
+#: text/chapter8.md:563
#, markdown-text
msgid ""
"However, once a reference cell has been created by `new`, its region type is "
"already fixed, so it would be a type error to try to use the reference cell "
-"outside the code delimited by `run`. This is what allows `run` to safely "
-"remove the `ST` effect, and turn `simulate` into a pure function!"
+"outside the code delimited by `run`. This allows `run` to safely remove the "
+"`ST` effect and turn `simulate` into a pure function!"
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:563
+#: text/chapter8.md:564
#, no-wrap
msgid ""
"simulate' :: Number -> Number -> Int -> Number\n"
@@ -22623,13 +22726,13 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:569
+#: text/chapter8.md:570
#, markdown-text
msgid "You can even try running this function in PSCi:"
msgstr ""
#. type: Fenced code block (text)
-#: text/chapter8.md:570
+#: text/chapter8.md:571
#, no-wrap
msgid ""
"> import Main\n"
@@ -22651,7 +22754,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:590
+#: text/chapter8.md:591
#, markdown-text
msgid ""
"In fact, if we inline the definition of `simulate` at the call to `run`, as "
@@ -22659,7 +22762,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (hs)
-#: text/chapter8.md:591
+#: text/chapter8.md:592
#, no-wrap
msgid ""
"simulate :: Number -> Number -> Int -> Number\n"
@@ -22679,16 +22782,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:609
+#: text/chapter8.md:610
#, markdown-text
msgid ""
-"then the compiler will notice that the reference cell is not allowed to "
-"escape its scope, and can safely turn `ref` into a `var`. Here is the "
-"generated JavaScript for `simulate` inlined with `run`:"
+"Then the compiler will notice that the reference cell cannot escape its "
+"scope and can safely turn `ref` into a `var`. Here is the generated "
+"JavaScript for `simulate` inlined with `run`:"
msgstr ""
#. type: Fenced code block (javascript)
-#: text/chapter8.md:610
+#: text/chapter8.md:611
#, no-wrap
msgid ""
"var simulate = function (x0) {\n"
@@ -22717,24 +22820,24 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:636
-#, markdown-text
+#: text/chapter8.md:637
+#, markdown-text, no-wrap
msgid ""
-"Note that this resulting JavaScript is not as optimal as it could be. See "
+"> Note that this resulting JavaScript is not as optimal as it could be. See "
"[this "
"issue](https://github.com/purescript-contrib/purescript-book/issues/121) for "
"more details. The above snippet should be updated once that issue is "
-"resolved."
+"resolved.\n"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:638
+#: text/chapter8.md:639
#, markdown-text
msgid "For comparison, this is the generated JavaScript of the non-inlined form:"
msgstr ""
#. type: Fenced code block (js)
-#: text/chapter8.md:639
+#: text/chapter8.md:640
#, no-wrap
msgid ""
"var simulate = function (x0) {\n"
@@ -22763,16 +22866,16 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:665
+#: text/chapter8.md:666
#, markdown-text
msgid ""
"The `ST` effect is a good way to generate short JavaScript when working with "
"locally-scoped mutable state, especially when used together with actions "
-"like `for`, `foreach`, and `while` which generate efficient loops."
+"like `for`, `foreach`, and `while`, which generate efficient loops."
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter8.md:671
+#: text/chapter8.md:672
#, markdown-text
msgid ""
"(Medium) Rewrite the `safeDivide` function as `exceptionDivide` and throw an "
@@ -22781,7 +22884,7 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter8.md:671
+#: text/chapter8.md:672
#, markdown-text
msgid ""
"(Medium) Write a function `estimatePi :: Int -> Number` that uses `n` terms "
@@ -22792,7 +22895,7 @@ msgid ""
msgstr ""
#. type: Bullet: '1. '
-#: text/chapter8.md:671
+#: text/chapter8.md:672
#, markdown-text
msgid ""
"(Medium) Write a function `fibonacci :: Int -> Int` to compute the `n`th "
@@ -22802,13 +22905,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter8.md:672
+#: text/chapter8.md:673
#, markdown-text, no-wrap
msgid "DOM Effects"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:675
+#: text/chapter8.md:676
#, markdown-text
msgid ""
"In the final sections of this chapter, we will apply what we have learned "
@@ -22816,33 +22919,33 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:677
+#: text/chapter8.md:678
#, markdown-text
msgid ""
-"There are a number of PureScript packages for working directly with the DOM, "
-"or with open-source DOM libraries. For example:"
+"There are several PureScript packages for working directly with the DOM or "
+"open-source DOM libraries. For example:"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter8.md:681
+#: text/chapter8.md:682
#, markdown-text
msgid ""
"[`web-dom`](https://github.com/purescript-web/purescript-web-dom) provides "
-"type definitions and low level interface implementations for the W3C DOM "
+"type definitions and low-level interface implementations for the W3C DOM "
"spec."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter8.md:681
+#: text/chapter8.md:682
#, markdown-text
msgid ""
"[`web-html`](https://github.com/purescript-web/purescript-web-html) provides "
-"type definitions and low level interface implementations for the W3C HTML5 "
+"type definitions and low-level interface implementations for the W3C HTML5 "
"spec."
msgstr ""
#. type: Bullet: '- '
-#: text/chapter8.md:681
+#: text/chapter8.md:682
#, markdown-text
msgid ""
"[`jquery`](https://github.com/paf31/purescript-jquery) is a set of bindings "
@@ -22850,41 +22953,40 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:683
+#: text/chapter8.md:684
#, markdown-text
msgid ""
-"There are also PureScript libraries which build abstractions on top of these "
+"There are also PureScript libraries that build abstractions on top of these "
"libraries, such as"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter8.md:687
+#: text/chapter8.md:688
#, markdown-text
msgid ""
-"[`thermite`](https://github.com/paf31/purescript-thermite), which builds on "
+"[`thermite`](https://github.com/paf31/purescript-thermite) builds on "
"[`react`](https://github.com/purescript-contrib/purescript-react)"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter8.md:687
+#: text/chapter8.md:688
#, markdown-text
msgid ""
-"[`react-basic-hooks`](https://github.com/megamaddu/purescript-react-basic-hooks), "
-"which builds on "
-"[`react-basic`](https://github.com/lumihq/purescript-react-basic)"
+"[`react-basic-hooks`](https://github.com/megamaddu/purescript-react-basic-hooks) "
+"builds on [`react-basic`](https://github.com/lumihq/purescript-react-basic)"
msgstr ""
#. type: Bullet: '- '
-#: text/chapter8.md:687
+#: text/chapter8.md:688
#, markdown-text
msgid ""
-"[`halogen`](https://github.com/purescript-halogen/purescript-halogen) which "
+"[`halogen`](https://github.com/purescript-halogen/purescript-halogen) "
"provides a type-safe set of abstractions on top of a custom virtual DOM "
"library."
msgstr ""
#. type: Plain text
-#: text/chapter8.md:689
+#: text/chapter8.md:690
#, markdown-text
msgid ""
"In this chapter, we will use the `react-basic-hooks` library to add a user "
@@ -22893,13 +22995,13 @@ msgid ""
msgstr ""
#. type: Title ##
-#: text/chapter8.md:690
+#: text/chapter8.md:691
#, markdown-text, no-wrap
msgid "An Address Book User Interface"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:693
+#: text/chapter8.md:694
#, markdown-text
msgid ""
"Using the `react-basic-hooks` library, we will define our application as a "
@@ -22911,7 +23013,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:695
+#: text/chapter8.md:696
#, markdown-text
msgid ""
"A full tutorial for the React library is well beyond the scope of this "
@@ -22921,18 +23023,18 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:697
+#: text/chapter8.md:698
#, markdown-text
msgid ""
-"We are going to build a form which will allow a user to add a new entry into "
+"We are going to build a form that will allow a user to add a new entry into "
"our address book. The form will contain text boxes for the various fields "
-"(first name, last name, city, state, etc.), and an area in which validation "
+"(first name, last name, city, state, etc.) and an area where validation "
"errors will be displayed. As the user types text into the text boxes, the "
"validation errors will be updated."
msgstr ""
#. type: Plain text
-#: text/chapter8.md:699
+#: text/chapter8.md:700
#, markdown-text
msgid ""
"To keep things simple, the form will have a fixed shape: the different phone "
@@ -22941,7 +23043,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:701
+#: text/chapter8.md:702
#, markdown-text
msgid ""
"You can launch the web app from the `exercises/chapter8` directory with the "
@@ -22949,7 +23051,7 @@ msgid ""
msgstr ""
#. type: Fenced code block (shell)
-#: text/chapter8.md:702
+#: text/chapter8.md:703
#, no-wrap
msgid ""
"$ npm install\n"
@@ -22958,7 +23060,7 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:709
+#: text/chapter8.md:710
#, markdown-text
msgid ""
"If development tools such as `spago` and `parcel` are installed globally, "
@@ -22968,11 +23070,11 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:711
+#: text/chapter8.md:712
#, markdown-text
msgid ""
"`parcel` should launch a browser window with our \"Address Book\" app. If "
-"you keep the `parcel` terminal open, and rebuild with `spago` in another "
+"you keep the `parcel` terminal open and rebuild with `spago` in another "
"terminal, the page should automatically refresh with your latest edits. You "
"can also configure automatic rebuilds (and therefore automatic page refresh) "
"on file-save if you're using an "
@@ -22983,33 +23085,33 @@ msgid ""
msgstr ""
#. type: Plain text
-#: text/chapter8.md:713
+#: text/chapter8.md:714
#, markdown-text
msgid ""
-"In this Address Book app, you should be able to enter some values into the "
-"form fields and see the validation errors printed onto the page."
+"In this Address Book app, you can enter some values into the form fields and "
+"see the validation errors printed onto the page."
msgstr ""
#. type: Plain text
-#: text/chapter8.md:715
+#: text/chapter8.md:716
#, markdown-text
msgid "Let's explore how it works."
msgstr ""
#. type: Plain text
-#: text/chapter8.md:717
+#: text/chapter8.md:718
#, markdown-text
msgid "The `src/index.html` file is minimal:"
msgstr ""
#. type: Fenced code block (html)
-#: text/chapter8.md:718
+#: text/chapter8.md:719
#, no-wrap
msgid "{{#include ../exercises/chapter8/src/index.html}}\n"
msgstr ""
#. type: Plain text
-#: text/chapter8.md:723
+#: text/chapter8.md:724
#, markdown-text, no-wrap
msgid ""
"The `