From ac49a07c4c9aee0d015e3f3dbd7fba243e31d1ef Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 5 May 2017 16:07:03 +0200 Subject: [PATCH 001/112] Update JSON, add redirect-from --- Gemfile | 1 + Gemfile.lock | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 37f5eaa42e..bcd3be7d03 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,3 @@ source 'https://rubygems.org' gem 'github-pages', group: :jekyll_plugins +gem 'jekyll-redirect-from' diff --git a/Gemfile.lock b/Gemfile.lock index f3afc6d455..2b1dd61641 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -98,7 +98,7 @@ GEM gemoji (~> 2.0) html-pipeline (~> 2.2) jekyll (>= 3.0) - json (1.8.3) + json (1.8.6) kramdown (1.11.1) liquid (3.0.6) listen (3.0.6) @@ -140,6 +140,7 @@ PLATFORMS DEPENDENCIES github-pages + jekyll-redirect-from BUNDLED WITH - 1.13.6 + 1.14.6 From cc88ed1ce7506681648b8660d1ea3cb44394cc86 Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 5 May 2017 16:07:41 +0200 Subject: [PATCH 002/112] Guides to replace overviews landing --- guides.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 guides.md diff --git a/guides.md b/guides.md new file mode 100644 index 0000000000..140e0085df --- /dev/null +++ b/guides.md @@ -0,0 +1,91 @@ +--- +layout: guides-index +title: Guides and Overviews +languages: [ja, zh-cn, es] +redirect_from: + - /overviews +--- + +## Guides +* [Frequently Asked Questions](http://docs.scala-lang.org/tutorials/FAQ/finding-symbols) from StackOverflow. +* [Scala for Java Programmers](http://docs.scala-lang.org/tutorials/scala-for-java-programmers.html) compares the two languages. +* [Style Guide](http://docs.scala-lang.org/style/) + +
+

Core Scala

+
+ * Scala's Collections Library + * [Introduction](/overviews/collections/introduction.html) + * [Mutable and Immutable Collections](/overviews/collections/overview.html) + * [Trait Traversable](/overviews/collections/trait-traversable.html) + * [Trait Iterable](/overviews/collections/trait-iterable.html) + * [The sequence traits Seq, IndexedSeq, and LinearSeq](/overviews/collections/seqs.html) + * [Sets](/overviews/collections/sets.html) + * [Maps](/overviews/collections/maps.html) + * [Concrete Immutable Collection Classes](/overviews/collections/concrete-immutable-collection-classes.html) + * [Concrete Mutable Collection Classes](/overviews/collections/concrete-mutable-collection-classes.html) + * [Arrays](/overviews/collections/arrays.html) + * [Strings](/overviews/collections/strings.html) + * [Performance Characteristics](/overviews/collections/performance-characteristics.html) + * [Equality](/overviews/collections/equality.html) + * [Views](/overviews/collections/views.html) + * [Iterators](/overviews/collections/iterators.html) + * [Creating Collections From Scratch](/overviews/collections/creating-collections-from-scratch.html) + * [Conversions Between Java and Scala Collections](/overviews/collections/conversions-between-java-and-scala-collections.html) + * [Migrating from Scala 2.7](/overviews/collections/migrating-from-scala-27.html) + * [The Architecture of Scala Collections](/overviews/core/architecture-of-scala-collections.html) + * [String Interpolation](/overviews/core/string-interpolation.html) New in 2.10 + * [Implicit Classes](/overviews/core/implicit-classes.html) New in 2.10 + * [Value Classes and Universal Traits](/overviews/core/value-classes.html) New in 2.10 + * [Binary Compatibility of Scala Releases](/overviews/core/binary-compatibility-of-scala-releases.html) + +
+

Reference / Documentation

+
+ * Scaladoc + * [Overview](/overviews/scaladoc/overview.html) + * [Using Scaladoc Effectively](/overviews/scaladoc/interface.html) + * [Authoring Scaladoc](/overviews/scaladoc/for-library-authors.html) + * Scala REPL + * [Overview](/overviews/repl/overview.html) + +
+

Parallel and Concurrent Programming

+
+ * [Futures and Promises](/overviews/core/futures.html) New in 2.10 + * Scala's Parallel Collections Library + * [Overview](/overviews/parallel-collections/overview.html) + * [Concrete Parallel Collection Classes](/overviews/parallel-collections/concrete-parallel-collections.html) + * [Parallel Collection Conversions](/overviews/parallel-collections/conversions.html) + * [Concurrent Tries](/overviews/parallel-collections/ctries.html) + * [Architecture of the Parallel Collections Library](/overviews/parallel-collections/architecture.html) + * [Creating Custom Parallel Collections](/overviews/parallel-collections/custom-parallel-collections.html) + * [Configuring Parallel Collections](/overviews/parallel-collections/configuration.html) + * [Measuring Performance](/overviews/parallel-collections/performance.html) + * [The Scala Actors Migration Guide](/overviews/core/actors-migration-guide.html) New in 2.10 + * [The Scala Actors API](/overviews/core/actors.html) Deprecated + +
+

Metaprogramming

+
+ * Reflection Experimental + * [Overview](/overviews/reflection/overview.html) + * [Environment, Universes, and Mirrors](/overviews/reflection/environment-universes-mirrors.html) + * [Symbols, Trees, and Types](/overviews/reflection/symbols-trees-types.html) + * [Annotations, Names, Scopes, and More](/overviews/reflection/annotations-names-scopes.html) + * [TypeTags and Manifests](/overviews/reflection/typetags-manifests.html) + * [Thread Safety](/overviews/reflection/thread-safety.html) + * [Changes in Scala 2.11](/overviews/reflection/changelog211.html) + * Macros Experimental + * [Use Cases](/overviews/macros/usecases.html) + * [Blackbox Vs Whitebox](/overviews/macros/blackbox-whitebox.html) + * [Def Macros](/overviews/macros/overview.html) + * [Quasiquotes](/overviews/quasiquotes/intro.html) + * [Macro Bundles](/overviews/macros/bundles.html) + * [Implicit Macros](/overviews/macros/implicits.html) + * [Extractor Macros](/overviews/macros/extractors.html) + * [Type Providers](/overviews/macros/typeproviders.html) + * [Macro Annotations](/overviews/macros/annotations.html) + * [Macro Paradise](/overviews/macros/paradise.html) + * [Roadmap](/overviews/macros/roadmap.html) + * [Changes in 2.11](/overviews/macros/changelog211.html) From 814e2a62358dc52cd91398cba19b6cb1e25c83b5 Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 5 May 2017 16:11:53 +0200 Subject: [PATCH 003/112] Updated description --- guides.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guides.md b/guides.md index 140e0085df..6e2ee4c10b 100644 --- a/guides.md +++ b/guides.md @@ -3,12 +3,12 @@ layout: guides-index title: Guides and Overviews languages: [ja, zh-cn, es] redirect_from: - - /overviews + - /overviews/index.html --- ## Guides * [Frequently Asked Questions](http://docs.scala-lang.org/tutorials/FAQ/finding-symbols) from StackOverflow. -* [Scala for Java Programmers](http://docs.scala-lang.org/tutorials/scala-for-java-programmers.html) compares the two languages. +* [Scala for Java Programmers](http://docs.scala-lang.org/tutorials/scala-for-java-programmers.html): a comparison of the two languages. * [Style Guide](http://docs.scala-lang.org/style/)
From d39f6cdff0115b7bb1091cf89693cdb3dd5d7a8e Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Mon, 8 May 2017 14:18:07 +0200 Subject: [PATCH 004/112] Ignore ruby version --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 22a496be82..c4dfe40928 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ _site +.ruby-version .DS_Store .project .settings From d4b0479be260e0b6e27b00cd45ab5afbbc488849 Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 26 May 2017 14:18:43 +0200 Subject: [PATCH 005/112] updated json --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f3afc6d455..e83f3fe6d5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -98,7 +98,7 @@ GEM gemoji (~> 2.0) html-pipeline (~> 2.2) jekyll (>= 3.0) - json (1.8.3) + json (1.8.6) kramdown (1.11.1) liquid (3.0.6) listen (3.0.6) @@ -142,4 +142,4 @@ DEPENDENCIES github-pages BUNDLED WITH - 1.13.6 + 1.14.6 From 408561548bbc52fbd61129f8df7a245b520b5a60 Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 26 May 2017 14:20:51 +0200 Subject: [PATCH 006/112] jekyll-redirect-from --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 37f5eaa42e..bcd3be7d03 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,3 @@ source 'https://rubygems.org' gem 'github-pages', group: :jekyll_plugins +gem 'jekyll-redirect-from' From 83cf79baddb6242a2b8b2285e3b3482db4d60a32 Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 26 May 2017 14:26:02 +0200 Subject: [PATCH 007/112] bumped scala version --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index e6e6343bf4..4a966d7cdc 100644 --- a/_config.yml +++ b/_config.yml @@ -15,7 +15,7 @@ keywords: - Document - Guide -scala-version: 2.12.0 +scala-version: 2.12.2 highlighter: rouge permalink: /:categories/:title.html From 531539d24b5a430e0dd73fc12e17c99c519a3b18 Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 26 May 2017 14:27:07 +0200 Subject: [PATCH 008/112] Added redirect-from gem --- Gemfile.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile.lock b/Gemfile.lock index e83f3fe6d5..2b1dd61641 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -140,6 +140,7 @@ PLATFORMS DEPENDENCIES github-pages + jekyll-redirect-from BUNDLED WITH 1.14.6 From 6a03b97e79bc92ff2a38ad407a053bce3abdfacd Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 26 May 2017 14:50:02 +0200 Subject: [PATCH 009/112] New headers --- _data/nav-header.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 _data/nav-header.yml diff --git a/_data/nav-header.yml b/_data/nav-header.yml new file mode 100644 index 0000000000..204e1c1b38 --- /dev/null +++ b/_data/nav-header.yml @@ -0,0 +1,12 @@ +- title: Documentation + url: / +- title: Download + url: https://scala-lang.org/download/ +- title: Community + url: https://scala-lang.org/community/ +- title: Libraries + url: https://index.scala-lang.org +- title: Contribute + url: https://scala-lang.org/contribute/ +- title: Blog + url: https://scala-lang.org/blog/ From 970b29d863b9feab6c235aace5465b4d1eb6cdab Mon Sep 17 00:00:00 2001 From: Travis Lee Date: Fri, 26 May 2017 14:52:48 +0200 Subject: [PATCH 010/112] replaced layouts --- _includes/allsids.txt | 28 -- _includes/blog-list.html | 75 ++++ _includes/cheatsheet-header.txt | 352 ---------------- _includes/cheatsheet-sidebar.txt | 40 -- _includes/collections.txt | 4 - _includes/column-list-of-items.html | 18 + _includes/contributing-header.txt | 217 ---------- _includes/contributing-toc.txt | 7 - _includes/contributions-projects-list.html | 36 ++ _includes/coursera-stats-js.txt | 21 - _includes/{disqus.txt => disqus.html} | 0 _includes/download-resource-list.html | 31 ++ _includes/downloads-list.html | 19 + _includes/events-training-list-bottom.html | 12 + _includes/events-training-list-top.html | 73 ++++ _includes/footer.html | 55 +++ _includes/footer.txt | 15 - _includes/footerbar.txt | 53 --- _includes/frontpage-content.txt | 72 ---- _includes/frontpage-footer.txt | 6 - _includes/frontpage-header.txt | 153 ------- _includes/gen-toc.txt | 6 - _includes/glossary-header.txt | 349 ---------------- _includes/glossary-sidebar.txt | 6 - _includes/header-coursera.txt | 180 -------- _includes/header.txt | 159 ------- _includes/headerbottom.html | 3 + _includes/headertop.html | 30 ++ _includes/index-header.txt | 264 ------------ .../inner-page-blog-detail-main-content.html | 27 ++ _includes/inner-page-main-content.html | 12 + _includes/localized-overview-index.txt | 34 -- _includes/masthead-community.html | 37 ++ _includes/masthead-documentation.html | 19 + _includes/navbar-inner.html | 19 + _includes/online-courses.html | 91 ++++ _includes/pager.txt | 10 - _includes/paginator.html | 9 + _includes/scastie.html | 23 ++ _includes/search-header.txt | 267 ------------ _includes/sidebar-toc.html | 39 ++ _includes/sips-topbar.txt | 17 - _includes/thanks-to.txt | 14 - _includes/toc-large.txt | 28 -- _includes/toc.txt | 57 --- _includes/topbar.txt | 47 --- _includes/tutorial-list.html | 7 + _includes/tutorial-toc.txt | 6 - _includes/twitter-feed.html | 15 + _includes/upcoming-training.html | 38 ++ _includes/worldmap.html | 0 _layouts/blog-detail.html | 6 + _layouts/blog-list.html | 9 + _layouts/cheatsheet.html | 32 -- _layouts/contribute.html | 33 -- _layouts/default.html | 2 - _layouts/downloadpage.html | 100 +++++ _layouts/events.html | 13 + _layouts/frontpage.html | 387 +++++++++++++++++- _layouts/glossary.html | 35 -- _layouts/guides-index.html | 43 -- _layouts/guides-thanks.html | 12 - _layouts/index.html | 23 -- _layouts/inner-page-community.html | 9 + _layouts/inner-page-documentation.html | 9 + _layouts/inner-page-no-masthead.html | 6 + _layouts/inner-page-parent-searchbar.html | 32 ++ _layouts/inner-page-parent.html | 23 ++ _layouts/learn.html | 38 ++ _layouts/news-coursera.html | 21 - _layouts/news.html | 25 -- _layouts/overview-large.html | 79 ++-- _layouts/overview.html | 55 --- _layouts/page.html | 33 -- _layouts/redirected.html | 16 - _layouts/search.html | 23 -- _layouts/sip-landing.html | 28 -- _layouts/sip.html | 23 -- _layouts/slip.html | 23 -- _layouts/training.html | 9 + _layouts/tutorial.html | 53 --- 81 files changed, 1354 insertions(+), 2946 deletions(-) delete mode 100644 _includes/allsids.txt create mode 100644 _includes/blog-list.html delete mode 100644 _includes/cheatsheet-header.txt delete mode 100644 _includes/cheatsheet-sidebar.txt delete mode 100644 _includes/collections.txt create mode 100644 _includes/column-list-of-items.html delete mode 100644 _includes/contributing-header.txt delete mode 100644 _includes/contributing-toc.txt create mode 100644 _includes/contributions-projects-list.html delete mode 100644 _includes/coursera-stats-js.txt rename _includes/{disqus.txt => disqus.html} (100%) create mode 100644 _includes/download-resource-list.html create mode 100644 _includes/downloads-list.html create mode 100644 _includes/events-training-list-bottom.html create mode 100644 _includes/events-training-list-top.html create mode 100644 _includes/footer.html delete mode 100644 _includes/footer.txt delete mode 100644 _includes/footerbar.txt delete mode 100644 _includes/frontpage-content.txt delete mode 100644 _includes/frontpage-footer.txt delete mode 100644 _includes/frontpage-header.txt delete mode 100644 _includes/gen-toc.txt delete mode 100644 _includes/glossary-header.txt delete mode 100644 _includes/glossary-sidebar.txt delete mode 100644 _includes/header-coursera.txt delete mode 100644 _includes/header.txt create mode 100644 _includes/headerbottom.html create mode 100644 _includes/headertop.html delete mode 100644 _includes/index-header.txt create mode 100644 _includes/inner-page-blog-detail-main-content.html create mode 100644 _includes/inner-page-main-content.html delete mode 100644 _includes/localized-overview-index.txt create mode 100644 _includes/masthead-community.html create mode 100644 _includes/masthead-documentation.html create mode 100644 _includes/navbar-inner.html create mode 100644 _includes/online-courses.html delete mode 100644 _includes/pager.txt create mode 100644 _includes/paginator.html create mode 100644 _includes/scastie.html delete mode 100644 _includes/search-header.txt create mode 100644 _includes/sidebar-toc.html delete mode 100644 _includes/sips-topbar.txt delete mode 100644 _includes/thanks-to.txt delete mode 100644 _includes/toc-large.txt delete mode 100644 _includes/toc.txt delete mode 100644 _includes/topbar.txt create mode 100644 _includes/tutorial-list.html delete mode 100644 _includes/tutorial-toc.txt create mode 100644 _includes/twitter-feed.html create mode 100644 _includes/upcoming-training.html delete mode 100644 _includes/worldmap.html create mode 100644 _layouts/blog-detail.html create mode 100644 _layouts/blog-list.html delete mode 100644 _layouts/cheatsheet.html delete mode 100644 _layouts/contribute.html delete mode 100644 _layouts/default.html create mode 100644 _layouts/downloadpage.html create mode 100644 _layouts/events.html delete mode 100644 _layouts/glossary.html delete mode 100644 _layouts/guides-index.html delete mode 100644 _layouts/guides-thanks.html delete mode 100644 _layouts/index.html create mode 100644 _layouts/inner-page-community.html create mode 100644 _layouts/inner-page-documentation.html create mode 100644 _layouts/inner-page-no-masthead.html create mode 100644 _layouts/inner-page-parent-searchbar.html create mode 100644 _layouts/inner-page-parent.html create mode 100644 _layouts/learn.html delete mode 100644 _layouts/news-coursera.html delete mode 100644 _layouts/news.html delete mode 100644 _layouts/overview.html delete mode 100644 _layouts/page.html delete mode 100644 _layouts/redirected.html delete mode 100644 _layouts/search.html delete mode 100644 _layouts/sip-landing.html delete mode 100644 _layouts/sip.html delete mode 100644 _layouts/slip.html create mode 100644 _layouts/training.html delete mode 100644 _layouts/tutorial.html diff --git a/_includes/allsids.txt b/_includes/allsids.txt deleted file mode 100644 index dd33e8b365..0000000000 --- a/_includes/allsids.txt +++ /dev/null @@ -1,28 +0,0 @@ -

Pending SIPs

-
    - {% for post in site.categories.pending %} -
  • {{ post.title }} ( {{ post.date | date: "%b %Y" }} ) - {% if post.vote-status %} - {% if post.vote-status == 'accepted' %} - Accepted - {% elsif post.vote-status == 'deferred' %} - Deferred - {% elsif post.vote-status == 'postponed' %} - Postponed - {% elsif post.vote-status == 'under review' %} - Under Review - {% elsif post.vote-status == 'under revision' %} - Under Revision - {% elsif post.vote-status == 'numbered' %} - Numbered - {% elsif post.vote-status == 'dormant' %} - Dormant - {% elsif post.vote-status == 'not accepted' %} - Not Accepted - {% elsif post.vote-status == 'rejected' %} - Not Accepted - {% endif %} - {% endif %} -
  • - {% endfor %} -
diff --git a/_includes/blog-list.html b/_includes/blog-list.html new file mode 100644 index 0000000000..a82eeccc4e --- /dev/null +++ b/_includes/blog-list.html @@ -0,0 +1,75 @@ +{% comment %}Use the include variable 'category' to select the category to show (included in blog-categories.yml), or assign it to "all" if you want to show all posts.{% endcomment %} + +
+
+
+
+ +
+ {% for post in paginator.posts %} +
+

{{post.title}}

+

{{post.date | date: "%A %-d %B %Y"}}

+ {% if post.by %}

{{post.by}}

{% endif %} + {% if post.tags %} +
    + {% for tag in post.tags %} +
  • {{tag}}
  • + {% endfor %} +
+ {% endif %} +
+ {% endfor %} +
+
+ {% for category in site.data.blog-categories %} + {% if category.categoryId == include.category %} + {% assign currentCategoryPath = category.url %} + {% endif %} + {% endfor %} + + {% capture urlPath %}{% if include.category == "all" %}blog{% else %}{{currentCategoryPath}}{% endif %}{% endcapture %} + {% assign urlPath = urlPath | split: '/' | join: '/' | remove_first: '/' %} + {% include paginator.html urlPath=urlPath %} +
+ {% assign highlights = "" | split: "," %} + {% for post in site.posts %} + {% if post.isHighlight == true %} + {% assign highlights = highlights | push: post %} + {% endif %} + {% endfor %} + + {% for post in highlights %} + {% if forloop.first %} +
+
+
Highlights
+
+ {% endif %} +
+

{{post.title}}

+ {% if post.by %}

{{post.by}}

{% endif %} + {% if post.tags %} + {% for tag in post.tags %} +
    +
  • {{tag}}
  • +
+ {% endfor %} + {% endif %} +
+ {% if forloop.last %} +
+
+
+ {% endif %} + {% endfor %} + +
+
\ No newline at end of file diff --git a/_includes/cheatsheet-header.txt b/_includes/cheatsheet-header.txt deleted file mode 100644 index 32965d410c..0000000000 --- a/_includes/cheatsheet-header.txt +++ /dev/null @@ -1,352 +0,0 @@ - - - - - {% if page.title %}{{ page.title }} - {% endif %}{{ site.title }} - {% if page.description %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_includes/cheatsheet-sidebar.txt b/_includes/cheatsheet-sidebar.txt deleted file mode 100644 index af79fde132..0000000000 --- a/_includes/cheatsheet-sidebar.txt +++ /dev/null @@ -1,40 +0,0 @@ -{% for pg in site.pages %} - {% if pg.layout == 'cheatsheet' and pg.title == page.title and pg.languages %} - {% assign languages = pg.languages %} - {% endif %} -{% endfor %} - -{% if page.language %} - {% capture intermediate %}{{ page.url | remove_first: page.language }}{% endcapture %} - {% capture rootTutorialURL %}{{ intermediate | remove_first: '/' }}{% endcapture %} -{% else %} - {% assign rootTutorialURL = page.url %} -{% endif %} - -
-
- {% if languages %} -

Languages

-
    -
  • English
  • - {% for l in languages %} - {% assign lang = site.data.languages[l] %} -
  • {{lang.name}}
  • - {% endfor %} -
- {% endif %} -

Contents

-
-

Other Cheatsheets

-
    - {% for pg in site.pages %} - {% if pg.layout == 'cheatsheet' and pg.title != page.title %} -
  • {{ pg.title }}
  • - {% endif %} - {% endfor %} -
- {% if page.about %} -
About

{{ page.about }}

- {% endif %} -
-
\ No newline at end of file diff --git a/_includes/collections.txt b/_includes/collections.txt deleted file mode 100644 index 826c2699b8..0000000000 --- a/_includes/collections.txt +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/_includes/column-list-of-items.html b/_includes/column-list-of-items.html new file mode 100644 index 0000000000..eb9e1600be --- /dev/null +++ b/_includes/column-list-of-items.html @@ -0,0 +1,18 @@ +{% comment %} + Layouts using this include should pass an include variable called 'collection' referencing a collection carrying the data (i.e.: contribute_community_tickets, contribute_resources...) +{% endcomment %} +
+ {% for item in include.collection %} +
+ +
+ {{item.content}} +
+
+ {% endfor %} +
\ No newline at end of file diff --git a/_includes/contributing-header.txt b/_includes/contributing-header.txt deleted file mode 100644 index 338528e402..0000000000 --- a/_includes/contributing-header.txt +++ /dev/null @@ -1,217 +0,0 @@ - - - - - {% if page.title %}{{ page.title }} - {% endif %}{{ site.title }} - {% if page.description %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_includes/contributing-toc.txt b/_includes/contributing-toc.txt deleted file mode 100644 index 471940e7ec..0000000000 --- a/_includes/contributing-toc.txt +++ /dev/null @@ -1,7 +0,0 @@ -
-
-

Contents

-
- {% include thanks-to.txt %} -
-
diff --git a/_includes/contributions-projects-list.html b/_includes/contributions-projects-list.html new file mode 100644 index 0000000000..5a4c9bdbe7 --- /dev/null +++ b/_includes/contributions-projects-list.html @@ -0,0 +1,36 @@ +{% comment %} + Layouts using this include should pass an include variable called 'collection' referencing a collection carrying the data +{% endcomment %} +
+ {% for item in include.collection %} +
+ +
+

{{item.description}}

+
    + {% if item.homeLink %} +
  • Home
  • + {% if item.contributingLink or item.readmeLink or item.issuesLink %}
  • {% endif %} + {% endif %} + {% if item.issuesLink %} +
  • Issues
  • + {% if item.contributingLink or item.readmeLink %}
  • {% endif %} + {% endif %} + {% if item.readmeLink %} +
  • ReadMe
  • + {% if item.contributingLink %}
  • {% endif %} + {% endif %} + {% if item.contributingLink %} +
  • Contributing
  • + {% endif %} +
+
+
+ {% endfor %} + +
\ No newline at end of file diff --git a/_includes/coursera-stats-js.txt b/_includes/coursera-stats-js.txt deleted file mode 100644 index a65c30925b..0000000000 --- a/_includes/coursera-stats-js.txt +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/_includes/disqus.txt b/_includes/disqus.html similarity index 100% rename from _includes/disqus.txt rename to _includes/disqus.html diff --git a/_includes/download-resource-list.html b/_includes/download-resource-list.html new file mode 100644 index 0000000000..017af455af --- /dev/null +++ b/_includes/download-resource-list.html @@ -0,0 +1,31 @@ +

Other resources

+ +

You can find the installer download links for other operating systems, as well as documentation and source code archives for Scala {{page.release_version}} below.

+ +
+ + + + + {% unless page.dont_show_sizes %} + + {% endunless %} + + + +{% for resource in page.resources %} + + + + {% unless page.dont_show_sizes %} + + {% endunless %} + +{% endfor %} + +
ArchiveSystemSize
+ + {{ resource[1] }} + + {{ resource[3] }}{{ resource[4] }}
+
diff --git a/_includes/downloads-list.html b/_includes/downloads-list.html new file mode 100644 index 0000000000..f8a7554901 --- /dev/null +++ b/_includes/downloads-list.html @@ -0,0 +1,19 @@ +{% for top in (0..3) reversed %} + {% for major in (0..20) reversed %} + {% assign possibleVersionShort = top | append:'.' | append:major %} + {% assign sz = possibleVersionShort | size %} + {% if 3 == sz %} + {% assign possibleVersion = possibleVersionShort | append:'.' %} + {% else %} + {% assign possibleVersion = possibleVersionShort %} + {% endif %} + {% for page in site.downloads %} + {% assign releaseVersion = page.release_version | truncate:4, '' %} + {% if releaseVersion == possibleVersion %} + + {% endif %} + {% endfor %} + {% endfor %} +{% endfor %} \ No newline at end of file diff --git a/_includes/events-training-list-bottom.html b/_includes/events-training-list-bottom.html new file mode 100644 index 0000000000..f863e64506 --- /dev/null +++ b/_includes/events-training-list-bottom.html @@ -0,0 +1,12 @@ +
+ + {% if paginator.total_pages > 1 %} +
    + {% for page in (1..paginator.total_pages) %} +
  • + {{page}} +
  • + {% endfor %} +
+ {% endif %} + \ No newline at end of file diff --git a/_includes/events-training-list-top.html b/_includes/events-training-list-top.html new file mode 100644 index 0000000000..e3a7d0004c --- /dev/null +++ b/_includes/events-training-list-top.html @@ -0,0 +1,73 @@ +{% capture currentYear %}{{site.time | date: '%Y' | plus: 0}}{% endcapture %} + +
+
+
+ {% comment %}Because of Jekyll limitations, we need to pass the paginated collection to iterate in an include variable 'collection'{% endcomment %} + + {% capture firstMonth %}{{include.collection.first.date | date: "%m"}}{% endcapture %} + {% assign firstMonthNum = firstMonth | plus: 0 %} + {% capture lastMonth %}{{include.collection.last.date | date: "%m"}}{% endcapture %} + {% assign lastMonthNum = lastMonth | plus: 0 %} + + {% for m in (firstMonth..lastMonth) %} + {% assign currentMonthEvents = '' | split: ','' %} + + {% for event in include.collection %} + {% capture month %}{{event.date | date: "%m"}}{% endcapture %} + {% assign monthNum = month | plus: 0 %} + {% if monthNum == m %} + {% assign currentMonthEvents = currentMonthEvents | push: event %} + {% endif %} + {% endfor %} + + {% capture monthName %} + {% case m %} + {% when 1 %}January + {% when 2 %}February + {% when 3 %}March + {% when 4 %}April + {% when 5 %}May + {% when 6 %}June + {% when 7 %}July + {% when 8 %}August + {% when 9 %}September + {% when 10 %}October + {% when 11 %}November + {% when 12 %}December + {% endcase %} + {% endcapture %} + + {% for event in currentMonthEvents %} + {% capture year %}{{event.date | date: "%Y"}}{% endcapture %} + {% capture day %}{{event.date | date: "%d"}}{% endcapture %} + {% if forloop.first %} +

{{monthName}} {{year}}

+ + {% endif %} + {% endfor %} + {% endfor %} \ No newline at end of file diff --git a/_includes/footer.html b/_includes/footer.html new file mode 100644 index 0000000000..e333a548ac --- /dev/null +++ b/_includes/footer.html @@ -0,0 +1,55 @@ +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + {% if page.includeTOC == true %} + + + {% endif %} + + + + + + + + diff --git a/_includes/footer.txt b/_includes/footer.txt deleted file mode 100644 index 65dfc817bc..0000000000 --- a/_includes/footer.txt +++ /dev/null @@ -1,15 +0,0 @@ -{% include footerbar.txt %} - - - - - - - - diff --git a/_includes/footerbar.txt b/_includes/footerbar.txt deleted file mode 100644 index 881aad4375..0000000000 --- a/_includes/footerbar.txt +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - diff --git a/_includes/frontpage-content.txt b/_includes/frontpage-content.txt deleted file mode 100644 index d45b10893a..0000000000 --- a/_includes/frontpage-content.txt +++ /dev/null @@ -1,72 +0,0 @@ -
- -

Community-driven documentation for Scala.

- -
    -
  • - Thumbnail -

    Overviews and Guides

    -

    Collections, Actors, Swing, and more.

    -

    Go there

    -
  • -
  • - Thumbnail -

    Tutorials

    -

    Coming from Java? Python? Ruby? Tutorials which help the transition from language XYZ to Scala.

    -

    Go there

    -
  • -
  • - Thumbnail -

    Glossary

    -

    Lost on some terminology? Check the glossary, direct from the book, Programming in Scala.

    -

    Go there

    -
  • -
-
- -
-
-
- -
- -

Available Documentation

- -

Scala Improvement Process Available

-

Read language improvement proposals, participate in discussions surrounding submitted proposals, or submit your own improvement proposal.

- -

Guides and Overviews Some Available

-

Some guides, such as Martin Odersky’s Collections Overview are now available.

- -

Tutorials Some Available

-

Some tutorials, such as the Scala for Java Programmers guide, are now available.

- -

Glossary Available

-

With permission from Artima Inc., we reproduce the glossary from Programming in Scala here, for easy reference.

- -

Cheatsheets Available

-

We've currently got one cheatsheet, thanks to Brendan O’Connor. Contributions in this area are welcome.

- -

Scala Style Guide Available

-

Thanks to Daniel Spiewak and David Copeland for putting together such an excellent style guide, and thanks to Simon Ochsenreither for converting it to Markdown.

- -

Language Specification Available

-

The official definition of Scala. For when you just have to know the truth.

- -

 

 

- -
-
-

Contributions Welcomed!

- This site was designed for core committers and the community alike to build documentation. We’d love help of any kind – from detailed articles or overviews of Scala’s language features, to help converting documents to Markdown. -

 

-

If you’d like to help, please see the Contribute section of the site, and feel free to contact Heather.

- -

Recent Comments

- - -
- -
-
-
diff --git a/_includes/frontpage-footer.txt b/_includes/frontpage-footer.txt deleted file mode 100644 index 5dd888b0d8..0000000000 --- a/_includes/frontpage-footer.txt +++ /dev/null @@ -1,6 +0,0 @@ - - {% include footerbar.txt %} - - - - diff --git a/_includes/frontpage-header.txt b/_includes/frontpage-header.txt deleted file mode 100644 index 9a41e3c5d5..0000000000 --- a/_includes/frontpage-header.txt +++ /dev/null @@ -1,153 +0,0 @@ - - - - - {% if page.title %}{{ page.title }} - {% endif %}{{ site.title }} - {% if page.description %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_includes/gen-toc.txt b/_includes/gen-toc.txt deleted file mode 100644 index c5124cfd18..0000000000 --- a/_includes/gen-toc.txt +++ /dev/null @@ -1,6 +0,0 @@ -
-
-

Contents

-
-
-
diff --git a/_includes/glossary-header.txt b/_includes/glossary-header.txt deleted file mode 100644 index 12ad34335e..0000000000 --- a/_includes/glossary-header.txt +++ /dev/null @@ -1,349 +0,0 @@ - - - - - {% if page.title %}{{ page.title }} - {% endif %}{{ site.title }} - {% if page.description %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_includes/glossary-sidebar.txt b/_includes/glossary-sidebar.txt deleted file mode 100644 index e047cce8c2..0000000000 --- a/_includes/glossary-sidebar.txt +++ /dev/null @@ -1,6 +0,0 @@ -
-
-

Terms

-
-
-
diff --git a/_includes/header-coursera.txt b/_includes/header-coursera.txt deleted file mode 100644 index 49a664246c..0000000000 --- a/_includes/header-coursera.txt +++ /dev/null @@ -1,180 +0,0 @@ - - - - - {% if page.partof %}{{ page.partof | replace: '-',' ' | split:" " | capitalize | join:" " }} - {% endif %}{% if page.title %}{{ page.title }} - {% endif %}{{ site.title }} - {% if page.description %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_includes/header.txt b/_includes/header.txt deleted file mode 100644 index ff4295a4b0..0000000000 --- a/_includes/header.txt +++ /dev/null @@ -1,159 +0,0 @@ - - - - {% if page.partof %}{% assign words = page.partof | split: '-' %}{% for word in words %}{{ word | capitalize }} {% endfor %}- {% endif %}{% if page.title %}{{ page.title }} - {% endif %}{{ site.title }} - {% if page.description %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_includes/headerbottom.html b/_includes/headerbottom.html new file mode 100644 index 0000000000..75ae52c268 --- /dev/null +++ b/_includes/headerbottom.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/_includes/headertop.html b/_includes/headertop.html new file mode 100644 index 0000000000..bfc55927d5 --- /dev/null +++ b/_includes/headertop.html @@ -0,0 +1,30 @@ + + + + {% if page.title %}{{ page.title }} | {% endif %}{{ site.title }} + {% if page.description %} + + {% endif %} + + + + + + + + + + + + + + + + + + + + + + diff --git a/_includes/index-header.txt b/_includes/index-header.txt deleted file mode 100644 index 4a2043a74e..0000000000 --- a/_includes/index-header.txt +++ /dev/null @@ -1,264 +0,0 @@ - - - - - {% if page.title %}{{ page.title }} - {% endif %}{{ site.title }} - {% if page.description %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_includes/inner-page-blog-detail-main-content.html b/_includes/inner-page-blog-detail-main-content.html new file mode 100644 index 0000000000..bf616325b4 --- /dev/null +++ b/_includes/inner-page-blog-detail-main-content.html @@ -0,0 +1,27 @@ +
+
+
+
+
+
+

{{page.date | date: "%A %-d %B %Y"}}

+

{{page.by}}

+
+ {% if page.tags %} +
    + {% for tag in page.tags %} +
  • {{tag}}
  • + {% endfor %} +
+ {% endif %} +
+
{{page.category | upcase}}
+

{{page.title}}

+ {{content}} +
+
+ + {% include sidebar-toc.html %} +
+
+
\ No newline at end of file diff --git a/_includes/inner-page-main-content.html b/_includes/inner-page-main-content.html new file mode 100644 index 0000000000..9587a304cb --- /dev/null +++ b/_includes/inner-page-main-content.html @@ -0,0 +1,12 @@ +
+
+
+
+ {{content}} +
+
+ + + {% include sidebar-toc.html %} +
+
diff --git a/_includes/localized-overview-index.txt b/_includes/localized-overview-index.txt deleted file mode 100644 index c63454870b..0000000000 --- a/_includes/localized-overview-index.txt +++ /dev/null @@ -1,34 +0,0 @@ -{% for enpost in site.categories.core %} -{% for post in site.pages %} -{% if post.overview and post.overview == enpost.overview and post.language == page.language %} - -{% if post.partof %} -* {{ post.title }} {{ post.label-text }} - {% for pg in site.pages %} - {% if pg.partof == post.partof and pg.outof and pg.language == page.language %} - {% assign totalPages = pg.outof %} - {% endif %} - {% endfor %} - - {% if totalPages %} -
    - {% for i in (1..totalPages) %} - {% for pg in site.pages %} - {% if pg.partof == post.partof and pg.num and pg.num == i and pg.language == page.language %} -
  • {{ pg.title }}
  • - {% endif %} - {% endfor %} - {% endfor %} -
- {% else %} **ERROR**. Couldn't find the total number of pages in this set of tutorial articles. Have you declared the `outof` tag in your YAML front matter? - {% endif %} -{% else %} - {% if post.hidden == true %} - {% else %} -* [{{ post.title }}]({{ site.baseurl }}{{ post.url }}) {{ post.label-text }} - {% endif %} -{% endif %} - -{% endif %} -{% endfor %} -{% endfor %} diff --git a/_includes/masthead-community.html b/_includes/masthead-community.html new file mode 100644 index 0000000000..76f7b0628a --- /dev/null +++ b/_includes/masthead-community.html @@ -0,0 +1,37 @@ +
+
+
+
+
+

Discourse

+ Mailing list +
    + {% for forum in site.data.chats-forums.discourseForums %} +
  • + {{forum.title}} +
    +

    {{forum.title}}

    +

    {{forum.subtitle}}

    +
    +
  • + {% endfor %} +
+
+
+

Gitter

+ Real-time (topic-specialized) chat + +
+
+
+
+
\ No newline at end of file diff --git a/_includes/masthead-documentation.html b/_includes/masthead-documentation.html new file mode 100644 index 0000000000..a773e89d0c --- /dev/null +++ b/_includes/masthead-documentation.html @@ -0,0 +1,19 @@ +
+
+
+
+ {% for link in page.links %} + +
+ +
{{link.title}}
+
+
+

{{link.description}}

+
+
+ {% endfor %} +
+
+
+
\ No newline at end of file diff --git a/_includes/navbar-inner.html b/_includes/navbar-inner.html new file mode 100644 index 0000000000..c68cdda64b --- /dev/null +++ b/_includes/navbar-inner.html @@ -0,0 +1,19 @@ + diff --git a/_includes/online-courses.html b/_includes/online-courses.html new file mode 100644 index 0000000000..9f91d0404b --- /dev/null +++ b/_includes/online-courses.html @@ -0,0 +1,91 @@ + +
+
+

Online Courses

+
+ + {% comment %} + We're going to follow the next ordering for the online courses: + 1- First we'll show those items that belong to an specific specialization (i.e.: Scala's progfun in Coursera). Those will be ordered alphabetically by title and each item under the specialization by the `specialization-order` tag in each one. + 2- After those, courses that don't belong to any specific specialization. + We'll only show those courses that are not finished yet. + {% endcomment %} + + {% assign specializations = '' | split: ',' %} + {% assign courses = '' | split: ',' %} + {% assign upcomingCourses = '' | split: ',' %} + {% capture now %}{{site.time | date: '%s' | plus: 0}}{% endcapture %} + + {% for course in site.online_courses %} + {% unless specializations contains course.specialization %} + {% assign specializations = specializations | push: course.specialization %} + {% endunless %} + + {% capture endDate %}{{course.end-date | date: '%s' | plus: 86400}}{% endcapture %} + {% if now <= endDate %} + {% assign upcomingCourses = upcomingCourses | push: course %} + {% endif %} + {% endfor %} + + {% for specialization in specializations %} + {% assign specCourses = '' | split: ',' %} + + {% for course in upcomingCourses %} + {% if course.specialization %} + {% if course.specialization == specialization %} + {% assign specCourses = specCourses | push: course %} + {% endif %} + + {% assign sortedSpecCourses = specCourses | sort: 'specialization-order' %} + {% endif %} + {% endfor %} + {% for sortedCourse in sortedSpecCourses %} + {% assign courses = courses | push: sortedCourse %} + {% endfor %} + {% endfor %} + + {% for course in upcomingCourses %} + {% unless course.specialization %} + {% assign courses = courses | push: course %} + {% endunless %} + {% endfor %} + + +
+

Visit all the Online Courses courses

+
+
\ No newline at end of file diff --git a/_includes/pager.txt b/_includes/pager.txt deleted file mode 100644 index 417efc705a..0000000000 --- a/_includes/pager.txt +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/_includes/paginator.html b/_includes/paginator.html new file mode 100644 index 0000000000..7212e643f4 --- /dev/null +++ b/_includes/paginator.html @@ -0,0 +1,9 @@ +{% if paginator.total_pages > 1 %} +
    + {% for page in (1..paginator.total_pages) %} +
  • + {{page}} +
  • + {% endfor %} +
+{% endif %} \ No newline at end of file diff --git a/_includes/scastie.html b/_includes/scastie.html new file mode 100644 index 0000000000..fb7a6ec65a --- /dev/null +++ b/_includes/scastie.html @@ -0,0 +1,23 @@ +
+
+
+

Run Scala in your browser

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer commodo neque eget placerat dapibus. Mauris ullamcorper dui eu pellentesque venenatis. Nam non elit vitae dolor posuere eleifend a facilisis diam

+
+
+ +
+
+
+
+ +
+
+ + Run Scala code interactively +
+
+
+
\ No newline at end of file diff --git a/_includes/search-header.txt b/_includes/search-header.txt deleted file mode 100644 index a013e1d165..0000000000 --- a/_includes/search-header.txt +++ /dev/null @@ -1,267 +0,0 @@ - - - - - {% if page.title %}{{ page.title }} - {% endif %}{{ site.title }} - {% if page.description %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/_includes/sidebar-toc.html b/_includes/sidebar-toc.html new file mode 100644 index 0000000000..1f5216067d --- /dev/null +++ b/_includes/sidebar-toc.html @@ -0,0 +1,39 @@ +{% if page.includeTOC or layout.includeTOC %} + {% if page.includeTOC != false %} + +
+ +
+ {% endif %} +{% endif %} diff --git a/_includes/sips-topbar.txt b/_includes/sips-topbar.txt deleted file mode 100644 index 65400087e2..0000000000 --- a/_includes/sips-topbar.txt +++ /dev/null @@ -1,17 +0,0 @@ - - diff --git a/_includes/thanks-to.txt b/_includes/thanks-to.txt deleted file mode 100644 index 1c375077b8..0000000000 --- a/_includes/thanks-to.txt +++ /dev/null @@ -1,14 +0,0 @@ -
-

Thank you

It helps many

-
- -This site and the documentation it contains is the result of a tremendous amount of work by a large number of people over time, from the first Scala team members, to today’s newcomers. In an effort to only scratch the surface, we list some of those whose help was invaluable in the realization of this iteration of the Scala Documentation repository. - - -`scala.reflect.ClassManifest` は Scala 2.10 から廃止予定となり、将来のマイナーリリースにおいて +`scala.reflect.ClassManifests` は Scala 2.10 から廃止予定となり、将来のマイナーリリースにおいて `scala.reflect.Manifest` も廃止予定として `TypeTag` と `ClassTag` に道を開けることを予定している。 そのため、マニフェストを使ったコードは型タグを使うものに移行することを推奨する。 diff --git a/resources/js/functions.js b/resources/js/functions.js index f5a8663602..a0382e7691 100644 --- a/resources/js/functions.js +++ b/resources/js/functions.js @@ -1,3 +1,4 @@ + // Sliding Panel and scala in a nutshell $(document).ready(function() { $('.navigation-panel-button,.navigation-fade-screen,.navigation-panel-close').on('click touchstart', function(e) { @@ -321,6 +322,7 @@ function DropDown(el) { this.opts = this.dd.find('ul.dropdown > li'); this.val = ''; this.index = -1; + this.href = ''; this.initEvents(); } DropDown.prototype = { @@ -337,6 +339,8 @@ DropDown.prototype = { obj.val = opt.text(); obj.index = opt.index(); obj.placeholder.text(obj.val); + obj.href = opt.find('a').attr("href"); + window.location.href = obj.href; }); }, getValue: function() { From 207980e7866349ed30a671d2a46a035fa77fa257 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Wed, 26 Jul 2017 20:00:10 +0200 Subject: [PATCH 060/112] Some Japanese and Chinese translations --- _config.yml | 3 + _ja/overviews/macros/annotations.md | 5 +- _ja/overviews/macros/blackbox-whitebox.md | 5 +- _ja/overviews/macros/bundles.md | 5 +- _ja/overviews/macros/extractors.md | 7 ++- _ja/overviews/macros/implicits.md | 8 +-- _ja/overviews/macros/inference.md | 2 +- _ja/overviews/macros/overview.md | 5 +- _ja/overviews/macros/paradise.md | 5 +- _ja/overviews/macros/quasiquotes.md | 5 +- _ja/overviews/macros/roadmap.md | 5 +- _ja/overviews/macros/typemacros.md | 2 +- _ja/overviews/macros/typeproviders.md | 5 +- _ja/overviews/macros/untypedmacros.md | 2 +- _ja/overviews/macros/usecases.md | 5 +- .../parallel-collections/architecture.md | 4 +- .../concrete-parallel-collections.md | 24 ++++---- .../parallel-collections/configuration.md | 16 ++--- .../parallel-collections/conversions.md | 6 +- _ja/overviews/parallel-collections/ctries.md | 24 ++++---- .../custom-parallel-collections.md | 32 +++++----- .../parallel-collections/overview.md | 38 ++++++------ .../parallel-collections/performance.md | 30 +++++----- .../reflection/annotations-names-scopes.md | 6 +- .../environment-universes-mirrors.md | 6 +- _ja/overviews/reflection/overview.md | 8 ++- .../reflection/symbols-trees-types.md | 6 +- _ja/overviews/reflection/thread-safety.md | 6 +- .../reflection/typetags-manifests.md | 7 ++- .../overviews/collections/arrays.md | 32 +++++----- .../concrete-immutable-collection-classes.md | 15 ++--- .../concrete-mutable-collection-classes.md | 9 +-- ...ions-between-java-and-scala-collections.md | 11 ++-- .../creating-collections-from-scratch.md | 7 ++- .../overviews/collections/equality.md | 10 ++-- .../overviews/collections/introduction.md | 5 +- .../overviews/collections/iterators.md | 16 ++--- .../overviews/collections/maps.md | 23 ++++---- .../collections/migrating-from-scala-27.md | 7 ++- .../overviews/collections/overview.md | 14 +++-- .../performance-characteristics.md | 5 +- .../overviews/collections/seqs.md | 5 +- .../overviews/collections/sets.md | 15 ++--- .../overviews/collections/strings.md | 6 +- .../overviews/collections/trait-iterable.md | 21 +++---- .../collections/trait-traversable.md | 5 +- .../overviews/collections/views.md | 14 +++-- .../parallel-collections/architecture.md | 4 +- .../concrete-parallel-collections.md | 4 +- .../parallel-collections/configuration.md | 21 +++---- .../parallel-collections/conversions.md | 9 +-- .../overviews/parallel-collections/ctries.md | 27 +++++---- .../custom-parallel-collections.md | 59 ++++++++++--------- .../parallel-collections/overview.md | 48 +++++++-------- .../parallel-collections/performance.md | 41 ++++++------- {zh-cn/overviews => _zh-cn}/thanks.md | 0 56 files changed, 393 insertions(+), 322 deletions(-) rename {zh-cn => _zh-cn}/overviews/collections/arrays.md (81%) rename {zh-cn => _zh-cn}/overviews/collections/concrete-immutable-collection-classes.md (99%) rename {zh-cn => _zh-cn}/overviews/collections/concrete-mutable-collection-classes.md (99%) rename {zh-cn => _zh-cn}/overviews/collections/conversions-between-java-and-scala-collections.md (95%) rename {zh-cn => _zh-cn}/overviews/collections/creating-collections-from-scratch.md (98%) rename {zh-cn => _zh-cn}/overviews/collections/equality.md (89%) rename {zh-cn => _zh-cn}/overviews/collections/introduction.md (98%) rename {zh-cn => _zh-cn}/overviews/collections/iterators.md (99%) rename {zh-cn => _zh-cn}/overviews/collections/maps.md (98%) rename {zh-cn => _zh-cn}/overviews/collections/migrating-from-scala-27.md (97%) rename {zh-cn => _zh-cn}/overviews/collections/overview.md (98%) rename {zh-cn => _zh-cn}/overviews/collections/performance-characteristics.md (98%) rename {zh-cn => _zh-cn}/overviews/collections/seqs.md (99%) rename {zh-cn => _zh-cn}/overviews/collections/sets.md (97%) rename {zh-cn => _zh-cn}/overviews/collections/strings.md (93%) rename {zh-cn => _zh-cn}/overviews/collections/trait-iterable.md (92%) rename {zh-cn => _zh-cn}/overviews/collections/trait-traversable.md (99%) rename {zh-cn => _zh-cn}/overviews/collections/views.md (98%) rename {zh-cn => _zh-cn}/overviews/parallel-collections/architecture.md (98%) rename {zh-cn => _zh-cn}/overviews/parallel-collections/concrete-parallel-collections.md (99%) rename {zh-cn => _zh-cn}/overviews/parallel-collections/configuration.md (97%) rename {zh-cn => _zh-cn}/overviews/parallel-collections/conversions.md (94%) rename {zh-cn => _zh-cn}/overviews/parallel-collections/ctries.md (98%) rename {zh-cn => _zh-cn}/overviews/parallel-collections/custom-parallel-collections.md (98%) rename {zh-cn => _zh-cn}/overviews/parallel-collections/overview.md (98%) rename {zh-cn => _zh-cn}/overviews/parallel-collections/performance.md (98%) rename {zh-cn/overviews => _zh-cn}/thanks.md (100%) diff --git a/_config.yml b/_config.yml index b8093846ce..47948ddce4 100644 --- a/_config.yml +++ b/_config.yml @@ -25,6 +25,9 @@ collections: ja: # Japanese translations output: true permalink: /:collection/:path.html + zh-cn: # Chinese (Simplified) translations + output: true + permalink: /:collection/:path.html scala-version: 2.12.2 diff --git a/_ja/overviews/macros/annotations.md b/_ja/overviews/macros/annotations.md index f6303cc427..e609a667be 100644 --- a/_ja/overviews/macros/annotations.md +++ b/_ja/overviews/macros/annotations.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 9 -outof: 11 title: マクロアノテーション --- diff --git a/_ja/overviews/macros/blackbox-whitebox.md b/_ja/overviews/macros/blackbox-whitebox.md index 05da79201b..02a58155d2 100644 --- a/_ja/overviews/macros/blackbox-whitebox.md +++ b/_ja/overviews/macros/blackbox-whitebox.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 2 -outof: 11 title: blackbox vs whitebox --- diff --git a/_ja/overviews/macros/bundles.md b/_ja/overviews/macros/bundles.md index 9bb11b0c42..9f99498891 100644 --- a/_ja/overviews/macros/bundles.md +++ b/_ja/overviews/macros/bundles.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 4 -outof: 11 title: マクロバンドル --- diff --git a/_ja/overviews/macros/extractors.md b/_ja/overviews/macros/extractors.md index b55912a04b..a20cd50ab4 100644 --- a/_ja/overviews/macros/extractors.md +++ b/_ja/overviews/macros/extractors.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 6 -outof: 11 title: 抽出子マクロ --- @@ -77,7 +78,7 @@ typer とのよどみない会話をお膳立てするには全ての部分が [run/t5903a](https://github.com/scala/scala/tree/00624a39ed84c3fd245dd9df7454d4cec4399e13/test/files/run/t5903a)、 [run/t5903b](https://github.com/scala/scala/tree/00624a39ed84c3fd245dd9df7454d4cec4399e13/test/files/run/t5903b)、 [run/t5903c](https://github.com/scala/scala/tree/00624a39ed84c3fd245dd9df7454d4cec4399e13/test/files/run/t5903c)、 -[run/t5903d](https://github.com/scala/scala/tree/00624a39ed84c3fd245dd9df7454d4cec4399e13/test/files/run/t5903d) +[run/t5903d](https://github.com/scala/scala/tree/00624a39ed84c3fd245dd9df7454d4cec4399e13/test/files/run/t5903d) などのテストケースを参照してほしい。 ## blackbox vs whitebox diff --git a/_ja/overviews/macros/implicits.md b/_ja/overviews/macros/implicits.md index 48ad5ce666..02cc44191c 100644 --- a/_ja/overviews/macros/implicits.md +++ b/_ja/overviews/macros/implicits.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 5 -outof: 11 title: implicit マクロ --- @@ -134,6 +135,3 @@ blackbox な具現化と whitebox な具現化には大きな違いが一つあ 関数従属性の具現化は [whitebox](/ja/overviews/macros/blackbox-whitebox.html) マクロじゃないと動作しないことにも注意。 関数従属性の具現化を [blackbox](/ja/overviews/macros/blackbox-whitebox.html) だと宣言すると正しく動作しない。 - - - diff --git a/_ja/overviews/macros/inference.md b/_ja/overviews/macros/inference.md index d58efde658..af477dff8c 100644 --- a/_ja/overviews/macros/inference.md +++ b/_ja/overviews/macros/inference.md @@ -1,5 +1,5 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false diff --git a/_ja/overviews/macros/overview.md b/_ja/overviews/macros/overview.md index 4fad06e9a5..13657e7d49 100644 --- a/_ja/overviews/macros/overview.md +++ b/_ja/overviews/macros/overview.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 3 -outof: 11 title: def マクロ --- diff --git a/_ja/overviews/macros/paradise.md b/_ja/overviews/macros/paradise.md index 8e2f65d435..47fdd6d83f 100644 --- a/_ja/overviews/macros/paradise.md +++ b/_ja/overviews/macros/paradise.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 10 -outof: 11 title: マクロパラダイス --- diff --git a/_ja/overviews/macros/quasiquotes.md b/_ja/overviews/macros/quasiquotes.md index e7e25238da..80a155847b 100644 --- a/_ja/overviews/macros/quasiquotes.md +++ b/_ja/overviews/macros/quasiquotes.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 8 -outof: 11 title: 準クォート --- diff --git a/_ja/overviews/macros/roadmap.md b/_ja/overviews/macros/roadmap.md index a3c425ccdf..09516fa328 100644 --- a/_ja/overviews/macros/roadmap.md +++ b/_ja/overviews/macros/roadmap.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 11 -outof: 11 title: ロードマップ --- diff --git a/_ja/overviews/macros/typemacros.md b/_ja/overviews/macros/typemacros.md index 2908d39936..e18e5bec49 100644 --- a/_ja/overviews/macros/typemacros.md +++ b/_ja/overviews/macros/typemacros.md @@ -1,5 +1,5 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false diff --git a/_ja/overviews/macros/typeproviders.md b/_ja/overviews/macros/typeproviders.md index 5286dc0de6..86ab0eb156 100644 --- a/_ja/overviews/macros/typeproviders.md +++ b/_ja/overviews/macros/typeproviders.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 7 -outof: 11 title: 型プロバイダ --- diff --git a/_ja/overviews/macros/untypedmacros.md b/_ja/overviews/macros/untypedmacros.md index b364849f8e..d8ae597381 100644 --- a/_ja/overviews/macros/untypedmacros.md +++ b/_ja/overviews/macros/untypedmacros.md @@ -1,5 +1,5 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false diff --git a/_ja/overviews/macros/usecases.md b/_ja/overviews/macros/usecases.md index 1e31306f90..86fefa9277 100644 --- a/_ja/overviews/macros/usecases.md +++ b/_ja/overviews/macros/usecases.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview language: ja discourse: false partof: macros +overview-name: Macros + num: 1 -outof: 11 title: ユースケース --- diff --git a/_ja/overviews/parallel-collections/architecture.md b/_ja/overviews/parallel-collections/architecture.md index def035fa5c..9cce07b915 100644 --- a/_ja/overviews/parallel-collections/architecture.md +++ b/_ja/overviews/parallel-collections/architecture.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 並列コレクションライブラリのアーキテクチャ discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 5 language: ja --- diff --git a/_ja/overviews/parallel-collections/concrete-parallel-collections.md b/_ja/overviews/parallel-collections/concrete-parallel-collections.md index dc485ed399..e7d7f98eca 100644 --- a/_ja/overviews/parallel-collections/concrete-parallel-collections.md +++ b/_ja/overviews/parallel-collections/concrete-parallel-collections.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 具象並列コレクションクラス discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 2 language: ja --- @@ -18,10 +20,10 @@ language: ja scala> val pa = scala.collection.parallel.mutable.ParArray.tabulate(1000)(x => 2 * x + 1) pa: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 3, 5, 7, 9, 11, 13,... - + scala> pa reduce (_ + _) res0: Int = 1000000 - + scala> pa map (x => (x - 1) / 2) res1: scala.collection.parallel.mutable.ParArray[Int] = ParArray(0, 1, 2, 3, 4, 5, 6, 7,... @@ -41,7 +43,7 @@ language: ja scala> val pv = scala.collection.parallel.immutable.ParVector.tabulate(1000)(x => x) pv: scala.collection.parallel.immutable.ParVector[Int] = ParVector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,... - + scala> pv filter (_ % 2 == 0) res0: scala.collection.parallel.immutable.ParVector[Int] = ParVector(0, 2, 4, 6, 8, 10, 12, 14, 16, 18,... @@ -54,13 +56,13 @@ language: ja ## 並列範囲 -並列範囲 ([`ParRange`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/parallel/immutable/ParRange.html)) +並列範囲 ([`ParRange`](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/parallel/immutable/ParRange.html)) は、順序付けされた等間隔の要素の列だ。 並列範囲は、逐次版の [Range](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Range.html) と同様に作成される: scala> 1 to 3 par res0: scala.collection.parallel.immutable.ParRange = ParRange(1, 2, 3) - + scala> 15 to 5 by -2 par res1: scala.collection.parallel.immutable.ParRange = ParRange(15, 13, 11, 9, 7, 5) @@ -72,13 +74,13 @@ language: ja 並列ハッシュテーブルは要素を内部の配列に格納し、各要素のハッシュコードにより格納する位置を決定する。 並列可変ハッシュ集合 ( -[mutable.ParHashSet](http://www.scala-lang.org/api/{{ site.scala-version}}/scala/collection/parallel/mutable/ParHashSet.html)) +[mutable.ParHashSet](http://www.scala-lang.org/api/{{ site.scala-version}}/scala/collection/parallel/mutable/ParHashSet.html)) と並列可変ハッシュマップ ([mutable.ParHashMap](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/parallel/mutable/ParHashMap.html)) はハッシュテーブルに基づいている。 scala> val phs = scala.collection.parallel.mutable.ParHashSet(1 until 2000: _*) phs: scala.collection.parallel.mutable.ParHashSet[Int] = ParHashSet(18, 327, 736, 1045, 773, 1082,... - + scala> phs map (x => x * x) res0: scala.collection.parallel.mutable.ParHashSet[Int] = ParHashSet(2181529, 2446096, 99225, 2585664,... @@ -103,7 +105,7 @@ language: ja scala> val phs = scala.collection.parallel.immutable.ParHashSet(1 until 1000: _*) phs: scala.collection.parallel.immutable.ParHashSet[Int] = ParSet(645, 892, 69, 809, 629, 365, 138, 760, 101, 479,... - + scala> phs map { x => x * x } sum res0: Int = 332833500 @@ -122,12 +124,12 @@ language: ja scala> val numbers = scala.collection.parallel.mutable.ParTrieMap((1 until 100) zip (1 until 100): _*) map { case (k, v) => (k.toDouble, v.toDouble) } numbers: scala.collection.parallel.mutable.ParTrieMap[Double,Double] = ParTrieMap(0.0 -> 0.0, 42.0 -> 42.0, 70.0 -> 70.0, 2.0 -> 2.0,... - + scala> while (numbers.nonEmpty) { | numbers foreach { case (num, sqrt) => | val nsqrt = 0.5 * (sqrt + num / sqrt) | numbers(num) = nsqrt - | if (math.abs(nsqrt - sqrt) < 0.01) { + | if (math.abs(nsqrt - sqrt) < 0.01) { | println(num, nsqrt) | numbers.remove(num) | } diff --git a/_ja/overviews/parallel-collections/configuration.md b/_ja/overviews/parallel-collections/configuration.md index 497933b42c..abc16c8854 100644 --- a/_ja/overviews/parallel-collections/configuration.md +++ b/_ja/overviews/parallel-collections/configuration.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 並列コレクションの設定 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 7 language: ja --- @@ -26,13 +28,13 @@ JVM 1.5 とその他のフォーク/ジョインプールをサポートしな scala> import scala.collection.parallel._ import scala.collection.parallel._ - + scala> val pc = mutable.ParArray(1, 2, 3) pc: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 2, 3) - + scala> pc.tasksupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(2)) pc.tasksupport: scala.collection.parallel.TaskSupport = scala.collection.parallel.ForkJoinTaskSupport@4a5d484a - + scala> pc map { _ + 1 } res0: scala.collection.parallel.mutable.ParArray[Int] = ParArray(2, 3, 4) @@ -41,7 +43,7 @@ JVM 1.5 とその他のフォーク/ジョインプールをサポートしな scala> pc.tasksupport = new ThreadPoolTaskSupport() pc.tasksupport: scala.collection.parallel.TaskSupport = scala.collection.parallel.ThreadPoolTaskSupport@1d914a39 - + scala> pc map { _ + 1 } res1: scala.collection.parallel.mutable.ParArray[Int] = ParArray(2, 3, 4) @@ -51,9 +53,9 @@ JVM 1.5 とその他のフォーク/ジョインプールをサポートしな カスタムのタスクサポートを実装するには、`TaskSupport` トレイトを拡張して以下のメソッドを実装する: def execute[R, Tp](task: Task[R, Tp]): () => R - + def executeAndWaitResult[R, Tp](task: Task[R, Tp]): R - + def parallelismLevel: Int `execute` メソッドはタスクを非同期的にスケジューリングし、計算の結果をフューチャー値として返す。 diff --git a/_ja/overviews/parallel-collections/conversions.md b/_ja/overviews/parallel-collections/conversions.md index 0b2c219e91..73345f4fd9 100644 --- a/_ja/overviews/parallel-collections/conversions.md +++ b/_ja/overviews/parallel-collections/conversions.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 並列コレクションへの変換 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 3 language: ja --- @@ -35,7 +37,7 @@ language: ja 全ての並列コレクションは、`seq` メソッドを用いて何らかの順次コレクションに変換できる。 並列コレクションから順次コレクションへの変換は常に効率的で、定数時間で実行される。 並列可変コレクションに対して `seq` を呼び出すと、同じ内部構造にデータを格納した順次コレクションを返す。 -そのため、コレクションの変更は、他方にも見えることになる。 +そのため、コレクションの変更は、他方にも見えることになる。 ## 異なるコレクション型の間での変換 diff --git a/_ja/overviews/parallel-collections/ctries.md b/_ja/overviews/parallel-collections/ctries.md index c41cf186df..f8144460f1 100644 --- a/_ja/overviews/parallel-collections/ctries.md +++ b/_ja/overviews/parallel-collections/ctries.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 並行トライ discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 4 language: ja --- @@ -26,14 +28,14 @@ language: ja case class Entry(num: Double) { var sqrt = num } - + val length = 50000 - + // リストを準備する val entries = (1 until length) map { num => Entry(num.toDouble) } val results = ParTrieMap() for (e <- entries) results += ((e.num, e)) - + // 平方根を計算する while (results.nonEmpty) { for ((num, e) <- results) { @@ -58,33 +60,33 @@ language: ja 完了したら、対象ノードから初期ノードまでの経路を表示する。 val length = 1000 - + // Node 型を定義する type Node = (Int, Int); type Parent = (Int, Int); - + // Node 型の演算 def up(n: Node) = (n._1, n._2 - 1); def down(n: Node) = (n._1, n._2 + 1); def left(n: Node) = (n._1 - 1, n._2); def right(n: Node) = (n._1 + 1, n._2); - + // map と target を作る val target = (length / 2, length / 2); val map = Array.tabulate(length, length)((x, y) => (x % 3) != 0 || (y % 3) != 0 || (x, y) == target) def onMap(n: Node) = n._1 >= 0 && n._1 < length && n._2 >= 0 && n._2 < length - + // open マップ - ノード前線 // closed マップ - 滞在済みのノード val open = ParTrieMap[Node, Parent]() val closed = ParTrieMap[Node, Parent]() - + // 初期位置をいくつか追加する open((0, 0)) = null open((length - 1, length - 1)) = null open((0, length - 1)) = null open((length - 1, 0)) = null - + // 貪欲法による幅優先探索 while (open.nonEmpty && !open.contains(target)) { for ((node, parent) <- open) { @@ -101,7 +103,7 @@ language: ja open.remove(node) } } - + // 経路の表示 var pathnode = open(target) while (closed.contains(pathnode)) { diff --git a/_ja/overviews/parallel-collections/custom-parallel-collections.md b/_ja/overviews/parallel-collections/custom-parallel-collections.md index 1e3b0545c3..e02cae26b9 100644 --- a/_ja/overviews/parallel-collections/custom-parallel-collections.md +++ b/_ja/overviews/parallel-collections/custom-parallel-collections.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: カスタム並列コレクションの作成 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 6 language: ja --- @@ -24,7 +26,7 @@ language: ja 次に、全ての不変列にあるメソッドを定義する: def apply(i: Int) = str.charAt(i) - + def length = str.length この並列コレクションの直列版も定義しなければならない。 @@ -36,12 +38,12 @@ language: ja このスプリッタは `ParStringSplitter` と名づけ、列スプリッタの `SeqSplitter[Char]` を継承することにする: def splitter = new ParStringSplitter(str, 0, str.length) - + class ParStringSplitter(private var s: String, private var i: Int, private val ntl: Int) extends SeqSplitter[Char] { final def hasNext = i < ntl - + final def next = { val r = s.charAt(i) i += 1 @@ -55,7 +57,7 @@ language: ja 次に、現在のスプリッタを複製する `dup` というメソッドがある。 def remaining = ntl - i - + def dup = new ParStringSplitter(s, i, ntl) 最後に、現在のスプリッタの要素の部分集合を走査する別のスプリッタを作成するのに使われる `split` と `psplit` メソッドがある。 @@ -70,7 +72,7 @@ language: ja if (rem >= 2) psplit(rem / 2, rem - rem / 2) else Seq(this) } - + def psplit(sizes: Int*): Seq[ParStringSplitter] = { val splitted = new ArrayBuffer[ParStringSplitter] for (sz <- sizes) { @@ -124,28 +126,28 @@ language: ja var sz = 0 val chunks = new ArrayBuffer[StringBuilder] += new StringBuilder var lastc = chunks.last - + def size: Int = sz - + def +=(elem: Char): this.type = { lastc += elem sz += 1 this } - + def clear = { chunks.clear chunks += new StringBuilder lastc = chunks.last sz = 0 } - + def result: ParString = { val rsb = new StringBuilder for (sb <- chunks) rsb.append(sb) new ParString(rsb.toString) } - + def combine[U <: Char, NewTo >: ParString](other: Combiner[U, NewTo]) = if (other eq this) this else { val that = other.asInstanceOf[ParStringCombiner] sz += that.sz @@ -190,7 +192,7 @@ language: ja extends immutable.ParSeq[Char] with GenericParTemplate[Char, ParString] with ParSeqLike[Char, ParString, collection.immutable.WrappedString] { - + def companion = ParString コンパニオンオブジェクトの中で `CanBuildFrom` パラメータのための暗黙の値を提供する。 @@ -201,11 +203,11 @@ language: ja def apply(from: ParString) = newCombiner def apply() = newCombiner } - + def newBuilder: Combiner[Char, ParString] = newCombiner - + def newCombiner: Combiner[Char, ParString] = new ParStringCombiner - + def apply(elems: Char*): ParString = { val cb = newCombiner cb ++= elems diff --git a/_ja/overviews/parallel-collections/overview.md b/_ja/overviews/parallel-collections/overview.md index 56e81a6bbd..d4360d200c 100644 --- a/_ja/overviews/parallel-collections/overview.md +++ b/_ja/overviews/parallel-collections/overview.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 概要 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 1 language: ja --- @@ -28,7 +30,7 @@ language: ja val list = (1 to 10000).toList list.map(_ + 42) - + 同じ演算を並列に実行するには、単に順次コレクションである `list` に対して `par` メソッドを呼び出すだけでいい。後は、順次コレクションを普通に使うのと同じように並列コレクションを利用できる。上記の例は以下のように並列化できる: list.par.map(_ + 42) @@ -61,7 +63,7 @@ Scala の並列コレクションライブラリは、順次ライブラリコ scala> val lastNames = List("Smith","Jones","Frankenstein","Bach","Jackson","Rodin").par lastNames: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Frankenstein, Bach, Jackson, Rodin) - + scala> lastNames.map(_.toUpperCase) res0: scala.collection.parallel.immutable.ParSeq[String] = ParVector(SMITH, JONES, FRANKENSTEIN, BACH, JACKSON, RODIN) @@ -71,7 +73,7 @@ Scala の並列コレクションライブラリは、順次ライブラリコ scala> val parArray = (1 to 10000).toArray.par parArray: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 2, 3, ... - + scala> parArray.fold(0)(_ + _) res0: Int = 50005000 @@ -81,7 +83,7 @@ Scala の並列コレクションライブラリは、順次ライブラリコ scala> val lastNames = List("Smith","Jones","Frankenstein","Bach","Jackson","Rodin").par lastNames: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Frankenstein, Bach, Jackson, Rodin) - + scala> lastNames.filter(_.head >= 'J') res0: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Jackson, Rodin) @@ -107,22 +109,22 @@ Scala の並列コレクションライブラリは、順次ライブラリコ scala> val list = (1 to 1000).toList.par list: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3,… - + scala> list.foreach(sum += _); sum res01: Int = 467766 - + scala> var sum = 0 sum: Int = 0 - + scala> list.foreach(sum += _); sum res02: Int = 457073 - + scala> var sum = 0 sum: Int = 0 - + scala> list.foreach(sum += _); sum res03: Int = 468520 - + ここでは、`sum` が 0 に初期化されて、`list` に対して `foreach` が呼び出されるたびに `sum` が異なる値を持っていることが分かる。この非決定性の原因は**データ競合** (data race; 同一の可変変数に対する並行した読み書き) だ。 上の例だと、二つのスレッドが `sum` の**同じ**値を読み込んで、その `sum` の値に対して何らかの演算を実行した後で、`sum` に新しい値を書きこもうとするかもしれない。以下に示すように、その場合は、大切な結果の上書き(つまり、損失)が起きる可能性がある: @@ -140,16 +142,16 @@ Scala の並列コレクションライブラリは、順次ライブラリコ scala> val list = (1 to 1000).toList.par list: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3,… - + scala> list.reduce(_-_) res01: Int = -228888 - + scala> list.reduce(_-_) res02: Int = -61000 - + scala> list.reduce(_-_) res03: Int = -331818 - + 上の例では、`ParVector[Int]` の `reduce` メソッドが `_-_` と共に呼び出されている。 これは二つの不特定の要素を取り出し、前者から後者を引く。 並列コレクションフレームワークはスレッドを呼び出し、それぞれが実質的に、独自にコレクションから異なる部位を取り出し `reduce(_-_)` を実行するため、同じコレクションに `reduce(_-_)` を実行するたびに毎回異なった結果が得られることとなる。 @@ -157,11 +159,11 @@ Scala の並列コレクションライブラリは、順次ライブラリコ **注意:** 結合則が成立しない演算と同様に、交換則が成立しない演算も並列コレクションの高階関数に渡されると非決定的な振る舞いをみせると思われがちだが、それは間違っている。単純な例としては、文字列の連結がある。結合則は成立するが、交換則は成立しない演算だ: scala> val strings = List("abc","def","ghi","jk","lmnop","qrs","tuv","wx","yz").par - strings: scala.collection.parallel.immutable.ParSeq[java.lang.String] = ParVector(abc, def, ghi, jk, lmnop, qrs, tuv, wx, yz) - + strings: scala.collection.parallel.immutable.ParSeq[java.lang.String] = ParVector(abc, def, ghi, jk, lmnop, qrs, tuv, wx, yz) + scala> val alphabet = strings.reduce(_++_) alphabet: java.lang.String = abcdefghijklmnopqrstuvwxyz 並列コレクションにおける**「アウト・オブ・オーダー」**の意味論は、演算が(**時間的**な意味で、つまり非逐次的に)バラバラの順序で実行されるという意味であって、結果が(**空間的**に)バラバラに**「再合成」**されるという意味ではない。結果は、一般的に**順序どおり** (in-order) に再合成される。つまり、A、B、C の順番に分割された並列コレクションは、再び A、B、C の順番に再合成される。任意の B、C、A というような順序にはならない。 -異なる並列コレクションの型における、分割と再合成の詳細ついてはこのガイドの[アーキテクチャ](architecture.html)の節を参照してほしい。 +異なる並列コレクションの型における、分割と再合成の詳細ついてはこのガイドの[アーキテクチャ](architecture.html)の節を参照してほしい。 diff --git a/_ja/overviews/parallel-collections/performance.md b/_ja/overviews/parallel-collections/performance.md index baa2fa2fe5..9299680834 100644 --- a/_ja/overviews/parallel-collections/performance.md +++ b/_ja/overviews/parallel-collections/performance.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 性能の測定 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 8 outof: 8 language: ja @@ -54,14 +56,14 @@ Scala の標準ライブラリには `scala.testing.Benchmark` トレイトが import collection.parallel.mutable.ParTrieMap import collection.parallel.ForkJoinTaskSupport - + object Map extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val partrie = ParTrieMap((0 until length) zip (0 until length): _*) - + partrie.tasksupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { partrie map { kv => kv @@ -125,25 +127,25 @@ Scala の標準ライブラリには `scala.testing.Benchmark` トレイトが サイズがいくつであるかを大まかに示すために、以下に安価で副作用を伴わないベクトルの集約演算(この場合、sum)をクアッドコアの i7(ハイパースレッディング無し)で JDK7 上で実行した具体例を示す: import collection.parallel.immutable.ParVector - + object Reduce extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val parvector = ParVector((0 until length): _*) - + parvector.tasksupport = new collection.parallel.ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { parvector reduce { (a, b) => a + b } } } - + object ReduceSeq extends testing.Benchmark { val length = sys.props("length").toInt val vector = collection.immutable.Vector((0 until length): _*) - + def run = { vector reduce { (a, b) => a + b @@ -172,25 +174,25 @@ Scala の標準ライブラリには `scala.testing.Benchmark` トレイトが もう一つの具体例として、`mutable.ParHashMap` と(変換メソッドである)`map` メソッドに注目して同じ環境で以下のベンチマークを実行する: import collection.parallel.mutable.ParHashMap - + object Map extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val phm = ParHashMap((0 until length) zip (0 until length): _*) - + phm.tasksupport = new collection.parallel.ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { phm map { kv => kv } } } - + object MapSeq extends testing.Benchmark { val length = sys.props("length").toInt val hm = collection.mutable.HashMap((0 until length) zip (0 until length): _*) - + def run = { hm map { kv => kv diff --git a/_ja/overviews/reflection/annotations-names-scopes.md b/_ja/overviews/reflection/annotations-names-scopes.md index 94062de6fc..4a951d80ca 100644 --- a/_ja/overviews/reflection/annotations-names-scopes.md +++ b/_ja/overviews/reflection/annotations-names-scopes.md @@ -1,11 +1,13 @@ --- -layout: overview +layout: multipage-overview discourse: false partof: reflection +overview-name: Reflection + num: 4 -outof: 6 + language: ja title: アノテーション、名前、スコープ、その他 --- diff --git a/_ja/overviews/reflection/environment-universes-mirrors.md b/_ja/overviews/reflection/environment-universes-mirrors.md index 812dbeb8c0..68c68edc65 100644 --- a/_ja/overviews/reflection/environment-universes-mirrors.md +++ b/_ja/overviews/reflection/environment-universes-mirrors.md @@ -1,11 +1,13 @@ --- -layout: overview +layout: multipage-overview discourse: false partof: reflection +overview-name: Reflection + num: 2 -outof: 6 + language: ja title: 環境、ユニバース、ミラー --- diff --git a/_ja/overviews/reflection/overview.md b/_ja/overviews/reflection/overview.md index ae9dce9fcd..91a34ea6aa 100644 --- a/_ja/overviews/reflection/overview.md +++ b/_ja/overviews/reflection/overview.md @@ -1,9 +1,11 @@ --- -layout: overview +layout: multipage-overview partof: reflection +overview-name: Reflection + num: 1 -outof: 6 + language: ja title: 概要 @@ -298,4 +300,4 @@ API に基づいていることだ。これにより、マクロと実行時リ 詳細はこのガイドの[ミラー]({{ site.baseurl}}/ja/overviews/reflection/environment-universes-mirrors.html)の節か、 `scala.reflect.api` パッケージの[ミラーの API doc](http://www.scala-lang.org/api/{{ site.scala-version}}/scala/reflect/api/Mirrors.html) -を参考にしてほしい。 \ No newline at end of file +を参考にしてほしい。 diff --git a/_ja/overviews/reflection/symbols-trees-types.md b/_ja/overviews/reflection/symbols-trees-types.md index 044085848d..4d92411afc 100644 --- a/_ja/overviews/reflection/symbols-trees-types.md +++ b/_ja/overviews/reflection/symbols-trees-types.md @@ -1,11 +1,13 @@ --- -layout: overview +layout: multipage-overview discourse: false partof: reflection +overview-name: Reflection + num: 3 -outof: 6 + language: ja title: シンボル、構文木、型 --- diff --git a/_ja/overviews/reflection/thread-safety.md b/_ja/overviews/reflection/thread-safety.md index 75943380c5..d67254ae7a 100644 --- a/_ja/overviews/reflection/thread-safety.md +++ b/_ja/overviews/reflection/thread-safety.md @@ -1,11 +1,13 @@ --- -layout: overview +layout: multipage-overview discourse: false partof: reflection +overview-name: Reflection + num: 6 -outof: 6 + language: ja title: スレッドセーフティ --- diff --git a/_ja/overviews/reflection/typetags-manifests.md b/_ja/overviews/reflection/typetags-manifests.md index 47ac8dd259..c4eea9aee9 100644 --- a/_ja/overviews/reflection/typetags-manifests.md +++ b/_ja/overviews/reflection/typetags-manifests.md @@ -1,11 +1,12 @@ --- -layout: overview +layout: multipage-overview discourse: false partof: reflection +overview-name: Reflection + num: 5 -outof: 6 language: ja title: 型タグとマニフェスト @@ -142,7 +143,7 @@ context bound `[T: TypeTag]` からコンパイラは `TypeTag[T]` ある型がプリミティブ値クラスであるかを調べるには、型タグを (基底タグのコンパニオンオブジェクトで定義されている) 基底タグと比較することができる。もしくは、 <tag>.tpe.typeSymbol.isPrimitiveValueClass を使うこともできる。 -
  • マニフェストのコンパニオンオブジェクトに定義されるファクトリ・メソッドに相当するものは無い。 +
  • マニフェストのコンパニオンオブジェクトに定義されるファクトリ・メソッドに相当するものは無い。 代わりに、(クラスの場合は) Java か (型の場合は) Scala によって提供されるリフレクション API を使って対応する型を生成することができる。
  • いくつかのマニフェスト演算 (具体的には、<:<>:>、と typeArguments) はサポートされない。 diff --git a/zh-cn/overviews/collections/arrays.md b/_zh-cn/overviews/collections/arrays.md similarity index 81% rename from zh-cn/overviews/collections/arrays.md rename to _zh-cn/overviews/collections/arrays.md index 6fe0417638..348f8d14c8 100644 --- a/zh-cn/overviews/collections/arrays.md +++ b/_zh-cn/overviews/collections/arrays.md @@ -1,15 +1,17 @@ --- -layout: overview +layout: multipage-overview title: 数组 discourse: false partof: collections +overview-name: Collections + num: 10 language: zh-cn --- -在Scala中,[数组](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/Array.html)是一种特殊的collection。一方面,Scala数组与Java数组是一一对应的。即Scala数组Array[Int]可看作Java的Int[],Array[Double]可看作Java的double[],以及Array[String]可看作Java的String[]。但Scala数组比Java数组提供了更多内容。首先,Scala数组是一种泛型。即可以定义一个Array[T],T可以是一种类型参数或抽象类型。其次,Scala数组与Scala序列是兼容的 - 在需要Seq[T]的地方可由Array[T]代替。最后,Scala数组支持所有的序列操作。这里有个实际的例子: +在Scala中,[数组](http://www.scala-lang.org/api/2.10.0/scala/Array.html)是一种特殊的collection。一方面,Scala数组与Java数组是一一对应的。即Scala数组Array[Int]可看作Java的Int[],Array[Double]可看作Java的double[],以及Array[String]可看作Java的String[]。但Scala数组比Java数组提供了更多内容。首先,Scala数组是一种泛型。即可以定义一个Array[T],T可以是一种类型参数或抽象类型。其次,Scala数组与Scala序列是兼容的 - 在需要Seq[T]的地方可由Array[T]代替。最后,Scala数组支持所有的序列操作。这里有个实际的例子: scala> val a1 = Array(1, 2, 3) a1: Array[Int] = Array(1, 2, 3) @@ -58,7 +60,7 @@ ArrayOps的对象会通过隐式转换自动的插入,因此上述的代码等 scala> intArrayOps(a1).reverse res5: Array[Int] = Array(3, 2, 1) -这里的intArrayOps就是之前例子中插入的隐式转换。这里引出一个疑问,上面代码中,编译器为何选择了intArrayOps而不是WrappedArray做隐式转换?毕竟,两种转换都是将数组映射到支持reverse方法的类型,并且指定输入。答案是两种转换是有优先级次序的,ArrayOps转换比WrappedArray有更高的优先级。前者定义在Predef对象中,而后者定义在Predef的基类 `scala.LowPriorityImplicits` 中。子类、子对象中隐式转换的优先级高于基类。所以如果两种转换都可用,Predef中的会优先选取。字符串的情况也是如此。 +这里的intArrayOps就是之前例子中插入的隐式转换。这里引出一个疑问,上面代码中,编译器为何选择了intArrayOps而不是WrappedArray做隐式转换?毕竟,两种转换都是将数组映射到支持reverse方法的类型,并且指定输入。答案是两种转换是有优先级次序的,ArrayOps转换比WrappedArray有更高的优先级。前者定义在Predef对象中,而后者定义在继承自Predef的`scala.LowPriorityImplicits`类中。子类、子对象中隐式转换的优先级低于基类。所以如果两种转换都可用,Predef中的会优先选取。字符串的情况也是如此。 数组与序列兼容,并支持所有序列操作的方法,你现在应该已经了然于胸。那泛型呢?在Java中你不可以定义一个以T为类型参数的`T[]`。那么Scala的`Array[T]`是如何做的呢?事实上一个像`Array[T] `的泛型数组在运行时态可任意为Java的八个原始数组类型像`byte[]`, `short[]`, `char[]`, `int[]`, `long[]`, `float[]`, `double[]`, `boolean[]`,甚至它可以是一个对象数组。最常见的运行时态类型是AnyRef ,它包括了所有的这些类型(相当于java.lang.Object),因此这样的类型可以通过Scala编译器映射到`Array[T]`.在运行时,当`Array[T]`类型的数组元素被访问或更新时,就会有一个序列的类型测试用于确定真正的数组类型,随后就是java中的正确的数组操作。这些类型测试会影响数组操作的效率。这意味着如果你需要更大的性能,你应该更喜欢具体而明确的泛型数组。代表通用的泛型数组是不够的,因此,也必然有一种方式去创造泛型数组。这是一个更难的问题,需要一点点的帮助你。为了说明这个问题,考虑下面用一个通用的方法去创造数组的尝试。 @@ -69,30 +71,29 @@ ArrayOps的对象会通过隐式转换自动的插入,因此上述的代码等 arr(i / 2) = xs(i) arr } - + evenElems方法返回一个新数组,该数组包含了参数向量xs的所有元素甚至在向量中的位置。evenElems 主体的第一行构建了结果数组,将相同元素类型作为参数。所以根据T的实际类型参数,这可能是一个`Array[Int]`,或者是一个`Array[Boolean]`,或者是一个在java中有一些其他基本类型的数组,或者是一个有引用类型的数组。但是这些类型有不同的运行时表达,那么Scala如何在运行时选择正确的呢?事实上,它不是基于信息传递做的,因为与类型参数T相对应的实际类型在运行时已被抹去。这就是为什么你在编译上面的代码时会出现如下的错误信息: error: cannot find class manifest for element type T val arr = new Array[T]((arr.length + 1) / 2) ^ -这里需要你做的就是通过提供一些运行时的实际元素类型参数的线索来帮助编译器处理。这个运行时的提示采取的形式是一个`scala.reflect.ClassTag`类型的类声明。一个类声明就是一个类型描述对象,给对象描述了一个类型的顶层类。另外,类声明也有`scala.reflect.Manifest`类型的所有声明,它描述了类型的各个方面。但对于数组创建而言,只需要提供类声明。 +这里需要你做的就是通过提供一些运行时的实际元素类型参数的线索来帮助编译器处理。这个运行时的提示采取的形式是一个`scala.reflect.ClassManifest`类型的类声明。一个类声明就是一个类型描述对象,给对象描述了一个类型的顶层类。另外,类声明也有`scala.reflect.Manifest`类型的所有声明,它描述了类型的各个方面。但对于数组创建而言,只需要提供类声明。 如果你指示编译器那么做它就会自动的构建类声明。“指示”意味着你决定一个类声明作为隐式参数,像这样: - def evenElems[T](xs: Vector[T])(implicit m: ClassTag[T]): Array[T] = ... - + def evenElems[T](xs: Vector[T])(implicit m: ClassManifest[T]): Array[T] = ... + 使用一个替换和较短的语法。通过用一个上下文绑定你也可以要求类型与一个类声明一起。这种方式是跟在一个冒号类型和类名为ClassManifest的后面,想这样: - import scala.reflect.ClassTag // this works - def evenElems[T: ClassTag](xs: Vector[T]): Array[T] = { + def evenElems[T: ClassManifest](xs: Vector[T]): Array[T] = { val arr = new Array[T]((xs.length + 1) / 2) for (i <- 0 until xs.length by 2) arr(i / 2) = xs(i) arr } - + 这两个evenElems的修订版本意思是完全相同的。当Array[T] 构造时,在任何情况下会发生的是,编译器会寻找类型参数T的一个类声明,这就是说,它会寻找ClassManifest[T]一个隐式类型的值。如果如此的一个值被发现,声明会用来构造正确的数组类型。否则,你就会看到一个错误信息像上面一样。 下面是一些使用evenElems 方法的REPL 交互。 @@ -105,16 +106,15 @@ evenElems方法返回一个新数组,该数组包含了参数向量xs的所有 在这两种情况下,Scala编译器自动的为元素类型构建一个类声明(首先,Int,然后String)并且通过它传递evenElems 方法的隐式参数。编译器可以对所有的具体类型构造,但如果论点本身是另一个没有类声明的类型参数就不可以。例如,下面的错误: scala> def wrap[U](xs: Vector[U]) = evenElems(xs) - :6: error: No ClassTag available for U. + :6: error: No ClassManifest available for U. def wrap[U](xs: Vector[U]) = evenElems(xs) ^ 这里所发生的是,evenElems 需要一个类型参数U的类声明,但是没有发现。这种情况下的解决方案是,当然,是为了U的另一个隐式类声明。所以下面起作用了: - scala> def wrap[U: ClassTag](xs: Vector[U]) = evenElems(xs) - wrap: [U](xs: Vector[U])(implicit evidence$1: scala.reflect.ClassTag[U])Array[U] - -这个实例还显示在定义U的上下文绑定里这仅是一个简短的隐式参数命名为`ClassTag[U]`类型的`evidence$1`。 + scala> def wrap[U: ClassManifest](xs: Vector[U]) = evenElems(xs) + wrap: [U](xs: Vector[U])(implicit evidence$1: ClassManifest[U])Array[U] -总结,泛型数组创建需要类声明。所以每当创建一个类型参数T的数组,你还需要提供一个T的隐式类声明。最简单的方法是声明类型参数与ClassManifest的上下文绑定,如 `[T: ClassTag]`。 +这个实例还显示在定义U的上下文绑定里这仅是一个简短的隐式参数命名为`ClassManifest[U]`类型的`evidence$1`。 +总结,泛型数组创建需要类声明。所以每当创建一个类型参数T的数组,你还需要提供一个T的隐式类声明。最简单的方法是声明类型参数与ClassManifest的上下文绑定,如 `[T: ClassManifest]`。 diff --git a/zh-cn/overviews/collections/concrete-immutable-collection-classes.md b/_zh-cn/overviews/collections/concrete-immutable-collection-classes.md similarity index 99% rename from zh-cn/overviews/collections/concrete-immutable-collection-classes.md rename to _zh-cn/overviews/collections/concrete-immutable-collection-classes.md index 52ab4f2fe6..f7ae54da96 100644 --- a/zh-cn/overviews/collections/concrete-immutable-collection-classes.md +++ b/_zh-cn/overviews/collections/concrete-immutable-collection-classes.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 具体的不可变集实体类 discourse: false partof: collections +overview-name: Collections + num: 8 language: zh-cn --- @@ -28,7 +30,7 @@ List通常被认为是Scala中最重要的数据结构,所以我们在此不 scala> val str = 1 #:: 2 #:: 3 #:: Stream.empty str: scala.collection.immutable.Stream[Int] = Stream(1, ?) - + 该stream的头结点是1,尾是2和3.尾部并没有被打印出来,因为还没有被计算。stream被特别定义为懒惰计算,并且stream的toString方法很谨慎的设计为不去做任何额外的计算。 下面给出一个稍复杂些的例子。这里讲一个以两个给定的数字为起始的斐波那契数列转换成stream。斐波那契数列的定义是,序列中的每个元素等于序列中在它之前的两个元素之和。 @@ -99,7 +101,7 @@ Vector结构通常被表示成具有高分支因子的树(树或者图的分 ## Immutable Queues(不可变队列) -[Queue](http://www.scala-lang.org/api/2.10.0/scala/collection/immutable/Queue.html)是一种与stack很相似的数据结构,除了与stack的后入先出不同,Queue结构的是先入先出的。 +[Queue](http://www.scala-lang.org/api/2.10.0/scala/collection/immutable/Queue.html)是一种与stack很相似的数据结构,除了与stack的后入先出不同,Queue结构的是先入先出的。 这里给出一个创建空不可变queue的例子: @@ -116,7 +118,7 @@ Vector结构通常被表示成具有高分支因子的树(树或者图的分 scala> val has123 = has1.enqueue(List(2, 3)) has123: scala.collection.immutable.Queue[Int] = Queue(1, 2, 3) - + 如果想要从queue的头部删除一个元素,您可以使用dequeue方法: scala> val (element, has23) = has123.dequeue @@ -138,7 +140,7 @@ Vector结构通常被表示成具有高分支因子的树(树或者图的分 scala> 1 until 3 res2: scala.collection.immutable.Range = Range(1, 2) - + Range类的空间复杂度是恒定的,因为只需要三个数字就可以定义一个Range类:起始、结束和步长值。也正是因为有这样的特性,对Range类多数操作都非常非常的快。 ## Hash Tries @@ -182,8 +184,7 @@ BitSet操作的运行时间是非常快的。查找测试仅仅需要固定时 [ListMap](http://www.scala-lang.org/api/2.10.0/scala/collection/immutable/ListMap.html)被用来表示一个保存键-值映射的链表。一般情况下,ListMap操作都需要遍历整个列表,所以操作的运行时间也同列表长度成线性关系。实际上ListMap在Scala中很少使用,因为标准的不可变映射通常速度会更快。唯一的例外是,在构造映射时由于某种原因,链表中靠前的元素被访问的频率大大高于其他的元素。 scala> val map = scala.collection.immutable.ListMap(1->"one", 2->"two") - map: scala.collection.immutable.ListMap[Int,java.lang.String] = + map: scala.collection.immutable.ListMap[Int,java.lang.String] = Map(1 -> one, 2 -> two) scala> map(2) res30: String = "two" - diff --git a/zh-cn/overviews/collections/concrete-mutable-collection-classes.md b/_zh-cn/overviews/collections/concrete-mutable-collection-classes.md similarity index 99% rename from zh-cn/overviews/collections/concrete-mutable-collection-classes.md rename to _zh-cn/overviews/collections/concrete-mutable-collection-classes.md index 3c7216f0bb..5e0b578dcf 100644 --- a/zh-cn/overviews/collections/concrete-mutable-collection-classes.md +++ b/_zh-cn/overviews/collections/concrete-mutable-collection-classes.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 具体的可变容器类 discourse: false partof: collections +overview-name: Collections + num: 9 language: zh-cn --- @@ -24,7 +26,7 @@ language: zh-cn res33: buf.type = ArrayBuffer(1, 10) scala> buf.toArray res34: Array[Int] = Array(1, 10) - + ## List Buffers [ListBuffer](http://www.scala-lang.org/api/2.10.0/scala/collection/mutable/ListBuffer.html) 类似于数组缓冲。区别在于前者内部实现是链表, 而非数组。如果你想把构造完的缓冲转换为列表,那就用列表缓冲,别用数组缓冲。 @@ -43,7 +45,7 @@ language: zh-cn 数组缓冲用来构建数组,列表缓冲用来创建列表。类似地,[StringBuilder](http://www.scala-lang.org/api/2.10.0/scala/collection/mutable/StringBuilder.html) 用来构造字符串。作为常用的类,字符串构造器已导入到默认的命名空间。直接用 new StringBuilder就可创建字符串构造器 ,像这样: scala> val buf = new StringBuilder - buf: StringBuilder = + buf: StringBuilder = scala> buf += 'a' res38: buf.type = a scala> buf ++= "bcdef" @@ -167,4 +169,3 @@ Concurrent Map可以同时被多个线程访问。除了[Map](http://www.scala-l res50: bits.type = BitSet(1, 3) scala> bits res51: scala.collection.mutable.BitSet = BitSet(1, 3) - diff --git a/zh-cn/overviews/collections/conversions-between-java-and-scala-collections.md b/_zh-cn/overviews/collections/conversions-between-java-and-scala-collections.md similarity index 95% rename from zh-cn/overviews/collections/conversions-between-java-and-scala-collections.md rename to _zh-cn/overviews/collections/conversions-between-java-and-scala-collections.md index d2141263f6..af302dd49f 100644 --- a/zh-cn/overviews/collections/conversions-between-java-and-scala-collections.md +++ b/_zh-cn/overviews/collections/conversions-between-java-and-scala-collections.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Java和Scala容器的转换 discourse: false partof: collections +overview-name: Collections + num: 17 language: zh-cn --- @@ -43,10 +45,10 @@ import之后,就可以在Scala容器和与之对应的Java容器之间进行 还有一些Scala容器类型可以转换成对应的Java类型,但是并没有将相应的Java类型转换成Scala类型的能力,它们是: - Seq => java.util.List + Seq => java.util.List mutable.Seq => java.util.List - Set => java.util.Set - Map => java.util.Map + Set => java.util.Set + Map => java.util.Map 因为Java并未区分可变容器不可变容器类型,所以,虽然能将`scala.immutable.List`转换成`java.util.List`,但所有的修改操作都会抛出“UnsupportedOperationException”。参见下例: @@ -55,4 +57,3 @@ import之后,就可以在Scala容器和与之对应的Java容器之间进行 scala> jul.add(7) java.lang.UnsupportedOperationException at java.util.AbstractList.add(AbstractList.java:131) - diff --git a/zh-cn/overviews/collections/creating-collections-from-scratch.md b/_zh-cn/overviews/collections/creating-collections-from-scratch.md similarity index 98% rename from zh-cn/overviews/collections/creating-collections-from-scratch.md rename to _zh-cn/overviews/collections/creating-collections-from-scratch.md index fd08e1c85f..3d9df97e09 100644 --- a/zh-cn/overviews/collections/creating-collections-from-scratch.md +++ b/_zh-cn/overviews/collections/creating-collections-from-scratch.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 从头定义新容器 discourse: false partof: collections +overview-name: Collections + num: 16 language: zh-cn --- @@ -24,7 +26,7 @@ language: zh-cn 实际上,上述每个例子都被“暗地里”转换成了对某个对象的apply方法的调用。例如,上述第三行会展开成如下形式: List.apply(1.0, 2.0) - + 可见,这里调用的是List类的伴生对象的apply方法。该方法可以接受任意多个参数,并将这些参数作为元素,生成一个新的列表。在Scala标准库中,无论是List、Stream、Vector等具体的实现类还是Seq、Set、Traversable等抽象基类,每个容器类都伴一个带apply方法的伴生对象。针对后者,调用apply方法将得到对应抽象基类的某个默认实现,例如: scala > List(1,2,3) @@ -56,4 +58,3 @@ range,用于生成步长为step的整型序列,并且iterate,将某个函 | S.range(start, end) | start, start + 1, ... end-1的序列。(译注:注意始左闭右开区间) | | S.range(start, end, step) | 生成以start为起始元素、step为步长、最大值不超过end的递增序列(左闭右开)。 | | S.iterate(x, n)(f) | 生成一个长度为n的序列,其元素值分别为x、f(x)、f(f(x))、…… | - diff --git a/zh-cn/overviews/collections/equality.md b/_zh-cn/overviews/collections/equality.md similarity index 89% rename from zh-cn/overviews/collections/equality.md rename to _zh-cn/overviews/collections/equality.md index 5e4a75dda6..39eedc64dd 100644 --- a/zh-cn/overviews/collections/equality.md +++ b/_zh-cn/overviews/collections/equality.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 等价性 discourse: false partof: collections +overview-name: Collections + num: 13 language: zh-cn --- @@ -17,7 +19,7 @@ language: zh-cn scala> import collection.mutable.{HashMap, ArrayBuffer} import collection.mutable.{HashMap, ArrayBuffer} scala> val buf = ArrayBuffer(1, 2, 3) - buf: scala.collection.mutable.ArrayBuffer[Int] = + buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3) scala> val map = HashMap(buf -> 3) map: scala.collection.mutable.HashMap[scala.collection。 @@ -26,7 +28,7 @@ language: zh-cn res13: Int = 3 scala> buf(0) += 1 scala> map(buf) - java.util.NoSuchElementException: key not found: - ArrayBuffer(2, 2, 3) + java.util.NoSuchElementException: key not found: + ArrayBuffer(2, 2, 3) 在这个例子中,由于数组xs的散列码已经在倒数第二行发生了改变,最后一行的选择操作将很有可能失败。因此,基于散列码的查找函数将会查找另一个位置,而不是xs所存储的位置。 diff --git a/zh-cn/overviews/collections/introduction.md b/_zh-cn/overviews/collections/introduction.md similarity index 98% rename from zh-cn/overviews/collections/introduction.md rename to _zh-cn/overviews/collections/introduction.md index 1e93bd1c7b..1cad8b7988 100644 --- a/zh-cn/overviews/collections/introduction.md +++ b/_zh-cn/overviews/collections/introduction.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 简介 discourse: false partof: collections +overview-name: Collections + num: 1 language: zh-cn --- @@ -34,4 +36,3 @@ language: zh-cn 这个代码比使用传统的类运行一到三个循环更加简明(三个循环处理一个数组,是由于中间结果需要有其它地方做缓存)。一旦你已经学习了基本的集合词汇,你将也发现写这种代码显式的循环更简单和更安全。而且,这个拆分操作是非常快速,并且在多核处理器上采用并行集合类达到更快的速度(并行集合类已经Scala 2.9的一部分发布)。 本文档从一个用户的角度出发,提供了一个关于Scala集合类的 API的深入讨论。它将带你体验它定义的所有的基础类和方法。 - diff --git a/zh-cn/overviews/collections/iterators.md b/_zh-cn/overviews/collections/iterators.md similarity index 99% rename from zh-cn/overviews/collections/iterators.md rename to _zh-cn/overviews/collections/iterators.md index 76e80bc0ad..4325c15710 100644 --- a/zh-cn/overviews/collections/iterators.md +++ b/_zh-cn/overviews/collections/iterators.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Iterators discourse: false partof: collections +overview-name: Collections + num: 15 language: zh-cn --- @@ -13,9 +15,9 @@ language: zh-cn 让迭代器it逐个返回所有元素最简单的方法是使用while循环: - while (it.hasNext) + while (it.hasNext) println(it.next()) - + Scala为Traversable, Iterable和Seq类中的迭代器提供了许多类似的方法。比如:这些类提供了foreach方法以便在迭代器返回的每个元素上执行指定的程序。使用foreach方法可以将上面的循环缩写为: it foreach println @@ -27,7 +29,7 @@ Scala为Traversable, Iterable和Seq类中的迭代器提供了许多类似的方 在迭代器或traversable容器中调用foreach方法的最大区别是:当在迭代器中完成调用foreach方法后会将迭代器保留在最后一个元素的位置。所以在这个迭代器上再次调用next方法时会抛出NoSuchElementException异常。与此不同的是,当在容器中调用foreach方法后,容器中的元素数量不会变化(除非被传递进来的函数删除了元素,但不赞成这样做,因为这会导致意想不到的结果)。 迭代器的其他操作跟Traversable一样具有相同的特性。例如:迭代器提供了map方法,该方法会返回一个新的迭代器: - + scala> val it = Iterator("a", "number", "of", "words") it: Iterator[java.lang.String] = non-empty iterator scala> it.map(_.length) @@ -39,7 +41,7 @@ Scala为Traversable, Iterable和Seq类中的迭代器提供了许多类似的方 5 scala> it.next() java.util.NoSuchElementException: next on empty iterator - + 如你所见,在调用了it.map方法后,迭代器it移动到了最后一个元素的位置。 另一个例子是关于dropWhile方法,它用来在迭代器中找到第一个具有某些属性的元素。比如:在上文所说的迭代器中找到第一个具有两个以上字符的单词,你可以这样写: @@ -150,14 +152,14 @@ Scala为Traversable, Iterable和Seq类中的迭代器提供了许多类似的方 def skipEmptyWordsNOT(it: Iterator[String]) = while (it.next().isEmpty) {} - + 但仔细看看这段代码,就会发现明显的错误:代码确实会跳过空字符串,但同时它也跳过了第一个非空字符串! 要解决这个问题,可以使用带缓冲能力的迭代器。[BufferedIterator]类是[Iterator]的子类,提供了一个附加的方法,head。在BufferedIterator中调用head 会返回它指向的第一个元素,但是不会令迭代器步进。使用BufferedIterator,跳过空字符串的方法可以写成下面这样: def skipEmptyWords(it: BufferedIterator[String]) = while (it.head.isEmpty) { it.next() } - + 通过调用buffered方法,所有迭代器都可以转换成BufferedIterator。参见下例: scala> val it = Iterator(1, 2, 3, 4) diff --git a/zh-cn/overviews/collections/maps.md b/_zh-cn/overviews/collections/maps.md similarity index 98% rename from zh-cn/overviews/collections/maps.md rename to _zh-cn/overviews/collections/maps.md index 69ce48f158..76e07b2152 100644 --- a/zh-cn/overviews/collections/maps.md +++ b/_zh-cn/overviews/collections/maps.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 映射 discourse: false partof: collections +overview-name: Collections + num: 7 language: zh-cn --- @@ -79,7 +81,7 @@ language: zh-cn getOrElseUpdate特别适合用于访问用作缓存的映射(Map)。假设调用函数f开销巨大: - scala> def f(x: String) = { + scala> def f(x: String) = { println("taking my time."); sleep(100) x.reverse } f: (x: String)String @@ -94,7 +96,7 @@ getOrElseUpdate特别适合用于访问用作缓存的映射(Map)。假设 scala> def cachedF(s: String) = cache.getOrElseUpdate(s, f(s)) cachedF: (s: String)String scala> cachedF("abc") - + 稍等片刻。 res3: String = cba @@ -105,12 +107,12 @@ getOrElseUpdate特别适合用于访问用作缓存的映射(Map)。假设 def cachedF(arg: String) = cache get arg match { case Some(result) => result - case None => + case None => val result = f(x) cache(arg) = result result } - + ## 同步集合(Set)和映射(Map) 无论什么样的Map实现,只需混入`SychronizedMap trait`,就可以得到对应的线程安全版的Map。例如,我们可以像下述代码那样在HashMap中混入SynchronizedMap。这个示例一上来先从`scala.colletion.mutable`包中import了两个trait:Map、SynchronizedMap,和一个类:HashMap。接下来,示例中定义了一个单例对象MapMaker,其中定义了一个方法makeMap。该方法的返回值类型是一个同时以String为键值类型的可变映射。 @@ -126,19 +128,19 @@ getOrElseUpdate特别适合用于访问用作缓存的映射(Map)。假设 } } } - + 混入SynchronizedMap trait makeMap方法中的第1个语句构造了一个新的混入了SynchronizedMap trait的可变映射: new HashMap[String, String] with SynchronizedMap[String, String] - + 针对这段代码,Scala编译器会合成HashMap的一个混入了SynchronizedMap trait的子类,同时生成(并返回)该合成子类的一个实例。处于下面这段代码的缘故,这个合成类还覆写了default方法: override def default(key: String) = "Why do you want to know?" - + 当向某个Map查询给定的键所对应的值,而Map中不存在与该键相关联的值时,默认情况下会触发一个NoSuchElementException异常。不过,如果自定义一个Map类并覆写default方法,便可以针对不存在的键返回一个default方法返回的值。所以,编译器根据上述代码合成的HashMap子类在碰到不存在的键时将会反过来质问你“Why do you want to know?” makeMap方法返回的可变映射混入了 SynchronizedMap trait,因此可以用在多线程环境下。对该映射的每次访问都是同步的。以下示例展示的是从解释器内以单个线程访问该映射: @@ -159,10 +161,9 @@ makeMap方法返回的可变映射混入了 SynchronizedMap trait,因此可以 同步集合(synchronized set)的创建方法与同步映射(synchronized map)类似。例如,我们可以通过混入SynchronizedSet trait来创建同步哈希集: - import scala.collection.mutable //导入包scala.collection.mutable + import scala.collection.mutable //导入包scala.collection.mutable val synchroSet = new mutable.HashSet[Int] with mutable.SynchronizedSet[Int] - -最后,如有使用同步容器(synchronized collection)的需求,还可以考虑使用`java.util.concurrent`中提供的并发容器(concurrent collections)。 +最后,如有使用同步容器(synchronized collection)的需求,还可以考虑使用`java.util.concurrent`中提供的并发容器(concurrent collections)。 diff --git a/zh-cn/overviews/collections/migrating-from-scala-27.md b/_zh-cn/overviews/collections/migrating-from-scala-27.md similarity index 97% rename from zh-cn/overviews/collections/migrating-from-scala-27.md rename to _zh-cn/overviews/collections/migrating-from-scala-27.md index fdf824e26c..016fc7ce69 100644 --- a/zh-cn/overviews/collections/migrating-from-scala-27.md +++ b/_zh-cn/overviews/collections/migrating-from-scala-27.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview title: Scala 2.7迁移指南 discourse: false partof: collections +overview-name: Collections + num: 18 -outof: 18 language: zh-cn --- @@ -36,7 +37,7 @@ Scala 2.7中容器的旧有功能基本上全部予以保留。某些功能被 m.keys ^ res2: Iterable[Int] = Set(1, 3) - + 老版本的库中有两个部分被整个移除,所以在deprecation警告中看不到它们。 scala.collection.jcl包被移除了。这个包试图在Scala中模拟某些Java的容器,但是该包破坏了Scala的一些对称性。绝大多数人,当他们需要Java容器的时候,他们会直接选用java.util。 diff --git a/zh-cn/overviews/collections/overview.md b/_zh-cn/overviews/collections/overview.md similarity index 98% rename from zh-cn/overviews/collections/overview.md rename to _zh-cn/overviews/collections/overview.md index 04ba44a9ad..526d6c658f 100644 --- a/zh-cn/overviews/collections/overview.md +++ b/_zh-cn/overviews/collections/overview.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Mutable和Immutable集合 discourse: false partof: collections +overview-name: Collections + num: 2 language: zh-cn --- @@ -26,8 +28,8 @@ Scala 集合类系统地区分了可变的和不可变的集合。可变集合 一个有用的约定,如果你想要同时使用可变和不可变集合类,只导入collection.mutable包即可。 - import scala.collection.mutable //导入包scala.collection.mutable - + import scala.collection.mutable //导入包scala.collection.mutable + 然而,像没有前缀的Set这样的关键字, 仍然指的是一个不可变集合,然而`mutable.Set`指的是可变的副本(可变集合)。 集合树的最后一个包是`collection.generic`。这个包包含了集合的构建块。集合类延迟了`collection.generic`类中的部分操作实现,另一方面集合框架的用户需要引用`collection.generic`中类在异常情况中。 @@ -36,7 +38,7 @@ Scala 集合类系统地区分了可变的和不可变的集合。可变集合 scala.collection.immutable.List // 这是它的定义位置 scala.List //通过scala 包中的别名 - List // 因为scala._ + List // 因为scala._ // 总是是被自动导入。 其它类型的别名有: [Traversable](http://www.scala-lang.org/api/current/scala/collection/Traversable.html), [Iterable](http://www.scala-lang.org/api/current/scala/collection/Iterable.html), [Seq](http://www.scala-lang.org/api/current/scala/collection/Seq.html), [IndexedSeq](http://www.scala-lang.org/api/current/scala/collection/IndexedSeq.html), [Iterator](http://www.scala-lang.org/api/current/scala/collection/Iterator.html), [Stream](http://www.scala-lang.org/api/current/scala/collection/immutable/Stream.html), [Vector](http://www.scala-lang.org/api/current/scala/collection/immutable/Vector.html), [StringBuilder](http://www.scala-lang.org/api/current/scala/collection/mutable/StringBuilder.html), [Range](http://www.scala-lang.org/api/current/scala/collection/immutable/Range.html)。 @@ -67,7 +69,7 @@ Scala 集合类系统地区分了可变的和不可变的集合。可变集合 Buffer(x, y, z) IndexedSeq(1.0, 2.0) LinearSeq(a, b, c) - + 相同的原则也应用于特殊的集合实现,例如: List(1, 2, 3) @@ -77,7 +79,7 @@ Scala 集合类系统地区分了可变的和不可变的集合。可变集合 Traversable类提供了所有集合支持的API,同时,对于特殊类型也是有意义的。例如,Traversable类 的map方法会返回另一个Traversable对象作为结果,但是这个结果类型在子类中被重写了。例如,在一个List上调用map会又生成一个List,在Set上调用会再生成一个Set,以此类推。 - scala> List(1, 2, 3) map (_ + 1) + scala> List(1, 2, 3) map (_ + 1) res0: List[Int] = List(2, 3, 4) scala> Set(1, 2, 3) map (_ * 2) res0: Set[Int] = Set(2, 4, 6) diff --git a/zh-cn/overviews/collections/performance-characteristics.md b/_zh-cn/overviews/collections/performance-characteristics.md similarity index 98% rename from zh-cn/overviews/collections/performance-characteristics.md rename to _zh-cn/overviews/collections/performance-characteristics.md index 562b389286..b9e5719a68 100644 --- a/zh-cn/overviews/collections/performance-characteristics.md +++ b/_zh-cn/overviews/collections/performance-characteristics.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 性能特点 discourse: false partof: collections +overview-name: Collections + num: 12 language: zh-cn --- @@ -83,4 +85,3 @@ language: zh-cn |add | 添加一个新的元素到一个集合中或者添加一个键值对到一个映射中。 | |remove | 移除一个集合中的一个元素或者移除一个映射中一个键。 | |min | 集合中的最小元素,或者映射中的最小键。 | - diff --git a/zh-cn/overviews/collections/seqs.md b/_zh-cn/overviews/collections/seqs.md similarity index 99% rename from zh-cn/overviews/collections/seqs.md rename to _zh-cn/overviews/collections/seqs.md index 311eeaf585..c9640efd99 100644 --- a/zh-cn/overviews/collections/seqs.md +++ b/_zh-cn/overviews/collections/seqs.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 序列trait:Seq、IndexedSeq及LinearSeq discourse: false partof: collections +overview-name: Collections + num: 5 language: zh-cn --- @@ -104,4 +106,3 @@ ListBuffer和ArrayBuffer是常用的buffer实现 。顾名思义,ListBuffer依 | buf.clear() | 移除buffer中的所有元素。 | | **克隆:** | | | buf.clone | 与buf具有相同元素的新buffer。 | - diff --git a/zh-cn/overviews/collections/sets.md b/_zh-cn/overviews/collections/sets.md similarity index 97% rename from zh-cn/overviews/collections/sets.md rename to _zh-cn/overviews/collections/sets.md index 7ee603b58d..6694dfb2cc 100644 --- a/zh-cn/overviews/collections/sets.md +++ b/_zh-cn/overviews/collections/sets.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 集合 discourse: false partof: collections +overview-name: Collections + num: 6 language: zh-cn --- @@ -17,7 +19,7 @@ language: zh-cn 例如 val fruit = Set("apple", "orange", "peach", "banana") - fruit: scala.collection.immutable.Set[java.lang.String] = + fruit: scala.collection.immutable.Set[java.lang.String] = Set(apple, orange, peach, banana) scala> fruit("peach") res0: Boolean = true @@ -56,11 +58,11 @@ language: zh-cn 可变集合提供加法类方法,可以用来添加、删除或更新元素。下面对这些方法做下总结。 -## mutable.Set 类的操作 +## mutable.Set 类的操作 | WHAT IT IS | WHAT IT DOES | |------------------|------------------------| -| **加法:** | | +| **加法:** | | | `xs += x` | 把元素 `x` 添加到集合 `xs` 中。该操作有副作用,它会返回左操作符,这里是 `xs` 自身。 | | `xs += (x, y, z)` | 添加指定的元素到集合 `xs` 中,并返回 `xs` 本身。(同样有副作用) | | `xs ++= ys` | 添加集合 `ys` 中的所有元素到集合 `xs` 中,并返回 `xs` 本身。(表达式有副作用) | @@ -110,7 +112,7 @@ language: zh-cn 集合的两个特质是 `SortedSet` 和 `BitSet`。 -## 有序集(SortedSet) +## 有序集(SortedSet) [SortedSet](http://www.scala-lang.org/api/current/scala/collection/SortedSet.html) 是指以特定的顺序(这一顺序可以在创建集合之初自由的选定)排列其元素(使用iterator或foreach)的集合。 [SortedSet](http://www.scala-lang.org/api/current/scala/collection/SortedSet.html) 的默认表示是有序二叉树,即左子树上的元素小于所有右子树上的元素。这样,一次简单的顺序遍历能按增序返回集合中的所有元素。Scala的类 `immutable.TreeSet` 使用红黑树实现,它在维护元素顺序的同时,也会保证二叉树的平衡,即叶节点的深度差最多为1。 @@ -141,9 +143,8 @@ language: zh-cn scala> res3 from "three" res5: scala.collection.immutable.TreeSet[String] = TreeSet(three, two) -## 位集合(Bitset) +## 位集合(Bitset) 位集合是由单字或多字的紧凑位实现的非负整数的集合。其内部使用 `Long` 型数组来表示。第一个 `Long` 元素表示的范围为0到63,第二个范围为64到127,以此类推(值为0到127的非可变位集合通过直接将值存储到第一个或第两个 `Long` 字段的方式,优化掉了数组处理的消耗)。对于每个 `Long`,如果有相应的值包含于集合中则它对应的位设置为1,否则该位为0。这里遵循的规律是,位集合的大小取决于存储在该集合的最大整数的值的大小。假如N是为集合所要表示的最大整数,则集合的大小就是 `N/64` 个长整形字,或者 `N/8` 个字节,再加上少量额外的状态信息字节。 因此当位集合包含的元素值都比较小时,它比其他的集合类型更紧凑。位集合的另一个优点是它的 `contains` 方法(成员测试)、`+=` 运算(添加元素)、`-=` 运算(删除元素)都非常的高效。 - diff --git a/zh-cn/overviews/collections/strings.md b/_zh-cn/overviews/collections/strings.md similarity index 93% rename from zh-cn/overviews/collections/strings.md rename to _zh-cn/overviews/collections/strings.md index 741cca219e..2743dc3481 100644 --- a/zh-cn/overviews/collections/strings.md +++ b/_zh-cn/overviews/collections/strings.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 字符串 discourse: false partof: collections +overview-name: Collections + num: 11 language: zh-cn --- @@ -17,7 +19,7 @@ language: zh-cn res6: String = olleh scala> str.map(_.toUpper) res7: String = HELLO - scala> str drop 3 + scala> str drop 3 res8: String = lo scala> str slice (1, 4) res9: String = ell diff --git a/zh-cn/overviews/collections/trait-iterable.md b/_zh-cn/overviews/collections/trait-iterable.md similarity index 92% rename from zh-cn/overviews/collections/trait-iterable.md rename to _zh-cn/overviews/collections/trait-iterable.md index 8373ce49fd..066639c768 100644 --- a/zh-cn/overviews/collections/trait-iterable.md +++ b/_zh-cn/overviews/collections/trait-iterable.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Trait Iterable discourse: false partof: collections +overview-name: Collections + num: 4 language: zh-cn --- @@ -14,8 +16,8 @@ language: zh-cn def foreach[U](f: Elem => U): Unit = { val it = iterator while (it.hasNext) f(it.next()) - } - + } + 许多Iterable 的子类覆写了Iteable的foreach标准实现,因为它们提供了更多有效的实现。记住,由于性能问题,foreach是Traversable所有操作能够实现的基础。 Iterable有两个方法返回迭代器:grouped和sliding。然而,这些迭代器返回的不是单个元素,而是原容器(collection)元素的全部子序列。这些最大的子序列作为参数传给这些方法。grouped方法返回元素的增量分块,sliding方法生成一个滑动元素的窗口。两者之间的差异通过REPL的作用能够清楚看出。 @@ -36,7 +38,7 @@ Iterable有两个方法返回迭代器:grouped和sliding。然而,这些迭 res6: List[Int] = List(2, 3, 4) scala> sit.next() res7: List[Int] = List(3, 4, 5) - + 当只有一个迭代器可用时,Trait Iterable增加了一些其他方法,为了能被有效的实现的可遍历的情况。这些方法总结在下面的表中。 ## Trait Iterable操作 @@ -45,23 +47,22 @@ Iterable有两个方法返回迭代器:grouped和sliding。然而,这些迭 |--------------|--------------| | **抽象方法:** | | | xs.iterator | xs迭代器生成的每一个元素,以相同的顺序就像foreach一样遍历元素。 | -| **其他迭代器:** | | +| **其他迭代器:** | | | xs grouped size | 一个迭代器生成一个固定大小的容器(collection)块。 | | xs sliding size | 一个迭代器生成一个固定大小的滑动窗口作为容器(collection)的元素。 | -| **子容器(Subcollection):** | | +| **子容器(Subcollection):** | | | xs takeRight n | 一个容器(collection)由xs的最后n个元素组成(或,若定义的元素是无序,则由任意的n个元素组成)。 | | xs dropRight n | 一个容器(collection)由除了xs 被取走的(执行过takeRight ()方法)n个元素外的其余元素组成。 | -| **拉链方法(Zippers):** | | +| **拉链方法(Zippers):** | | | xs zip ys | 把一对容器 xs和ys的包含的元素合成到一个iterabale。 | | xs zipAll (ys, x, y) | 一对容器 xs 和ys的相应的元素合并到一个iterable ,实现方式是通过附加的元素x或y,把短的序列被延展到相对更长的一个上。 | | xs.zip WithIndex | 把一对容器xs和它的序列,所包含的元素组成一个iterable 。 | -| **比对:** | | +| **比对:** | | | xs sameElements ys | 测试 xs 和 ys 是否以相同的顺序包含相同的元素。 | -在Iterable下的继承层次结构你会发现有三个traits:[Seq](https://www.scala-lang.org/api/current/scala/collection/Seq.html),[Set](https://www.scala-lang.org/api/current/scala/collection/Set.html),和 [Map](https://www.scala-lang.org/api/current/scala/collection/Map.html)。这三个Traits有一个共同的特征,它们都实现了[PartialFunction](https://www.scala-lang.org/api/current/scala/PartialFunction.html) trait以及它的应用和isDefinedAt 方法。然而,每一个trait实现的 `PartialFunction` 方法却各不相同。 +在Iterable下的继承层次结构你会发现有三个traits:[Seq](https://www.scala-lang.org/api/current/scala/collection/Seq.html),[Set](https://www.scala-lang.org/api/current/scala/collection/Set.html),和 [Map](https://www.scala-lang.org/api/current/scala/collection/Map.html)。这三个Traits有一个共同的特征,它们都实现了[PartialFunction](https://www.scala-lang.org/api/current/scala/PartialFunction.html) trait以及它的应用和isDefinedAt 方法。然而,每一个trait实现的 `PartialFunction` 方法却各不相同。 例如序列,使用用的是位置索引,它里面的元素的总是从0开始编号。即`Seq(1, 2, 3)(1) `为2。例如sets,使用的是成员测试。例如`Set('a', 'b', 'c')('b') `算出来的是true,而`Set()('a')`为false。最后,maps使用的是选择。比如`Map('a' -> 1, 'b' -> 10, 'c' -> 100)('b')` 得到的是10。 接下来,我们将详细的介绍三种类型的容器(collection)。 - diff --git a/zh-cn/overviews/collections/trait-traversable.md b/_zh-cn/overviews/collections/trait-traversable.md similarity index 99% rename from zh-cn/overviews/collections/trait-traversable.md rename to _zh-cn/overviews/collections/trait-traversable.md index 7471078c62..d239bcadb4 100644 --- a/zh-cn/overviews/collections/trait-traversable.md +++ b/_zh-cn/overviews/collections/trait-traversable.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Trait Traversable discourse: false partof: collections +overview-name: Collections + num: 3 language: zh-cn --- @@ -119,4 +121,3 @@ Traversable同时定义的很多具体方法,如下表所示。这些方法可 | **视图(View):** | | | xs.view | 通过容器xs生成一个视图。 | | xs view (from, to) | 生成一个表示在指定索引范围内的xs元素的视图。 | - diff --git a/zh-cn/overviews/collections/views.md b/_zh-cn/overviews/collections/views.md similarity index 98% rename from zh-cn/overviews/collections/views.md rename to _zh-cn/overviews/collections/views.md index 5f6a8ff1e5..a7e076c5eb 100644 --- a/zh-cn/overviews/collections/views.md +++ b/_zh-cn/overviews/collections/views.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 视图 discourse: false partof: collections +overview-name: Collections + num: 14 language: zh-cn --- @@ -32,9 +34,9 @@ language: zh-cn v: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> v map (_ + 1) map (_ * 2) - res5: scala.collection.immutable.Vector[Int] = + res5: scala.collection.immutable.Vector[Int] = Vector(4, 6, 8, 10, 12, 14, 16, 18, 20, 22) - + 在最后一条语句中,表达式`v map (_ + 1) ` 构建了一个新的vector对象,该对象被map第二次调用`(_ * 2)`而转换成第3个vector对象。很多情况下,从map的第一次调用构造一个中间结果有点浪费资源。上述示例中,将map的两次操作结合成一次单一的map操作执行得会更快些。如果这两次操作同时可行,则可亲自将它们结合成一次操作。但通常,数据结构的连续转换出现在不同的程序模块里。融合那些转换将会破坏其模块性。更普遍的做法是通过把vector对象首先转换成其视图,然后把所有的转换作用于该视图,最后强制将视图转换成vector对象,从而避开出现中间结果这种情况。 scala> (v.view map (_ + 1) map (_ * 2)).force @@ -43,9 +45,9 @@ language: zh-cn 让我们按这个步骤一步一步再做一次: scala> val vv = v.view - vv: scala.collection.SeqView[Int,Vector[Int]] = + vv: scala.collection.SeqView[Int,Vector[Int]] = SeqView(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) - + v.view 给出了SeqView对象,它是一个延迟计算的Seq。SeqView有两个参数,第一个是整型(Int)表示视图单元的类型。第二个Vector[Int]数组表示当需要强制将视图转回时构造函数的类型。 将第一个map 转换成视图可得到: @@ -74,7 +76,7 @@ map的结果是输出`SeqViewM(...)`的值。实质是记录函数`map (_ + 1)` 现在,假设你有一个很长序列的单词表,你想在这个序列的第一百万个字内找到回文。你能复用findPalidrome么?当然,你可以写: findPalindrome(words take 1000000) - + 这很好地解决了两个方面问题:提取序列的第一个百万单词,找到一个回文结构。但缺点是,它总是构建由一百万个字组成的中间序列,即使该序列的第一个单词已经是一个回文。所以可能,999 '999个单词在根本没被检查就复制到中间的结果(数据结构中)。很多程序员会在这里放弃转而编写给定参数前缀的寻找回文的自定义序列。但对于视图(views),这没必要。简单地写: findPalindrome(words.view take 1000000) diff --git a/zh-cn/overviews/parallel-collections/architecture.md b/_zh-cn/overviews/parallel-collections/architecture.md similarity index 98% rename from zh-cn/overviews/parallel-collections/architecture.md rename to _zh-cn/overviews/parallel-collections/architecture.md index c4cc0b7a5f..b2aeb118b2 100644 --- a/zh-cn/overviews/parallel-collections/architecture.md +++ b/_zh-cn/overviews/parallel-collections/architecture.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 并行集合库的架构 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 5 language: zh-cn --- diff --git a/zh-cn/overviews/parallel-collections/concrete-parallel-collections.md b/_zh-cn/overviews/parallel-collections/concrete-parallel-collections.md similarity index 99% rename from zh-cn/overviews/parallel-collections/concrete-parallel-collections.md rename to _zh-cn/overviews/parallel-collections/concrete-parallel-collections.md index 4b49e90d7d..793beebcda 100644 --- a/zh-cn/overviews/parallel-collections/concrete-parallel-collections.md +++ b/_zh-cn/overviews/parallel-collections/concrete-parallel-collections.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 具体并行集合类 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 2 language: zh-cn --- diff --git a/zh-cn/overviews/parallel-collections/configuration.md b/_zh-cn/overviews/parallel-collections/configuration.md similarity index 97% rename from zh-cn/overviews/parallel-collections/configuration.md rename to _zh-cn/overviews/parallel-collections/configuration.md index 015dc62916..362b06920e 100644 --- a/zh-cn/overviews/parallel-collections/configuration.md +++ b/_zh-cn/overviews/parallel-collections/configuration.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 配置并行集合 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 7 language: zh-cn --- @@ -22,21 +24,21 @@ language: zh-cn scala> import scala.collection.parallel._ import scala.collection.parallel._ - + scala> val pc = mutable.ParArray(1, 2, 3) pc: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 2, 3) - + scala> pc.tasksupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(2)) pc.tasksupport: scala.collection.parallel.TaskSupport = scala.collection.parallel.ForkJoinTaskSupport@4a5d484a - + scala> pc map { _ + 1 } res0: scala.collection.parallel.mutable.ParArray[Int] = ParArray(2, 3, 4) - + 以上代码配置并行集合使用parallelism 级别为2的fork-join池。配置并行集合使用线程池执行器: scala> pc.tasksupport = new ThreadPoolTaskSupport() pc.tasksupport: scala.collection.parallel.TaskSupport = scala.collection.parallel.ThreadPoolTaskSupport@1d914a39 - + scala> pc map { _ + 1 } res1: scala.collection.parallel.mutable.ParArray[Int] = ParArray(2, 3, 4) @@ -45,14 +47,13 @@ language: zh-cn 通过继承TaskSupport 特征并实现下列方法,可实现一个典型的任务支持: def execute[R, Tp](task: Task[R, Tp]): () => R - + def executeAndWaitResult[R, Tp](task: Task[R, Tp]): R - + def parallelismLevel: Int - + execute方法异步调度任务并且返回等待计算结果的未来状态。executeAndWait 方法功能一样,但只当任务完成时才返回。parallelismLevel 简单地返回任务支持用于调度任务的处理器目标数量。 **引用** 1. [On a Generic Parallel Collection Framework, June 2011](http://infoscience.epfl.ch/record/165523/files/techrep.pdf) - diff --git a/zh-cn/overviews/parallel-collections/conversions.md b/_zh-cn/overviews/parallel-collections/conversions.md similarity index 94% rename from zh-cn/overviews/parallel-collections/conversions.md rename to _zh-cn/overviews/parallel-collections/conversions.md index e33697cbbc..788ca55630 100644 --- a/zh-cn/overviews/parallel-collections/conversions.md +++ b/_zh-cn/overviews/parallel-collections/conversions.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 并行容器的转换 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 3 language: zh-cn --- @@ -15,12 +17,12 @@ language: zh-cn | 顺序 |并行 | |------|-----| -|可变性(mutable)| | +|可变性(mutable)| | |Array|ParArray| |HashMap| ParHashMap| |HashSet| ParHashSet| |TrieMap| ParTrieMap| -|不可变性(immutable)| | +|不可变性(immutable)| | |Vector | ParVector| |Range | ParRange| |HashMap | ParHashMap| @@ -49,4 +51,3 @@ language: zh-cn |toSeq | ParSeq | |toSet | ParSet | |toMap | ParMap | - diff --git a/zh-cn/overviews/parallel-collections/ctries.md b/_zh-cn/overviews/parallel-collections/ctries.md similarity index 98% rename from zh-cn/overviews/parallel-collections/ctries.md rename to _zh-cn/overviews/parallel-collections/ctries.md index 46b135895c..d3a2dc7d53 100644 --- a/zh-cn/overviews/parallel-collections/ctries.md +++ b/_zh-cn/overviews/parallel-collections/ctries.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 并发字典树 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 4 language: zh-cn --- @@ -19,14 +21,14 @@ language: zh-cn case class Entry(num: Double) { var sqrt = num } - + val length = 50000 - + // 准备链表 val entries = (1 until length) map { num => Entry(num.toDouble) } val results = ParTrieMap() for (e <- entries) results += ((e.num, e)) - + // 计算平方根 while (results.nonEmpty) { for ((num, e) <- results) { @@ -36,40 +38,40 @@ language: zh-cn } else e.sqrt = nsqrt } } - + 注意,在上面的计算平方根的巴比伦算法([3](http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method))中,某些数据会比别的数据收敛的更快。基于这个因素,我们希望能够尽快把他们从结果中剔除,只遍历那些真正需要耗时处理的元素。 另一个例子是广度优先搜索算法,该算法迭代地在末端节点遍历,直到找到通往目标的路径,或遍历完所有周围节点。一个二维地图上的节点定义为Int的元组。map定义为二维布尔值数组,用来表示各个位置是否已经到达。然后,定义2个并行字典树映射,open和closed。其中,映射open保存接着需要被遍历的末端节点。映射closed保存所有已经被遍历过的节点。映射open使用恰当节点来初始化,用以从地图的一角开始搜索,并找到通往地图中心的路径。随后,并行地对映射open中的所有节点迭代遍历,直到没有节点可以遍历。每次一个节点被遍历时,将它从映射open中移除,并放置在映射closed中。一旦执行完成,输出从目标节点到初始节点的路径。 (译者注:如扫雷,不断判断当前位置(末端节点)上下左右是否为地雷(二维布尔数组),从起始位置逐渐向外扩张。) val length = 1000 - + //定义节点类型 type Node = (Int, Int); type Parent = (Int, Int); - + //定义节点类型上的操作 def up(n: Node) = (n._1, n._2 - 1); def down(n: Node) = (n._1, n._2 + 1); def left(n: Node) = (n._1 - 1, n._2); def right(n: Node) = (n._1 + 1, n._2); - + // 创建一个map及一个target val target = (length / 2, length / 2); val map = Array.tabulate(length, length)((x, y) => (x % 3) != 0 || (y % 3) != 0 || (x, y) == target) def onMap(n: Node) = n._1 >= 0 && n._1 < length && n._2 >= 0 && n._2 < length - + //open列表 - 前节点 // closed 列表 - 已处理的节点 val open = ParTrieMap[Node, Parent]() val closed = ParTrieMap[Node, Parent]() - + // 加入一对起始位置 open((0, 0)) = null open((length - 1, length - 1)) = null open((0, length - 1)) = null open((length - 1, 0)) = null - + // 贪婪广度优先算法路径搜索 while (open.nonEmpty && !open.contains(target)) { for ((node, parent) <- open) { @@ -86,7 +88,7 @@ language: zh-cn open.remove(node) } } - + // 打印路径 var pathnode = open(target) while (closed.contains(pathnode)) { @@ -108,4 +110,3 @@ size操作也基于快照。一种直接的实现方式是,size调用仅仅生 [具有高效非阻塞快照的并发字典树][2](http://lampwww.epfl.ch/~prokopec/ctries-snapshot.pdf) [计算平方根的方法][3](http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) [人生游戏模拟程序][4](https://github.com/axel22/ScalaDays2012-TrieMap)(译注:类似大富翁的棋盘游戏) - diff --git a/zh-cn/overviews/parallel-collections/custom-parallel-collections.md b/_zh-cn/overviews/parallel-collections/custom-parallel-collections.md similarity index 98% rename from zh-cn/overviews/parallel-collections/custom-parallel-collections.md rename to _zh-cn/overviews/parallel-collections/custom-parallel-collections.md index bdb8bc2e7c..d6abd74588 100644 --- a/zh-cn/overviews/parallel-collections/custom-parallel-collections.md +++ b/_zh-cn/overviews/parallel-collections/custom-parallel-collections.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 创建自定义并行容器 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 6 language: zh-cn --- @@ -17,40 +19,40 @@ language: zh-cn class ParString(val str: String) extends immutable.ParSeq[Char] { - + 接着,我们定义非可变序列必须实现的方法: def apply(i: Int) = str.charAt(i) - + def length = str.length - + 我们还得定义这个并行容器的序列化副本。这里,我们返回WrappedString类: def seq = new collection.immutable.WrappedString(str) - + 最后,我们必须为并行字符串容器定义一个splitter。我们给这个splitter起名ParStringSplitter,让它继承一个序列splitter,即,SeqSplitter[Char]: def splitter = new ParStringSplitter(str, 0, str.length) - + class ParStringSplitter(private var s: String, private var i: Int, private val ntl: Int) extends SeqSplitter[Char] { - + final def hasNext = i < ntl - + final def next = { val r = s.charAt(i) i += 1 r } - + 上面的代码中,ntl为字符串的总长,i是当前位置,s是字符串本身。 除了next和hasNext方法,并行容器迭代器,或者称它为splitter,还需要序列容器迭代器中的一些其他的方法。首先,他们有一个方法叫做 remaining,它返回这个分割器尚未遍历的元素数量。其次,需要dup方法用于复制当前的分割器。 def remaining = ntl - i - + def dup = new ParStringSplitter(s, i, ntl) - + 最后,split和psplit方法用于创建splitter,这些splitter 用来遍历当前分割器的元素的子集。split方法,它返回一个分割器的序列,它用来遍历互不相交,互不重叠的分隔器元素子集,其中没有一个是空的。如果当前分割器有1个或更少的元素,然后就返回一个序列的分隔器。psplit方法必须返回和指定sizes参数个数一致的分割器序列。如果sizes参数指定元素数量小于当前分配器,然后一个带有额外的分配器就会附加在分配器的尾部。如果sizes参数比在当前分配器的剩余元素大很多,需要更多的元素,它将为每个分配器添加一个空的分配器。最后,调用split或psplit方法使得当前分配器无效。 def split = { @@ -58,7 +60,7 @@ language: zh-cn if (rem >= 2) psplit(rem / 2, rem - rem / 2) else Seq(this) } - + def psplit(sizes: Int*): Seq[ParStringSplitter] = { val splitted = new ArrayBuffer[ParStringSplitter] for (sz <- sizes) { @@ -71,7 +73,7 @@ language: zh-cn } } } - + 综上所述,split方法是通过psplit来实现的,它常用于并行序列计算中。由于不需要psplit,并行映射、集合、迭代器的实现,通常就更容易些。 因此,我们得到了一个并行字符串类。它唯一的缺点是,调用类似filter等转换方法不是生成并行串,而是生成并行向量,这可能是个折中的选择 - filter方法如果生成串而非向量,代价也许是昂贵的。 @@ -85,11 +87,11 @@ language: zh-cn class ParString(val str: String) extends immutable.ParSeq[Char] with ParSeqLike[Char, ParString, collection.immutable.WrappedString] - + 所有的方法仍然和以前一样,只是我们会增加一个额外的protected方法newCombiner,它在内部被filter方法调用。 protected[this] override def newCombiner: Combiner[Char, ParString] = new ParStringCombiner - + 接下来我们定义ParStringCombiner类。组合子是builders的子类型,它们引进了名叫combine的方法,该方法接收另一个组合子作为参数,并返回一个新的组合子,该新的组合子包含了当前组合子和参数中的组合子中的所有元素。当前组合子和参数中的组合子在调用combine方法之后将会失效。如果参数中的组合子和当前的组合子是同一个对象,那么combine方法仅仅返回当前的组合子。该方法通常情况下是高效的,最坏情况下时间复杂度为元素个数的对数,因为它在一次并行计算中会被多次调用。 我们的ParStringCombiner会在内部维护一个字符串生成器的序列。它通过在序列的最后一个字符串builder中增加一个元素的方式,来实现+=方法。并且通过串联当前和参数中的组合子的串builder列表来实现combine方法。result方法,在并行计算结束后被调用,它会通过将所有字符串生成器添加在一起来产生一个并行串。这样一来,元素只在末端被复制一次,避免了每调一次combine方法就被复制一次。理想情况下,我们想并行化这一进程,并在它们并行时候进行复制(并行数组正在被这样做),但没有办法检测到的字符串的内部表现,这是我们能做的最好的 - 我们不得不忍受这种顺序化的瓶颈。 @@ -98,28 +100,28 @@ language: zh-cn var sz = 0 val chunks = new ArrayBuffer[StringBuilder] += new StringBuilder var lastc = chunks.last - + def size: Int = sz - + def +=(elem: Char): this.type = { lastc += elem sz += 1 this } - + def clear = { chunks.clear chunks += new StringBuilder lastc = chunks.last sz = 0 } - + def result: ParString = { val rsb = new StringBuilder for (sb <- chunks) rsb.append(sb) new ParString(rsb.toString) } - + def combine[U <: Char, NewTo >: ParString](other: Combiner[U, NewTo]) = if (other eq this) this else { val that = other.asInstanceOf[ParStringCombiner] sz += that.sz @@ -128,12 +130,12 @@ language: zh-cn this } } - + ### 大体上我如何来实现一个组合子? 没有现成的秘诀——它的实现依赖于手头上的数据结构,通常在实现上也需要一些创造性。但是,有几种方法经常被采用: -1. 连接和合并。一些数据结构在这些操作上有高效的实现(经常是对数级的)。如果手头的容器可以由这样的一些数据结构支撑,那么它们的组合子就可以是容器本身。 Finger trees,ropes和各种堆尤其适合使用这种方法。 +1. 连接和合并。一些数据结构在这些操作上有高效的实现(经常是对数级的)。如果手头的容器可以由这样的一些数据结构支撑,那么它们的组合子就可以是容器本身。 Finger trees,ropes和各种堆尤其适合使用这种方法。 2. 两阶段赋值,是在并行数组和并行哈希表中采用的方法,它假设元素子集可以被高效的划分到连续的排序桶中,这样最终的数据结构就可以并行的构建。第一阶段,不同的处理器独立的占据这些桶,并把这些桶连接在一起。第二阶段,数据结构被分配,不同的处理器使用不相交的桶中的元素并行地占据部分数据结构。必须注意的是,各处理器修改的部分不能有交集,否则,可能会产生微妙的并发错误。正如在前面的篇幅中介绍的,这种方法很容易应用到随机存取序列。 @@ -147,7 +149,7 @@ ParString类还没有完成。虽然我们已经实现了一个自定义的组 extends immutable.ParSeq[Char] with GenericParTemplate[Char, ParString] with ParSeqLike[Char, ParString, collection.immutable.WrappedString] { - + def companion = ParString 在这个伴生对象内部,我们隐式定义了CanBuildFrom。 @@ -157,18 +159,18 @@ ParString类还没有完成。虽然我们已经实现了一个自定义的组 def apply(from: ParString) = newCombiner def apply() = newCombiner } - + def newBuilder: Combiner[Char, ParString] = newCombiner - + def newCombiner: Combiner[Char, ParString] = new ParStringCombiner - + def apply(elems: Char*): ParString = { val cb = newCombiner cb ++= elems cb.result } } - + ### 进一步定制——并发和其他容器 实现一个并发容器(与并行容器不同,并发容器是像collection.concurrent.TrieMap一样可以被并发修改的)并不总是简单明了的。尤其是组合器,经常需要仔细想想。到目前为止,在大多数描述的并行容器中,组合器都使用两步评估。第一步元素被不同的处理器加入到组合器中,组合器被合并在一起。第二步,在所有元素完成处理后,结果容器就被创建。 @@ -183,10 +185,9 @@ ParString类还没有完成。虽然我们已经实现了一个自定义的组 def shouldSplitFurther[S](coll: ParIterable[S], parallelismLevel: Int) = remaining > thresholdFromSize(coll.size, parallelismLevel) - + 同样的,一个splitter可以持有一个计数器,来计算splitter被分割的次数。并且,如果split次数超过3+log(并行级别),shouldSplitFurther将直接返回true。这避免了必须去调用remaining方法。 此外,对于一个指定的容器如果调用remaining方法开销不低(比如,他需要评估容器中元素的数量),那么在splitter中的方法isRemainingCheap就应该被重写并返回false。 最后,若果在splitter中的remaining方法实现起来极其麻烦,你可以重写容器中的isStrictSplitterCollection方法,并返回false。虽然这些容器将不能够执行一些严格依赖splitter的方法,比如,在remaining方法中返回一个正确的值。重点是,这并不影响 for-comprehension 中使用的方法。 - diff --git a/zh-cn/overviews/parallel-collections/overview.md b/_zh-cn/overviews/parallel-collections/overview.md similarity index 98% rename from zh-cn/overviews/parallel-collections/overview.md rename to _zh-cn/overviews/parallel-collections/overview.md index 408962cbe2..160432d0c3 100644 --- a/zh-cn/overviews/parallel-collections/overview.md +++ b/_zh-cn/overviews/parallel-collections/overview.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 概述 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 1 language: zh-cn --- @@ -51,27 +53,27 @@ Scala的并行容器库设计创意般的同Scala的(序列)容器库(从2 scala> val lastNames = List("Smith","Jones","Frankenstein","Bach","Jackson","Rodin").par astNames: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Frankenstein, Bach, Jackson, Rodin) - + scala> lastNames.map(_.toUpperCase) res0: scala.collection.parallel.immutable.ParSeq[String] = ParVector(SMITH, JONES, FRANKENSTEIN, BACH, JACKSON, RODIN) #### fold 通过fold计算一个ParArray中所有数的累加值: - + scala> val parArray = (1 to 10000).toArray.par parArray: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 2, 3, ... - + scala> parArray.fold(0)(_ + _) res0: Int = 50005000 - + #### filter 使用并行过滤器来选择按字母顺序排在“K”之后的姓名。(译者注:这个例子有点问题,应该是排在“J”之后的) scala> val lastNames = List("Smith","Jones","Frankenstein","Bach","Jackson","Rodin").par astNames: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Frankenstein, Bach, Jackson, Rodin) - + scala> lastNames.filter(_.head >= 'J') res0: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Jackson, Rodin) @@ -85,11 +87,11 @@ Scala的并行容器库设计创意般的同Scala的(序列)容器库(从2 import scala.collection.parallel.immutable.ParVector val pv = new ParVector[Int] - + 第二种,通过从一个顺序容器转换得来: val pv = Vector(1,2,3,4,5,6,7,8,9).par - + 这里需要着重强调的是这些转换方法:通过调用顺序容器(sequential collections)的par方法,顺序容器(sequential collections)可以被转换为并行容器;通过调用并行容器的seq方法,并行容器可以被转换为顺序容器。 注意:那些天生就有序的容器(意思是元素必须一个接一个的访问),像lists,queues和streams,通过拷贝元素到类似的并行容器中被转换为它们的并行对应物。例如List--被转换为一个标准的不可变的并行序列中,就是ParVector。当然,其他容器类型不需要这些拷贝的开销,比如:Array,Vector,HashMap等等。 @@ -113,25 +115,25 @@ Scala的并行容器库设计创意般的同Scala的(序列)容器库(从2 scala> var sum = 0 sum: Int = 0 - + scala> val list = (1 to 1000).toList.par list: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3,… - + scala> list.foreach(sum += _); sum res01: Int = 467766 - + scala> var sum = 0 sum: Int = 0 - + scala> list.foreach(sum += _); sum res02: Int = 457073 - + scala> var sum = 0 sum: Int = 0 - + scala> list.foreach(sum += _); sum res03: Int = 468520 - + 从上述例子我们可以看到虽然每次 sum 都被初始化为0,在list的foreach每次调用之后,sum都得到不同的值。这个不确定的源头就是数据竞争 -- 同时读/写同一个可变变量(mutable variable)。 在上面这个例子中,可能同时有两个线程在读取同一个sum的值,某些操作花了些时间后,它们又试图写一个新的值到sum中,可能的结果就是某个有用的值被覆盖了(因此丢失了),如下表所示: @@ -140,7 +142,7 @@ Scala的并行容器库设计创意般的同Scala的(序列)容器库(从2 线程B: 读取sum的值, sum = 0 sum的值: 0 线程A: sum 加上760, 写 sum = 760 sum的值: 760 线程B: sum 加上12, 写 sum = 12 sum的值: 12 - + 上面的示例演示了一个场景:两个线程读相同的值:0。在这种情况下,线程A读0并且累计它的元素:0+760,线程B,累计0和它的元素:0+12。在各自计算了和之后,它们各自把计算结果写入到sum中。从线程A到线程B,线程A写入后,马上被线程B写入的值覆盖了,值760就完全被覆盖了(因此丢失了)。 ### 非关联(non-associative)操作 @@ -149,26 +151,26 @@ Scala的并行容器库设计创意般的同Scala的(序列)容器库(从2 scala> val list = (1 to 1000).toList.par list: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3,… - + scala> list.reduce(_-_) res01: Int = -228888 - + scala> list.reduce(_-_) res02: Int = -61000 - + scala> list.reduce(_-_) res03: Int = -331818 - + 在上面这个例子中,我们对 ParVector[Int]调用 reduce 函数,并给他 _-_ 参数(简单的两个非命名元素),从第二个减去第一个。因为并行容器(parallel collections)框架创建线程来在容器的不同部分执行reduce(-),而由于执行顺序的不确定性,两次应用reduce(-)在并行容器上很可能会得到不同的结果。 注意:通常人们认为,像不可结合(non-associative)作,不可交换(non-commutative)操作传递给并行容器的高阶函数同样导致非确定的行为。但和不可结合是不一样的,一个简单的例子是字符串联合(concatenation),就是一个可结合但不可交换的操作: scala> val strings = List("abc","def","ghi","jk","lmnop","qrs","tuv","wx","yz").par - strings: scala.collection.parallel.immutable.ParSeq[java.lang.String] = ParVector(abc, def, ghi, jk, lmnop, qrs, tuv, wx, yz) - + strings: scala.collection.parallel.immutable.ParSeq[java.lang.String] = ParVector(abc, def, ghi, jk, lmnop, qrs, tuv, wx, yz) + scala> val alphabet = strings.reduce(_++_) alphabet: java.lang.String = abcdefghijklmnopqrstuvwxyz - + 并行容器的“乱序”语义仅仅意味着操作被执行是没有顺序的(从时间意义上说,就是非顺序的),并不意味着结果的重“组合”也是乱序的(从空间意义上)。恰恰相反,结果一般总是按序组合的 -- 一个并行容器被分成A,B,C三部分,按照这个顺序,将重新再次按照A,B,C的顺序组合。而不是某种其他随意的顺序如B,C,A。 关于并行容器在不同的并行容器类型上怎样进行分解和组合操作的更多信息,请参见。 diff --git a/zh-cn/overviews/parallel-collections/performance.md b/_zh-cn/overviews/parallel-collections/performance.md similarity index 98% rename from zh-cn/overviews/parallel-collections/performance.md rename to _zh-cn/overviews/parallel-collections/performance.md index 6f58dadac9..265842facb 100644 --- a/zh-cn/overviews/parallel-collections/performance.md +++ b/_zh-cn/overviews/parallel-collections/performance.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: 测量性能 discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 8 language: zh-cn --- @@ -35,27 +37,27 @@ scala.testing.Benchmark trait 是在Scala标准库中被预先定义的,并按 import collection.parallel.mutable.ParTrieMap import collection.parallel.ForkJoinTaskSupport - + object Map extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val partrie = ParTrieMap((0 until length) zip (0 until length): _*) - + partrie.tasksupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { partrie map { kv => kv } } } - + run方法包含了基准测试代码,重复运行时测量执行时间。上面的Map对象扩展了scala.testing.Benchmark trait,同时,参数par为系统的并行度,length为trie中元素数量的长度。 在编译上面的程序之后,可以这样运行: java -server -cp .:../../build/pack/lib/scala-library.jar -Dpar=1 -Dlength=300000 Map 10 - + server参数指定需要使用server类型的虚拟机。cp参数指定了类文件的路径,包含当前文件夹的类文件以及以及scala类库的jar包。参数-Dpar和-Dlength分别对应并行度和元素数量。最后,10意味着基准测试需要在同一个JVM中运行的次数。 在i7四核超线程处理器上将par的值设置为1、2、4、8并获得对应的执行时间。 @@ -84,31 +86,31 @@ collection的大小所对应的实际并发消耗取决于很多因素。部分 即使单独的来看,对上面的问题进行推断并给出关于容器应有大小的明确答案也是不容易的。为了粗略的说明容器的应有大小,我们给出了一个无副作用的在i7四核处理器(没有使用超线程)和JDK7上运行的并行矢量减(在这个例子中进行的是求和)处理性能的例子: import collection.parallel.immutable.ParVector - + object Reduce extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val parvector = ParVector((0 until length): _*) - + parvector.tasksupport = new collection.parallel.ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { parvector reduce { (a, b) => a + b } } } - + object ReduceSeq extends testing.Benchmark { val length = sys.props("length").toInt val vector = collection.immutable.Vector((0 until length): _*) - + def run = { vector reduce { (a, b) => a + b } } - + } 首先我们设定在元素数量为250000的情况下运行基准测试,在线程数设置为1、2、4的情况下得到了如下结果: @@ -129,25 +131,25 @@ collection的大小所对应的实际并发消耗取决于很多因素。部分 在另一个例子中,我们使用mutable.ParHashMap和map方法(一个转换方法),并在同样的环境中运行下面的测试程序: import collection.parallel.mutable.ParHashMap - + object Map extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val phm = ParHashMap((0 until length) zip (0 until length): _*) - + phm.tasksupport = new collection.parallel.ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { phm map { kv => kv } } } - + object MapSeq extends testing.Benchmark { val length = sys.props("length").toInt val hm = collection.mutable.HashMap((0 until length) zip (0 until length): _*) - + def run = { hm map { kv => kv @@ -162,7 +164,7 @@ collection的大小所对应的实际并发消耗取决于很多因素。部分 Map$ 138 68 57 56 57 56 56 55 54 55 java -server -cp .:../../build/pack/lib/scala-library.jar -Dpar=4 -Dlength=120000 Map 10 10 Map$ 124 54 42 40 38 41 40 40 39 39 - + 现在,如果我们将元素数量降低到15000来跟序列化哈希映射做比较: java -server -cp .:../../build/pack/lib/scala-library.jar -Dpar=1 -Dlength=15000 Map 10 10 @@ -171,11 +173,10 @@ collection的大小所对应的实际并发消耗取决于很多因素。部分 Map$ 48 15 9 8 7 7 6 7 8 6 java -server -cp .:../../build/pack/lib/scala-library.jar -Dlength=15000 MapSeq 10 10 MapSeq$ 39 9 9 9 8 9 9 9 9 9 - + 对这个容器和操作来说,当元素数量大于15000的时候采用并发是有意义的(通常情况下,对于数组和向量来说使用更少的元素来并行处理hashmap和hashset是可行的但不是必须的)。 **引用** 1. [Anatomy of a flawed microbenchmark,Brian Goetz](http://www.ibm.com/developerworks/java/library/j-jtp02225/index.html) 2. [Dynamic compilation and performance measurement, Brian Goetz](http://www.ibm.com/developerworks/library/j-jtp12214/) - diff --git a/zh-cn/overviews/thanks.md b/_zh-cn/thanks.md similarity index 100% rename from zh-cn/overviews/thanks.md rename to _zh-cn/thanks.md From 0a78fc3b6be8acbddb2744a22b27a81f62b9ca1b Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Wed, 26 Jul 2017 20:21:31 +0200 Subject: [PATCH 061/112] More overview translations ready --- _config.yml | 10 ++- .../parallel-collections/architecture.md | 32 +++++---- .../concrete-parallel-collections.md | 34 ++++----- .../parallel-collections/configuration.md | 20 +++--- .../parallel-collections/conversions.md | 15 ++-- .../overviews/parallel-collections/ctries.md | 52 +++++++------- .../custom-parallel-collections.md | 41 +++++------ .../parallel-collections/overview.md | 2 +- .../parallel-collections/performance.md | 69 ++++++++++--------- .../parallel-collections/architecture.md | 8 ++- .../concrete-parallel-collections.md | 23 ++++--- .../parallel-collections/configuration.md | 16 +++-- .../parallel-collections/conversions.md | 4 +- .../overviews/parallel-collections/ctries.md | 30 ++++---- .../custom-parallel-collections.md | 42 +++++------ .../parallel-collections/overview.md | 45 ++++++------ .../parallel-collections/performance.md | 40 +++++------ 17 files changed, 245 insertions(+), 238 deletions(-) rename {es => _es}/overviews/parallel-collections/architecture.md (92%) rename {es => _es}/overviews/parallel-collections/concrete-parallel-collections.md (99%) rename {es => _es}/overviews/parallel-collections/configuration.md (97%) rename {es => _es}/overviews/parallel-collections/conversions.md (95%) rename {es => _es}/overviews/parallel-collections/ctries.md (94%) rename {es => _es}/overviews/parallel-collections/custom-parallel-collections.md (99%) rename {es => _es}/overviews/parallel-collections/overview.md (99%) rename {es => _es}/overviews/parallel-collections/performance.md (95%) rename {ru => _ru}/overviews/parallel-collections/architecture.md (98%) rename {ru => _ru}/overviews/parallel-collections/concrete-parallel-collections.md (99%) rename {ru => _ru}/overviews/parallel-collections/configuration.md (98%) rename {ru => _ru}/overviews/parallel-collections/conversions.md (98%) rename {ru => _ru}/overviews/parallel-collections/ctries.md (98%) rename {ru => _ru}/overviews/parallel-collections/custom-parallel-collections.md (99%) rename {ru => _ru}/overviews/parallel-collections/overview.md (99%) rename {ru => _ru}/overviews/parallel-collections/performance.md (98%) diff --git a/_config.yml b/_config.yml index 47948ddce4..bee78be1fd 100644 --- a/_config.yml +++ b/_config.yml @@ -15,6 +15,8 @@ keywords: - Document - Guide +scala-version: 2.12.2 + collections: style: output: true @@ -28,8 +30,12 @@ collections: zh-cn: # Chinese (Simplified) translations output: true permalink: /:collection/:path.html - -scala-version: 2.12.2 + ru: # Russian translations + output: true + permalink: /:collection/:path.html + es: # Spanish translations + output: true + permalink: /:collection/:path.html highlighter: rouge permalink: /:categories/:title.html:output_ext diff --git a/es/overviews/parallel-collections/architecture.md b/_es/overviews/parallel-collections/architecture.md similarity index 92% rename from es/overviews/parallel-collections/architecture.md rename to _es/overviews/parallel-collections/architecture.md index ad57a77f74..fa8d146b00 100644 --- a/es/overviews/parallel-collections/architecture.md +++ b/_es/overviews/parallel-collections/architecture.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Arquitectura de la librería de colecciones paralelas de Scala discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 5 language: es --- @@ -12,16 +14,16 @@ language: es Del mismo modo que la librería de colecciones secuencial, la versión paralela ofrece un gran número de operaciones uniformes sobre un amplio abanico de implementaciones de diversas colecciones. Siguiendo la filosofía de la versión -secuencial, se pretende evitar la duplicación de código mediante el uso de +secuencial, se pretende evitar la duplicación de código mediante el uso de "plantillas" de colecciones paralelas, las cuales permiten que las operaciones sean definidas una sola vez, pudiendo ser heredadas por las diferentes implementaciones. El uso de este enfoque facilita de manera notable el **mantenimiento** y la **extensibilidad** -de la librería. En el caso del primero -- gracias a que cada operación se implementa una única -vez y es heredada por todas las colecciones, el mantenimiento es más sencillo y robusto; la -corrección de posibles errores se progaga hacia abajo en la jerarquía de clases en lugar de -duplicar las implementaciones. Del mismo modo, los motivos anteriores facilitan que la librería al completo sea -más sencilla de extender -- la mayor parte de las nuevas colecciones podrán heredar la mayoría de sus +de la librería. En el caso del primero -- gracias a que cada operación se implementa una única +vez y es heredada por todas las colecciones, el mantenimiento es más sencillo y robusto; la +corrección de posibles errores se progaga hacia abajo en la jerarquía de clases en lugar de +duplicar las implementaciones. Del mismo modo, los motivos anteriores facilitan que la librería al completo sea +más sencilla de extender -- la mayor parte de las nuevas colecciones podrán heredar la mayoría de sus operaciones. ## Core Abstractions @@ -33,7 +35,7 @@ de dos abstracciones básicas -- `Splitter`s y `Combiner`s El trabajo de un `Splitter`, como su propio nombre indica, consiste en dividir una colección paralela en una partición no trivial de sus elementos. La idea principal -es dividir dicha colección en partes más pequeñas hasta alcanzar un tamaño en el que +es dividir dicha colección en partes más pequeñas hasta alcanzar un tamaño en el que se pueda operar de manera secuencial sobre las mismas. trait Splitter[T] extends Iterator[T] { @@ -41,8 +43,8 @@ se pueda operar de manera secuencial sobre las mismas. } Curiosamente, los `Splitter` son implementados como `Iterator`s, por lo que además de -particionar, son utilizados por el framework para recorrer una colección paralela -(dado que heredan los métodos `next`y `hasNext` presentes en `Iterator`). +particionar, son utilizados por el framework para recorrer una colección paralela +(dado que heredan los métodos `next`y `hasNext` presentes en `Iterator`). Este "splitting iterator" presenta una característica única: su método `split` divide `this` (recordad que un `Splitter` es de tipo `Iterator`) en un conjunto de `Splitter`s cada uno de los cuales recorre un subconjunto disjunto del total de @@ -51,7 +53,7 @@ un `Splitter` es invalidado una vez su método `split` es invocado. Generalmente las colecciones son divididas, utilizando `Splitter`s, en subconjuntos con un tamaño aproximadamente idéntico. En situaciones donde se necesitan un tipo de -particiones más arbitrarias, particularmente en las secuencias paralelas, se utiliza un +particiones más arbitrarias, particularmente en las secuencias paralelas, se utiliza un `PreciseSplitter`, el cual hereda de `Splitter` y define un meticuloso método de particionado: `psplit`. @@ -67,14 +69,14 @@ Mientras que en las colecciones secuenciales los elementos pueden ser añadidos `result`, en el caso de las colecciones paralelas los `Combiner` presentan un método llamado `combine` que acepta otro `Combiner`como argumento y retona un nuevo `Combiner`, el cual contiene la unión de ambos. Tras la invocación del método `combine` ambos -`Combiner` son invalidados. +`Combiner` son invalidados. trait Combiner[Elem, To] extends Builder[Elem, To] { def combine(other: Combiner[Elem, To]): Combiner[Elem, To] } Los dos parametros de tipo `Elem` y `To` presentes en el fragmento de código anterior -representan el tipo del elemento y de la colección resultante respectivamente. +representan el tipo del elemento y de la colección resultante respectivamente. _Nota:_ Dados dos `Combiner`s, `c1` y `c2` donde `c1 eq c2` toma el valor `true` (esto implica que son el mismo `Combiner`), la invocación de `c1.combine(c2)` @@ -94,10 +96,10 @@ a continuación.
    El objetivo es, por supuesto, integrar tan estrechamente como sea posible las colecciones -secuenciales y paralelas, permitendo llevar a cabo una sustitución directa entre ambos +secuenciales y paralelas, permitendo llevar a cabo una sustitución directa entre ambos tipos de colecciones. -Con el objetivo de tener una referencia a una colección que podría ser secuencial o +Con el objetivo de tener una referencia a una colección que podría ser secuencial o paralela (de modo que sea posible "intercambiar" la colección paralela y la secuencial mediante la invocación de `par` y `seq` respectivamente), necesitamos un supertipo común a los tipos de las dos colecciones. Este es el origen de los traits "generales" mostrados diff --git a/es/overviews/parallel-collections/concrete-parallel-collections.md b/_es/overviews/parallel-collections/concrete-parallel-collections.md similarity index 99% rename from es/overviews/parallel-collections/concrete-parallel-collections.md rename to _es/overviews/parallel-collections/concrete-parallel-collections.md index a10dd23d57..f8c65f1e3b 100644 --- a/es/overviews/parallel-collections/concrete-parallel-collections.md +++ b/_es/overviews/parallel-collections/concrete-parallel-collections.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Clases Concretas de las Colecciones Paralelas discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 2 language: es --- @@ -15,10 +17,10 @@ Una secuencia [ParArray](http://www.scala-lang.org/api/{{ site.scala-version }}/ scala> val pa = scala.collection.parallel.mutable.ParArray.tabulate(1000)(x => 2 * x + 1) pa: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 3, 5, 7, 9, 11, 13,... - + scala> pa reduce (_ + _) res0: Int = 1000000 - + scala> pa map (x => (x - 1) / 2) res1: scala.collection.parallel.mutable.ParArray[Int] = ParArray(0, 1, 2, 3, 4, 5, 6, 7,... @@ -33,7 +35,7 @@ Un [ParVector](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/coll scala> val pv = scala.collection.parallel.immutable.ParVector.tabulate(1000)(x => x) pv: scala.collection.parallel.immutable.ParVector[Int] = ParVector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,... - + scala> pv filter (_ % 2 == 0) res0: scala.collection.parallel.immutable.ParVector[Int] = ParVector(0, 2, 4, 6, 8, 10, 12, 14, 16, 18,... @@ -47,7 +49,7 @@ Un [ParRange](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/colle scala> 1 to 3 par res0: scala.collection.parallel.immutable.ParRange = ParRange(1, 2, 3) - + scala> 15 to 5 by -2 par res1: scala.collection.parallel.immutable.ParRange = ParRange(15, 13, 11, 9, 7, 5) @@ -63,7 +65,7 @@ Las tablas hash paralelas almacenan sus elementos en un array subyacente y los a scala> val phs = scala.collection.parallel.mutable.ParHashSet(1 until 2000: _*) phs: scala.collection.parallel.mutable.ParHashSet[Int] = ParHashSet(18, 327, 736, 1045, 773, 1082,... - + scala> phs map (x => x * x) res0: scala.collection.parallel.mutable.ParHashSet[Int] = ParHashSet(2181529, 2446096, 99225, 2585664,... @@ -73,13 +75,13 @@ Los "Mapas Hash" (Hash Maps) y los "Conjuntos Hash" (Hash Sets) secuenciales pue ## Hash Tries Paralelos -Los Hash Tries paralelos son la contraparte paralela de los hash tries inmutables, que son usados para representar conjuntos y mapas inmutables de forma eficiente. Las clases involucradas son: [immutable.ParHashSet](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/parallel/immutable/ParHashSet.html) +Los Hash Tries paralelos son la contraparte paralela de los hash tries inmutables, que son usados para representar conjuntos y mapas inmutables de forma eficiente. Las clases involucradas son: [immutable.ParHashSet](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/parallel/immutable/ParHashSet.html) y [immutable.ParHashMap](http://www.scala-lang.org/api/{{ site.scala-version}}/scala/collection/parallel/immutable/ParHashMap.html). scala> val phs = scala.collection.parallel.immutable.ParHashSet(1 until 1000: _*) phs: scala.collection.parallel.immutable.ParHashSet[Int] = ParSet(645, 892, 69, 809, 629, 365, 138, 760, 101, 479,... - + scala> phs map { x => x * x } sum res0: Int = 332833500 @@ -94,12 +96,12 @@ Un [concurrent.TrieMap](http://www.scala-lang.org/api/{{ site.scala-version }}/s scala> val numbers = scala.collection.parallel.mutable.ParTrieMap((1 until 100) zip (1 until 100): _*) map { case (k, v) => (k.toDouble, v.toDouble) } numbers: scala.collection.parallel.mutable.ParTrieMap[Double,Double] = ParTrieMap(0.0 -> 0.0, 42.0 -> 42.0, 70.0 -> 70.0, 2.0 -> 2.0,... - + scala> while (numbers.nonEmpty) { | numbers foreach { case (num, sqrt) => | val nsqrt = 0.5 * (sqrt + num / sqrt) | numbers(num) = nsqrt - | if (math.abs(nsqrt - sqrt) < 0.01) { + | if (math.abs(nsqrt - sqrt) < 0.01) { | println(num, nsqrt) | numbers.remove(num) | } @@ -170,15 +172,3 @@ La segunda tabla trata sets y maps, tanto mutables como inmutables, con las sigu | **remove** | Removing an element from a set or a key from a map. | | **remove** | Elimina un elemento de un set o una clave de un map. | | **min** | El menor elemento de un set o la menor clave de un mapa. | - - - - - - - - - - - - diff --git a/es/overviews/parallel-collections/configuration.md b/_es/overviews/parallel-collections/configuration.md similarity index 97% rename from es/overviews/parallel-collections/configuration.md rename to _es/overviews/parallel-collections/configuration.md index ae8f0933eb..6e921bc9a5 100644 --- a/es/overviews/parallel-collections/configuration.md +++ b/_es/overviews/parallel-collections/configuration.md @@ -1,17 +1,19 @@ --- -layout: overview +layout: multipage-overview title: Configurando las colecciones paralelas discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 7 language: es --- ## "Task support" -Las colecciones paralelas son modulares respecto al modo en que las operaciones +Las colecciones paralelas son modulares respecto al modo en que las operaciones son planificadas. Cada colección paralela es planificada con un objeto "task support" el cual es responsable de la planificación y el balanceo de las tareas a los distintos procesadores. @@ -20,7 +22,7 @@ El objeto "task support" mantiene internamente un referencia a un pool de hilos cómo y cuando las tareas son divididas en tareas más pequeñas. Para conocer más en detalle cómo funciona internamente diríjase al informe técnico \[[1][1]\]. -En la actualidad las colecciones paralelas disponen de unas cuantas implementaciones de +En la actualidad las colecciones paralelas disponen de unas cuantas implementaciones de "task support". El `ForkJoinTaskSupport` utiliza internamente un fork-join pool y es utilizado por defecto en JVM 1.6 o superiores. `ThreadPoolTaskSupport`, menos eficiente, es utilizado como mecanismo de reserva para JVM 1.5 y máquinas virtuales que no soporten los fork join pools. El @@ -34,13 +36,13 @@ A continuación se muestra cómo se puede modificar el objeto "task support" de scala> import scala.collection.parallel._ import scala.collection.parallel._ - + scala> val pc = mutable.ParArray(1, 2, 3) pc: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 2, 3) - + scala> pc.tasksupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(2)) pc.tasksupport: scala.collection.parallel.TaskSupport = scala.collection.parallel.ForkJoinTaskSupport@4a5d484a - + scala> pc map { _ + 1 } res0: scala.collection.parallel.mutable.ParArray[Int] = ParArray(2, 3, 4) @@ -49,7 +51,7 @@ paralelismo. Para indicar que la colección utilice un thread pool executor tend scala> pc.tasksupport = new ThreadPoolTaskSupport() pc.tasksupport: scala.collection.parallel.TaskSupport = scala.collection.parallel.ThreadPoolTaskSupport@1d914a39 - + scala> pc map { _ + 1 } res1: scala.collection.parallel.mutable.ParArray[Int] = ParArray(2, 3, 4) @@ -62,9 +64,9 @@ Para llevar a cabo una implementación personalizada de un nuevo objeto "task su extender del trait `TaskSupport` e implementar los siguientes métodos: def execute[R, Tp](task: Task[R, Tp]): () => R - + def executeAndWaitResult[R, Tp](task: Task[R, Tp]): R - + def parallelismLevel: Int El método `execute` planifica una tarea asíncrona y retorna una "future" sobre la que diff --git a/es/overviews/parallel-collections/conversions.md b/_es/overviews/parallel-collections/conversions.md similarity index 95% rename from es/overviews/parallel-collections/conversions.md rename to _es/overviews/parallel-collections/conversions.md index a2d8b1c7b8..8461a2f9aa 100644 --- a/es/overviews/parallel-collections/conversions.md +++ b/_es/overviews/parallel-collections/conversions.md @@ -1,9 +1,11 @@ --- -layout: overview +layout: multipage-overview title: Conversiones en colecciones paralelas discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 3 language: es --- @@ -18,7 +20,7 @@ versiones utilizan la misma estructura de datos interna. Una excepción al caso anterior es el caso de los hash maps y hash sets mutables, donde el proceso de conversión es un poco más costoso la primera vez que el método `par` es llamado, aunque las posteriores invocaciones de dicho método ofrecerán un tiempo de ejecución -constante. Nótese que en el caso de las colecciones mutables, los cambios en la +constante. Nótese que en el caso de las colecciones mutables, los cambios en la colección secuencial son visibles en su homóloga paralela en el caso de que compartan la estructura de datos subyacente. @@ -38,7 +40,7 @@ la estructura de datos subyacente. Otro tipo de colecciones, como las listas, colas o `streams`, son inherentemente secuenciales en el sentido de que los elementos deben ser accedidos uno tras otro. La versión paralela de estas estructuras se obtiene mediante la copia de los elementos -en una colección paralela. Por ejemplo, una lista funcional es convertida en una +en una colección paralela. Por ejemplo, una lista funcional es convertida en una secuencia paralela inmutable; un vector paralelo. Cada colección paralela puede ser convertida a su variante secuencial mediante el uso @@ -50,8 +52,8 @@ colecciones serán visibles en la otra. ## Conversiones entre diferentes tipo de colecciones -Ortogonal a la conversión entre colecciones secuenciales y paralelas, las colecciones -pueden convertirse entre diferentes tipos. Por ejemplo, la llamada al método `toSeq` +Ortogonal a la conversión entre colecciones secuenciales y paralelas, las colecciones +pueden convertirse entre diferentes tipos. Por ejemplo, la llamada al método `toSeq` convierte un conjunto secuencial en una secuencia secuencial, mientras que si invocamos dicho método sobre un conjunto paralelo obtendremos una secuencia paralela. La regla general is que si existe una versión paralela de `X`, el método `toX` convierte la colección @@ -72,6 +74,3 @@ A continuación se muestra un resumen de todos los métodos de conversión: | `toSeq` | `ParSeq` | | `toSet` | `ParSet` | | `toMap` | `ParMap` | - - - diff --git a/es/overviews/parallel-collections/ctries.md b/_es/overviews/parallel-collections/ctries.md similarity index 94% rename from es/overviews/parallel-collections/ctries.md rename to _es/overviews/parallel-collections/ctries.md index 02dacda410..c45a112520 100644 --- a/es/overviews/parallel-collections/ctries.md +++ b/_es/overviews/parallel-collections/ctries.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Tries Concurrentes discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 4 language: es --- @@ -14,9 +16,9 @@ si la estructura es modificada durante el recorrido de la misma. De hecho, esto también sucede en la mayor parte de las colecciones mutables. Los "tries" concurrentes presentan una característica especial, permitiendo la modificación de los mismos mientras están siendo recorridos. Las modificaciones solo son visibles -en los recorridos posteriores a las mismas. Ésto aplica tanto a los "tries" secuenciales +en los recorridos posteriores a las mismas. Ésto aplica tanto a los "tries" secuenciales como a los paralelos. La única diferencia entre ambos es que el primero de ellos -recorre todos los elementos de la estructura de manera secuencial mientras que +recorre todos los elementos de la estructura de manera secuencial mientras que el segundo lo hace en paralelo. Esta propiedad nos permite escribir determinados algoritmos de un modo mucho más @@ -25,20 +27,20 @@ iterativa y diferentes elementos necesitan distinto número de iteraciones para procesados. El siguiente ejemplo calcula la raíz cuadrada de un conjunto de números. Cada iteración -actualiza, de manera iterativa, el valor de la raíz cuadrada. Aquellos números cuyas +actualiza, de manera iterativa, el valor de la raíz cuadrada. Aquellos números cuyas raíces convergen son eliminados del mapa. case class Entry(num: Double) { var sqrt = num } - + val length = 50000 - + // prepare the list val entries = (1 until length) map { num => Entry(num.toDouble) } val results = ParTrieMap() for (e <- entries) results += ((e.num, e)) - + // compute square roots while (results.nonEmpty) { for ((num, e) <- results) { @@ -56,45 +58,45 @@ elementos sobre los que realmente necesitamos trabajar son recorridos. Otro ejemplo es el algoritmo de búsqueda en anchura, el cual iterativamente expande el "nodo cabecera" hasta que encuentra un camino hacia el objetivo o no existen más nodos a expandir. Definamos -un nodo en mapa 2D como una tupla de enteros (`Int`s). Definamos un `map` como un array de +un nodo en mapa 2D como una tupla de enteros (`Int`s). Definamos un `map` como un array de booleanos de dos dimensiones el cual determina si un determinado slot está ocupado o no. Posteriormente, -declaramos dos "concurrent tries maps" -- `open` contiene todos los nodos que deben ser expandidos +declaramos dos "concurrent tries maps" -- `open` contiene todos los nodos que deben ser expandidos ("nodos cabecera") mientras que `close` continene todos los nodos que ya han sido expandidos. Comenzamos -la búsqueda desde las esquinas del mapa en busca de un camino hasta el centro del mismo -- -e inicializamos el mapa `open` con los nodos apropiados. Iterativamamente, y en paralelo, +la búsqueda desde las esquinas del mapa en busca de un camino hasta el centro del mismo -- +e inicializamos el mapa `open` con los nodos apropiados. Iterativamamente, y en paralelo, expandimos todos los nodos presentes en el mapa `open` hasta que agotamos todos los elementos que necesitan ser expandidos. Cada vez que un nodo es expandido, se elimina del mapa `open` y se -añade en el mapa `closed`. Una vez finalizado el proceso, se muestra el camino desde el nodo +añade en el mapa `closed`. Una vez finalizado el proceso, se muestra el camino desde el nodo destino hasta el nodo inicial. - + val length = 1000 - + // define the Node type type Node = (Int, Int); type Parent = (Int, Int); - + // operations on the Node type def up(n: Node) = (n._1, n._2 - 1); def down(n: Node) = (n._1, n._2 + 1); def left(n: Node) = (n._1 - 1, n._2); def right(n: Node) = (n._1 + 1, n._2); - + // create a map and a target val target = (length / 2, length / 2); val map = Array.tabulate(length, length)((x, y) => (x % 3) != 0 || (y % 3) != 0 || (x, y) == target) def onMap(n: Node) = n._1 >= 0 && n._1 < length && n._2 >= 0 && n._2 < length - + // open list - the nodefront // closed list - nodes already processed val open = ParTrieMap[Node, Parent]() val closed = ParTrieMap[Node, Parent]() - + // add a couple of starting positions open((0, 0)) = null open((length - 1, length - 1)) = null open((0, length - 1)) = null open((length - 1, 0)) = null - + // greedy bfs path search while (open.nonEmpty && !open.contains(target)) { for ((node, parent) <- open) { @@ -111,7 +113,7 @@ destino hasta el nodo inicial. open.remove(node) } } - + // print path var pathnode = open(target) while (closed.contains(pathnode)) { @@ -121,11 +123,11 @@ destino hasta el nodo inicial. println() -Los "tries" concurrentes también soportan una operación atómica, no bloqueante y de +Los "tries" concurrentes también soportan una operación atómica, no bloqueante y de tiempo constante conocida como `snapshot`. Esta operación genera un nuevo `trie` -concurrente en el que se incluyen todos los elementos es un instante determinado de -tiempo, lo que en efecto captura el estado del "trie" en un punto específico. -Esta operación simplemente crea una nueva raíz para el "trie" concurrente. Posteriores +concurrente en el que se incluyen todos los elementos es un instante determinado de +tiempo, lo que en efecto captura el estado del "trie" en un punto específico. +Esta operación simplemente crea una nueva raíz para el "trie" concurrente. Posteriores actualizaciones reconstruyen, de manera perezosa, la parte del "trie" concurrente que se ha visto afectada por la actualización, manteniendo intacto el resto de la estructura. En primer lugar, esto implica que la propia operación de `snapshot` no es costosa en si misma @@ -133,7 +135,7 @@ puesto que no necesita copiar los elementos. En segundo lugar, dado que la optim "copy-and-write" solo copia determinadas partes del "trie" concurrente, las sucesivas actualizaciones escalan horizontalmente. El método `readOnlySnapshot` es ligeramente más efeciente que el método `snapshot`, pero retorna un mapa en modo solo lectura que no -puede ser modificado. Este tipo de estructura de datos soporta una operación atómica y en tiempo +puede ser modificado. Este tipo de estructura de datos soporta una operación atómica y en tiempo constante llamada `clear` la cual está basada en el anterior mecanismo de `snapshot`. Si desea conocer en más detalle cómo funcionan los "tries" concurrentes y el mecanismo de diff --git a/es/overviews/parallel-collections/custom-parallel-collections.md b/_es/overviews/parallel-collections/custom-parallel-collections.md similarity index 99% rename from es/overviews/parallel-collections/custom-parallel-collections.md rename to _es/overviews/parallel-collections/custom-parallel-collections.md index 632e683ca1..802d9f5286 100644 --- a/es/overviews/parallel-collections/custom-parallel-collections.md +++ b/_es/overviews/parallel-collections/custom-parallel-collections.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Creating Custom Parallel Collections discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 6 language: es --- @@ -29,7 +31,7 @@ are logically immutable sequences, we have parallel strings inherit Next, we define methods found in every immutable sequence: def apply(i: Int) = str.charAt(i) - + def length = str.length We have to also define the sequential counterpart of this parallel collection. @@ -42,12 +44,12 @@ name the splitter `ParStringSplitter` and have it inherit a sequence splitter, that is, `SeqSplitter[Char]`: def splitter = new ParStringSplitter(str, 0, str.length) - + class ParStringSplitter(private var s: String, private var i: Int, private val ntl: Int) extends SeqSplitter[Char] { final def hasNext = i < ntl - + final def next = { val r = s.charAt(i) i += 1 @@ -64,9 +66,9 @@ of elements this splitter has yet to traverse. Next, they have a method called `dup` which duplicates the current splitter. def remaining = ntl - i - + def dup = new ParStringSplitter(s, i, ntl) - + Finally, methods `split` and `psplit` are used to create splitters which traverse subsets of the elements of the current splitter. Method `split` has the contract that it returns a sequence of splitters which traverse disjoint, @@ -86,7 +88,7 @@ invalidates the current splitter. if (rem >= 2) psplit(rem / 2, rem - rem / 2) else Seq(this) } - + def psplit(sizes: Int*): Seq[ParStringSplitter] = { val splitted = new ArrayBuffer[ParStringSplitter] for (sz <- sizes) { @@ -158,28 +160,28 @@ live with this sequential bottleneck. var sz = 0 val chunks = new ArrayBuffer[StringBuilder] += new StringBuilder var lastc = chunks.last - + def size: Int = sz - + def +=(elem: Char): this.type = { lastc += elem sz += 1 this } - + def clear = { chunks.clear chunks += new StringBuilder lastc = chunks.last sz = 0 } - + def result: ParString = { val rsb = new StringBuilder for (sb <- chunks) rsb.append(sb) new ParString(rsb.toString) } - + def combine[U <: Char, NewTo >: ParString](other: Combiner[U, NewTo]) = if (other eq this) this else { val that = other.asInstanceOf[ParStringCombiner] sz += that.sz @@ -245,7 +247,7 @@ define the companion object of `ParString`. extends immutable.ParSeq[Char] with GenericParTemplate[Char, ParString] with ParSeqLike[Char, ParString, collection.immutable.WrappedString] { - + def companion = ParString Inside the companion object we provide an implicit evidence for the `CanBuildFrom` parameter. @@ -256,11 +258,11 @@ Inside the companion object we provide an implicit evidence for the `CanBuildFro def apply(from: ParString) = newCombiner def apply() = newCombiner } - + def newBuilder: Combiner[Char, ParString] = newCombiner - + def newCombiner: Combiner[Char, ParString] = new ParStringCombiner - + def apply(elems: Char*): ParString = { val cb = newCombiner cb ++= elems @@ -323,10 +325,3 @@ collection to return `false`. Such collections will fail to execute some methods which rely on splitters being strict, i.e. returning a correct value in the `remaining` method. Importantly, this does not effect methods used in for-comprehensions. - - - - - - - diff --git a/es/overviews/parallel-collections/overview.md b/_es/overviews/parallel-collections/overview.md similarity index 99% rename from es/overviews/parallel-collections/overview.md rename to _es/overviews/parallel-collections/overview.md index 3079e8816a..ebb1aa1c56 100644 --- a/es/overviews/parallel-collections/overview.md +++ b/_es/overviews/parallel-collections/overview.md @@ -1,5 +1,5 @@ --- -layout: overview +layout: multipage-overview title: Overview discourse: false diff --git a/es/overviews/parallel-collections/performance.md b/_es/overviews/parallel-collections/performance.md similarity index 95% rename from es/overviews/parallel-collections/performance.md rename to _es/overviews/parallel-collections/performance.md index ba3db20ce5..26d2237b10 100644 --- a/es/overviews/parallel-collections/performance.md +++ b/_es/overviews/parallel-collections/performance.md @@ -1,12 +1,13 @@ --- -layout: overview +layout: multipage-overview title: Midiendo el rendimiento discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 8 -outof: 8 language: es --- @@ -14,50 +15,50 @@ language: es Algunas veces el modelo de rendimiento de la JVM se complica debido a comentarios sobre el mismo, y como resultado de los mismos, se tienen concepciones equívocas del mismo. -Por diferentes motivos, determinado código podría ofrecer un rendimiento o escalabilidad +Por diferentes motivos, determinado código podría ofrecer un rendimiento o escalabilidad inferior a la esperada. A continuación ofrecemos algunos ejemplos. Uno de los principales motivos es que el proceso de compilación de una aplicación que se ejecuta sobre la JVM no es el mismo que el de un lenguaje compilado de manera estática -(véase \[[2][2]\]). Los compiladores de Java y Scala traducen el código fuente en *bytecode* y -el conjunto de optimizaciones que llevan a cabo es muy reducido. En la mayoría de las JVM +(véase \[[2][2]\]). Los compiladores de Java y Scala traducen el código fuente en *bytecode* y +el conjunto de optimizaciones que llevan a cabo es muy reducido. En la mayoría de las JVM modernas, una vez el bytecode es ejecutado, se convierte en código máquina dependiente de la arquitectura de la máquina subyacente. Este proceso es conocido como compilación "just-int-time". -Sin embargo, el nivel de optimización del código es muy bajo puesto que dicho proceso deber ser +Sin embargo, el nivel de optimización del código es muy bajo puesto que dicho proceso deber ser lo más rápido posible. Con el objetivo de evitar el proceso de recompilación, el llamado compilador HotSpot optimiza únicamente aquellas partes del código que son ejecutadas de manera frecuente. Esto supone que los desarrolladores de "benchmarks" deberán ser conscientes que los -programas podrían presentar rendimientos dispares en diferentes ejecuciones. Múltiples ejecuciones +programas podrían presentar rendimientos dispares en diferentes ejecuciones. Múltiples ejecuciones de un mismo fragmento de código (por ejemplo un método) podrían ofrecer rendimientos dispares en función de si se ha llevado a cabo un proceso de optimización del código entre dichas ejecuciones. Adicionalmente, la medición de los tiempos de ejecución de un fragmento de código podría incluir el tiempo en el que el propio compilador JIT lleva a cabo el proceso de optimizacion, falseando los resultados. Otro elemento "oculto" que forma parte de la JVM es la gestión automática de la memoria. De vez en cuando, -la ejecución de un programa es detenida para que el recolector de basura entre en funcionamiento. Si el +la ejecución de un programa es detenida para que el recolector de basura entre en funcionamiento. Si el programa que estamos analizando realiza alguna reserva de memoria (algo que la mayoría de programas hacen), el recolector de basura podría entrar en acción, posiblemente distorsionando los resultados. Con el objetivo de disminuir los efectos de la recolección de basura, el programa bajo estudio deberá ser ejecutado en múltiples ocasiones para disparar numerosas recolecciones de basura. -Una causa muy común que afecta de manera notable al rendimiento son las conversiones implícitas que se +Una causa muy común que afecta de manera notable al rendimiento son las conversiones implícitas que se llevan a cabo cuando se pasa un tipo primitivo a un método que espera un argumento genérico. En tiempo de ejecución, los tipos primitivos con convertidos en los objetos que los representan, de manera que puedan ser pasados como argumentos en los métodos que presentan parámetros genéricos. Este proceso implica un conjunto extra de reservas de memoria y es más lento, ocasionando nueva basura en el heap. Cuando nos referimos al rendimiento en colecciones paralelas la contención de la memoria es un problema muy -común, dado que el desarrollador no dispone de un control explícito sobre la asignación de los objetos. +común, dado que el desarrollador no dispone de un control explícito sobre la asignación de los objetos. De hecho, debido a los efectos ocasionados por la recolección de basura, la contención puede producirse en un estado posterior del ciclo de vida de la aplicación, una vez los objetos hayan ido circulando por la memoria. Estos efectos deberán ser tenidos en cuenta en el momento en que se esté desarrollando un benchmark. ## Ejemplo de microbenchmarking -Numerosos enfoques permiten evitar los anteriores efectos durante el periodo de medición. -En primer lugar, el microbenchmark debe ser ejecutado el número de veces necesario que -permita asegurar que el compilador just-in-time ha compilado a código máquina y que -ha optimizado el código resultante. Esto es lo que comunmente se conoce como fase de +Numerosos enfoques permiten evitar los anteriores efectos durante el periodo de medición. +En primer lugar, el microbenchmark debe ser ejecutado el número de veces necesario que +permita asegurar que el compilador just-in-time ha compilado a código máquina y que +ha optimizado el código resultante. Esto es lo que comunmente se conoce como fase de calentamiento. El microbenchmark debe ser ejecutado en una instancia independiente de la máquina virtual @@ -72,20 +73,20 @@ Finalmente, con el objetivo de reducir la posibilidad de que una recolección de durante la ejecución del benchmark, idealmente, debería producirse un ciclo de recolección de basura antes de la ejecución del benchmark, retrasando el siguiente ciclo tanto como sea posible. -El trait `scala.testing.Benchmark` se predefine en la librería estándar de Scala y ha sido diseñado con +El trait `scala.testing.Benchmark` se predefine en la librería estándar de Scala y ha sido diseñado con el punto anterior en mente. A continuación se muestra un ejemplo del benchmarking de un operación map sobre un "trie" concurrente: import collection.parallel.mutable.ParTrieMap import collection.parallel.ForkJoinTaskSupport - + object Map extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val partrie = ParTrieMap((0 until length) zip (0 until length): _*) - + partrie.tasksupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { partrie map { kv => kv @@ -95,7 +96,7 @@ sobre un "trie" concurrente: El método `run` encapsula el código del microbenchmark que será ejecutado de manera repetitiva y cuyos tiempos de ejecución serán medidos. El anterior objeto `Map` extiende -el trait `scala.testing.Benchmark` y parsea los parámetros `par` (nivel de paralelismo) y +el trait `scala.testing.Benchmark` y parsea los parámetros `par` (nivel de paralelismo) y `length` (número de elementos en el trie). Ambos parámetros son especificados a través de propiedades del sistema. @@ -109,7 +110,7 @@ la librería de Scala. Los argumentos `-Dpar` y `-Dlength` representan el nivel el número de elementos respectivamente. Por último, `10` indica el número de veces que el benchmark debería ser ejecutado en una misma máquina virtual. -Los tiempos de ejecución obtenidos estableciendo `par` a los valores `1`, `2`, `4` y `8` sobre un +Los tiempos de ejecución obtenidos estableciendo `par` a los valores `1`, `2`, `4` y `8` sobre un procesador quad-core i7 con hyperthreading habilitado son los siguientes: Map$ 126 57 56 57 54 54 54 53 53 53 @@ -131,7 +132,7 @@ El tamaño de la colección a partir de la cual la paralelización merece la pen depende de numerosos factores. Algunos de ellos, aunque no todos, son: - Arquitectura de la máquina. Diferentes tipos de CPU ofrecen diferente características - de rendimiento y escalabilidad. Por ejemplo, si la máquina es multicore o presenta + de rendimiento y escalabilidad. Por ejemplo, si la máquina es multicore o presenta múltiples procesadores comunicados mediante la placa base. - Versión y proveedor de la JVM. Diferentes máquinas virtuales llevan a cabo @@ -158,7 +159,7 @@ depende de numerosos factores. Algunos de ellos, aunque no todos, son: producir contención. - Gestión de memoria. Cuando se reserva espacio para muchos objectos es posible - que se dispare un ciclo de recolección de basura. Dependiendo de cómo se + que se dispare un ciclo de recolección de basura. Dependiendo de cómo se distribuyan las referencias de los objetos el ciclo de recolección puede llevar más o menos tiempo. @@ -168,27 +169,27 @@ colección. Para ilustrar de manera aproximada cuál debería ser el valor de di a continuación, se presenta un ejemplo de una sencilla operación de reducción, __sum__ en este caso, libre de efectos colaterales sobre un vector en un procesador i7 quad-core (hyperthreading deshabilitado) sobre JDK7 - + import collection.parallel.immutable.ParVector - + object Reduce extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val parvector = ParVector((0 until length): _*) - + parvector.tasksupport = new collection.parallel.ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { parvector reduce { (a, b) => a + b } } } - + object ReduceSeq extends testing.Benchmark { val length = sys.props("length").toInt val vector = collection.immutable.Vector((0 until length): _*) - + def run = { vector reduce { (a, b) => a + b @@ -219,25 +220,25 @@ En un ejemplo diferente, utilizamos `mutable.ParHashMap` y el método `map` (un y ejecutamos el siguiente benchmark en el mismo entorno: import collection.parallel.mutable.ParHashMap - + object Map extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val phm = ParHashMap((0 until length) zip (0 until length): _*) - + phm.tasksupport = new collection.parallel.ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { phm map { kv => kv } } } - + object MapSeq extends testing.Benchmark { val length = sys.props("length").toInt val hm = collection.mutable.HashMap((0 until length) zip (0 until length): _*) - + def run = { hm map { kv => kv @@ -273,4 +274,4 @@ los que serían requeridos por arrays o vectores). 2. [Dynamic compilation and performance measurement, Brian Goetz][2] [1]: http://www.ibm.com/developerworks/java/library/j-jtp02225/index.html "flawed-benchmark" - [2]: http://www.ibm.com/developerworks/library/j-jtp12214/ "dynamic-compilation" \ No newline at end of file + [2]: http://www.ibm.com/developerworks/library/j-jtp12214/ "dynamic-compilation" diff --git a/ru/overviews/parallel-collections/architecture.md b/_ru/overviews/parallel-collections/architecture.md similarity index 98% rename from ru/overviews/parallel-collections/architecture.md rename to _ru/overviews/parallel-collections/architecture.md index 2337cbeb28..d7652bb896 100644 --- a/ru/overviews/parallel-collections/architecture.md +++ b/_ru/overviews/parallel-collections/architecture.md @@ -1,15 +1,17 @@ --- -layout: overview +layout: multipage-overview title: Архитектура библиотеки параллельных коллекций discourse: false partof: parallel-collections +overview-name: Parallel Collections + language: ru num: 5 --- -Как и обычная библиотека коллекций Scala, библиотека параллельных коллекций содержит большое количество операций, для которых, в свою очередь, существует множество различных реализаций. И так же, как последовательная, параллельная библиотека избегает повторений кода путем реализации большинства операций посредством собственных "шаблонов", которые достаточно объявить один раз, а потом наследовать в различных реализациях параллельных коллекций. +Как и обычная библиотека коллекций Scala, библиотека параллельных коллекций содержит большое количество операций, для которых, в свою очередь, существует множество различных реализаций. И так же, как последовательная, параллельная библиотека избегает повторений кода путем реализации большинства операций посредством собственных "шаблонов", которые достаточно объявить один раз, а потом наследовать в различных реализациях параллельных коллекций. Преимущества этого подхода сильно облегчают **поддержку** и **расширяемость**. Поддержка станет простой и надежной, когда одна реализация операции над параллельной коллекцией унаследуется всеми параллельными коллекциями; исправления ошибок в этом случае сами распространятся вниз по иерархии классов, а не потребуют дублировать реализации. По тем же причинам всю библиотеку проще расширять-- новые классы коллекций наследуют большинство имеющихся операций. @@ -18,7 +20,7 @@ num: 5 Упомянутые выше "шаблонные" трейты реализуют большинство параллельных операций в терминах двух ключевых абстракций -- разделителей (`Splitter`) и компоновщиков (`Combiner`). -### Разделители +### Разделители Задача разделителя `Splitter`, как и предполагает имя, заключается в том, чтобы разбить параллельную коллекцию на непустые разделы. А основная идея-- в том, чтобы разбивать коллекцию на более мелкие части, пока их размер не станет подходящим для последовательной обработки. diff --git a/ru/overviews/parallel-collections/concrete-parallel-collections.md b/_ru/overviews/parallel-collections/concrete-parallel-collections.md similarity index 99% rename from ru/overviews/parallel-collections/concrete-parallel-collections.md rename to _ru/overviews/parallel-collections/concrete-parallel-collections.md index 377b0798ed..81c7807c89 100644 --- a/ru/overviews/parallel-collections/concrete-parallel-collections.md +++ b/_ru/overviews/parallel-collections/concrete-parallel-collections.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Конкретные классы параллельных коллекций discourse: false partof: parallel-collections +overview-name: Parallel Collections + language: ru num: 2 --- @@ -15,10 +17,10 @@ num: 2 scala> val pa = scala.collection.parallel.mutable.ParArray.tabulate(1000)(x => 2 * x + 1) pa: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 3, 5, 7, 9, 11, 13,... - + scala> pa reduce (_ + _) res0: Int = 1000000 - + scala> pa map (x => (x - 1) / 2) res1: scala.collection.parallel.mutable.ParArray[Int] = ParArray(0, 1, 2, 3, 4, 5, 6, 7,... @@ -32,11 +34,11 @@ num: 2 scala> val pv = scala.collection.parallel.immutable.ParVector.tabulate(1000)(x => x) pv: scala.collection.parallel.immutable.ParVector[Int] = ParVector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,... - + scala> pv filter (_ % 2 == 0) res0: scala.collection.parallel.immutable.ParVector[Int] = ParVector(0, 2, 4, 6, 8, 10, 12, 14, 16, 18,... -Неизменяемые векторы представлены 32-ичными деревьями (32-way trees), поэтому [разделители]({{ site.baseurl }}/ru/overviews/parallel-collections/architecture.html#core_abstractions) разбивают их, назначая по поддереву каждому новому разделителю. +Неизменяемые векторы представлены 32-ичными деревьями (32-way trees), поэтому [разделители]({{ site.baseurl }}/ru/overviews/parallel-collections/architecture.html#core_abstractions) разбивают их, назначая по поддереву каждому новому разделителю. [Компоновщики]({{ site.baseurl }}/ru/overviews/parallel-collections/architecture.html#core_abstractions) в настоящий момент хранят вектор из элементов и компонуют путем отложенного копирования. По этой причине методы трансформации менее масштабируемы по сравнению с теми же методами параллельного массива. Как только в будущем релизе Scala станет доступной операция конкатенации векторов, компоновщики станут образовываться путем конкатенации, и от этого методы трансформации станут гораздо более эффективными. Параллельный вектор является параллельным аналогом последовательной коллекции [Vector](http://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Vector.html), и преобразования одного в другое занимают постоянное время. @@ -47,7 +49,7 @@ num: 2 scala> 1 to 3 par res0: scala.collection.parallel.immutable.ParRange = ParRange(1, 2, 3) - + scala> 15 to 5 by -2 par res1: scala.collection.parallel.immutable.ParRange = ParRange(15, 13, 11, 9, 7, 5) @@ -59,7 +61,7 @@ num: 2 scala> val phs = scala.collection.parallel.mutable.ParHashSet(1 until 2000: _*) phs: scala.collection.parallel.mutable.ParHashSet[Int] = ParHashSet(18, 327, 736, 1045, 773, 1082,... - + scala> phs map (x => x * x) res0: scala.collection.parallel.mutable.ParHashSet[Int] = ParHashSet(2181529, 2446096, 99225, 2585664,... @@ -73,7 +75,7 @@ num: 2 scala> val phs = scala.collection.parallel.immutable.ParHashSet(1 until 1000: _*) phs: scala.collection.parallel.immutable.ParHashSet[Int] = ParSet(645, 892, 69, 809, 629, 365, 138, 760, 101, 479,... - + scala> phs map { x => x * x } sum res0: Int = 332833500 @@ -87,12 +89,12 @@ num: 2 scala> val numbers = scala.collection.parallel.mutable.ParTrieMap((1 until 100) zip (1 until 100): _*) map { case (k, v) => (k.toDouble, v.toDouble) } numbers: scala.collection.parallel.mutable.ParTrieMap[Double,Double] = ParTrieMap(0.0 -> 0.0, 42.0 -> 42.0, 70.0 -> 70.0, 2.0 -> 2.0,... - + scala> while (numbers.nonEmpty) { | numbers foreach { case (num, sqrt) => | val nsqrt = 0.5 * (sqrt + num / sqrt) | numbers(num) = nsqrt - | if (math.abs(nsqrt - sqrt) < 0.01) { + | if (math.abs(nsqrt - sqrt) < 0.01) { | println(num, nsqrt) | numbers.remove(num) | } @@ -162,4 +164,3 @@ num: 2 | **add** | Добавление нового элемента во множество или новой пары ключ/значение в ассоциативный массив. | | **remove** | Удаление элемента из множества или ключа из ассоциативного массива. | | **min** | Минимальный элемент множества или минимальный ключ ассоциативного массива. | - diff --git a/ru/overviews/parallel-collections/configuration.md b/_ru/overviews/parallel-collections/configuration.md similarity index 98% rename from ru/overviews/parallel-collections/configuration.md rename to _ru/overviews/parallel-collections/configuration.md index cd77b91642..9b616f8367 100644 --- a/ru/overviews/parallel-collections/configuration.md +++ b/_ru/overviews/parallel-collections/configuration.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Конфигурирование параллельных коллекций discourse: false partof: parallel-collections +overview-name: Parallel Collections + language: ru num: 7 --- @@ -21,13 +23,13 @@ num: 7 scala> import scala.collection.parallel._ import scala.collection.parallel._ - + scala> val pc = mutable.ParArray(1, 2, 3) pc: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 2, 3) - + scala> pc.tasksupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(2)) pc.tasksupport: scala.collection.parallel.TaskSupport = scala.collection.parallel.ForkJoinTaskSupport@4a5d484a - + scala> pc map { _ + 1 } res0: scala.collection.parallel.mutable.ParArray[Int] = ParArray(2, 3, 4) @@ -35,7 +37,7 @@ num: 7 scala> pc.tasksupport = new ThreadPoolTaskSupport() pc.tasksupport: scala.collection.parallel.TaskSupport = scala.collection.parallel.ThreadPoolTaskSupport@1d914a39 - + scala> pc map { _ + 1 } res1: scala.collection.parallel.mutable.ParArray[Int] = ParArray(2, 3, 4) @@ -44,9 +46,9 @@ num: 7 Чтобы реализовать собственный механизм поддержки задач, достаточно унаследовать трейт `TaskSupport` и реализовать следующие методы: def execute[R, Tp](task: Task[R, Tp]): () => R - + def executeAndWaitResult[R, Tp](task: Task[R, Tp]): R - + def parallelismLevel: Int Метод `execute` планирует асинхронное выполнение задачи и возвращает "future" в качестве ссылки к будущему результату выполнения. Метод `executeAndWait` делает то же самое, но возвращает результат только после завершения задачи. Метод `parallelismLevel` просто возвращает предпочитаемое количество ядер, которое будет использовано для вычислений. diff --git a/ru/overviews/parallel-collections/conversions.md b/_ru/overviews/parallel-collections/conversions.md similarity index 98% rename from ru/overviews/parallel-collections/conversions.md rename to _ru/overviews/parallel-collections/conversions.md index 9a98ab01af..8579774ec9 100644 --- a/ru/overviews/parallel-collections/conversions.md +++ b/_ru/overviews/parallel-collections/conversions.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Преобразования параллельных коллекций discourse: false partof: parallel-collections +overview-name: Parallel Collections + language: ru num: 3 --- diff --git a/ru/overviews/parallel-collections/ctries.md b/_ru/overviews/parallel-collections/ctries.md similarity index 98% rename from ru/overviews/parallel-collections/ctries.md rename to _ru/overviews/parallel-collections/ctries.md index aeb6d8a0ae..b463a48e21 100644 --- a/ru/overviews/parallel-collections/ctries.md +++ b/_ru/overviews/parallel-collections/ctries.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Многопоточные префиксные деревья discourse: false partof: parallel-collections +overview-name: Parallel Collections + language: ru num: 4 --- @@ -18,14 +20,14 @@ num: 4 case class Entry(num: Double) { var sqrt = num } - + val length = 50000 - + // готовим исходные данные val entries = (1 until length) map { num => Entry(num.toDouble) } val results = ParTrieMap() for (e <- entries) results += ((e.num, e)) - + // вычисляем квадратные корни while (results.nonEmpty) { for ((num, e) <- results) { @@ -41,35 +43,35 @@ num: 4 Другим примером является алгоритм поиска в ширину, который итеративно расширяет очередь перебираемых узлов до тех пор, пока или не будет найден целевой узел, или не закончатся узлы, за счет которых можно расширить поиск. Определим точку на двухмерной карте как кортеж значений `Int`. Обозначим как `map` двухмерный массив булевых значений, которые обозначают, занята соответствующая ячейка или нет. Затем объявим два многопоточных дерева-- `open`, которое содержит все точки, которые требуется раскрыть, и `closed`, в котором хранятся уже обработанные точки. Мы намерены начать поиск с углов карты и найти путь к центру-- инициализируем ассоциативный массив `open` подходящими точками. Затем будем раскрывать параллельно все точки, содержащиеся в ассоциативном массиве `open` до тех пор, пока больше не останется точек. Каждый раз, когда точка раскрывается, она удаляется из массива `open` и помещается в массив `closed`. Выполнив все это, выведем путь от целевого до стартового узла. - + val length = 1000 - + // объявляем тип Node type Node = (Int, Int); type Parent = (Int, Int); - + // операции над типом Node def up(n: Node) = (n._1, n._2 - 1); def down(n: Node) = (n._1, n._2 + 1); def left(n: Node) = (n._1 - 1, n._2); def right(n: Node) = (n._1 + 1, n._2); - + // создаем карту и целевую точку val target = (length / 2, length / 2); val map = Array.tabulate(length, length)((x, y) => (x % 3) != 0 || (y % 3) != 0 || (x, y) == target) def onMap(n: Node) = n._1 >= 0 && n._1 < length && n._2 >= 0 && n._2 < length - + // список open - фронт обработки // список closed - уже обработанные точки val open = ParTrieMap[Node, Parent]() val closed = ParTrieMap[Node, Parent]() - + // добавляем несколько стартовых позиций open((0, 0)) = null open((length - 1, length - 1)) = null open((0, length - 1)) = null open((length - 1, 0)) = null - + // "жадный" поиск в ширину while (open.nonEmpty && !open.contains(target)) { for ((node, parent) <- open) { @@ -86,7 +88,7 @@ num: 4 open.remove(node) } } - + // выводим путь var pathnode = open(target) while (closed.contains(pathnode)) { @@ -100,12 +102,12 @@ num: 4 Многопоточные префиксные деревья также поддерживают атомарную, неблокирующую(lock-free) операцию `snapshot`, выполнение которой осуществляется за постоянное время. Эта операция создает новое многопоточное дерево со всеми элементами на некоторый выбранный момент времени, создавая таким образом снимок состояния дерева в этот момент. На самом деле, операция `snapshot` просто создает новый корень дерева. Последующие изменения отложенно перестраивают ту часть многопоточного дерева, которая соответствует изменению, и оставляет нетронутой ту часть, которая не изменилась. Прежде всего это означает, что операция 'snapshot' сама по себе не затратна, так как не происходит копирования элементов. Кроме того, так как оптимизация "копирования при записи" создает копии только измененных частей дерева, последующие модификации горизонтально масштабируемы. -Метод `readOnlySnapshot` чуть более эффективен, чем метод `snapshot`, но он возвращает неизменяемый ассоциативный массив, который доступен только для чтения. Многопоточные деревья также поддерживают атомарную операцию постоянного времени `clear`, основанную на рассмотренном механизме снимков. +Метод `readOnlySnapshot` чуть более эффективен, чем метод `snapshot`, но он возвращает неизменяемый ассоциативный массив, который доступен только для чтения. Многопоточные деревья также поддерживают атомарную операцию постоянного времени `clear`, основанную на рассмотренном механизме снимков. Чтобы подробнее узнать о том, как работают многопоточные деревья и их снимки, смотрите \[[1][1]\] и \[[2][2]\]. На рассмотренном механизме снимков основана работа итераторов многопоточных деревьев. Прежде чем будет создан объект-итератор, берется снимок многопоточного дерева. Таким образом, итератор перебирает только те элементы дерева, которые присутствовали на момент создания снимка. Фактически, итераторы используют те снимки, которые дают доступ только на чтение. -На том же механизме снимков основана операция `size`. В качестве примитивной реализации этой операции можно просто создать итератор (то есть, снимок) и перебрать все элементы, подсчитывая их. Таким образом, каждый вызов операции `size` будет требовать времени, прямо пропорционального числу элементов. Однако, многопоточные деревья в целях оптимизации кэшируют размеры своих отдельных частей, тем самым уменьшая временную сложность метода `size` до амортизированно-логарифмической. В результате получается, что если вызвать метод `size` один раз, можно осуществлять последующие вызовы `size` затрачивая минимум ресурсов, вычисляя, как правило, размеры только тех частей, которые изменились после последнего вызова `size`. Кроме того, вычисление размера параллельных многопоточных деревьев выполняется параллельно. +На том же механизме снимков основана операция `size`. В качестве примитивной реализации этой операции можно просто создать итератор (то есть, снимок) и перебрать все элементы, подсчитывая их. Таким образом, каждый вызов операции `size` будет требовать времени, прямо пропорционального числу элементов. Однако, многопоточные деревья в целях оптимизации кэшируют размеры своих отдельных частей, тем самым уменьшая временную сложность метода `size` до амортизированно-логарифмической. В результате получается, что если вызвать метод `size` один раз, можно осуществлять последующие вызовы `size` затрачивая минимум ресурсов, вычисляя, как правило, размеры только тех частей, которые изменились после последнего вызова `size`. Кроме того, вычисление размера параллельных многопоточных деревьев выполняется параллельно. ## Ссылки diff --git a/ru/overviews/parallel-collections/custom-parallel-collections.md b/_ru/overviews/parallel-collections/custom-parallel-collections.md similarity index 99% rename from ru/overviews/parallel-collections/custom-parallel-collections.md rename to _ru/overviews/parallel-collections/custom-parallel-collections.md index a42d3e1326..6fd6fa9f58 100644 --- a/ru/overviews/parallel-collections/custom-parallel-collections.md +++ b/_ru/overviews/parallel-collections/custom-parallel-collections.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Создание пользовательской параллельной коллекции discourse: false partof: parallel-collections +overview-name: Parallel Collections + language: ru num: 6 --- @@ -21,7 +23,7 @@ num: 6 Затем определим методы, которые есть в любой неизменяемой последовательности: def apply(i: Int) = str.charAt(i) - + def length = str.length Кроме того, мы должны решить, что будем возвращать в качестве последовательного аналога нашей параллельной коллекции. Пусть это будет класс `WrappedString`: @@ -31,12 +33,12 @@ num: 6 И наконец, требуется задать разделитель для наших параллельных строк. Назовем его `ParStringSplitter` и сделаем его потомком разделителя последовательностей, то есть типа `SeqSplitter[Char]`: def splitter = new ParStringSplitter(str, 0, str.length) - + class ParStringSplitter(private var s: String, private var i: Int, private val ntl: Int) extends SeqSplitter[Char] { final def hasNext = i < ntl - + final def next = { val r = s.charAt(i) i += 1 @@ -48,9 +50,9 @@ num: 6 Итераторы (или разделители) параллельных коллекций требуют еще несколько методов помимо `next` и `hasNext`, характерных для итераторов последовательных коллекций. Для начала, у них есть метод `remaining`, возвращающий количество элементов, которые данному разделителю еще предстоит перебрать. Затем, метод `dup`, дублирующий текущий разделитель. def remaining = ntl - i - + def dup = new ParStringSplitter(s, i, ntl) - + И наконец, методы `split` и `psplit`, которые используются для создания разделителей, перебирающих подмножества элементов текущего разделителя. Для метода `split` действует соглашение, что он возвращает последовательность разделителей, перебирающих непересекающиеся подмножества элементов текущего разделителя, ни одно из которых не является пустым. Если текущий разделитель покрывает один или менее элементов, `split` возвращает саму последовательность этого разделителя. Метод `psplit` должен возвращать последовательность разделителей, перебирающих точно такое количество элементов, которое задано значениями размеров, указанных параметром `sizes`. Если параметр `sizes` требует отделить меньше элементов, чем покрыто текущим разделителем, то дополнительный разделитель со всеми остальными элементами размещается в конце последовательности. Если в параметре `sizes` указано больше элементов, чем содержится в текущем разделителе, для каждого размера, на который не хватило элементов, будет добавлен пустой разделитель. Наконец, вызов `split` или `psplit` делает текущий разделитель недействительным. def split = { @@ -58,7 +60,7 @@ num: 6 if (rem >= 2) psplit(rem / 2, rem - rem / 2) else Seq(this) } - + def psplit(sizes: Int*): Seq[ParStringSplitter] = { val splitted = new ArrayBuffer[ParStringSplitter] for (sz <- sizes) { @@ -98,28 +100,28 @@ num: 6 var sz = 0 val chunks = new ArrayBuffer[StringBuilder] += new StringBuilder var lastc = chunks.last - + def size: Int = sz - + def +=(elem: Char): this.type = { lastc += elem sz += 1 this } - + def clear = { chunks.clear chunks += new StringBuilder lastc = chunks.last sz = 0 } - + def result: ParString = { val rsb = new StringBuilder for (sb <- chunks) rsb.append(sb) new ParString(rsb.toString) } - + def combine[U <: Char, NewTo >: ParString](other: Combiner[U, NewTo]) = if (other eq this) this else { val that = other.asInstanceOf[ParStringCombiner] sz += that.sz @@ -130,16 +132,16 @@ num: 6 } -## Как реализовать собственный компоновщик? +## Как реализовать собственный компоновщик? Тут нет стандартного рецепта, -- все зависит от имеющейся структуры данных, и обычно требует изобретательности со стороны того, кто пишет реализацию. Тем не менее, можно выделить несколько подходов, которые обычно применяются: 1. Конкатенация и объединение. Некоторые структуры данных позволяют реализовать эти операции эффективно (обычно с логарифмической сложностью), и если требуемая коллекция представлена такой структурой данных, ее компоновщик может быть самой такой коллекцией. Особенно хорошо этот подход работает для подвешенных деревьев (finger trees), веревок (ropes) и различных видов куч. 2. Двухфазное выполнение. Подход, применяемый в случае параллельных массивов и параллельных хэш-таблиц; он предполагает, что элементы могут быть эффективно рассортированы по готовым для конкатенации блокам, из которых результирующая структура данных может быть построена параллельно. В первую фазу блоки заполняются независимо различными процессорами, и в конце просто соединяются конкатенацией. Во вторую фазу происходит выделение памяти для целевой структуры данных, и после этого различные процессоры заполняют различные ее части, используя элементы непересекающихся блоков. -Следует принять меры для того, чтобы различные процессоры никогда не изменяли одну и ту же часть структуры данных, иначе не избежать трудноуловимых, связанных с многопоточностью ошибок. Такой подход легко применить к последовательностям с произвольным доступом, как было показано в предыдущем разделе. +Следует принять меры для того, чтобы различные процессоры никогда не изменяли одну и ту же часть структуры данных, иначе не избежать трудноуловимых, связанных с многопоточностью ошибок. Такой подход легко применить к последовательностям с произвольным доступом, как было показано в предыдущем разделе. -3. Многопоточная структура данных. Так как последние два подхода, в сущности, не требуют использования примитивных механизмов синхронизации, предполагается, что структура будет строиться несколькими потоками так, что два различных процессора никогда не будут изменять одну и ту же область памяти. Существует большое количество многопоточных структур данных, которые могут безопасно изменяться несколькими процессорами одновременно, среди таких можно упомянуть многопоточные списки с пропусками (skip lists), многопоточные хэш-таблицы, `split-ordered` списки и многопоточные АВЛ-деревья. +3. Многопоточная структура данных. Так как последние два подхода, в сущности, не требуют использования примитивных механизмов синхронизации, предполагается, что структура будет строиться несколькими потоками так, что два различных процессора никогда не будут изменять одну и ту же область памяти. Существует большое количество многопоточных структур данных, которые могут безопасно изменяться несколькими процессорами одновременно, среди таких можно упомянуть многопоточные списки с пропусками (skip lists), многопоточные хэш-таблицы, `split-ordered` списки и многопоточные АВЛ-деревья. При этом требуется следить, чтобы у выбранной многопоточной структуры был горизонтально масштабируемый метод вставки. У многопоточных параллельных коллекций компоновщик может быть представлен самой коллекцией, и единственный его экземпляр обычно используется всеми процессорами, занятыми в выполнении параллельной операции. ## Интеграция с фреймворком коллекций @@ -150,7 +152,7 @@ num: 6 extends immutable.ParSeq[Char] with GenericParTemplate[Char, ParString] with ParSeqLike[Char, ParString, collection.immutable.WrappedString] { - + def companion = ParString Внутрь объекта-компаньона помещаем скрытый параметр-доказательство `CanBuildFrom`. @@ -161,11 +163,11 @@ num: 6 def apply(from: ParString) = newCombiner def apply() = newCombiner } - + def newBuilder: Combiner[Char, ParString] = newCombiner - + def newCombiner: Combiner[Char, ParString] = new ParStringCombiner - + def apply(elems: Char*): ParString = { val cb = newCombiner cb ++= elems @@ -183,7 +185,7 @@ num: 6 Чтобы добиться лучшей балансировки нагрузки, разделители делятся на более мелкие разделители. По умолчанию решение о том, что дальнейшее разделение не требуется, принимается на основе информации, возвращенной методом `remaining`. Для некоторых коллекций вызов метода `remaining` может быть затратным, и решение о разделении лучше принять другими способами. В этом случае нужно перегрузить метод `shouldSplitFurther` разделителя. -В реализации по умолчанию разделитель делится, если число оставшихся элементов больше, чем размер коллекции деленный на взятый восемь раз уровень параллелизма. +В реализации по умолчанию разделитель делится, если число оставшихся элементов больше, чем размер коллекции деленный на взятый восемь раз уровень параллелизма. def shouldSplitFurther[S](coll: ParIterable[S], parallelismLevel: Int) = remaining > thresholdFromSize(coll.size, parallelismLevel) diff --git a/ru/overviews/parallel-collections/overview.md b/_ru/overviews/parallel-collections/overview.md similarity index 99% rename from ru/overviews/parallel-collections/overview.md rename to _ru/overviews/parallel-collections/overview.md index 44e5e692c9..5d2f43f083 100644 --- a/ru/overviews/parallel-collections/overview.md +++ b/_ru/overviews/parallel-collections/overview.md @@ -1,10 +1,12 @@ --- -layout: overview +layout: multipage-overview title: Обзор discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 1 language: ru --- @@ -25,7 +27,7 @@ language: ru val list = (1 to 10000).toList list.map(_ + 42) - + Чтобы выполнить ту же самую операцию параллельно, требуется просто вызвать метод `par` на последовательной коллекции `list`. После этого можно работать с параллельной коллекцией так же, как и с последовательной. То есть, пример выше примет вид: @@ -56,7 +58,7 @@ _Примечание:_ Некоторые из последующих прим scala> val lastNames = List("Smith","Jones","Frankenstein","Bach","Jackson","Rodin").par lastNames: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Frankenstein, Bach, Jackson, Rodin) - + scala> lastNames.map(_.toUpperCase) res0: scala.collection.parallel.immutable.ParSeq[String] = ParVector(SMITH, JONES, FRANKENSTEIN, BACH, JACKSON, RODIN) @@ -66,7 +68,7 @@ _Примечание:_ Некоторые из последующих прим scala> val parArray = (1 to 10000).toArray.par parArray: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 2, 3, ... - + scala> parArray.fold(0)(_ + _) res0: Int = 50005000 @@ -76,7 +78,7 @@ _Примечание:_ Некоторые из последующих прим scala> val lastNames = List("Smith","Jones","Frankenstein","Bach","Jackson","Rodin").par lastNames: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Frankenstein, Bach, Jackson, Rodin) - + scala> lastNames.filter(_.head >= 'J') res0: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Jackson, Rodin) @@ -86,7 +88,7 @@ _Примечание:_ Некоторые из последующих прим В общем виде, есть два варианта создания параллельной коллекции: -Первый, с использованием ключевого слова `new` и подходящего оператора import: +Первый, с использованием ключевого слова `new` и подходящего оператора import: import scala.collection.parallel.immutable.ParVector val pv = new ParVector[Int] @@ -112,7 +114,7 @@ _На заметку:_ Коллекции, являющиеся последов 1. **Операции, имеющие побочные эффекты, могут нарушать детерминизм** 2. **Неассоциативные операции могут нарушать детерминизм** -### Операции, имеющие побочные эффекты. +### Операции, имеющие побочные эффекты. Вследствие использования фреймворком параллельных коллекций семантики _многопоточного_ выполнения, в большинстве случаев для соблюдения детерминизма требуется избегать выполнения на коллекциях операций, которые выполняют побочные действия. В качестве простого примера попробуем использовать метод доступа `foreach` для увеличения значения переменной `var`, объявленной вне замыкания, которое было передано `foreach`. @@ -121,22 +123,22 @@ _На заметку:_ Коллекции, являющиеся последов scala> val list = (1 to 1000).toList.par list: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3,… - + scala> list.foreach(sum += _); sum res01: Int = 467766 - + scala> var sum = 0 sum: Int = 0 - + scala> list.foreach(sum += _); sum res02: Int = 457073 - + scala> var sum = 0 sum: Int = 0 - + scala> list.foreach(sum += _); sum res03: Int = 468520 - + В примере видно, что несмотря на то, что каждый раз `sum` инициализируется в 0, при каждом новом вызове `foreach` на предложенном `list`, `sum` получает различное значение. Источником недетерминизма является так называемая _гонка_-- параллельное чтение/запись одной и той же изменяемой переменной. В примере выше возможен случай, когда два потока прочитают _одно и то же_ значение переменной `sum`, потратят некоторое время на выполнение операции над этим значением `sum`, а потом попытаются записать новое значение в `sum`, что может привести к перезаписи (а следовательно, к потере) значимого результата, как показано ниже: @@ -154,27 +156,26 @@ _На заметку:_ Коллекции, являющиеся последов scala> val list = (1 to 1000).toList.par list: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3,… - + scala> list.reduce(_-_) res01: Int = -228888 - + scala> list.reduce(_-_) res02: Int = -61000 - + scala> list.reduce(_-_) res03: Int = -331818 - + В примере выше, мы берем `ParVector[Int]`, вызываем функцию `reduce`, и передаем ей `_-_`, которая просто берет два неименованных элемента, и вычитает один из другого. Вследствие того, что фреймворк параллельных коллекций порождает потоки и независимо выполняет `reduce(_-_)` на разных частях коллекции, результат двух запусков `reduce(_-_)` на одной и той же коллекции не будет одним и тем же. -_Примечание:_ Часто возникает мысль, что так же, как и в случае с неассоциативными, некоммутативные операции, переданные в более высокую уровнем функцию на параллельной коллекции, приводят к недетеминированному поведению. Это неверно, простой пример -- конкатенация строк -- ассоциативная, но некоммутативная операция: +_Примечание:_ Часто возникает мысль, что так же, как и в случае с неассоциативными, некоммутативные операции, переданные в более высокую уровнем функцию на параллельной коллекции, приводят к недетеминированному поведению. Это неверно, простой пример -- конкатенация строк -- ассоциативная, но некоммутативная операция: scala> val strings = List("abc","def","ghi","jk","lmnop","qrs","tuv","wx","yz").par - strings: scala.collection.parallel.immutable.ParSeq[java.lang.String] = ParVector(abc, def, ghi, jk, lmnop, qrs, tuv, wx, yz) - + strings: scala.collection.parallel.immutable.ParSeq[java.lang.String] = ParVector(abc, def, ghi, jk, lmnop, qrs, tuv, wx, yz) + scala> val alphabet = strings.reduce(_++_) alphabet: java.lang.String = abcdefghijklmnopqrstuvwxyz _"Неупорядоченная"_ семантика параллельных коллекций означает только то, что операции будут выполнены не по порядку (во _временном_ отношении. То есть, не последовательно), она не означает, что результат будет "*перемешан*" относительно изначального порядка (в _пространственном_ отношении). Напротив, результат будет практически всегда пересобран _по-порядку_-- то есть, параллельная коллекция, разбитая на части в порядке A, B, C, будет снова объединена в том же порядке A, B, C, а не в каком-то произвольном, например, B, C, A. -Если требуется больше информации о том, как разделяются и комбинируются операции на различных типах коллекций, посетите раздел [Архитектура]({{ site.baseurl }}/ru/overviews/parallel-collections/architecture.html) этого руководства. - +Если требуется больше информации о том, как разделяются и комбинируются операции на различных типах коллекций, посетите раздел [Архитектура]({{ site.baseurl }}/ru/overviews/parallel-collections/architecture.html) этого руководства. diff --git a/ru/overviews/parallel-collections/performance.md b/_ru/overviews/parallel-collections/performance.md similarity index 98% rename from ru/overviews/parallel-collections/performance.md rename to _ru/overviews/parallel-collections/performance.md index b55cd366b3..f1ae027304 100644 --- a/ru/overviews/parallel-collections/performance.md +++ b/_ru/overviews/parallel-collections/performance.md @@ -1,20 +1,21 @@ --- -layout: overview +layout: multipage-overview title: Измерение производительности discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 8 -outof: 8 language: ru --- ## Производительность JVM -При описании модели производительности выполнения кода на JVM иногда ограничиваются несколькими комментариями, и как результат-- не всегда становится хорошо понятно, что в силу различных причин написанный код может быть не таким производительным или расширяемым, как можно было бы ожидать. В этой главе будут приведены несколько примеров. +При описании модели производительности выполнения кода на JVM иногда ограничиваются несколькими комментариями, и как результат-- не всегда становится хорошо понятно, что в силу различных причин написанный код может быть не таким производительным или расширяемым, как можно было бы ожидать. В этой главе будут приведены несколько примеров. -Одной из причин является то, что процесс компиляции выполняющегося на JVM приложения не такой, как у языка со статической компиляцией (как можно увидеть здесь \[[2][2]\]). Компиляторы Java и Scala обходятся минимальной оптимизацией при преобразовании исходных текстов в байткод JVM. При первом запуске на большинстве современных виртуальных Java-машин байткод преобразуется в машинный код той архитектуры, на которой он запущен. Это преобразование называется компиляцией "на лету" или JIT-компиляцией (JIT от just-in-time). Однако из-за того, что компиляция "на лету" должна быть быстрой, уровень оптимизации при такой компиляции остается низким. Более того, чтобы избежать повторной компиляции, компилятор HotSpot оптимизирует только те участки кода, которые выполняются часто. Поэтому тот, кто пишет тест производительности, должен учитывать, что программа может показывать разную производительность каждый раз, когда ее запускают: многократное выполнение одного и того же куска кода (то есть, метода) на одном экземпляре JVM может демонстрировать очень разные результаты замеров производительности в зависимости от того, оптимизировался ли определенный код между запусками. Более того, измеренное время выполнения некоторого участка кода может включать в себя время, за которое произошла сама оптимизация JIT-компилятором, что сделает результат измерения нерепрезентативным. +Одной из причин является то, что процесс компиляции выполняющегося на JVM приложения не такой, как у языка со статической компиляцией (как можно увидеть здесь \[[2][2]\]). Компиляторы Java и Scala обходятся минимальной оптимизацией при преобразовании исходных текстов в байткод JVM. При первом запуске на большинстве современных виртуальных Java-машин байткод преобразуется в машинный код той архитектуры, на которой он запущен. Это преобразование называется компиляцией "на лету" или JIT-компиляцией (JIT от just-in-time). Однако из-за того, что компиляция "на лету" должна быть быстрой, уровень оптимизации при такой компиляции остается низким. Более того, чтобы избежать повторной компиляции, компилятор HotSpot оптимизирует только те участки кода, которые выполняются часто. Поэтому тот, кто пишет тест производительности, должен учитывать, что программа может показывать разную производительность каждый раз, когда ее запускают: многократное выполнение одного и того же куска кода (то есть, метода) на одном экземпляре JVM может демонстрировать очень разные результаты замеров производительности в зависимости от того, оптимизировался ли определенный код между запусками. Более того, измеренное время выполнения некоторого участка кода может включать в себя время, за которое произошла сама оптимизация JIT-компилятором, что сделает результат измерения нерепрезентативным. Кроме этого, результат может включать в себя потраченное на стороне JVM время на осуществление операций автоматического управления памятью. Время от времени выполнение программы прерывается и вызывается сборщик мусора. Если исследуемая программа размещает хоть какие-нибудь данные в куче (а большинство программ JVM размещают), значит сборщик мусора должен запуститься, возможно, исказив при этом результаты измерений. Можно нивелировать влияние сборщика мусора на результат, запустив измеряемую программу множество раз, и тем самым спровоцировав большое количество циклов сборки мусора. @@ -30,20 +31,20 @@ language: ru Кроме того, запуск следует производить на серверной версии HotSpot JVM, которая выполняет более агрессивную оптимизацию. -Наконец, чтобы уменьшить вероятность того, что сборка мусора произойдет посреди микротеста, лучше всего добиться выполнения цикла сборки мусора перед началом теста, а следующий цикл отложить настолько, насколько это возможно. +Наконец, чтобы уменьшить вероятность того, что сборка мусора произойдет посреди микротеста, лучше всего добиться выполнения цикла сборки мусора перед началом теста, а следующий цикл отложить настолько, насколько это возможно. В стандартной библиотеке Scala предопределен трейт `scala.testing.Benchmark`, спроектированный с учетом приведенных выше соображений. Ниже приведен пример тестирования производительности операции `map` многопоточного префиксного дерева: import collection.parallel.mutable.ParTrieMap import collection.parallel.ForkJoinTaskSupport - + object Map extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val partrie = ParTrieMap((0 until length) zip (0 until length): _*) - + partrie.tasksupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { partrie map { kv => kv @@ -85,25 +86,25 @@ language: ru Даже рассматривая вышеперечисленные факторы по отдельности, не так-то просто рассуждать о влиянии каждого, а тем более дать точный ответ, каким же должен быть размер коллекции. Чтобы в первом приближении проиллюстрировать, каким же он должен быть, приведем пример выполнения быстрой и не вызывающей побочных эффектов операции сокращения параллельного вектора (в нашем случае-- суммированием) на четырехъядерном процессоре i7 (без использования гиперпоточности) на JDK7: import collection.parallel.immutable.ParVector - + object Reduce extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val parvector = ParVector((0 until length): _*) - + parvector.tasksupport = new collection.parallel.ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { parvector reduce { (a, b) => a + b } } } - + object ReduceSeq extends testing.Benchmark { val length = sys.props("length").toInt val vector = collection.immutable.Vector((0 until length): _*) - + def run = { vector reduce { (a, b) => a + b @@ -132,25 +133,25 @@ language: ru В качестве еще одного примера возьмем метод `map` (метод трансформации) коллекции `mutable.ParHashMap` и запустим следующий тест производительности в той же среде: import collection.parallel.mutable.ParHashMap - + object Map extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt val phm = ParHashMap((0 until length) zip (0 until length): _*) - + phm.tasksupport = new collection.parallel.ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(par)) - + def run = { phm map { kv => kv } } } - + object MapSeq extends testing.Benchmark { val length = sys.props("length").toInt val hm = collection.mutable.HashMap((0 until length) zip (0 until length): _*) - + def run = { hm map { kv => kv @@ -185,6 +186,3 @@ language: ru [1]: http://www.ibm.com/developerworks/java/library/j-jtp02225/index.html "flawed-benchmark" [2]: http://www.ibm.com/developerworks/library/j-jtp12214/ "dynamic-compilation" - - - From 7267efb29b60b63338eacd10bd688bca085221a2 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Wed, 26 Jul 2017 21:14:21 +0200 Subject: [PATCH 062/112] Working on single-page translation documentation --- _data/overviews.yml | 3 +- .../parallel-collections/overview.md | 2 + _includes/masthead-documentation.html | 2 +- .../sidebar-toc-singlepage-overview.html | 10 +++ _ja/overviews/core/collections.md | 7 -- _ja/overviews/core/futures.md | 68 +++++++++---------- _ja/overviews/core/macros.md | 11 --- _ja/overviews/core/parallel-collections.md | 9 --- _ja/overviews/core/reflection.md | 11 --- _ja/overviews/core/string-interpolation.md | 19 +++--- _ja/overviews/core/value-classes.md | 10 +-- _overviews/core/actors-migration-guide.md | 2 + _overviews/core/actors.md | 2 + .../core/architecture-of-scala-collections.md | 2 + .../binary-compatibility-of-scala-releases.md | 2 + _overviews/core/futures.md | 2 + _overviews/core/implicit-classes.md | 2 + _overviews/core/string-interpolation.md | 2 + _overviews/core/value-classes.md | 2 + 19 files changed, 79 insertions(+), 89 deletions(-) delete mode 100644 _ja/overviews/core/collections.md delete mode 100644 _ja/overviews/core/macros.md delete mode 100644 _ja/overviews/core/parallel-collections.md delete mode 100644 _ja/overviews/core/reflection.md diff --git a/_data/overviews.yml b/_data/overviews.yml index abae1135be..9c9e4e0352 100644 --- a/_data/overviews.yml +++ b/_data/overviews.yml @@ -1,3 +1,4 @@ + - category: Core Scala description: "Guides and overviews covering central libraries in the Scala standard library, core language features, and more." overviews: @@ -67,8 +68,8 @@ description: "Reference material on core Scala tools like Scaladoc and the Scala REPL." overviews: - title: Scaladoc - icon: book url: "scaladoc/overview.html" + icon: book description: "Scala's API documentation generation tool." subdocs: - title: Overview diff --git a/_es/overviews/parallel-collections/overview.md b/_es/overviews/parallel-collections/overview.md index ebb1aa1c56..b5f13762f2 100644 --- a/_es/overviews/parallel-collections/overview.md +++ b/_es/overviews/parallel-collections/overview.md @@ -5,6 +5,8 @@ title: Overview discourse: false partof: parallel-collections +overview-name: Parallel Collections + num: 1 language: es --- diff --git a/_includes/masthead-documentation.html b/_includes/masthead-documentation.html index a773e89d0c..1ac8cc7230 100644 --- a/_includes/masthead-documentation.html +++ b/_includes/masthead-documentation.html @@ -16,4 +16,4 @@
    {{link.title}}
    - \ No newline at end of file + diff --git a/_includes/sidebar-toc-singlepage-overview.html b/_includes/sidebar-toc-singlepage-overview.html index dd8b53074c..bc417951a7 100644 --- a/_includes/sidebar-toc-singlepage-overview.html +++ b/_includes/sidebar-toc-singlepage-overview.html @@ -13,6 +13,16 @@
    Contents
  • {{ lang.name }}
  • {% endfor %} + {% elsif page.language %} + {% assign engPath = page.id | remove_first: "/" | remove_first: page.language | append: '.html' %} + {% assign engPg = site.overviews | where: 'partof', page.partof | first %} + {% endif %}
    diff --git a/_ja/overviews/core/collections.md b/_ja/overviews/core/collections.md deleted file mode 100644 index 15219520aa..0000000000 --- a/_ja/overviews/core/collections.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: overview -overview: collections -partof: collections -language: ja -title: Scala コレクションライブラリ ---- diff --git a/_ja/overviews/core/futures.md b/_ja/overviews/core/futures.md index f05a980e1a..11b88dcda3 100644 --- a/_ja/overviews/core/futures.md +++ b/_ja/overviews/core/futures.md @@ -1,11 +1,11 @@ --- -layout: overview -label-color: success -label-text: New in 2.10 -overview: futures -language: ja +layout: singlepage-overview title: Future と Promise +partof: futures + +language: ja + discourse: false --- @@ -28,8 +28,8 @@ The futures and promises API builds upon the notion of an `ExecutionContext`, an execution environment designed to manage resources such as thread pools between parallel frameworks and libraries (detailed in an accompanying SIP, forthcoming). Futures and -promises are created through such `ExecutionContext`s. For example, this makes it possible, -in the case of an application which requires blocking futures, for an underlying execution +promises are created through such `ExecutionContext`s. For example, this makes it possible, +in the case of an application which requires blocking futures, for an underlying execution environment to resize itself if necessary to guarantee progress. --> @@ -59,7 +59,7 @@ Future オブジェクトを返すメソッドだということだ。 import scala.concurrent._ import ExecutionContext.Implicits.global - + val session = socialNetwork.createSessionFor("user", credentials) val f: Future[List[Friend]] = Future { session.getFriends() @@ -83,7 +83,7 @@ Future オブジェクトを返すメソッドだということだ。 試みが失敗すると、例外が発生するかもしれない。 以下の例では、`session` 変数の初期化が不正なため、`future` ブロック内の計算が `NullPointerException` を投げる。この Future `f` は、この例外とともに失敗する: - + val session = null val f: Future[List[Friend]] = Future { session.getFriends @@ -134,7 +134,7 @@ Future の実装の多くは、Future の結果を知りたくなったクライ val f: Future[List[String]] = Future { session.getRecentPosts } - + f onComplete { case Success(posts) => for (post <- posts) println(post) case Failure(t) => println("エラーが発生した: " + t.getMessage) @@ -146,7 +146,7 @@ Future の実装の多くは、Future の結果を知りたくなったクライ val f: Future[List[String]] = Future { session.getRecentPosts } - + f onSuccess { case posts => for (post <- posts) println(post) } @@ -156,7 +156,7 @@ Future の実装の多くは、Future の結果を知りたくなったクライ val f: Future[List[String]] = Future { session.getRecentPosts } - + f onFailure { case t => println("エラーが発生した: " + t.getMessage) } @@ -173,7 +173,7 @@ Future の実装の多くは、Future の結果を知りたくなったクライ val f = Future { 2 / 0 } - + f onFailure { case npe: NullPointerException => println("これが表示されているとしたらビックリ。") @@ -258,13 +258,13 @@ Future 内の値が利用可能となることを必要とするため、Future val rateQuote = Future { connection.getCurrentValue(USD) } - + rateQuote onSuccess { case quote => val purchase = Future { if (isProfitable(quote)) connection.buy(amount, quote) else throw new Exception("有益ではない") } - + purchase onSuccess { case _ => println(amount + " USD を購入した") } @@ -297,12 +297,12 @@ Future の投射はコレクションの投射と同様に考えることがで val rateQuote = Future { connection.getCurrentValue(USD) } - - val purchase = rateQuote map { quote => + + val purchase = rateQuote map { quote => if (isProfitable(quote)) connection.buy(amount, quote) else throw new Exception("有益ではない") } - + purchase onSuccess { case _ => println(amount + " USD を購入した") } @@ -333,13 +333,13 @@ Future の設計指針の 1つは for 内包表記から利用できるように val usdQuote = Future { connection.getCurrentValue(USD) } val chfQuote = Future { connection.getCurrentValue(CHF) } - + val purchase = for { usd <- usdQuote chf <- chfQuote if isProfitable(usd, chf) } yield connection.buy(amount, chf) - + purchase onSuccess { case _ => println(amount + " CHF を購入した") } @@ -417,9 +417,9 @@ Future は同じ `Throwable` とともに失敗する。 } map { chf => "値: " + chf + "CHF" } - + val anyQuote = usdQuote fallbackTo chfQuote - + anyQuote onSuccess { println(_) } `andThen` コンビネータは副作用の目的のためだけに用いられる。 @@ -501,17 +501,17 @@ Future の結果に対してブロックする方法を以下に具体例で説 import scala.concurrent._ import scala.concurrent.duration._ - + def main(args: Array[String]) { val rateQuote = Future { connection.getCurrentValue(USD) } - + val purchase = rateQuote map { quote => if (isProfitable(quote)) connection.buy(amount, quote) else throw new Exception("有益ではない") } - + Await.result(purchase, 0 nanos) } @@ -572,16 +572,16 @@ Promise の `p` は `p.future` によって返される Future を完了させ import scala.concurrent.{ Future, Promise } import scala.concurrent.ExecutionContext.Implicits.global - + val p = Promise[T]() val f = p.future - + val producer = Future { val r = produceSomething() p success r continueDoingSomethingUnrelated() } - + val consumer = Future { startDoingSomething() f onSuccess { @@ -603,7 +603,7 @@ Promise の `p` は `p.future` によって返される Future を完了させ val p = Promise[T]() val f = p.future - + val producer = Future { val r = someComputation if (isInvalid(r)) @@ -643,9 +643,9 @@ HTTP レスポンスにのみ興味がある場合で、これは最初に Promi val f = Future { 1 } val p = Promise[Int]() - + p completeWith f - + p.future onSuccess { case x => println(x) } @@ -704,7 +704,7 @@ for library writers 抽象クラスの `Duration` は以下のメソッドを定義する: -1. 時間の単位の変換 (`toNanos`、`toMicros`、`toMillis`、 +1. 時間の単位の変換 (`toNanos`、`toMicros`、`toMillis`、 `toSeconds`、`toMinutes`、`toHours`、`toDays`、及び `toUnit(unit: TimeUnit)`)。 2. 時間の比較 (`<`、`<=`、`>`、および `>=`)。 3. 算術演算 (`+`、`-`、`*`、`/`、および `unary_-`)。 @@ -714,14 +714,14 @@ for library writers `Duration` は以下の方法で作成することができる: 1. `Int` もしくは `Long` 型からの暗黙の変換する。例: `val d = 100 millis`。 -2. `Long` の長さと `java.util.concurrent.TimeUnit` を渡す。例: `val d = Duration(100, MILLISECONDS)`。 +2. `Long` の長さと `java.util.concurrent.TimeUnit` を渡す。例: `val d = Duration(100, MILLISECONDS)`。 3. 時間の長さを表す文字列をパースする。例: `val d = Duration("1.2 µs")`。 `Duration` は `unapply` メソッドも提供するため、パータンマッチング構文の中から使うこともできる。以下に具体例をみる: import scala.concurrent.duration._ import java.util.concurrent.TimeUnit._ - + // 作成 val d1 = Duration(100, MILLISECONDS) // from Long and TimeUnit val d2 = Duration(100, "millis") // from Long and String diff --git a/_ja/overviews/core/macros.md b/_ja/overviews/core/macros.md deleted file mode 100644 index aa66256fad..0000000000 --- a/_ja/overviews/core/macros.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -layout: overview -partof: macros -overview: macros -language: ja -label-color: important -label-text: Experimental -title: マクロ - -discourse: false ---- diff --git a/_ja/overviews/core/parallel-collections.md b/_ja/overviews/core/parallel-collections.md deleted file mode 100644 index 5b518ebb4d..0000000000 --- a/_ja/overviews/core/parallel-collections.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: overview -overview: parallel-collections -partof: parallel-collections -language: ja -title: Scala 並列コレクションライブラリ - -discourse: false ---- diff --git a/_ja/overviews/core/reflection.md b/_ja/overviews/core/reflection.md deleted file mode 100644 index cf269b9679..0000000000 --- a/_ja/overviews/core/reflection.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -layout: overview -partof: reflection -overview: reflection -language: ja -label-color: important -label-text: Experimental -title: リフレクション - -discourse: false ---- \ No newline at end of file diff --git a/_ja/overviews/core/string-interpolation.md b/_ja/overviews/core/string-interpolation.md index 1b3e6d34f6..23491ae1d7 100644 --- a/_ja/overviews/core/string-interpolation.md +++ b/_ja/overviews/core/string-interpolation.md @@ -1,12 +1,11 @@ --- -layout: overview -discourse: true -label-color: success -language: ja -label-text: New in 2.10 -overview: string-interpolation +layout: singlepage-overview title: 文字列の補間 +partof: string-interpolation + +language: ja + discourse: false --- @@ -53,7 +52,7 @@ Scala は `s`、`f`、そして `raw` という 3つの補間子 (interpolator) `f` 補間子は型安全だ。整数のみで動作する書式に `Double` を渡すとコンパイラはエラーを表示する。例えば: val height: Double = 1.9d - + scala> f"$height%4d" :9: error: type mismatch; found : Double @@ -68,7 +67,7 @@ Scala は `s`、`f`、そして `raw` という 3つの補間子 (interpolator) `raw` 補間子は `s` 補間子に似ているが、違いは文字列リテラル内でエスケープを実行しないことだ。以下の加工文字列をみてみよう: scala> s"a\nb" - res0: String = + res0: String = a b @@ -94,9 +93,9 @@ Scala では、全ての加工文字列リテラルは簡単なコード変換 implicit class JsonHelper(val sc: StringContext) extends AnyVal { def json(args: Any*): JSONObject = sys.error("TODO - IMPLEMENT") } - + def giveMeSomeJson(x: JSONObject): Unit = ... - + giveMeSomeJson(json"{ name: $name, id: $id }") この例では、文字列の補間を使って JSON リテラル構文に挑戦している。この構文を使うには implicit クラスの `JsonHelper` がスコープにある必要があり、また `json` メソッドを実装する必要がある。注意して欲しいのは書式文字列リテラルが文字列ではなく、`JSONObject` を返す点だ。 diff --git a/_ja/overviews/core/value-classes.md b/_ja/overviews/core/value-classes.md index ea034f5d44..406a86e4e2 100644 --- a/_ja/overviews/core/value-classes.md +++ b/_ja/overviews/core/value-classes.md @@ -1,11 +1,11 @@ --- -layout: overview -label-color: success -label-text: New in 2.10 -language: ja -overview: value-classes +layout: singlepage-overview title: 値クラスと汎用トレイト +partof: value-classes + +language: ja + discourse: false --- diff --git a/_overviews/core/actors-migration-guide.md b/_overviews/core/actors-migration-guide.md index 28975e1916..11aab78b7d 100644 --- a/_overviews/core/actors-migration-guide.md +++ b/_overviews/core/actors-migration-guide.md @@ -2,6 +2,8 @@ layout: singlepage-overview title: The Scala Actors Migration Guide +partof: actor-migration + languages: [zh-cn] permalink: /overviews/core/:title.html diff --git a/_overviews/core/actors.md b/_overviews/core/actors.md index f49d9389d9..35cad2a205 100644 --- a/_overviews/core/actors.md +++ b/_overviews/core/actors.md @@ -2,6 +2,8 @@ layout: singlepage-overview title: The Scala Actors API +partof: actors + languages: [zh-cn, es] permalink: /overviews/core/:title.html diff --git a/_overviews/core/architecture-of-scala-collections.md b/_overviews/core/architecture-of-scala-collections.md index 5d46bebbb4..5cb894f1e9 100644 --- a/_overviews/core/architecture-of-scala-collections.md +++ b/_overviews/core/architecture-of-scala-collections.md @@ -2,6 +2,8 @@ layout: singlepage-overview title: The Architecture of Scala Collections +partof: collections-architecture + languages: [zh-cn] permalink: /overviews/core/:title.html diff --git a/_overviews/core/binary-compatibility-of-scala-releases.md b/_overviews/core/binary-compatibility-of-scala-releases.md index d7e6b0cc18..e2cbd0bd72 100644 --- a/_overviews/core/binary-compatibility-of-scala-releases.md +++ b/_overviews/core/binary-compatibility-of-scala-releases.md @@ -2,6 +2,8 @@ layout: singlepage-overview title: Binary Compatibility of Scala Releases +partof: binary-compatibility + permalink: /overviews/core/:title.html --- diff --git a/_overviews/core/futures.md b/_overviews/core/futures.md index 729c8b158b..6c3704b3ca 100644 --- a/_overviews/core/futures.md +++ b/_overviews/core/futures.md @@ -2,6 +2,8 @@ layout: singlepage-overview title: Futures and Promises +partof: futures + languages: [ja, zh-cn] permalink: /overviews/core/:title.html diff --git a/_overviews/core/implicit-classes.md b/_overviews/core/implicit-classes.md index 4d0ae4cb0d..8e9b29214f 100644 --- a/_overviews/core/implicit-classes.md +++ b/_overviews/core/implicit-classes.md @@ -2,6 +2,8 @@ layout: singlepage-overview title: Implicit Classes +partof: implicit-classes + languages: [zh-cn] permalink: /overviews/core/:title.html diff --git a/_overviews/core/string-interpolation.md b/_overviews/core/string-interpolation.md index 10b94c7c97..a2215e8ef5 100644 --- a/_overviews/core/string-interpolation.md +++ b/_overviews/core/string-interpolation.md @@ -4,6 +4,8 @@ title: String Interpolation discourse: true +partof: string-interpolation + languages: [es, ja, zh-cn] permalink: /overviews/core/:title.html diff --git a/_overviews/core/value-classes.md b/_overviews/core/value-classes.md index 949744fb0b..ef44e45fa9 100644 --- a/_overviews/core/value-classes.md +++ b/_overviews/core/value-classes.md @@ -2,6 +2,8 @@ layout: singlepage-overview title: Value Classes and Universal Traits +partof: value-classes + languages: [ja, zh-cn] permalink: /overviews/core/:title.html From 4737955765295d160239facf25d85fbdc0325cbc Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Wed, 26 Jul 2017 21:28:06 +0200 Subject: [PATCH 063/112] Rest of the single-page overview translations --- _es/overviews/core/actors.md | 497 ++++++++++++++++ _es/overviews/core/string-interpolation.md | 132 +++++ .../overviews/core/actors-migration-guide.md | 465 +++++++++++++++ _zh-cn/overviews/core/actors.md | 308 ++++++++++ .../core/architecture-of-scala-collections.md | 543 ++++++++++++++++++ _zh-cn/overviews/core/futures.md | 503 ++++++++++++++++ _zh-cn/overviews/core/implicit-classes.md | 83 +++ _zh-cn/overviews/core/string-interpolation.md | 121 ++++ _zh-cn/overviews/core/value-classes.md | 260 +++++++++ 9 files changed, 2912 insertions(+) create mode 100644 _es/overviews/core/actors.md create mode 100644 _es/overviews/core/string-interpolation.md create mode 100644 _zh-cn/overviews/core/actors-migration-guide.md create mode 100644 _zh-cn/overviews/core/actors.md create mode 100644 _zh-cn/overviews/core/architecture-of-scala-collections.md create mode 100644 _zh-cn/overviews/core/futures.md create mode 100644 _zh-cn/overviews/core/implicit-classes.md create mode 100644 _zh-cn/overviews/core/string-interpolation.md create mode 100644 _zh-cn/overviews/core/value-classes.md diff --git a/_es/overviews/core/actors.md b/_es/overviews/core/actors.md new file mode 100644 index 0000000000..8c74bcf92e --- /dev/null +++ b/_es/overviews/core/actors.md @@ -0,0 +1,497 @@ +--- +layout: singlepage-overview +title: API de actores en Scala + +partof: actors + +language: es + +discourse: false +--- + +**Philipp Haller and Stephen Tu** + +**Traducción e interpretación: Miguel Ángel Pastor Olivar** + +## Introducción + +La presente guía describe el API del paquete `scala.actors` de Scala 2.8/2.9. El documento se estructura en diferentes grupos lógicos. La jerarquía de "traits" es tenida en cuenta para llevar a cabo la estructuración de las secciones individuales. La atención se centra en el comportamiento exhibido en tiempo de ejecución por varios de los métodos presentes en los traits anteriores, complementando la documentación existente en el Scaladoc API. + +## Traits de actores: Reactor, ReplyReactor, y Actor + +### The Reactor trait + +`Reactor` es el padre de todos los traits relacionados con los actores. Heredando de este trait podremos definir actores con una funcionalidad básica de envío y recepción de mensajes. + +El comportamiento de un `Reactor` se define mediante la implementación de su método `act`. Este método es ejecutado una vez el `Reactor` haya sido iniciado mediante la invocación del método `start`, retornando el `Reactor`. El método `start`es *idempotente*, lo cual significa que la invocación del mismo sobre un actor que ya ha sido iniciado no surte ningún efecto. + +El trait `Reactor` tiene un parámetro de tipo `Msg` el cual determina el tipo de mensajes que un actor es capaz de recibir. + +La invocación del método `!` de un `Reactor` envía un mensaje al receptor. La operación de envío de un mensaje mediante el operador `!` es asíncrona por lo que el actor que envía el mensaje no se bloquea esperando a que el mensaje sea recibido sino que su ejecución continua de manera inmediata. Por ejemplo, `a ! msg` envia `msg` a `a`. Todos los actores disponen de un *buzón* encargado de regular los mensajes entrantes hasta que son procesados. + +El trait `Reactor` trait también define el método `forward`. Este método es heredado de `OutputChannel` y tiene el mismo efecto que el método `!`. Aquellos traits que hereden de `Reactor`, en particular el trait `ReplyActor`, sobreescriben este método para habilitar lo que comunmente se conocen como *"implicit reply destinations"* (ver a continuación) + +Un `Reactor` recibe mensajes utilizando el método `react`. Este método espera un argumento de tipo `PartialFunction[Msg, Unit]` el cual define cómo los mensajes de tipo `Msg` son tratados una vez llegan al buzón de un actor. En el siguiente ejemplo, el actor espera recibir la cadena "Hello", para posteriomente imprimir un saludo: + + react { + case "Hello" => println("Hi there") + } + +La invocación del método `react` nunca retorna. Por tanto, cualquier código que deba ejecutarse tras la recepción de un mensaje deberá ser incluido dentro de la función parcial pasada al método `react`. Por ejemplo, dos mensajes pueden ser recibidos secuencialmente mediante la anidación de dos llamadas a `react`: + + react { + case Get(from) => + react { + case Put(x) => from ! x + } + } + +El trait `Reactor` también ofrece una serie de estructuras de control que facilitan la programación utilizando el mecanismo de `react`. + +#### Terminación y estados de ejecución + +La ejecución de un `Reactor` finaliza cuando el cuerpo del método `act` ha sido completado. Un `Reactor` también pueden terminarse a si mismo de manera explícita mediante el uso del método `exit`. El tipo de retorno de `exit` es `Nothing`, dado que `exit` siempre dispara una excepción. Esta excepción únicamente se utiliza de manera interna y nunca debería ser capturada. + +Un `Reactor` finalizado pueden ser reiniciado mediante la invocación de su método `restart`. La invocación del método anterior sobre un `Reactor` que no ha terminado su ejecución lanza una excepción de tipo `IllegalStateException`. El reinicio de un actor que ya ha terminado provoca que el método `act` se ejecute nuevamente. + +El tipo `Reactor` define el método `getState`, el cual retorna, como un miembro de la enumeración `Actor.State`, el estado actual de la ejecución del actor. Un actor que todavía no ha sido iniciado se encuentra en el estado `Actor.State.New`. Si el actor se está ejecutando pero no está esperando por ningún mensaje su estado será `Actor.State.Runnable`. En caso de que el actor haya sido suspendido mientras espera por un mensaje estará en el estado `Actor.State.Suspended`. Por último, un actor ya terminado se encontrará en el estado `Actor.State.Terminated`. + +#### Manejo de excepciones + +El miembro `exceptionHandler` permite llevar a cabo la definición de un manejador de excepciones que estará habilitado durante toda la vida del `Reactor`: + + def exceptionHandler: PartialFunction[Exception, Unit] + +Este manejador de excepciones (`exceptionHandler`) retorna una función parcial que se utiliza para gestionar excepciones que no hayan sido tratadas de ninguna otra manera. Siempre que una excepción se propague fuera del método `act` de un `Reactor` el manejador anterior será aplicado a dicha excepción, permitiendo al actor ejecutar código de limpieza antes de que se termine. Nótese que la visibilidad de `exceptionHandler` es `protected`. + +El manejo de excepciones mediante el uso de `exceptionHandler` encaja a la perfección con las estructuras de control utilizadas para programas con el método `react`. Siempre que una excepción es manejada por la función parcial retornada por `excepctionHandler`, la ejecución continua con la "closure" actual: + + loop { + react { + case Msg(data) => + if (cond) // process data + else throw new Exception("cannot process data") + } + } + +Assumiendo que `Reactor` sobreescribe el atributo `exceptionHandler`, tras el lanzamiento de una excepción en el cuerpo del método `react`, y una vez ésta ha sido gestionada, la ejecución continua con la siguiente iteración del bucle. + +### The ReplyReactor trait + +El trait `ReplyReactor` extiende `Reactor[Any]` y sobrescribe y/o añade los siguientes métodos: + +- El método `!` es sobrescrito para obtener una referencia al actor + actual (el emisor). Junto al mensaje actual, la referencia a dicho + emisor es enviada al buzón del actor receptor. Este último dispone de + acceso al emisor del mensaje mediante el uso del método `sender` (véase más abajo). + +- El método `forward` es sobrescrito para obtener una referencia al emisor + del mensaje que actualmente está siendo procesado. Junto con el mensaje + actual, esta referencia es enviada como el emisor del mensaje actual. + Como consuencia de este hecho, `forward` nos permite reenviar mensajes + en nombre de actores diferentes al actual. + +- El método (añadido) `sender` retorna el emisor del mensaje que está siendo + actualmente procesado. Puesto que un mensaje puede haber sido reenviado, + `sender` podría retornar un actor diferente al que realmente envió el mensaje. + +- El método (añadido) `reply` envía una respuesta al emisor del último mensaje. + `reply` también es utilizado para responder a mensajes síncronos o a mensajes + que han sido enviados mediante un "future" (ver más adelante). + +- El método (añadido) `!?` ofrece un *mecanismo síncrono de envío de mensajes*. + La invocación de `!?` provoca que el actor emisor del mensaje se bloquee hasta + que se recibe una respuesta, momento en el cual retorna dicha respuesta. Existen + dos variantes sobrecargadas. La versión con dos parámetros recibe un argumento + adicional que representa el tiempo de espera (medido en milisegundos) y su tipo + de retorno es `Option[Any]` en lugar de `Any`. En caso de que el emisor no + reciba una respuesta en el periodo de espera establecido, el método `!?` retornará + `None`; en otro caso retornará la respuesta recibida recubierta con `Some`. + +- Los métodos (añadidos) `!!` son similares al envío síncrono de mensajes en el sentido de + que el receptor puede enviar una respuesta al emisor del mensaje. Sin embargo, en lugar + de bloquear el actor emisor hasta que una respuesta es recibida, retornan una instancia de + `Future`. Esta última puede ser utilizada para recuperar la respuesta del receptor una + vez se encuentre disponible; asimismo puede ser utilizada para comprobar si la respuesta + está disponible sin la necesidad de bloquear el emisor. Existen dos versiones sobrecargadas. + La versión que acepta dos parámetros recibe un argumento adicional de tipo + `PartialFunction[Any, A]`. Esta función parcial es utilizada para realizar el post-procesado de + la respuesta del receptor. Básicamente, `!!` retorna un "future" que aplicará la anterior + función parcial a la repuesta (una vez recibida). El resultado del "future" es el resultado + de este post-procesado. + +- El método (añadido) `reactWithin` permite llevar a cabo la recepción de mensajes en un periodo + determinado de tiempo. En comparación con el método `react`, recibe un parámetro adicional, + `msec`, el cual representa el periodo de tiempo, expresado en milisegundos, hasta que el patrón `TIMEOUT` + es satisfecho (`TIMEOUT` es un "case object" presente en el paquete `scala.actors`). Ejemplo: + + reactWithin(2000) { + case Answer(text) => // process text + case TIMEOUT => println("no answer within 2 seconds") + } + +- El método `reactWithin` también permite realizar accesos no bloqueantes al buzón. Si + especificamos un tiempo de espera de 0 milisegundos, primeramente el buzón será escaneado + en busca de un mensaje que concuerde. En caso de que no exista ningún mensaje concordante + tras el primer escaneo, el patrón `TIMEOUT` será satisfecho. Por ejemplo, esto nos permite + recibir determinado tipo de mensajes donde unos tienen una prioridad mayor que otros: + + reactWithin(0) { + case HighPriorityMsg => // ... + case TIMEOUT => + react { + case LowPriorityMsg => // ... + } + } + + En el ejemplo anterior, el actor procesa en primer lugar los mensajes `HighPriorityMsg` aunque + exista un mensaje `LowPriorityMsg` más antiguo en el buzón. El actor sólo procesará mensajes + `LowPriorityMsg` en primer lugar en aquella situación donde no exista ningún `HighProrityMsg` + en el buzón. + +Adicionalmente, el tipo `ReplyActor` añade el estado de ejecución `Actor.State.TimedSuspended`. Un actor suspendido, esperando la recepción de un mensaje mediante el uso de `reactWithin` se encuentra en dicho estado. + +### El trait Actor + +El trait `Actor` extiende de `ReplyReactor` añadiendo y/o sobrescribiendo los siguientes miembros: + +- El método (añadido) `receive` se comporta del mismo modo que `react`, con la excepción + de que puede retornar un resultado. Este hecho se ve reflejado en la definición del tipo, + que es polimórfico en el tipo del resultado: `def receive[R](f: PartialFunction[Any, R]): R`. + Sin embargo, la utilización de `receive` hace que el uso del actor + sea más pesado, puesto que el hilo subyacente es bloqueado mientras + el actor está esperando por la respuesta. El hilo bloqueado no está + disponible para ejecutar otros actores hasta que la invocación del + método `receive` haya retornado. + +- El método (añadido) `link` permite a un actor enlazarse y desenlazarse de otro + actor respectivamente. El proceso de enlazado puede utilizarse para monitorizar + y responder a la terminación de un actor. En particular, el proceso de enlazado + afecta al comportamiento mostrado en la ejecución del método `exit` tal y como + se escribe en el la documentación del API del trait `Actor`. + +- El atributo `trapExit` permite responder a la terminación de un actor enlazado, + independientemente de los motivos de su terminación (es decir, carece de importancia + si la terminación del actor es normal o no). Si `trapExit` toma el valor cierto en + un actor, este nunca terminará por culpa de los actores enlazados. En cambio, siempre + y cuando uno de sus actores enlazados finalice, recibirá un mensaje de tipo `Exit`. + `Exit` es una "case class" que presenta dos atributos: `from` referenciando al actor + que termina y `reason` conteniendo los motivos de la terminación. + +#### Terminación y estados de ejecución + +Cuando la ejecución de un actor finaliza, el motivo de dicha terminación puede ser +establecida de manera explícita mediante la invocación de la siguiente variante +del método `exit`: + + def exit(reason: AnyRef): Nothing + +Un actor cuyo estado de terminación es diferente del símbolo `'normal` propaga +los motivos de su terminación a todos aquellos actores que se encuentren enlazados +a él. Si el motivo de la terminación es una excepción no controlada, el motivo de +finalización será una instancia de la "case class" `UncaughtException`. + +El trait `Actor` incluye dos nuevos estados de ejecución. Un actor que se encuentra +esperando la recepción de un mensaje mediante la utilización del método `receive` se +encuentra en el método `Actor.State.Blocked`. Un actor esperado la recepción de un +mensaje mediante la utilización del método `receiveWithin` se encuentra en el estado +`Actor.State.TimeBlocked`. + +## Estructuras de control + +El trait `Reactor` define una serie de estructuras de control que simplifican el mecanismo +de programación con la función sin retorno `react`. Normalmente, una invocación al método +`react` no retorna nunca. Si el actor necesita ejecutar código a continuación de la invocación +anterior, tendrá que pasar, de manera explícita, dicho código al método `react` o utilizar +algunas de las estructuras que encapsulan este comportamiento. + +La estructura de control más basica es `andThen`. Permite registrar una `closure` que será +ejecutada una vez el actor haya terminado la ejecución de todo lo demas. + + actor { + { + react { + case "hello" => // processing "hello" + }: Unit + } andThen { + println("hi there") + } + } + +Por ejemplo, el actor anterior imprime un saludo tras realizar el procesado +del mensaje `hello`. Aunque la invocación del método `react` no retorna, +podemos utilizar `andThen` para registrar el código encargado de imprimir +el saludo a continuación de la ejecución del actor. + +Nótese que existe una *atribución de tipo* a continuación de la invocación +de `react` (`:Unit`). Básicamente, nos permite tratar el resultado de +`react` como si fuese de tipo `Unit`, lo cual es legal, puesto que el resultado +de una expresión siempre se puede eliminar. Es necesario llevar a cabo esta operación +dado que `andThen` no puede ser un miembro del tipo `Unit`, que es el tipo del resultado +retornado por `react`. Tratando el tipo de resultado retornado por `react` como +`Unit` permite llevar a cabo la aplicación de una conversión implícita la cual +hace que el miembro `andThen` esté disponible. + +El API ofrece unas cuantas estructuras de control adicionales: + +- `loop { ... }`. Itera de manera indefinidia, ejecutando el código entre +las llaves en cada una de las iteraciones. La invocación de `react` en el +cuerpo del bucle provoca que el actor se comporte de manera habitual ante +la llegada de un nuevo mensaje. Posteriormente a la recepción del mensaje, +la ejecución continua con la siguiente iteración del bucle actual. + +- `loopWhile (c) { ... }`. Ejecuta el código entre las llaves mientras la +condición `c` tome el valor `true`. La invocación de `react` en el cuerpo +del bucle ocasiona el mismo efecto que en el caso de `loop`. + +- `continue`. Continua con la ejecución de la closure actual. La invocación +de `continue` en el cuerpo de un `loop`o `loopWhile` ocasionará que el actor +termine la iteración en curso y continue con la siguiente. Si la iteración en +curso ha sido registrada utilizando `andThen`, la ejecución continua con la +segunda "closure" pasada como segundo argumento a `andThen`. + +Las estructuras de control pueden ser utilizadas en cualquier parte del cuerpo +del método `act` y en los cuerpos de los métodos que, transitivamente, son +llamados por `act`. Aquellos actores creados utilizando la sintáxis `actor { ... }` +pueden importar las estructuras de control desde el objeto `Actor`. + +#### Futures + +Los traits `RepyActor` y `Actor` soportan operaciones de envío de mensajes +(métodos `!!`) que, de manera inmediata, retornan un *future*. Un *future*, +es una instancia del trait `Future` y actúa como un manejador que puede +ser utilizado para recuperar la respuesta a un mensaje "send-with-future". + +El emisor de un mensaje "send-with-future" puede esperar por la respuesta del +future *aplicando* dicha future. Por ejemplo, el envío de un mensaje mediante +`val fut = a !! msg` permite al emisor esperar por el resultado del future +del siguiente modo: `val res = fut()`. + +Adicionalmente, utilizando el método `isSet`, un `Future` puede ser consultado +de manera no bloqueante para comprobar si el resultado está disponible. + +Un mensaje "send-with-future" no es el único modo de obtener una referencia a +un future. Estos pueden ser creados utilizando el método `future`. En el siguiente +ejemplo, `body` se ejecuta de manera concurrente, retornando un future como +resultado. + + val fut = Future { body } + // ... + fut() // wait for future + +Lo que hace especial a los futures en el contexto de los actores es la posibilidad +de recuperar su resultado utilizando las operaciones estándar de actores de +recepción de mensajes como `receive`, etc. Además, es posible utilizar las operaciones +basadas en eventos `react`y `reactWithin`. Esto permite a un actor esperar por el +resultado de un future sin la necesidad de bloquear el hilo subyacente. + +Las operaciones de recepción basadas en actores están disponibles a través del +atributo `inputChannel` del future. Dado un future de tipo `Future[T]`, el tipo +de `inputChannel` es `InputChannel[T]`. Por ejemplo: + + val fut = a !! msg + // ... + fut.inputChannel.react { + case Response => // ... + } + +## Canales + +Los canales pueden ser utilizados para simplificar el manejo de mensajes +que presentan tipos diferentes pero que son enviados al mismo actor. La +jerarquía de canales se divide en `OutputChannel` e `InputChannel`. + +Los `OutputChannel` pueden ser utilizados para enviar mensajes. Un +`OutputChannel` `out` soporta las siguientes operaciones: + +- `out ! msg`. Envía el mensaje `msg` a `out` de manera asíncrona. Cuando `msg` + es enviado directamente a un actor se incluye un referencia al actor emisor + del mensaje. + +- `out forward msg`. Reenvía el mensaje `msg` a `out` de manera asíncrona. + El actor emisor se determina en el caso en el que `msg` es reenviado a + un actor. + +- `out.receiver`. Retorna el único actor que está recibiendo mensajes que están + siendo enviados al canal `out`. + +- `out.send(msg, from)`. Envía el mensaje `msg` a `out` de manera asíncrona, + proporcionando a `from` como el emisor del mensaje. + +Nótese que el trait `OutputChannel` tiene un parámetro de tipo que especifica el +tipo de los mensajes que pueden ser enviados al canal (utilizando `!`, `forward`, +y `send`). Este parámetro de tipo es contra-variante: + + trait OutputChannel[-Msg] + +Los actores pueden recibir mensajes de un `InputChannel`. Del mismo modo que +`OutputChannel`, el trait `InputChannel` presenta un parámetro de tipo que +especifica el tipo de mensajes que pueden ser recibidos por el canal. En este caso, +el parámetro de tipo es covariante: + + trait InputChannel[+Msg] + +Un `InputChannel[Msg]` `in` soportal las siguientes operaciones. + +- `in.receive { case Pat1 => ... ; case Patn => ... }` (y de manera similar, + `in.receiveWithin`) recibe un mensaje proveniente de `in`. La invocación + del método `receive` en un canal de entrada presenta la misma semántica + que la operación estándar de actores `receive`. La única diferencia es que + la función parcial pasada como argumento tiene tipo `PartialFunction[Msg, R]` + donde `R` es el tipo de retorno de `receive`. + +- `in.react { case Pat1 => ... ; case Patn => ... }` (y de manera similar, + `in.reactWithin`). Recibe un mensaje de `in` utilizando la operación basada en + eventos `react`. Del mismo modo que la operación `react` en actores, el tipo + de retorno es `Nothing`, indicando que las invocaciones de este método nunca + retornan. Al igual que la operación `receive` anterior, la función parcial + que se pasa como argumento presenta un tipo más específico: + + PartialFunction[Msg, Unit] + +### Creando y compartiendo canales + +Los canales son creados utilizando la clase concreta `Channel`. Esta clase extiende +de `InputChannel` y `OutputChannel`. Un canal pueden ser compartido haciendo dicho +canal visible en el ámbito de múltiples actores o enviándolo como mensaje. + +El siguiente ejemplo muestra la compartición mediante publicación en ámbitos: + + actor { + var out: OutputChannel[String] = null + val child = actor { + react { + case "go" => out ! "hello" + } + } + val channel = new Channel[String] + out = channel + child ! "go" + channel.receive { + case msg => println(msg.length) + } + } + +La ejecución de este ejemplo imprime la cadena "5" en la consola. Nótese que el +actor `child` únicamente tiene acceso a `out`, que es un `OutputChannel[String]`. +La referencia al canal, la cual puede ser utilizada para llevar a cabo la recepción +de mensajes, se encuentra oculta. Sin embargo, se deben tomar precauciones y +asegurarse que el canal de salida es inicializado con un canal concreto antes de que +`child` le envíe ningún mensaje. En el ejemplo que nos ocupa, esto es llevado a cabo +mediante el mensaje "go". Cuando se está recibiendo de `channel` utilizando el método +`channel.receive` podemos hacer uso del hecho que `msg` es de tipo `String`, y por +lo tanto tiene un miembro `length`. + +Una alternativa a la compartición de canales es enviarlos a través de mensajes. +El siguiente fragmento de código muestra un sencillo ejemplo de aplicación: + + case class ReplyTo(out: OutputChannel[String]) + + val child = actor { + react { + case ReplyTo(out) => out ! "hello" + } + } + + actor { + val channel = new Channel[String] + child ! ReplyTo(channel) + channel.receive { + case msg => println(msg.length) + } + } + +La "case class" `ReplyTo` es un tipo de mensajes que utilizamos para distribuir +una referencia a un `OutputChannel[String]`. Cuando el actor `child` recibe un +mensaje de tipo `ReplyTo` éste envía una cadena a su canal de salida. El segundo +actor recibe en el canal del mismo modo que anteriormente. + +## Planificadores + +Un `Reactor`(o una instancia de uno de sus subtipos) es ejecutado utilizando un +*planificador*. El trait `Reactor` incluye el miembro `scheduler` el cual retorna el +planificador utilizado para ejecutar sus instancias: + + def scheduler: IScheduler + +La plataforma de ejecución ejecuta los actores enviando tareas al planificador mediante +el uso de los métodos `execute` definidos en el trait `IScheduler`. La mayor parte +del resto de métodos definidos en este trait únicamente adquieren cierto protagonismo +cuando se necesita implementar un nuevo planificador desde cero; algo que no es necesario +en muchas ocasiones. + +Los planificadores por defecto utilizados para ejecutar instancias de `Reactor` y +`Actor` detectan cuando los actores han finalizado su ejecución. En el momento que esto +ocurre, el planificador se termina a si mismo (terminando con cualquier hilo que estuviera +en uso por parte del planificador). Sin embargo, algunos planificadores como el +`SingleThreadedScheduler` (definido en el paquete `scheduler`) necesita ser terminado de +manera explícita mediante la invocación de su método `shutdown`). + +La manera más sencilla de crear un planificador personalizado consisten en extender la clase +`SchedulerAdapter`, implementando el siguiente método abstracto: + + def execute(fun: => Unit): Unit + +Por norma general, una implementación concreata utilizaría un pool de hilos para llevar a cabo +la ejecución del argumento por nombre `fun`. + +## Actores remotos + +Esta sección describe el API de los actores remotos. Su principal interfaz es el objecto +[`RemoteActor`](http://www.scala-lang.org/api/2.9.1/scala/actors/remote/RemoteActor$.html) definido +en el paquete `scala.actors.remote`. Este objeto facilita el conjunto de métodos necesarios para crear +y establecer conexiones a instancias de actores remotos. En los fragmentos de código que se muestran a +continuación se asume que todos los miembros de `RemoteActor` han sido importados; la lista completa +de importaciones utilizadas es la siguiente: + + import scala.actors._ + import scala.actors.Actor._ + import scala.actors.remote._ + import scala.actors.remote.RemoteActor._ + +### Iniciando actores remotos + +Un actore remot es identificado de manera unívoca por un +[`Symbol`](http://www.scala-lang.org/api/2.9.1/scala/Symbol.html). Este símbolo es único para la instancia +de la máquina virual en la que se está ejecutando un actor. Un actor remoto identificado con el nombre +`myActor` puede ser creado del siguiente modo. + + class MyActor extends Actor { + def act() { + alive(9000) + register('myActor, self) + // ... + } + } + +Nótese que el nombre únicamente puede ser registrado con un único actor al mismo tiempo. +Por ejemplo, para registrar el actor *A* como `'myActor` y posteriormente registrar otro +actor *B* como `'myActor`, debería esperar hasta que *A* haya finalizado. Este requisito +aplica a lo largo de todos los puertos, por lo que registrando a *B* en un puerto diferente +no sería suficiente. + +### Connecting to remote actors + +Establecer la conexión con un actor remoto es un proceso simple. Para obtener una referencia remota +a un actor remoto que está ejecutándose en la máquina `myMachine` en el puerto 8000 con el nombre +`'anActor`, tendremos que utilizar `select`del siguiente modo: + + val myRemoteActor = select(Node("myMachine", 8000), 'anActor) + +El actor retornado por `select` es de tipo `AbstractActor`, que proporciona esencialmente el mismo +interfaz que un actor normal, y por lo tanto es compatible con las habituales operaciones de envío +de mensajes: + + myRemoteActor ! "Hello!" + receive { + case response => println("Response: " + response) + } + myRemoteActor !? "What is the meaning of life?" match { + case 42 => println("Success") + case oops => println("Failed: " + oops) + } + val future = myRemoteActor !! "What is the last digit of PI?" + +Nótese que la operación `select` es perezosa; no inicializa ninguna conexión de red. Simplemente crea +una nueva instancia de `AbstractActor` que está preparada para iniciar una nueva conexión de red en el +momento en que sea necesario (por ejemplo cuando el método '!' es invocado). diff --git a/_es/overviews/core/string-interpolation.md b/_es/overviews/core/string-interpolation.md new file mode 100644 index 0000000000..392a0b68ac --- /dev/null +++ b/_es/overviews/core/string-interpolation.md @@ -0,0 +1,132 @@ +--- +layout: singlepage-overview +title: Interpolación de cadenas + +partof: string-interpolation + +language: es + +discourse: false +--- + +**Josh Suereth** + +**Traducción e interpretación: Miguel Ángel Pastor Olivar** + +## Introducción + +Desde la versión 2.10.0, Scala ofrece un nuevo mecanismo para la creación de cadenas a partir de nuestros datos mediante la técnica de interpolación de cadenas. +Este nuevo mecanismo permite a los usuarios incluir referencias a variables de manera directa en cadenas de texto "procesadas". Por ejemplo: + + val name = "James" + println(s"Hello, $name") // Hello, James + +En el ejemplo anterior, el literal `s"Hello, $name"` es una cadena "procesada". Esto significa que el compilador debe realizar un trabajo adicional durante el tratamiento de dicha cadena. Una cadena "procesada" se denota mediante un conjunto de caracteres que preceden al símbolo `"`. La interpolación de cadenas ha sido introducida por [SIP-11](http://docs.scala-lang.org/sips/pending/string-interpolation.html), el cual contiene todos los detalles de implementación. + +## Uso + +Scala ofrece tres métodos de interpolación de manera nativa: `s`, `f` and `raw`. + +### Interpolador `s` + +El uso del prefijo `s` en cualquier cadena permite el uso de variables de manera directa dentro de la propia cadena. Ya hemos visto el ejemplo anterior: + + val name = "James" + println(s"Hello, $name") // Hello, James + +`$name` se anida dentro de la cadena "procesada" de tipo `s`. El interpolador `s` sabe como insertar el valor de la variable `name` en lugar indicado, dando como resultado la cadena `Hello, James`. Mediante el uso del interpolador `s`, cualquier nombre disponible en el ámbito puede ser utilizado dentro de la cadena. + +Las interpolaciones pueden recibir expresiones arbitrarias. Por ejemplo: + + println(s"1 + 1 = ${1 + 1}") + +imprimirá la cadena `1 + 1 = 2`. Cualquier expresión puede ser embebida en `${}` + +### Interpolador `f` + +Prefijando `f` a cualquier cadena permite llevar a cabo la creación de cadenas formateadas, del mismo modo que `printf` es utilizado en otros lenguajes. Cuando utilizamos este interpolador, todas las referencias a variables deben estar seguidas por una cadena de formateo que siga el formato `printf-`, como `%d`. Veamos un ejemplo: + + val height = 1.9d + val name = "James" + println(f"$name%s is $height%2.2f meters tall") // James is 1.90 meters tall + +El interpolador `f` es seguro respecto a tipos. Si pasamos un número real a una cadena de formateo que sólo funciona con números enteros, el compilador emitirá un error. Por ejemplo: + + val height: Double = 1.9d + + scala> f"$height%4d" + :9: error: type mismatch; + found : Double + required: Int + f"$height%4d" + ^ + +El interpolador `f` hace uso de las utilidades de formateo de cadenas disponibles en java. Los formatos permitidos tras el carácter `%` son descritos en [Formatter javadoc](http://docs.oracle.com/javase/1.6.0/docs/api/java/util/Formatter.html#detail). Si el carácter `%` no aparece tras la definición de una variable, `%s` es utilizado por defecto. + +### Interpolador `raw` + +El interpolador `raw` difiere del interpolador `s` en que el primero no realiza el escapado de literales contenidos en la cadena. A continuación se muestra un ejemplo de una cadena procesada: + + scala> s"a\nb" + res0: String = + a + b + +En el ejemplo anterior, el interpolador `s` ha reemplazado los caracteres `\n` con un salto de linea. El interpolador `raw` no llevará a cabo esta acción: + + scala> raw"a\nb" + res1: String = a\nb + +Esta cadena de interpolación es muy útil cuando se desea evitar que expresiones como `\n` se conviertan en un salto de línea. + +Adicionalmente a los interpoladores ofrecidos de serie por Scala, nosotros podremos definir nuestras propias cadenas de interpolación. + +## Uso avanzado + +En Scala, todas las cadenas "procesadas" son simples transformaciones de código. En cualquier punto en el que el compilador encuentra una cadena de texto con la forma: + + id"string content" + +la transforma en la llamada a un método (`id`) sobre una instancia de [StringContext](http://www.scala-lang.org/api/current/index.html#scala.StringContext). Este método también puede estar disponible en un ámbito implícito. Para definiir nuestra propia cadena de interpolación simplemente necesitamos crear una clase implícita que añada un nuevo método a la clase `StringContext`. A continuación se muestra un ejemplo: + + // Note: We extends AnyVal to prevent runtime instantiation. See + // value class guide for more info. + implicit class JsonHelper(val sc: StringContext) extends AnyVal { + def json(args: Any*): JSONObject = sys.error("TODO - IMPLEMENT") + } + + def giveMeSomeJson(x: JSONObject): Unit = ... + + giveMeSomeJson(json"{ name: $name, id: $id }") + +En este ejemplo, estamos intentando crear una cadena JSON mediante el uso de la interpolación de cadenas. La clase implícita `JsonHelper` debe estar disponible en el ámbito donde deseemos utilizar esta sintaxis, y el método `json` necesitaría ser implementado completamente. Sin embargo, el resutlado de dicha cadena de formateo no sería una cadena sino un objeto de tipo `JSONObject` + +Cuando el compilador encuentra la cadena `json"{ name: $name, id: $id }"` reescribe la siguiente expresión: + + new StringContext("{ name: ", ", id: ", " }").json(name, id) + +La clase implícita es utilizada para reescribir el fragmento anterior de la siguiente forma: + + new JsonHelper(new StringContext("{ name: ", ", id: ", " }")).json(name, id) + +De este modo, el método `json` tiene acceso a las diferentes partes de las cadenas así como cada una de las expresiones. Una implementación simple, y con errores, de este método podría ser: + + implicit class JsonHelper(val sc: StringContext) extends AnyVal { + def json(args: Any*): JSONObject = { + val strings = sc.parts.iterator + val expressions = args.iterator + var buf = new StringBuffer(strings.next) + while(strings.hasNext) { + buf append expressions.next + buf append strings.next + } + parseJson(buf) + } + } + +Cada una de las diferentes partes de la cadena "procesada" son expuestas en el atributo `parts` de la clase `StringContext`. Cada uno de los valores de la expresión se pasa en el argumento `args` del método `json`. Este método acepta dichos argumentos y genera una gran cadena que posteriormente convierte en un objecto de tipo JSON. Una implementación más sofisticada podría evitar la generación de la cadena anterior y llevar a cabo de manera directa la construcción del objeto JSON a partir de las cadenas y los valores de la expresión. + + +## Limitaciones + +La interpolación de cadenas no funciona con sentencias "pattern matching". Esta funcionalidad está planificada para su inclusión en la versión 2.11 de Scala. diff --git a/_zh-cn/overviews/core/actors-migration-guide.md b/_zh-cn/overviews/core/actors-migration-guide.md new file mode 100644 index 0000000000..e9a9b4b1ff --- /dev/null +++ b/_zh-cn/overviews/core/actors-migration-guide.md @@ -0,0 +1,465 @@ +--- +layout: singlepage-overview +title: Scala Actors迁移指南 + +partof: actor-migration + +language: zh-cn + +discourse: false +--- + +**Vojin Jovanovic 和 Philipp Haller 著** + +## 概述 + +从Scala的2.11.0版本开始,Scala的Actors库已经过时了。早在Scala2.10.0的时候,默认的actor库即是Akka。 + +为了方便的将Scala Actors迁移到Akka,我们提供了Actor迁移工具包(AMK)。通过在一个项目的类路径中添加scala-actors-migration.jar,AMK包含了一个针对Scala Actors扩展。此外,Akka 2.1包含一些特殊功能,比如ActorDSL singleton,可以实现更简单的转换功能,使Scala Actors代码变成Akka代码。本章内容的目的是用来指导用户完成迁移过程,并解释如何使用AMK。 + +本指南包括以下内容:在“迁移工具的局限性”章节中,我们在此概述了迁移工具的主要局限性。在“迁移概述”章节中我们描述了迁移过程和谈论了Scala的变化分布,使得迁移成为一种可能。最后,在“一步一步指导迁移到Akka”章节里,我们展示了一些迁移工作的例子,以及各个步骤,如果需要从Scala Actors迁移至Akka's actors,本节是推荐阅读的。 + +免责声明:并发代码是臭名昭著的,当出现bug时很难调试和修复。由于两个actor的不同实现,这种差异导致可能出现错误。迁移过程没一步后都建议进行完全的代码测试。 + +## 迁移工具的局限性 + +由于Akka和Scala的actor模型的完整功能不尽相同导致两者之间不能平滑地迁移。下面的列表解释了很难迁移的部分行为: + +1. 依靠终止原因和双向行为链接方法 - Scala和Akka actors有不同的故障处理和actor monitoring模型。在Scala actors模型中,如果一个相关联部分异常终止,相关联的actors终止。如果终止是显式跟踪(通过self.trapExit),actor可以从失败的actor收到终止的原因。通过Akka这个功能不能迁移到AMK。AMK允许迁移的只是[Akka monitoring](http://doc.akka.io/docs/akka/2.1.0/general/supervision.html#What_Lifecycle_Monitoring_Means)机制。Monitoring不同于连接,因为它是单向(unindirectional)的并且终止的原因是现在已知的。如果仅仅是monitoring机制是无法满足需求的,迁移的链接必须推迟到最后一刻(步骤5的迁移)。然后,当迁移到Akka,用户必须创建一个[监督层次(supervision hierarchy)](http://doc.akka.io/docs/akka/2.1.0/general/supervision.html),处理故障。 + +2. 使用restart方法——Akka不提供显式的重启actors,因此上述例子我们不能提供平滑迁移。用户必须更改系统,所以没有使用重启方法(restart method)。 + +3. 使用getState方法 - Akka actors没有显式状态,此功能无法迁移。用户代码必须没有getState调用。 + +4. 实例化后没有启动actors - Akka actors模型会在实例化后自动启动actors,所以用户不需要重塑系统来显式的在实例化后启动actors。 + +5. mailboxSize方法不存在Akka中,因此不能迁移。这种方法很少使用,很容易被删除。 + +## 迁移概述 + +### 迁移工具 + +在Scal 2.10.0 actors 是在[Scala distribution](http://www.scala-lang.org/downloads)中作为一个单独包(scala-actors.jar)存在的,并且他们的接口已被弃用。这种分布也包含在Akka actors的akka-actor.jar里。AMK同时存在Scala actors 和 akka-actor.jar之中。未来的主要版本的Scala将不包含Scala actors和AMK。 + +开始迁移,用户需要添加scala-actors.jar和scala-actors-migration.jar来构建他们的项目。添加scala-actors.jar和scala-actors-migration.jar允许使用下面描述的AMK。这些jar位于[Scala Tools](https://oss.sonatype.org/content/groups/scala-tools/org/scala-lang/)库和[Scala distribution](http://www.scala-lang.org/downloads)库中。 + +### 一步一步来迁移 + +Actor迁移工具使用起来应该有5步骤。每一步都设计为引入的基于代码的最小变化。在前四个迁移步骤的代码中将使用Scala actors来实现,并在该步完成后运行所有的系统测试。然而,方法和类的签名将被转换为与Akka相似。迁移工具在Scal方面引入了一种新的actor类型(ActWithStash)和强制执行actors的ActorRef接口。 + +该结果同样强制通过一个特殊的方法在ActorDSL 对象上创建actors。在这些步骤可以每次迁移一个actor。这降低了在同一时刻引入多个bug的可能性,同样降低了bug的复杂程度。 + +在Scala方面迁移完成后,用户应该改变import语句并变成使用Akka库。在Akka方面,ActorDSL和ActWithStash允许对Scala Actors和他们的生态系的react construct进行建模。这个步骤迁移所有actors到Akka的后端,会在系统中引入bug。一旦代码迁移到Akka,用户将能够使用Akka的所有的功能的。 + +### 一步一步指导迁移到Akka + +在这一章中,我们将通过actor迁移的5个步骤。在每一步之后的代码都要为可能的错误进行检测。在前4个步骤中可以一边迁移一个actor和一边测试功能。然而,最后一步迁移所有actors到Akka后它只能作为一个整体进行测试。在这个步骤之后系统应该具有和之前一样相同的功能,不过它将使用Akka actor库。 + +### 步骤1——万物皆是Actor + +Scala actors库提供了公共访问多个类型的actors。他们被组织在类层次结构和每个子类提供了稍微更丰富的功能。为了进一步的使迁移步骤更容易,我们将首先更改Actor类型系统中的每一个actor。这种迁移步骤很简单,因为Actor类位于层次结构的底部,并提供了广泛的功能。 + +来自Scala库的Actors应根据以下规则进行迁移: + +1. class MyServ extends Reactor[T] -> class MyServ extends Actor + +注意,反应器提供了一个额外的类型参数代表了类型的消息收到。如果用户代码中使用这些信息,那么一个需要:i)应用模式匹配与显式类型,或者ii)做一个向下的消息来自任何泛型T。 + +1. class MyServ extends ReplyReactor -> class MyServ extends Actor + +2. class MyServ extends DaemonActor -> class MyServ extends Actor + +为了为DaemonActor提供配对功能,将下列代码添加到类的定义。 + + override def scheduler: IScheduler = DaemonScheduler + +### 步骤2 - 实例化 + +在Akka中,actors可以访问只有通过ActorRef接口。ActorRef的实例可以通过在ActorDSL对象上调用actor方法或者通过调用ActorRefFactory实例的actorOf方法来获得。在Scala的AMK工具包中,我们提供了Akka ActorRef和ActorDSL的一个子集,该子集实际上是Akka库的一个单例对象(singleton object)。 + +这一步的迁移使所有actors访问通过ActorRefs。首先,我们现实如何迁移普通模式的实例化Sacla Actors。然后,我们将展示如何分别克服问题的ActorRef和Actor的不同接口。 + +#### Actor实例化 + +actor实例的转换规则(以下规则需要import scala.actors.migration._): + +1. 构造器调用实例化 + + val myActor = new MyActor(arg1, arg2) + myActor.start() + +应该被替换 + + ActorDSL.actor(new MyActor(arg1, arg2)) + +2. 用于创建Actors的DSL(译注:领域专用语言(Domain Specific Language)) + + val myActor = actor { + // actor 定义 + } +应该被替换 + + val myActor = ActorDSL.actor(new Actor { + def act() { + // actor 定义 + } + }) + +3. 从Actor Trait扩展来的对象 + + object MyActor extends Actor { + // MyActor 定义 + } + MyActor.start() +应该被替换 + + class MyActor extends Actor { + // MyActor 定义 + } + + object MyActor { + val ref = ActorDSL.actor(new MyActor) + } +所有的MyActor地想都应该被替换成MyActor.ref。 + +需要注意的是Akka actors在实例化的同时开始运行。actors创建并开始在迁移的系统的情况下,actors在不同的位置以及改变这可能会影响系统的行为,用户需要更改代码,以使得actors在实例化后立即开始执行。 + +远程actors也需要被获取作为ActorRefs。为了得到一个远程actor ActorRef需使用方法selectActorRef。 + +#### 不同的方法签名(signatures) + +至此为止我们已经改变了所有的actor实例化,返回ActorRefs,然而,我们还没有完成迁移工作。有不同的接口在ActorRefs和Actors中,因此我们需要改变在每个迁移实例上触发的方法。不幸的是,Scala Actors提供的一些方法不能迁移。对下列方法的用户需要找到一个解决方案: + +1. getState()——Akka中的actors 默认情况下由其监管actors(supervising actors)负责管理和重启。在这种情况下,一个actor的状态是不相关的。 + +2. restart() - 显式的重启一个Scala actor。在Akka中没有相应的功能。 + +所有其他Actor方法需要转换为两个ActorRef中的方法。转换是通过下面描述的规则。请注意,所有的规则需要导入以下内容: + + import scala.concurrent.duration._ + import scala.actors.migration.pattern.ask + import scala.actors.migration._ + import scala.concurrent._ +额外规则1-3的作用域定义在无限的时间需要一个隐含的超时。然而,由于Akka不允许无限超时,我们会使用100年。例如: + + implicit val timeout = Timeout(36500 days) + +规则: + +1. !!(msg: Any): Future[Any] 被?替换。这条规则会改变一个返回类型到scala.concurrent.Future这可能导致类型不匹配。由于scala.concurrent.Future比过去的返回值具有更广泛的功能,这种类型的错误可以很容易地固定在与本地修改: + + actor !! message -> respActor ? message + +2. !![A] (msg: Any, handler: PartialFunction[Any, A]): Future[A] 被?取代。处理程序可以提取作为一个单独的函数,并用来生成一个future对象结果。处理的结果应给出另一个future对象结果,就像在下面的例子: + + val handler: PartialFunction[Any, T] = ... // handler + actor !! (message, handler) -> (respActor ? message) map handler + +3. !? (msg: Any):任何被?替换都将阻塞在返回的future对象上 + + actor !? message -> + Await.result(respActor ? message, Duration.Inf) + +4. !? (msec: Long, msg: Any): Option[Any]任何被?替换都将显式的阻塞在future对象 + + actor !? (dur, message) -> + val res = respActor.?(message)(Timeout(dur milliseconds)) + val optFut = res map (Some(_)) recover { case _ => None } + Await.result(optFut, Duration.Inf) + +这里没有提到的公共方法是为了actors DSL被申明为公共的。他们只能在定义actor时使用,所以他们的这一步迁移是不相关的。 + +###第3步 - 从Actor 到 ActWithStash + +到目前为止,所有的控制器都继承自Actor trait。我们通过指定的工厂方法来实例化控制器,所有的控制器都可以通过接口ActorRef 来进行访问。现在我们需要把所有的控制器迁移的AMK 的 ActWithStash 类上。这个类的行为方式和Scala的Actor几乎完全一致,它提供了另外一些方法,对应于Akka的Actor trait。这使得控制器更易于逐步的迁移到Akka。 + +为了达到这个目的,所有的从Actor继承的类,按照下列的方式,需要改为继承自ActWithStash: + + class MyActor extends Actor -> class MyActor extends ActWithStash + +经过这样修改以后,代码会无法通过编译。因为ActWithStash中的receive 方法不能在act中像原来那样使用。要使代码通过编译,需要在所有的 receive 调用中加上类型参数。例如: + + receive { case x: Int => "Number" } -> + receive[String] { case x: Int => "Number" } + +另外,要使代码通过编译,还要在act方法前加上 override关键字,并且定义一个空的receive方法。act方法需要被重写,因为它在ActWithStash 的实现中模拟了Akka的消息处理循环。需要修改的地方请看下面的例子: + + class MyActor extends ActWithStash { + + // 空的 receive 方法 (现在还没有用) + def receive = {case _ => } + + override def act() { + // 原来代码中的 receive 方法改为 react。 + } + } +ActWithStash 的实例中,变量trapExit 的缺省值是true。如果希望改变,可以在初始化方法中把它设置为false。 + +远程控制器在ActWithStash 下无法直接使用,register('name, this)方法需要被替换为: + + registerActorRef('name, self) + +在后面的步骤中, registerActorRef 和 alive 方法的调用与其它方法一样。 + +现在,用户可以测试运行,整个系统的运行会和原来一样。ActWithStash 和Actor 拥有相同的基本架构,所以系统的运行会与原来没有什么区别。 + +### 第4步 - 去掉act 方法 + +在这一节,我们讨论怎样从ActWithStash中去掉act方法,以及怎样修改其他方法,使它与Akka更加契合. 这一环节会比较繁杂,所以我们建议最好一次只修改一个控制器。在Scala中,控制器的行为主要是在act方法的中定义。逻辑上来说,控制器是一个并发执行act方法的过程,执行完成后过程终止。在Akka中,控制器用一个全局消息处理器来依次处理它的的消息队列中的消息。这个消息处理器是一个receive函数返回的偏函数(partial function),该偏函数被应用与每一条消息上。 + +因为ActWithStash中Akka方法的行为依赖于移除的act方法,所以我们首先要做的是去掉act方法。然后,我们需要按照给定的规则修改scala.actors.Actor中每个方法的。 + +#### 怎样去除act 方法 + +在下面的列表中,我们给出了通用消息处理模式的修改规则。这个列表并不包含所有的模式,它只是覆盖了其中一些通用的模式。然而用户可以通过参考这些规则,通过扩展简单规则,将act方法移植到Akka。 + +嵌套调用react/reactWithin需要注意:消息处理偏函数需要做结构扩展,使它更接近Akka模式。尽管这种修改会很复杂,但是它允许任何层次的嵌套被移植。下面有相关的例子。 + +在复杂控制流中使用receive/receiveWithin需要注意:这个移植会比较复杂,因为它要求重构act方法。在消息处理偏函数中使用react 和 andThen可以使receive的调用模型化。下面是一些简单的例子。 + +1. 如果在act方法中有一些代码在第一个包含react的loop之前被执行,那么这些代码应该被放在preStart方法中。 + + def act() { + //初始化的代码放在这里 + loop { + react { ... } + } + } +应该被替换 + + override def preStart() { + //初始化的代码放在这里 + } + + def act() { + loop { + react{ ... } + } + } +其他的模式,如果在第一个react 之前有一些代码,也可以使用这个规则。 + +2. 当act 的形式为:一个简单loop循环嵌套react,用下面的方法。 + + def act() = { + loop { + react { + // body + } + } + } +应该被替换 + + def receive = { + // body + } + +3. 当act包含一个loopWhile 结构,用下面的方法。 + + def act() = { + loopWhile(c) { + react { + case x: Int => + // do task + if (x == 42) { + c = false + } + } + } + } +应该被替换 + + def receive = { + case x: Int => + // do task + if (x == 42) { + context.stop(self) + } + } + +4. 当act包含嵌套的react,用下面的规则: + + def act() = { + var c = true + loopWhile(c) { + react { + case x: Int => + // do task + if (x == 42) { + c = false + } else { + react { + case y: String => + // do nested task + } + } + } + } + } +应该被替换 + + def receive = { + case x: Int => + // do task + if (x == 42) { + context.stop(self) + } else { + context.become(({ + case y: String => + // do nested task + }: Receive).andThen(x => { + unstashAll() + context.unbecome() + }).orElse { case x => stash(x) }) + } + } + +5. reactWithin方法使用下面的修改规则: + + loop { + reactWithin(t) { + case TIMEOUT => // timeout processing code + case msg => // message processing code + } + } +应该被替换 + + import scala.concurrent.duration._ + + context.setReceiveTimeout(t millisecond) + def receive = { + case ReceiveTimeout => // timeout processing code + case msg => // message processing code + } + +6. 在Akka中,异常处理用另一种方式完成。如果要模拟Scala控制器的方式,那就用下面的方法 + + def act() = { + loop { + react { + case msg => + // 可能会失败的代码 + } + } + } + + override def exceptionHandler = { + case x: Exception => println("got exception") + } +应该被替换 + + def receive = PFCatch({ + case msg => + // 可能会失败的代码 + }, { case x: Exception => println("got exception") }) + PFCatch 的定义 + + class PFCatch(f: PartialFunction[Any, Unit], + handler: PartialFunction[Exception, Unit]) + extends PartialFunction[Any, Unit] { + + def apply(x: Any) = { + try { + f(x) + } catch { + case e: Exception if handler.isDefinedAt(e) => + handler(e) + } + } + + def isDefinedAt(x: Any) = f.isDefinedAt(x) + } + + object PFCatch { + def apply(f: PartialFunction[Any, Unit], + handler: PartialFunction[Exception, Unit]) = + new PFCatch(f, handler) + } + +PFCatch并不包含在AMK之中,所以它可以保留在移植代码中,AMK将会在下一版本中被删除。当整个移植完成后,错误处理也可以改由Akka来监管。 + +#### 修改Actor的方法 + +当我们移除了act方法以后,我们需要替换在Akka中不存在,但是有相似功能的方法。在下面的列表中,我们给出了两者的区别和替换方法: + +1. exit()/exit(reason) - 需要由 context.stop(self) 替换 + +2. receiver - 需要由 self 替换 + +3. reply(msg) - 需要由 sender ! msg 替换 + +4. link(actor) - 在Akka中,控制器之间的链接一部分由[supervision](http://doc.akka.io/docs/akka/2.1.0/general/supervision.html#What_Supervision_Means)来完成,一部分由[actor monitoring](http://doc.akka.io/docs/akka/2.1.0/general/supervision.html#What_Lifecycle_Monitoring_Means)来完成。在AMK中,我们只支持监测方法。因此,这部分Scala功能可以被完整的移植。 + +linking 和 watching 之间的区别在于:watching actor总是接受结束通知。然而,不像Scala的Exit消息包含结束的原因,Akka的watching 返回Terminated(a: ActorRef)消息,只包含ActorRef。获取结束原因的功能无法被移植。在Akka中,这一步骤可以在第4步之后,通过组织控制器的监管层级 [supervision hierarchy](http://doc.akka.io/docs/akka/2.1.0/general/supervision.html)来完成。 + +如果watching actors收到的消息不撇陪结束消息,控制器会被终止并抛出DeathPactException异常。注意就算watching actors正常的结束,也会发生这种情况。在Scala中,linked actors只要一方不正常的终止,另一方就会以相同的原因终止。 + +如果系统不能单独的用 watch actors来 移植,用户可以像原来那样用link和exit(reason)来使用。然而,因为act()重载了Exit消息,需要做如下的修改: + + case Exit(actor, reason) => + println("sorry about your " + reason) + ... +应该被替换 + + case t @ Terminated(actorRef) => + println("sorry about your " + t.reason) + ... +注意:在Scala和Akka的actor之间有另一种细微的区别:在Scala, link/watch 到已经终止的控制器不会有任何影响。在Akka中,看管已经终止的控制器会导致发送终止消息。这会在系统移植的第5 步导致不可预料的结果。 + +### 第5步 - Akka后端的移植 + +到目前为止,用户代码已经做好了移植到Akka actors的准备工作。现在我们可以把Scala actors迁移到Akka actor上。为了完成这一目标,需要配置build,去掉scala-actors.jar 和 scala-actors-migration.jar,把 akka-actor.jar 和 typesafe-config.jar加进来。AMK只能在Akka actor 2.1下正常工作,Akka actor 2.1已经包含在分发包 [Scala distribution](http://www.scala-lang.org/downloads)中, 可以用这样的方法配置。 + +经过这一步骤以后,因为包名的不同和API之间的细微差别,编译会失败。我们必须将每一个导入的actor从scala 修改为Akka。下列是部分需要修改的包名: + + scala.actors._ -> akka.actor._ + scala.actors.migration.ActWithStash -> akka.actor.ActorDSL._ + scala.actors.migration.pattern.ask -> akka.pattern.ask + scala.actors.migration.Timeout -> akka.util.Timeout + +当然,ActWithStash 中方法的声明 def receive = 必须加上前缀override。 + +在Scala actor中,stash 方法需要一个消息做为参数。例如: + + def receive = { + ... + case x => stash(x) + } + +在Akka中,只有当前处理的消息可以被隐藏(stashed)。因此,上面的例子可以替换为: + + def receive = { + ... + case x => stash() + } + +#### 添加Actor System + +Akka actor 组织在[Actor systems](http://doc.akka.io/docs/akka/2.1.0/general/actor-systems.html)系统中。每一个被实例化的actor必须属于某一个ActorSystem。因此,要添加一个ActorSystem 实例作为每个actor 实例调用的第一个参数。下面给出了例子。 + +为了完成该转换,你需要有一个actor system 实例。例如: + + val system = ActorSystem("migration-system") + +然后,做如下转换: + + ActorDSL.actor(...) -> ActorDSL.actor(system)(...) + +如果对actor 的调用都使用同一个ActorSystem ,那么它可以作为隐式参数来传递。例如: + + ActorDSL.actor(...) -> + import project.implicitActorSystem + ActorDSL.actor(...) + +当所有的主线程和actors结束后,Scala程序会终止。迁移到Akka后,当所有的主线程结束,所有的actor systems关闭后,程序才会结束。Actor systems 需要在程序退出前明确的中止。这需要通过在Actor system中调用shutdown 方法来完成。 + +#### 远程 Actors + +当代码迁移到Akka,远程actors就不再工作了。 registerActorFor 和 alive 方法需要被移除。 在Akka中,远程控制通过配置独立的完成。更多细节请参考[Akka remoting documentation](http://doc.akka.io/docs/akka/2.1.0/scala/remoting.html)。 + +#### 样例和问题 + +这篇文档中的所有程序片段可以在[Actors Migration test suite](http://github.com/scala/actors-migration/tree/master/src/test/)中找到,这些程序做为测试文件,前缀为actmig。 + +这篇文档和Actor移植组件由 [Vojin Jovanovic](http://people.epfl.ch/vojin.jovanovic)和[Philipp Haller](http://lampwww.epfl.ch/~phaller/)编写。 + +如果你发现任何问题或不完善的地方,请把它们报告给 [Scala Bugtracker](https://github.com/scala/actors-migration/issues)。 diff --git a/_zh-cn/overviews/core/actors.md b/_zh-cn/overviews/core/actors.md new file mode 100644 index 0000000000..bdcfc6bbcb --- /dev/null +++ b/_zh-cn/overviews/core/actors.md @@ -0,0 +1,308 @@ +--- +layout: singlepage-overview +title: The Scala Actors API + +partof: actors + +language: zh-cn + +discourse: false +--- + +**Philipp Haller 和 Stephen Tu 著** + +## 简介 + +本指南介绍了Scala 2.8和2.9中`scala.actors`包的API。这个包的内容因为逻辑上相通,所以放到了同一个类型的包内。这个trait在每个章节里面都会有所涉及。这章的重点在于这些traits所定义的各种方法在运行状态时的行为,由此来补充现有的Scala基础API。 + +注意:在Scala 2.10版本中这个Actors库将是过时的,并且在未来Scala发布的版本中将会被移除。开发者应该使用在`akka.actor`包中[Akka](http://akka.io/) actors来替代它。想了解如何将代码从Scala actors迁移到Akka请参考[Actors 迁移指南](http://docs.scala-lang.org/overviews/core/actors-migration-guide.html)章节。 + +## Actor trait:Reactor, ReplyReactor和Actor + +### Reactor trait + +Reactor 是所有`actor trait`的父级trait。扩展这个trait可以定义actor,其具有发送和接收消息的基本功能。 + +Reactor的行为通过实现其act方法来定义。一旦调用start方法启动Reactor,这个act方法便会执行,并返回这个Reactor对象本身。start方法是具有等幂性的,也就是说,在一个已经启动了的actor对象上调用它(start方法)是没有作用的。 + +Reactor trait 有一个Msg 的类型参数,这个参数指明这个actor所能接收的消息类型。 + +调用Reactor的!方法来向接收者发送消息。用!发送消息是异步的,这样意味着不会等待消息被接收——它在发送消息后便立刻往下执行。例如:`a ! msg`表示向`a`发送`msg`。每个actor都有各自的信箱(mailbox)作为缓冲来存放接收到的消息,直至这些消息得到处理。 + +Reactor trait中也定义了一个forward方法,这个方法继承于OutputChannel。它和!(感叹号,发送方法)有同样的作用。Reactor的SubTrait(子特性)——特别是`ReplyReactor trait`——覆写了此方法,使得它能够隐式地回复目标。(详细地看下面的介绍) + +一个Reactor用react方法来接收消息。react方法需要一个PartialFunction[Msg, Unit]类型的参数,当消息到达actor的邮箱之后,react方法根据这个参数来确定如何处理消息。在下面例子中,当前的actor等待接收一个“Hello”字符串,然后打印一句问候。 + + react { + case "Hello" => println("Hi there") + } + +调用react没有返回值。因此,在接收到一条消息后,任何要执行的代码必须被包含在传递给react方法的偏函数(partial function)中。举个例子,通过嵌套两个react方法调用可以按顺序接收到两条消息: + + react { + case Get(from) => + react { + case Put(x) => from ! x + } + } + +Reactor trait 也提供了控制结构,简化了react方法的代码。 + +### 终止和执行状态 + +当Reactor的act方法完整执行后, Reactor则随即终止执行。Reactor也可以显式地使用exit方法来终止自身。exit方法的返回值类型为Nothing,因为它总是会抛出异常。这个异常仅在内部使用,并且不应该去捕捉这个异常。 + +一个已终止的Reactor可以通过它的restart方法使它重新启动。对一个未终止的Reactor调用restart方法则会抛出`IllegalStateException`异常。重新启动一个已终止的actor则会使它的act方法重新运行。 + +Reactor定义了一个getState方法,这个方法可以将actor当前的运行状态作为Actor.State枚举的一个成员返回。一个尚未运行的actor处于`Actor.State.New`状态。一个能够运行并且不在等待消息的actor处于`Actor.State.Runnable`状态。一个已挂起,并正在等待消息的actor处于`Actor.State.Suspended`状态。一个已终止的actor处于`Actor.State.Terminated`状态。 + +### 异常处理 + +exceptionHandler成员允许定义一个异常处理程序,其在Reactor的整个生命周期均可用。 + + def exceptionHandler: PartialFunction[Exception, Unit] + +exceptionHandler返回一个偏函数,它用来处理其他没有被处理的异常。每当一个异常被传递到Reactor的act方法体之外时,这个成员函数就被应用到该异常,以允许这个actor在它结束前执行清理代码。注意:`exceptionHandler`的可见性为protected。 + +用exceptionHandler来处理异常并使用控制结构对与react的编程是非常有效的。每当exceptionHandler返回的偏函数处理完一个异常后,程序会以当前的后续闭包(continuation closure)继续执行。 + + loop { + react { + case Msg(data) => + if (cond) // 数据处理代码 + else throw new Exception("cannot process data") + } + } + +假设Reactor覆写了exceptionHandler,在处理完一个在react方法体内抛出的异常后,程序将会执行下一个循环迭代。 + +### ReplyReactor trait + +`ReplyReactor trait`扩展了`Reactor[Any]`并且增加或覆写了以下方法: + +!方法被覆写以获得一个当前actor对象(发送方)的引用,并且,这个发送方引用和实际的消息一起被传递到接收actor的信箱(mail box)中。接收方通过其sender方法访问消息的发送方(见下文)。 + +forward方法被覆写以获得一个引用,这个引用指向正在被处理的消息的发送方。引用和实际的消息一起作为当前消息的发送方传递。结果,forward方法允许代表不同于当前actor对象的actor对象转发消息。 + +增加的sender方法返回正被处理的消息的发送方。考虑到一个消息可能已经被转发,发送方可能不会返回实际发送消息的actor对象。 + +增加的reply方法向最后一个消息的发送方回复消息。reply方法也被用作回复一个同步消息发送或者一个使用future的消息发送(见下文)。 + +增加的!?方法提供同步消息发送。调用!?方法会引起发送方actor对象等待,直到收到一个响应,然后返回这个响应。重载的变量有两个。这个双参数变量需要额外的超时参数(以毫秒计),并且,它的返回类型是Option[Any]而不是Any。如果发送方在指定的超时期间没有收到一个响应,!?方法返回None,否则它会返回由Some包裹的响应。 + +增加的!!方法与同步消息发送的相似点在于,它们都允许从接收方传递一个响应。然而,它们返回Future实例,而不是阻塞发送中的actor对象直到接收响应。一旦Future对象可用,它可以被用来重新获得接收方的响应,还可以在不阻塞发送方的情况下,用于获知响应是否可用。重载的变量有两个。双参数变量需要额外的PartialFunction[Any,A]类型的参数。这个偏函数用于对接收方响应进行后处理。本质上,!!方法返回一个future对象,一旦响应被接收,这个future对象把偏函数应用于响应。future对象的结果就是后处理的结果。 + +增加的reactWithin方法允许在一段给定的时间段内接收消息。相对于react方法,这个方法需要一个额外的msec参数,用来指示在这个时间段(以毫秒计)直到匹配指定的TIMEOUT模式为止(TIMEOUT是包scala.actors中的用例对象(case object))。例如: + +reactWithin(2000) { case Answer(text) => // process text case TIMEOUT => println("no answer within 2 seconds") } + +reactWithin方法也允许以非阻塞方式访问信箱。当指定一个0毫秒的时间段时,首先会扫描信箱以找到一个匹配消息。如果在第一次扫描后没有匹配的消息,这个TIMEOUT模式将会匹配。例如,这使得接收某些消息会比其他消息有较高的优先级: + +reactWithin(0) { case HighPriorityMsg => // ... case TIMEOUT => react { case LowPriorityMsg => // ... } } + +在上述例子中,即使在信箱里有一个先到达的低优先级的消息,actor对象也会首先处理下一个高优先级的消息。actor对象只有在信箱里没有高优先级消息时才会首先处理一个低优先级的消息。 + +另外,ReplyReactor 增加了`Actor.State.TimedSuspended`执行状态。一个使用`reactWithin`方法等待接收消息而挂起的actor对象,处在` Actor.State.TimedSuspended `状态。 + +### Actor trait + +Actor trait扩展了`ReplyReactor`并增加或覆写了以下成员: + +增加的receive方法的行为类似react方法,但它可以返回一个结果。这可以在它的类型上反映——它的结果是多态的:def receive[R](f: PartialFunction[Any, R]): R。然而,因为actor对象挂起并等待消息时,receive方法会阻塞底层线程(underlying thread),使用receive方法使actor对象变得更加重量级。直到receive方法的调用返回,阻塞的线程将不能够执行其他actor对象。 + +增加的link和unlink方法允许一个actor对象将自身链接到另一个actor对象,或将自身从另一个actor对象断开链接。链接可以用来监控或对另一个actor对象的终止做出反应。特别要注意的是,正如在Actor trait的API文档中的解释,链接影响调用exit方法的行为。 + +trapExit成员允许对链接的actor对象的终止做出反应而无关其退出的原因(即,无关是否正常退出)。如果一个actor对象的trapExit成员被设置为true,则这个actor对象会因链接的actor对象而永远不会终止。相反,每当其中一个链接的actor对象个终止了,它将会收到类型为Exit的消息。这个Exit case class 有两个成员:from指终止的actor对象;reason指退出原因。 + +### 终止和执行状态 + +当终止一个actor对象的执行时,可以通过调用以下exit方法的变体,显式地设置退出原因: + + def exit(reason: AnyRef): Nothing +当一个actor对象以符号'normal以外的原因退出,会向所有链接到它的atocr对象传递其退出原因。如果一个actor对象由于一个未捕获异常终止,它的退出原因则为一个UncaughtException case class的实例。 + +Actor trait增加了两个新的执行状态。使用receive方法并正在等待接收消息的actor处在`Actor.State.Blocked`状态。使用receiveWithin方法并正在等待接收消息的actor处在`Actor.State.TimedBlocked`状态。 + +## 控制结构 + +Reactor trait定义了控制结构,它简化了无返回的react操作的编程。一般来说,一个react方法调用并不返回。如果actor对象随后应当执行代码,那么,或者显式传递actor对象的后续代码给react方法,或者可以使用下述控制结构,达到隐藏这些延续代码的目的。 + +最基础的控制结构是andThen,它允许注册一个闭包。一旦actor对象的所有其他代码执行完毕,闭包就会被执行。 + + actor { + { + react { + case "hello" => // 处理 "hello" + }: Unit + } andThen { + println("hi there") + } + } + +例如,上述actor实例在它处理了“hello”消息之后,打印一句问候。虽然调用react方法不会返回,我们仍然可以使用andThen来注册这段输出问候的代码(作为actor的延续)。 + +注意:在react方法的调用(: Unit)中存在一种类型归属。总而言之,既然表达式的结果经常可以被忽略,react方法的结果就可以合法地作为Unit类型来处理。andThen方法无法成为Nothing类型(react方法的结果类型)的一个成员,所以在这里有必要这样做。把react方法的结果类型当作Unit,允许实现一个隐式转换的应用,这个隐式转换使得andThen成员可用。 + +API还提供一些额外的控制结构: + +loop { ... }。无限循环,在每一次迭代中,执行括号中的代码。调用循环体内的react方法,actor对象同样会对消息做出反应。而后,继续执行这个循环的下次迭代。 + +loopWhile (c) { ... }。当条件c返回true,执行括号中的代码。调用循环体中的react方法和使用loop时的效果一样。 + +continue。继续执行当前的接下来的后续闭包(continuation closure)。在loop或loopWhile循环体内调用continue方法将会使actor对象结束当前的迭代并继续执行下次迭代。如果使用andThen注册了当前的后续代码,这个闭包会作为第二个参数传给andThen,并以此继续执行。 + +控制结构可以在Reactor对象的act方法中,以及在act方法(传递地)调用的方法中任意处使用。对于用actor{...}这样的缩略形式创建的actor,控制结构可以从Actor对象导入。 + +### Future + +ReplyReactor和Actor trait支持发送带有结果的消息(!!方法),其立即返回一个future实例。一个future即Future trait的一个实例,即可以用来重新获取一个send-with-future消息的响应的句柄。 + +一个send-with-future消息的发送方可以通过应用future来等待future的响应。例如,使用val fut = a !! msg 语句发送消息,允许发送方等待future的结果。如:val res = fut()。 + +另外,一个Future可以在不阻塞的情况下,通过isSet方法来查询并获知其结果是否可用。 + +send-with-future的消息并不是获得future的唯一的方法。future也可以通过future方法计算而得。下述例子中,计算体会被并行地启动运行,并返回一个future实例作为其结果: + + val fut = Future { body } + // ... + fut() // 等待future + +能够通过基于actor的标准接收操作(例如receive方法等)来取回future的结果,使得future实例在actor上下文中变得特殊。此外,也能够通过使用基于事件的操作(react方法和ractWithin方法)。这使得一个actor实例在等待一个future实例结果时不用阻塞它的底层线程。 + +通过future的inputChannel,使得基于actor的接收操作方法可用。对于一个类型为`Future[T]`的future对象而言,它的类型是`InputChannel[T]`。例如: + + val fut = a !! msg + // ... + fut.inputChannel.react { + case Response => // ... + } + +## Channel(通道) + +channnel可以用来对发送到同一actor的不同类型消息的处理进行简化。channel的层级被分为OutputChannel和InputChannel。 + +OutputChannel可用于发送消息。OutputChannel的out方法支持以下操作。 + +out ! msg。异步地向out方法发送msg。当msg直接发送给一个actor,一个发送中的actor的引用会被传递。 + +out forward msg。异步地转发msg给out方法。当msg被直接转发给一个actor,发送中的actor会被确定。 + +out.receiver。返回唯一的actor,其接收发送到out channel(通道)的消息。 + +out.send(msg, from)。异步地发送msg到out,并提供from作为消息的发送方。 + +注意:OutputChannel trait有一个类型参数,其指定了可以被发送到channel(通道)的消息类型(使用!、forward和send)。这个类型参数是逆变的: + + trait OutputChannel[-Msg] + +Actor能够从InputChannel接收消息。就像OutputChannel,InputChannel trait也有一个类型参数,用于指定可以从channel(通道)接收的消息类型。这个类型参数是协变的: + + trait InputChannel[+Msg] + +An` InputChannel[Msg] `in支持下列操作。 + +in.receive { case Pat1 => ... ; case Patn => ... }(以及类似的 in.receiveWithin)。从in接收一个消息。在一个输入channel(通道)上调用receive方法和actor的标准receive操作具有相同的语义。唯一的区别是,作为参数被传递的偏函数具有PartialFunction[Msg, R]类型,此处R是receive方法的返回类型。 + +in.react { case Pat1 => ... ; case Patn => ... }(以及类似的in.reactWithin)通过基于事件的react操作,从in方法接收一个消息。就像actor的react方法,返回类型是Nothing。这意味着此方法的调用不会返回。就像之前的receive操作,作为参数传递的偏函数有一个更具体的类型:PartialFunction[Msg, Unit] + +### 创建和共享channel + +channel通过使用具体的Channel类创建。它同时扩展了InputChannel和OutputChannel。使channel在多个actor的作用域(Scope)中可见,或者将其在消息中发送,都可以实现channel的共享。 + +下面的例子阐述了基于作用域(scope)的共享。 + + actor { + var out: OutputChannel[String] = null + val child = actor { + react { + case "go" => out ! "hello" + } + } + val channel = new Channel[String] + out = channel + child ! "go" + channel.receive { + case msg => println(msg.length) + } + } + +运行这个例子将输出字符串“5”到控制台。注意:子actor对out(一个OutputChannel[String])具有唯一的访问权。而用于接收消息的channel的引用则被隐藏了。然而,必须要注意的是,在子actor向输出channel发送消息之前,确保输出channel被初始化到一个具体的channel。通过使用“go”消息来完成消息发送。当使用channel.receive来从channel接收消息时,因为消息是String类型的,可以使用它提供的length成员。 + +另一种共享channel的可行的方法是在消息中发送它们。下面的例子对此作了阐述。 + + case class ReplyTo(out: OutputChannel[String]) + + val child = actor { + react { + case ReplyTo(out) => out ! "hello" + } + } + + actor { + val channel = new Channel[String] + child ! ReplyTo(channel) + channel.receive { + case msg => println(msg.length) + } + } + +ReplyTo case class是一个消息类型,用于分派一个引用到OutputChannel[String]。当子actor接收一个ReplyTo消息时,它向它的输出channel发送一个字符串。第二个actor则像以前一样接收那个channel上的消息。 + +## Scheduler + +scheduler用于执行一个Reactor实例(或子类型的一个实例)。Reactor trait引入了scheduler成员,其返回常用于执行Reactor实例的scheduler。 + + def scheduler: IScheduler + +运行时系统通过使用在IScheduler trait中定义的execute方法之一,向scheduler提交任务来执行actor。只有在完整实现一个新的scheduler时(但没有必要),此trait的大多数其他方法才是相关的。 + +默认的scheduler常用于执行Reactor实例,而当所有的actor完成其执行时,Actor则会检测环境。当这发生时,scheduler把它自己关闭(终止scheduler使用的任何线程)。然而,一些scheduler,比如SingleThreadedScheduler(位于scheduler包)必须要通过调用它们的shutdown方法显式地关闭。 + +创建自定义scheduler的最简单方法是通过扩展SchedulerAdapter,实现下面的抽象成员: + + def execute(fun: => Unit): Unit + +典型情况下,一个具体的实现将会使用线程池来执行它的按名参数fun。 + +## 远程Actor + +这一段描述了远程actor的API。它的主要接口是scala.actors.remote包中的RemoteActor对象。这个对象提供各种方法来创建和连接到远程actor实例。在下面的代码段中我们假设所有的RemoteActor成员都已被导入,所使用的完整导入列表如下: + + import scala.actors._ + import scala.actors.Actor._ + import scala.actors.remote._ + import scala.actors.remote.RemoteActor._ + +### 启动远程Actor + +远程actor由一个Symbol唯一标记。在这个远程Actor所执行JVM上,这个符号对于JVM实例是唯一的。由名称'myActor标记的远程actor可按如下方法创建。 + + class MyActor extends Actor { + def act() { + alive(9000) + register('myActor, self) + // ... + } + } + +记住:一个名字一次只能标记到一个单独的(存活的)actor。例如,想要标记一个actorA为'myActor,然后标记另一个actorB为'myActor。这种情况下,必须等待A终止。这个要求适用于所有的端口,因此简单地将B标记到不同的端口来作为A是不能满足要求的。 + +### 连接到远程Actor + +连接到一个远程actor也同样简单。为了获得一个远程Actor的远程引用(运行于机器名为myMachine,端口为8000,名称为'anActor),可按下述方式使用select方法: + + val myRemoteActor = select(Node("myMachine", 8000), 'anActor) + +从select函数返回的actor具有类型AbstractActor,这个类型本质上提供了和通常actor相同的接口,因此支持通常的消息发送操作: + + myRemoteActor ! "Hello!" + receive { + case response => println("Response: " + response) + } + myRemoteActor !? "What is the meaning of life?" match { + case 42 => println("Success") + case oops => println("Failed: " + oops) + } + val future = myRemoteActor !! "What is the last digit of PI?" + +记住:select方法是惰性的,它不实际初始化任何网络连接。仅当必要时(例如,调用!时),它会单纯地创建一个新的,准备好初始化新网络连接的AbstractActor实例。 diff --git a/_zh-cn/overviews/core/architecture-of-scala-collections.md b/_zh-cn/overviews/core/architecture-of-scala-collections.md new file mode 100644 index 0000000000..df77d22150 --- /dev/null +++ b/_zh-cn/overviews/core/architecture-of-scala-collections.md @@ -0,0 +1,543 @@ +--- +layout: singlepage-overview +title: Scala容器类体系结构 + +partof: collections-architecture + +language: zh-cn + +discourse: false +--- + +**Martin Odersky 和 Lex Spoon 著** + +本篇详细的介绍了Scala 容器类(collections)框架。通过与 [Scala 2.8 的 Collection API](http://docs.scala-lang.org/overviews/collections/introduction.html) 的对比,你会了解到更多框架的内部运作方式,同时你也将学习到如何通过几行代码复用这个容器类框架的功能来定义自己的容器类。 + +[Scala 2.8 容器API](http://docs.scala-lang.org/overviews/collections/introduction.html) 中包含了大量的 容器(collection)操作,这些操作在不同的许多容器类上表现为一致。假设,为每种 Collection 类型都用不同的方法代码实现,那么将导致代码的异常臃肿,很多代码将会仅仅是别处代码的拷贝。随着时间的推移,这些重复的代码也会带来不一致的问题,试想,相同的代码,在某个地方被修改了,而另外的地方却被遗漏了。而新的 容器类(collections)框架的设计原则目标就是尽量的避免重复,在尽可能少的地方定义操作(理想情况下,只在一处定义,当然也会有例外的情况存在)。设计中使用的方法是,在 Collection 模板中实现大部分的操作,这样就可以灵活的从独立的基类和实现中继承。后面的部分,我们会来详细阐述框架的各组成部分:模板(templates)、类(classes)以及trait(译注:类似于java里接口的概念),也会说明他们所支持的构建原则。 + +## Builders + +Builder类概要: + + package scala.collection.mutable + + class Builder[-Elem, +To] { + def +=(elem: Elem): this.type + def result(): To + def clear(): Unit + def mapResult[NewTo](f: To => NewTo): Builder[Elem, NewTo] = ... + } + +几乎所有的 Collection 操作都由遍历器(traversals)和构建器 (builders)来完成。Traversal 用可遍历类的foreach方法来实现,而构建新的 容器(collections)是由构建器类的实例来完成。上面的代码就是对这个类的精简描述。 + +我们用 b += x 来表示为构建器 b 加上元素 x。也可以一次加上多个元素,例如: b += (x, y) 及 b ++= x ,这类似于缓存(buffers)的工作方式(实际上,缓存就是构建器的增强版)。构建器的 result() 方法会返回一个collection。在获取了结果之后,构建器的状态就变成未定义,调用它的 clear() 方法可以把状态重置成空状态。构建器是通用元素类型,它适用于元素,类型,及它所返回的Collection。 + +通常,一个builder可以使用其他的builder来组合一个容器的元素,但是如果想要把其他builder返回的结果进行转换,例如,转成另一种类型,就需要使用Builder类的mapResult方法。假设,你有一个数组buffer,名叫 buf。一个ArrayBuffer的builder 的 result() 返回它自身。如果想用它去创建一个新的ArrayBuffer的builder,就可以使用 mapResult : + + scala> val buf = new ArrayBuffer[Int] + buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer() + + scala> val bldr = buf mapResult (_.toArray) + bldr: scala.collection.mutable.Builder[Int,Array[Int]] + = ArrayBuffer() + +结果值 bldr,是使用 buf 来收集元素的builder。当调用 bldr 的result时,其实是调用的 buf 的result,结果是返回的buf本身。接着这个数组buffer用 _.toArray 映射成了一个数组,结果 bldr 也就成了一个数组的 builder. + +## 分解(factoring out)通用操作 + +### TraversableLike类概述 + + package scala.collection + + class TraversableLike[+Elem, +Repr] { + def newBuilder: Builder[Elem, Repr] // deferred + def foreach[U](f: Elem => U) // deferred + ... + def filter(p: Elem => Boolean): Repr = { + val b = newBuilder + foreach { elem => if (p(elem)) b += elem } + b.result + } + } + +Collection库重构的主要设计目标是在拥有自然类型的同时又尽可能的共享代码实现。Scala的Collection 遵从“结果类型相同”的原则:只要可能,容器上的转换方法最后都会生成相同类型的Collection。例如,过滤操作对各种Collection类型都应该产生相同类型的实例。在List上应用过滤器应该获得List,在Map上应用过滤器,应该获得Map,如此等等。在下面的章节中,会告诉大家该原则的实现方法。 + +Scala的 Collection 库通过在 trait 实现中使用通用的构建器(builders)和遍历器(traversals)来避免代码重复、实现“结果类型相同”的原则。这些Trait的名字都有Like后缀。例如:IndexedSeqLike 是 IndexedSeq 的 trait 实现,再如,TraversableLike 是 Traversable 的 trait 实现。和普通的 Collection 只有一个类型参数不同,trait实现有两个类型参数。他们不仅参数化了容器的成员类型,也参数化了 Collection 所代表的类型,就像下面的 Seq[I] 或 List[T]。下面是TraversableLike开头的描述: + + trait TraversableLike[+Elem, +Repr] { ... } + +类型参数Elem代表Traversable的元素类型,参数Repr代表它自身。Repr上没有限制,甚至,Repr可以是非Traversable子类的实例。这就意味这,非容器子类的类,例如String和Array也可以使用所有容器实现trait中包含的操作。 + +以过滤器为例,这个操作只在TraversableLike定义了一次,就使得它适用于所有的容器类(collections)。通过查看前面 TraversableLike类的概述中相关的代码描述,我们知道,该trait声明了两个抽象方法,newBuilder 和 foreach,这些抽象方法在具体的collection类中实现。过滤器也是用这两个方法,通过相同的方式来实现的。首先,它用 newBuiler 方法构造一个新的builder,类型为 Repr 的类型。然后,使用 foreach 来遍历当前 collection 中的所有元素。一旦某个元素 x 满足谓词 p (即,p(x)为真),那么就把x加入到builder中。最后,用 builder 的 result 方法返回类型同 Repr 的 collection,里面的元素就是上面收集到的满足条件的所有元素。 + +容器(collections)上的映射操作就更复杂。例如:如果 f 是一个以一个String类型为参数并返回一个Int类型的函数,xs 是一个 List[String],那么在xs上使用该映射函数 f 应该会返回 List[Int]。同样,如果 ys 是一个 Array[String],那么通过 f 映射,应该返回 Array[Int]。 这里的难点在于,如何实现这样的效果而又不用分别针对 list 和 array 写重复的代码。TraversableLike 类的 newBuilder/foreach 也完成不了这个任务,因为他们需要生成一个完全相同类型的容器(collection)类型,而映射需要的是生成一个相同类型但内部元素类型却不同的容器。 + +很多情况下,甚至像map的构造函数的结果类型都可能是不那么简单的,因而需要依靠于其他参数类型,比如下面的例子: + + scala> import collection.immutable.BitSet + import collection.immutable.BitSet + + scala> val bits = BitSet(1, 2, 3) + bits: scala.collection.immutable.BitSet = BitSet(1, 2, 3) + + scala> bits map (_ * 2) + res13: scala.collection.immutable.BitSet = BitSet(2, 4, 6) + + scala> bits map (_.toFloat) + res14: scala.collection.immutable.Set[Float] + = Set(1.0, 2.0, 3.0) + +在一个BitSet上使用倍乘映射 _*2,会得到另一个BitSet。然而,如果在相同的BitSet上使用映射函数 (_.toFloat) 结果会得到一个 Set[Float]。这样也很合理,因为 BitSet 中只能放整型,而不能存放浮点型。 + +因此,要提醒大家注意,映射(map)的结果类型是由传进来的方法的类型决定的。如果映射函数中的参数会得到Int类型的值,那么映射的结果就是 BitSet。但如果是其他类型,那么映射的结果就是 Set 类型。后面会让大家了解 Scala 这种灵活的类型适应是如何实现的。 + +类似 BitSet 的问题不是唯一的,这里还有在map类型上应用map函数的交互式例子: + + scala> Map("a" -> 1, "b" -> 2) map { case (x, y) => (y, x) } + res3: scala.collection.immutable.Map[Int,java.lang.String] + = Map(1 -> a, 2 -> b) + + scala> Map("a" -> 1, "b" -> 2) map { case (x, y) => y } + res4: scala.collection.immutable.Iterable[Int] + = List(1, 2) + +第一个函数用于交换两个键值对。这个函数映射的结果是一个类似的Map,键和值颠倒了。事实上,地一个表达式产生了一个键值颠倒的map类型(在原map可颠倒的情况下)。然而,第二个函数,把键值对映射成一个整型,即成员变成了具体的值。在这种情况下,我们不可能把结果转换成Map类型,因此处理成,把结果转换成Map的一个可遍历的超类,这里是List。 + +你可能会问,哪为什么不强制让映射都返回相同类型的Collection呢?例如:BitSet上的映射只能接受整型到整型的函数,而Map上的映射只能接受键值对到键值对的函数。但这种约束从面向对象的观点来看是不能接受的,它会破坏里氏替换原则(Liskov substitution principle),即:Map是可遍历类,因此所有在可遍历类上的合法的操作都必然在Map中合法。 + +Scala通过重载来解决这个问题:Scala中的重载并非简单的复制Java的实现(Java的实现不够灵活),它使用隐式参数所提供的更加系统化的重载方式。 + +TraversableLike 中映射(map)的实现: + + def map[B, That](p: Elem => B) + (implicit bf: CanBuildFrom[B, That, This]): That = { + val b = bf(this) + for (x <- this) b += f(x) + b.result + } + +上面的代码展示了TraversableLike如何实现映射的trait。看起来非常类似于TraversableLike类的过滤器的实现。主要的区别在于,过滤器使用TraversableLike类的抽象方法 newBuilder,而映射使用的是Builder工场,它作为CanBuildFrom类型的一个额外的隐式参数传入。 + +CanBuildFrom trait: + + package scala.collection.generic + + trait CanBuildFrom[-From, -Elem, +To] { + // 创建一个新的构造器(builder) + def apply(from: From): Builder[Elem, To] + } + +上面的代码是 trait CanBuildFrom 的定义,它代表着构建者工场。它有三个参数:Elem是要创建的容器(collection)的元素的类型,To是要构建的容器(collection)的类型,From是该构建器工场适用的类型。通过定义适合的隐式定义的构建器工场,你就可以构建出符合你需要的类型转换行为。以 BitSet 类为例,它的伴生对象包含一个 CanBuildFrom[BitSet, Int, BitSet] 类型的构建器工场。这就意味着,当在一个 BitSet 上执行操作的时候,你可以创建另一个元素类型为整型的 BitSet。如果你需要的类型不同,那么,你还可以使用其他的隐式构建器工场,它们在Set的伴生对象中实现。下面就是一个更通用的构建器,A是通用类型参数: + + CanBuildFrom[Set[_], A, Set[A]] + +这就意味着,当操作一个任意Set(用现有的类型 Set[] 表述),我们可以再次创建一个 Set,并且无需关心它的元素类型A是什么。给你两个 CanBuildFrom 的隐式实例,你有可以利用 Scala 的隐式解析(implicit resolution)规则去挑选出其中最契合的一个。 + +所以说,隐式解析(implicit resolution)为类似映射的比较棘手的Collection操作提供了正确的静态类型。但是动态类型又怎么办呢?特别是,假设你有一个List,作为静态类型它有遍历方法,你在它上面使用一些映射(map)方法: + + scala> val xs: Iterable[Int] = List(1, 2, 3) + xs: Iterable[Int] = List(1, 2, 3) + + scala> val ys = xs map (x => x * x) + ys: Iterable[Int] = List(1, 4, 9) + +上述ys的静态类型是可遍历的(Iterable)类型。但是它的动态类型仍然必须是List类型的!此行为是间接被实现的。在CanBuildFrom的apply方法被作为参数传递源容器中。大多数的builder工厂仿制traversables(除建造工厂意外所有的叶子类型(leaf classes))将调用转发到集合的方法genericBuilder。反过来genericBuilder方法调用属于在定义它收集的建设者。所以Scala使用静态隐式解析,以解决map类型的限制问题,以及分派挑选对应于这些约束最佳的动态类型。 + +## 集成新容器 + +如果想要集成一个新的容器(Collection)类,以便受益于在正确类型上预定义的操作,需要做些什么呢?在下面几页中,将通过两个例子来进行演示。 + +### 集成序列(Sequence) + +RNA(核糖核酸)碱基(译者注:RNA链即很多不同RNA碱基的序列,RNA参考资料:http://zh.wikipedia.org/wiki/RNA): + + abstract class Base + case object A extends Base + case object T extends Base + case object G extends Base + case object U extends Base + + object Base { + val fromInt: Int => Base = Array(A, T, G, U) + val toInt: Base => Int = Map(A -> 0, T -> 1, G -> 2, U -> 3) + } + +假设需要为RNA链建立一个新的序列类型,这些RNA链是由碱基A(腺嘌呤)、T(胸腺嘧啶)、G(鸟嘌呤)、U(尿嘧啶)组成的序列。如上述列出的RNA碱基,很容易建立碱基的定义。 + +每个碱基都定义为一个具体对象(case object),该对象继承自一个共同的抽象类Base(碱基)。这个Base类具有一个伴生对象(companion object),该伴生对象定义了描述碱基和整数(0到3)之间映射的2个函数。可以从例子中看到,有两种不同的方式来使用容器(Collection)来实现这些函数。toInt函数通过一个从Base值到整数之间的映射(map)来实现。而它的逆函数fromInt则通过数组来实现。以上这些实现方法都基于一个事实,即“映射和数组都是函数”。因为他们都继承自Function1 trait。 + +下一步任务,便是为RNA链定义一个类。从概念上来看,一个RNA链就是一个简单的Seq[Base]。然而,RNA链可以很长,所以值的去花点时间来简化RNA链的表现形式。因为只有4种碱基,所以每个碱基可以通过2个比特位来区别。因此,在一个integer中,可以保存16个由2位比特标示的碱基。即构造一个Seq[Base]的特殊子类,并使用这种压缩的表示(packed representation)方式。 + +#### RNA链类的第一个版本 + + import collection.IndexedSeqLike + import collection.mutable.{Builder, ArrayBuffer} + import collection.generic.CanBuildFrom + + final class RNA1 private (val groups: Array[Int], + val length: Int) extends IndexedSeq[Base] { + + import RNA1._ + + def apply(idx: Int): Base = { + if (idx < 0 || length <= idx) + throw new IndexOutOfBoundsException + Base.fromInt(groups(idx / N) >> (idx % N * S) & M) + } + } + + object RNA1 { + + // 表示一组所需要的比特数 + private val S = 2 + + // 一个Int能够放入的组数 + private val N = 32 / S + + // 分离组的位掩码(bitmask) + private val M = (1 << S) - 1 + + def fromSeq(buf: Seq[Base]): RNA1 = { + val groups = new Array[Int]((buf.length + N - 1) / N) + for (i <- 0 until buf.length) + groups(i / N) |= Base.toInt(buf(i)) << (i % N * S) + new RNA1(groups, buf.length) + } + + def apply(bases: Base*) = fromSeq(bases) + } + +上面的RNA链类呈现出这个类的第一个版本,它将在以后被细化。类RNA1有一个构造函数,这个构造函数将int数组作为第一个参数。而这个数组包含打包压缩后的RNA数据,每个数组元素都有16个碱基,而最后一个元素则只有一部分有数据。第二个参数是长度,指定了数组中(和序列中)碱基的总数。RNA1类扩展了IndexedSeq[Base]。而IndexedSeq来自scala.collection.immutable,IndexedSeq定义了两个抽象方法:length和apply。这方法些需要在具体的子类中实现。类RNA1通过定义一个相同名字的参数字段来自动实现length。同时,通过类RNA1中给出的代码实现了索引方法apply。实质上,apply方法首先从数组中提取出一个整数值,然后再对这个整数中使用右移位(>>)和掩码(&)提取出正确的两位比特。私有常数S、N来自RNA1的伴生对象,S指定了每个包的尺寸(也就是2),N指定每个整数的两位比特包的数量,而M则是一个比特掩码,分离出一个字(word)的低S位。 + +注意,RNA1类的构造函数是一个私有函数。这意味着用户端无法通过调用new函数来创建RNA1序列的实例。这是有意义的,因为这能对用户隐藏RNA1序列包装数组的实现。如果用户端无法看到RNA序列的具体实现,以后任何时候,就可以做到改变RNA序列具体实现的同时,不影响到用户端代码。换句话说,这种设计实现了RNA序列的接口和实现之间解藕。然而,如果无法通过new来创建一个RNA序列,那就必须存在其他方法来创建它,否则整个类就变得毫无用处。事实上,有两种建立RNA序列的替代途径,两者都由RNA1的伴生对象(companion object)提供。第一个途径是fromSeq方法,这个方法将一个给定的碱基序列(也就是一个Seq[Base]类型的值)转换成RNA1类的实例。fromSeq方法将所有其序列参数内的碱基打包进一个数组。然后,将这个数组以及原序列的长度作为参数,调用RNA1的私有构造函数。这利用了一个事实:一个类的私有构造函数对于其伴生对象(companion object)是可见的。 + +创建RNA1实例的第二种途径由RNA1对象中的apply方法提供。它使用一个可变数量的Base类参数,并简单地将其作为序列指向fromSeq方法。这里是两个创建RNA实例的实际方案。 + + scala> val xs = List(A, G, T, A) + xs: List[Product with Base] = List(A, G, T, A) + + scala> RNA1.fromSeq(xs) + res1: RNA1 = RNA1(A, G, T, A) + + scala> val rna1 = RNA1(A, U, G, G, T) + rna1: RNA1 = RNA1(A, U, G, G, T) + +## 控制RNA类型中方法的返回值 + +这里有一些和RNA1抽象之间更多的交互操作 + + scala> rna1.length + res2: Int = 5 + + scala> rna1.last + res3: Base = T + + scala> rna1.take(3) + res4: IndexedSeq[Base] = Vector(A, U, G) + +前两个返回值正如预期,但最后一个——从rna1中获得前3个元素——的返回值则未必如预期。实际上,我们知道一个IndexedSeq[Base]作为返回值的静态类型而一个Vector作为返回值的动态类型,但我们更想看到一个RNA1的值。但这是无法做到的,因为之前在RNA1类中所做的一切仅仅是让RNA1扩展IndexedSeq。换句话说,IndexedSeq类具有一个take方法,其返回一个IndexedSeq。并且,这个方法是根据 IndexedSeq 的默认是用Vector来实现的。所以,这就是上一个交互中最后一行上所能看到的。 + +#### RNA链类的第二个版本 + + final class RNA2 private ( + val groups: Array[Int], + val length: Int + ) extends IndexedSeq[Base] with IndexedSeqLike[Base, RNA2] { + + import RNA2._ + + override def newBuilder: Builder[Base, RNA2] = + new ArrayBuffer[Base] mapResult fromSeq + + def apply(idx: Int): Base = // as before + } + +现在,明白了本质之后,下一个问题便是如何去改变它们。一种途径便是覆写(override)RNA1类中的take方法,可能如下所示: + + def take(count: Int): RNA1 = RNA1.fromSeq(super.take(count)) + +这对take函数有效,但drop、filter或者init又如何呢?事实上,序列(Sequence)中有超过50个方法同样返回序列。为了保持一致,所有这些方法都必须被覆写。这看起来越来越不像一个有吸引力的选择。幸运的是,有一种更简单的途径来达到同样的效果。RNA类不仅需要继承自IndexedSeq类,同时继承自它的实现trait(特性)IndexedSeqLike。如上面的RNA2所示。新的实现在两个方面与之前不同。第一个,RNA2类现在同样扩展自IndexedSeqLike[Base, RNA2]。这个IndexedSeqLike trait(特性)以可扩展的方式实现了所有IndexedSeq的具体方法。比如,如take、drop、filer或init的返回值类型即是传给IndexedSeqLike类的第二个类型参数,也就是说,在RNA2中的是RNA2本身。 + +为了能够做,IndexedSeqLike将自身建立在newBuilder抽象上,这个抽象能够创建正确类型的builder。IndexedSeqLike trait(特性)的子类必须覆写newBuilder以返回一个它们自身类型的容器。在RNA2类中,newBuilder方法返回一个Builder[Base, RNA2]类型的builder。 + +为了构造这个builder,首先创建一个ArrayBuffer,其自身就是一个Builder[Base, ArrayBuffer]。然后通过调用其mapResult方法来将这个ArrayBuffer转换为一个RNA2 builder。mapResult方法需要一个从ArrayBuffer到RNA2的转换函数来作为其参数。转换函数仅仅提供RNA2.fromSeq,其将一个任意的碱基序列转换为RNA2值(之前提到过,数组缓冲是一种序列,所以RNA2.fromSeq可对其使用)。 + +如果忘记声明newBuilder,将会得到一个如下的错误信息: + + RNA2.scala:5: error: overriding method newBuilder in trait + TraversableLike of type => scala.collection.mutable.Builder[Base,RNA2]; + method newBuilder in trait GenericTraversableTemplate of type + => scala.collection.mutable.Builder[Base,IndexedSeq[Base]] has + incompatible type + class RNA2 private (val groups: Array[Int], val length: Int) ^ + + one error found(发现一个错误) + +错误信息非常地长,并且很复杂,体现了容器(Collection)库错综复杂的组合。所以,最好忽略有关这些方法来源的信息,因为在这种情况下,它更多得是分散人的精力。而剩下的,则说明需要声明一个具有返回类型Builder[Base, RNA2]的newBuilder方法,但无法找到一个具有返回类型Builder[Base,IndexedSeq[Base]]的newBuilder方法。后者并不覆写前者。第一个方法——返回值类型为Builder[Base, RNA2]——是一个抽象方法,其在RNA2类中通过传递RNA2的类型参数给IndexedSeqLike,来以这种类型实例化。第二个方法的返回值类型为Builder[Base,IndexedSeq[Base]]——是由继承后的IndexedSeq类提供的。换句话说,如果没有声明一个以第一个返回值类型为返回值的newBuilder,RNA2类就是非法的。 + +改善了RNA2类中的实现之后,take、drop或filter方法现在便会按照预期执行: + + scala> val rna2 = RNA2(A, U, G, G, T) + rna2: RNA2 = RNA2(A, U, G, G, T) + + scala> rna2 take 3 + res5: RNA2 = RNA2(A, U, G) + + scala> rna2 filter (U !=) + res6: RNA2 = RNA2(A, G, G, T) + +### 使用map(映射)和friends(友元) + +然而,在容器中存在没有被处理的其他类别的方法。这些方法就不总会返回容器类型。它们可能返回同一类型的容器,但包含不同类型的元素。典型的例子就是map方法。如果s是一个Int的序列(Seq[Int]),f是将Int转换为String的方法,那么,s.map(f)将返回一个String的序列(Seq[String])。这样,元素类型在接收者和结果之间发生了改变,但容器的类型还是保持一致。 + +有一些其他的方法的行为与map类似,比如说flatMap、collect等,但另一些则不同。例如:++这个追加方法,它也可能因参数返回一个不同类型的结果——向Int类型的列表拼接一个String类型的列表将会得到一个Any类型的列表。至于这些方法如何适应RNA链,理想情况下应认为,在RNA链上进行碱基到碱基的映射将产生另外一个RNA链。(译者注:碱基为RNA链的“元素”) + + scala> val rna = RNA(A, U, G, G, T) + rna: RNA = RNA(A, U, G, G, T) + + scala> rna map { case A => T case b => b } + res7: RNA = RNA(T, U, G, G, T) + +同样,用 ++ 方法来拼接两个RNA链应该再次产生另外一个RNA链。 + + scala> rna ++ rna + res8: RNA = RNA(A, U, G, G, T, A, U, G, G, T) + +另一方面,在RNA链上进行碱基(类型)到其他类型的映射无法产生另外一个RNA链,因为新的元素有错误的类型。它只能产生一个序列而非RNA链。同样,向RNA链追加非Base类型的元素可以产生一个普通序列,但无法产生另一个RNA链。 + + scala> rna map Base.toInt + res2: IndexedSeq[Int] = Vector(0, 3, 2, 2, 1) + + scala> rna ++ List("missing", "data") + res3: IndexedSeq[java.lang.Object] = + Vector(A, U, G, G, T, missing, data) + +这就是在理想情况下应认为结果。但是,RNA2类并不提供这样的处理。事实上,如果你用RNA2类的实例来运行前两个例子,结果则是: + + scala> val rna2 = RNA2(A, U, G, G, T) + rna2: RNA2 = RNA2(A, U, G, G, T) + + scala> rna2 map { case A => T case b => b } + res0: IndexedSeq[Base] = Vector(T, U, G, G, T) + + scala> rna2 ++ rna2 + res1: IndexedSeq[Base] = Vector(A, U, G, G, T, A, U, G, G, T) + +所以,即使生成的容器元素类型是Base,map和++的结果也永远不会是RNA链。如需改善,则需要仔细查看map方法的签名(或++,它也有类似的方法签名)。map的方法最初在scala.collection.TraversableLike类中定义,具有如下签名: + + def map[B, That](f: A => B) + (隐含CBF:CanBuildFrom[修订版,B]): + +这里的A是一个容器元素的类型,而Repr是容器本身的类型,即传递给实现类(例如 TraversableLike和IndexedSeqLike)的第二个参数。map方法有两个以上的参数,B和That。参数B表示映射函数的结果类型,同时也是新容器中的元素类型。That作为map的结果类型。所以,That表示所创建的新容器的类型。 + +对于That类型如何确定,事实上,它是根据隐式参数cbf(CanBuildFrom[Repr,B,That]类型)被链接到其他类型。这些隐式CanBuildFrom由独立的容器类定义。大体上,CanBuildFrom[From,Elem,To]类型的值可以描述为:“有这么一种方法,由给定的From类型的容器,使用Elem类型,建立To的容器。” + +#### RNA链类的最终版本 + + final class RNA private (val groups: Array[Int], val length: Int) + extends IndexedSeq[Base] with IndexedSeqLike[Base, RNA] { + + import RNA._ + + // 在IndexedSeq中必须重新实现newBuilder + override protected[this] def newBuilder: Builder[Base, RNA] = + RNA.newBuilder + + // 在IndexedSeq中必须实现apply + def apply(idx: Int): Base = { + if (idx < 0 || length <= idx) + throw new IndexOutOfBoundsException + Base.fromInt(groups(idx / N) >> (idx % N * S) & M) + } + + // (可选)重新实现foreach, + // 来提高效率 + override def foreach[U](f: Base => U): Unit = { + var i = 0 + var b = 0 + while (i < length) { + b = if (i % N == 0) groups(i / N) else b >>> S + f(Base.fromInt(b & M)) + i += 1 + } + } + } + +#### RNA伴生对象的最终版本 + + object RNA { + + private val S = 2 // group中的比特(bit)数 + private val M = (1 << S) - 1 // 用于隔离group的比特掩码 + private val N = 32 / S // 一个Int中的group数 + + def fromSeq(buf: Seq[Base]): RNA = { + val groups = new Array[Int]((buf.length + N - 1) / N) + for (i <- 0 until buf.length) + groups(i / N) |= Base.toInt(buf(i)) << (i % N * S) + new RNA(groups, buf.length) + } + + def apply(bases: Base*) = fromSeq(bases) + + def newBuilder: Builder[Base, RNA] = + new ArrayBuffer mapResult fromSeq + + implicit def canBuildFrom: CanBuildFrom[RNA, Base, RNA] = + new CanBuildFrom[RNA, Base, RNA] { + def apply(): Builder[Base, RNA] = newBuilder + def apply(from: RNA): Builder[Base, RNA] = newBuilder + } + } + +现在在RNA2序列链上的 map 和 ++ 的行为变得更加清晰了。由于没有能够创建RNA2序列的CanBuildFrom实例,因此在从trait InexedSeq继承的伴生对象上得到的CanBuildFrom成为了第二选择。这隐式地创建了IndexedSeqs,也是在应用map到RNA2的时候发生的情况。 + +为了解决这个缺点,你需要在RNA类的同伴对象里定义CanBuildFrom的隐式实例。该实例的类型应该是CanBuildFrom [RNA, Base, RNA] 。即,这个实例规定,给定一个RNA链和新元素类型Base,可以建立另一个RNA链容器。上述有关RNA链的两个代码以及其伴生对象展示了细节。相较于类RNA2有两个重要的区别。首先, newBuilder的实现,从RNA类移到了它的伴生对象中。RNA类中新的newBuilder方法只是转发这个定义。其次,在RNA对象现在有个隐式的CanBuildFrom值。要创建这样的对象你需要在CanBuildFrom trait中定义两个apply方法。同时,为容器RNA创建一个新的builder,但参数列表不同。在apply()方法只是简单地以正确的类型创建builder。相比之下,apply(from)方法将原来的容器作为参数。在适应动态类型的builder的返回值与接收者的动态类型一致非常有用。在RNA的情况下,这不会起作用,因为RNA是final类,所以静态类型的RNA任何接收者同时具有RNA作为其动态类型。这就是为什么apply(from)也只是简单地调用newBuilder ,忽略其参数。 + +这样,RNA类(final)以原本的类型实现了所有容器方法。它的实现需要一些协议支持。从本质上讲,你需要知道newBuilder 工厂放在哪里以及canBuildFrom的隐式实现。在有利方面,能够以相对较少的代码,得到大量自动定义的方法。另外,如果不打算在容器中扩展take,drop,map或++这样操作,可以不写额外的代码,并在类RNA1所示的实现上结束工作。 + +到目前为止,讨论集中在以定义新序列所需的最少方法来获得特定类型。但在实践中,可能需要在序列上添加新的功能,或重写现有的方法,以获得更好的效果。其中一个例子就是重写RNA类的foreach方法。foreach是RNA本身的一个重要方法,因为它实现了遍历容器。此外,容器的许多其他方法的实现依赖foreach。因此,投入一些精力来做优化方法的实现有意义。IndexedSeq的foreach方法的标准实现仅仅使用aplly来选取容器的中的第i个元素(i从0到容器长度-1)。因此,对RNA链的每一个元素,标准实现选择一个数组元素,并从中解开一个碱基(base)。而RNA类上重写的foreach要聪明得多。对于每一个选定的数组元素,它立刻对其中所包含的所有碱基应用给定的方法。因此,数组选择和位拆包的工作大大减少。 + +### 整合 sets与 map + +在第二个实例中,将介绍如何将一个新的map类型整合到容器框架中的。其方式是通过使用关键字“Patricia trie”,实现以String作为类型的可变映射(mutable map)。术语“Patricia“实际上就是"Practical Algorithm to Retrieve Information Coded in Alphanumeric."(检索字母数字编码信息的实用算法) 的缩写。思想是以树的形式存储一个set或者map,在这种树中,后续字符作为子树可以用唯一确定的关键字查找。例如,一个 Patricia trie存储了三个字符串 "abc", "abd", "al", "all", "xy" 。如下: + +patricia 树的例子: + +![patricia.png](/resources/images/patricia.png) + +为了能够在trie中查找与字符串”abc“匹配的节点,只要沿着标记为”a“的子树,查找到标记为”b“的子树,最后到达标记为”c“的子树。如果 Patricia trie作为map使用,键所对应的值保存在一个可通过键定位的节点上。如果作为set,只需保存一个标记,说明set中存在这个节点。 + +使用Patricia tries的prefix map实现方式: + + import collection._ + + class PrefixMap[T] + extends mutable.Map[String, T] + with mutable.MapLike[String, T, PrefixMap[T]] { + + var suffixes: immutable.Map[Char, PrefixMap[T]] = Map.empty + var value: Option[T] = None + + def get(s: String): Option[T] = + if (s.isEmpty) value + else suffixes get (s(0)) flatMap (_.get(s substring 1)) + + def withPrefix(s: String): PrefixMap[T] = + if (s.isEmpty) this + else { + val leading = s(0) + suffixes get leading match { + case None => + suffixes = suffixes + (leading -> empty) + case _ => + } + suffixes(leading) withPrefix (s substring 1) + } + + override def update(s: String, elem: T) = + withPrefix(s).value = Some(elem) + + override def remove(s: String): Option[T] = + if (s.isEmpty) { val prev = value; value = None; prev } + else suffixes get (s(0)) flatMap (_.remove(s substring 1)) + + def iterator: Iterator[(String, T)] = + (for (v <- value.iterator) yield ("", v)) ++ + (for ((chr, m) <- suffixes.iterator; + (s, v) <- m.iterator) yield (chr +: s, v)) + + def += (kv: (String, T)): this.type = { update(kv._1, kv._2); this } + + def -= (s: String): this.type = { remove(s); this } + + override def empty = new PrefixMap[T] + } + +Patricia tries支持非常高效的查找和更新。另一个良好的特点是,支持通过前缀查找子容器。例如,在上述的patricia tree中,你可以从树根处按照“a”链接进行查找,获得所有以”a“为开头的键所组成的子容器。 + +依据这些思想,来看一下作为Patricia trie的映射实现方式。这种map称为PrefixMap。PrefixMap提供了withPrefix方法,这个方法根据给定的前缀查找子映射(submap),其包含了所有匹配该前缀的键。首先,使用键来定义一个prefix map,执行如下。 + + scala> val m = PrefixMap("abc" -> 0, "abd" -> 1, "al" -> 2, + "all" -> 3, "xy" -> 4) + m: PrefixMap[Int] = Map((abc,0), (abd,1), (al,2), (all,3), (xy,4)) + +然后,在m中调用withPrefix方法将产生另一个prefix map: + + scala> m withPrefix "a" + res14: PrefixMap[Int] = Map((bc,0), (bd,1), (l,2), (ll,3)) + +上面展示的代码表明了PrefixMap的定义方式。这个类以关联值T参数化,并扩展mutable.Map[String, T] 与 mutable.MapLike[String, T, PrefixMap[T]]。在RNA链的例子中对序列的处理也使用了这种模式,然后,为转换(如filter)继承实现类(如MapLike),用以获得正确的结果类型。 + +一个prefix map节点中含有两个可变字段:suffixes与value。value字段包含关联此节点的任意值,其初始化为None。suffixes字段包含从字符到PrefixMap值的映射(map),其初始化为空的map。 + +为什么要选择一种不可变map作为suffixes的实现方式?既然PrefixMap在整体上也是可变的,使用可变映射(map)是否更符合标准吗?这个问题的答案是,因为仅包含少量元素的不可变map,在空间和执行时间都非常高效。例如,包含有少于5个元素的map代表一个单独的对象。相比之下,就标准的可变map中的HashMap来说,即使为空时,也至少占用80bytes的空间。因此,如果普遍使用小容器,不可变的容器就优于可变的容器。在Patricia tries的例子中,预想除了树顶端节点之外,大部分节点仅包含少量的successor。所以在不可变映射(map)中存储这些successor效率很可能会更高。 + +现在看看映射(map)中第一个方法的实现:get。算法如下:为了获取prefix map里面和空字符串相关的值,简单地选取存储在树根节点上的任意值。另外,如果键字符串非空,尝试选取字符串的首字符匹配的子映射。如果产生一个map,继续去寻找map里面首个字符的之后的剩余键字符串。如果选取失败,键没有存储在map里面,则返回None。使用flatmap可以优雅地表示任意值上的联合选择。当对任意值ov,以及闭包f(其转而会返回任的值)进行应用时,如果ov和f都返回已定义的值,那么ov flatmap f将执行成功,否则ov flatmap f将返回None。 + +可变映射(map)之后的两个方法的实现是+=和-=。在prefixmap的实现中,它们按照其它两种方法定义:update和remove。 + +remove方法和get方法非常类似,除了在返回任何关联值之前,保存这个值的字段被设置为None。update方法首先会调用 withPrefix 方法找到需要被更新的树节点,然后将给定的值赋值给该节点的value字段。withPrefix 方法遍历整个树,如果在这个树中没有发现以这些前缀字符为节点的路径,它会根据需要创建子映射(sub-map)。 + +可变map最后一个需要实现的抽象方法是iterator。这个方法需要创建一个能够遍历map中所有键值对的迭代器iterator。对于任何给出的 prefix map,iterator 由如下几部分组成:首先,如果这个map中,在树根节点的value字段包含一个已定义的值Some(x),那么("", x)应为从iterator返回的第一个元素。此外,iterator需要串联存储在suffixes字段上的所有submap的iterator,并且还需要在这些返回的iterator每个键字符串的前面加上一个字符。进一步说,如果m是通过一个字符chr链接到根节点的submap ,并且(s, v)是一个从m.iterator返回的元素,那么这个根节点的iterator 将会转而返回(chr +: s, v)。在PrefixMap的iterator方法的实现中,这个逻辑可以非常简明地用两个for表达式实现。第一个for表达式在value.iterate上迭代。这表明Option值定义一个迭代器方法,如果Option值为None,则不返回任何元素。如果Option值为Some(x),则返回一个确切的元素x。 + +prefix map的伴生对象: + + import scala.collection.mutable.{Builder, MapBuilder} + import scala.collection.generic.CanBuildFrom + + object PrefixMap extends { + def empty[T] = new PrefixMap[T] + + def apply[T](kvs: (String, T)*): PrefixMap[T] = { + val m: PrefixMap[T] = empty + for (kv <- kvs) m += kv + m + } + + def newBuilder[T]: Builder[(String, T), PrefixMap[T]] = + new MapBuilder[String, T, PrefixMap[T]](empty) + + implicit def canBuildFrom[T] + : CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] = + new CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] { + def apply(from: PrefixMap[_]) = newBuilder[T] + def apply() = newBuilder[T] + } + } + +请注意,在PrefixMap中没有newBuilder方法的定义。这是没有必要的,因为maps和sets有默认的构造器,即MapBuilder类的实例。对可变映射来说,其默认的构造器初始时是一个空映射,然后使用映射的+= 方法连续增加元素。可变集合也类似。非可变映射和非可变集合的默认构造器则不同,它们使用无损的元素添加方法+,而非+=方法。 + +然而,为了构建合适的集合或映射,你都需要从一个空的集合或映射开始。empty方法提供了这样的功能,它是PrefixMap中最后定义的方法,该方法简单的返回一个新的PrefixMap。 + +现在我们来看看PrefixMap的伴生对象。事实上,并不是非要定义这种伴生对象,类PrefixMap自己就可以很好的完成它的功能。PrefixMap 对象的主要作用,是定义一些方便的工厂方法。它也定义了一个 CanBuildFrom,该方法可以让输入工作完成的更好。 + +其中有两个方法值得一提,它们是 empty 和 apply。同样的方法,在Scala的容器框架中的其他容器中都存在,因此在PrefixMap中定义它们也很合理。用这两种方法,你可以像写其他容器一样的编写PrefixMap: + + scala> PrefixMap("hello" -> 5, "hi" -> 2) + res0: PrefixMap[Int] = Map((hello,5), (hi,2)) + + scala> PrefixMap.empty[String] + res2: PrefixMap[String] = Map() + +另一个PrefixMap对象的成员是内置CanBuildFrom实例。它和上一节定义的CanBuildFrom目的相同:使得类似map等方法能返回最合适的类型。以PrefixMap的键值对映射函数为例,只要该函数生成串型和另一种类型组成的键值对,那么结果又会是一个PrefixMap。这里有一个例子: + + scala> res0 map { case (k, v) => (k + "!", "x" * v) } + res8: PrefixMap[String] = Map((hello!,xxxxx), (hi!,xx)) + +给出的函数参数是一个PrefixMap res0的键值对绑定,最终生成串型值对。map的结果是一个PrefixMap,只是String类型替代了int类型。如果在PrefixMap中没有内置的canBuildFrom ,那么结果将是一个普通的可变映射,而不是一个PrefixMap。 + +### 小结 + +总而言之,如果你想要将一个新的collection类完全的融入到框架中,需要注意以下几点: + +1. 决定容器应该是可变的,还是非可变的。 +2. 为容器选择正确的基类trait +3. 确保容器继承自适合的trait实现,这样它就能具有大多数的容器操作。 +4. 如果你想要map及类似的操作去返回你的容器类型的实例,那么就需要在类的伴生对象中提供一个隐式CanBuildFrom。 + +你现在已经了解Scala容器如何构建和如何构建新的容器类型。由于Scala丰富的抽象支持,新容器类型无需写代码就可以拥有大量的方法实现。 + +### 致谢 + +这些页面的素材改编自,由Odersky,Spoon和Venners编写的[Scala编程](http://www.artima.com/shop/programming_in_scala)第2版 。感谢Artima 对于出版的大力支持。 diff --git a/_zh-cn/overviews/core/futures.md b/_zh-cn/overviews/core/futures.md new file mode 100644 index 0000000000..0abecdd2e5 --- /dev/null +++ b/_zh-cn/overviews/core/futures.md @@ -0,0 +1,503 @@ +--- +layout: singlepage-overview +title: Future和Promise + +partof: futures + +language: zh-cn + +discourse: false +--- + +**Philipp Haller, Aleksandar Prokopec, Heather Miller, Viktor Klang, Roland Kuhn, and Vojin Jovanovic 著** + +## 简介 + +Future提供了一套高效便捷的非阻塞并行操作管理方案。其基本思想很简单,所谓Future,指的是一类占位符对象,用于指代某些尚未完成的计算的结果。一般来说,由Future指代的计算都是并行执行的,计算完毕后可另行获取相关计算结果。以这种方式组织并行任务,便可以写出高效、异步、非阻塞的并行代码。 + +默认情况下,future和promise并不采用一般的阻塞操作,而是依赖回调进行非阻塞操作。为了在语法和概念层面更加简明扼要地使用这些回调,Scala还提供了flatMap、foreach和filter等算子,使得我们能够以非阻塞的方式对future进行组合。当然,future仍然支持阻塞操作——必要时,可以阻塞等待future(不过并不鼓励这样做)。 + +## Future + +所谓Future,是一种用于指代某个尚未就绪的值的对象。而这个值,往往是某个计算过程的结果: + +- 若该计算过程尚未完成,我们就说该Future未就位; +- 若该计算过程正常结束,或中途抛出异常,我们就说该Future已就位。 + +Future的就位分为两种情况: + +- 当Future带着某个值就位时,我们就说该Future携带计算结果成功就位。 +- 当Future因对应计算过程抛出异常而就绪,我们就说这个Future因该异常而失败。 + +Future的一个重要属性在于它只能被赋值一次。一旦给定了某个值或某个异常,future对象就变成了不可变对象——无法再被改写。 + +创建future对象最简单的方法是调用future方法,该future方法启用异步(asynchronous)计算并返回保存有计算结果的futrue,一旦该future对象计算完成,其结果就变的可用。 + +注意_Future[T]_ 是表示future对象的类型,而future是方法,该方法创建和调度一个异步计算,并返回随着计算结果而完成的future对象。 + +这最好通过一个例子予以说明。 + +假设我们使用某些流行的社交网络的假定API获取某个用户的朋友列表,我们将打开一个新对话(session),然后发送一个请求来获取某个特定用户的好友列表。 + + import scala.concurrent._ + import ExecutionContext.Implicits.global + + val session = socialNetwork.createSessionFor("user", credentials) + val f: Future[List[Friend]] = Future { + session.getFriends() + } + +以上,首先导入scala.concurrent 包使得Future类型和future构造函数可见。我们将马上解释第二个导入。 + +然后我们初始化一个session变量来用作向服务器发送请求,用一个假想的 createSessionFor 方法来返回一个List[Friend]。为了获得朋友列表,我们必须通过网络发送一个请求,这个请求可能耗时很长。这能从调用getFriends方法得到解释。为了更好的利用CPU,响应到达前不应该阻塞(block)程序的其他部分执行,于是在计算中使用异步。future方法就是这样做的,它并行地执行指定的计算块,在这个例子中是向服务器发送请求和等待响应。 + +一旦服务器响应,future f 中的好友列表将变得可用。 + +未成功的尝试可能会导致一个异常(exception)。在下面的例子中,session的值未被正确的初始化,于是在future的计算中将抛出NullPointerException,future f 不会圆满完成,而是以此异常失败。 + + val session = null + val f: Future[List[Friend]] = Future { + session.getFriends + } + +`import ExecutionContext.Implicits.global` 上面的线条导入默认的全局执行上下文(global execution context),执行上下文执行执行提交给他们的任务,也可把执行上下文看作线程池,这对于future方法来说是必不可少的,因为这可以处理异步计算如何及何时被执行。我们可以定义自己的执行上下文,并在future上使用它,但是现在只需要知道你能够通过上面的语句导入默认执行上下文就足够了。 + +我们的例子是基于一个假定的社交网络API,此API的计算包含发送网络请求和等待响应。提供一个涉及到你能试着立即使用的异步计算的例子是公平的。假设你有一个文本文件,你想找出一个特定的关键字第一次出现的位置。当磁盘正在检索此文件内容时,这种计算可能会陷入阻塞,因此并行的执行该操作和程序的其他部分是合理的(make sense)。 + + val firstOccurrence: Future[Int] = Future { + val source = scala.io.Source.fromFile("myText.txt") + source.toSeq.indexOfSlice("myKeyword") + } + +### Callbacks(回调函数) + +现在我们知道如何开始一个异步计算来创建一个新的future值,但是我们没有展示一旦此结果变得可用后如何来使用,以便我们能够用它来做一些有用的事。我们经常对计算结果感兴趣而不仅仅是它的副作用。 + +在许多future的实现中,一旦future的client对future的结果感兴趣,它不得不阻塞它自己的计算直到future完成——然后才能使用future的值继续它自己的计算。虽然这在Scala的Future API(在后面会展示)中是允许的,但是从性能的角度来看更好的办法是一种完全非阻塞的方法,即在future中注册一个回调,future完成后这个回调称为异步回调。如果当注册回调时future已经完成,则回调可能是异步执行的,或在相同的线程中循序执行。 + +注册回调最通常的形式是使用OnComplete方法,即创建一个`Try[T] => U`类型的回调函数。如果future成功完成,回调则会应用到Success[T]类型的值中,否则应用到` Failure[T] `类型的值中。 + + `Try[T]` 和`Option[T]`或 `Either[T, S]`相似,因为它是一个可能持有某种类型值的单子。然而,它是特意设计来保持一个值或某个可抛出(throwable)对象。`Option[T]` 既可以是一个值(如:`Some[T]`)也可以是完全无值(如:`None`),如果`Try[T]`获得一个值则它为`Success[T]` ,否则为`Failure[T]`的异常。 `Failure[T]` 获得更多的关于为什么这儿没值的信息,而不仅仅是None。同时也可以把`Try[T]`看作一种特殊版本的`Either[Throwable, T]`,专门用于左值为可抛出类型(Throwable)的情形。 + +回到我们的社交网络的例子,假设我们想要获取我们最近的帖子并显示在屏幕上,我们通过调用getRecentPosts方法获得一个返回值List[String]——一个近期帖子的列表文本: + + val f: Future[List[String]] = Future { + session.getRecentPosts + } + + f onComplete { + case Success(posts) => for (post <- posts) println(post) + case Success(posts) => for (post <- posts) println(post) + } + +onComplete方法一般在某种意义上它允许客户处理future计算出的成功或失败的结果。对于仅仅处理成功的结果,onSuccess 回调使用如下(该回调以一个偏函数(partial function)为参数): + + val f: Future[List[String]] = Future { + session.getRecentPosts + } + + f onSuccess { + case posts => for (post <- posts) println(post) + } + +对于处理失败结果,onFailure回调使用如下: + + val f: Future[List[String]] = Future { + session.getRecentPosts + } + + f onFailure { + case t => println("An error has occured: " + t.getMessage) + } + + f onSuccess { + case posts => for (post <- posts) println(post) + } + +如果future失败,即future抛出异常,则执行onFailure回调。 + +因为偏函数具有 isDefinedAt方法, onFailure方法只有在特定的Throwable类型对象中被定义才会触发。下面例子中的onFailure回调永远不会被触发: + + val f = Future { + 2 / 0 + } + + f onFailure { + case npe: NullPointerException => + println("I'd be amazed if this printed out.") + } + +回到前面查找某个关键字第一次出现的例子,我们想要在屏幕上打印出此关键字的位置: + + val firstOccurrence: Future[Int] = Future { + val source = scala.io.Source.fromFile("myText.txt") + source.toSeq.indexOfSlice("myKeyword") + } + + firstOccurrence onSuccess { + case idx => println("The keyword first appears at position: " + idx) + } + + firstOccurrence onFailure { + case t => println("Could not process file: " + t.getMessage) + } + + onComplete,、onSuccess 和 onFailure 方法都具有Unit的结果类型,这意味着不能链接使用这些方法的回调。注意这种设计是为了避免暗示而刻意为之的,因为链接回调也许暗示着按照一定的顺序执行注册回调(回调注册在同一个future中是无序的)。 + +也就是说,我们现在应讨论论何时调用callback。因为callback需要future的值是可用的,所有回调只能在future完成之后被调用。然而,不能保证callback在完成future的线程或创建callback的线程中被调用。反而, 回调(callback)会在future对象完成之后的一些线程和一段时间内执行。所以我们说回调(callback)最终会被执行。 + +此外,回调(callback)执行的顺序不是预先定义的,甚至在相同的应用程序中callback的执行顺序也不尽相同。事实上,callback也许不是一个接一个连续的调用,但是可能会在同一时间同时执行。这意味着在下面的例子中,变量totalA也许不能在计算上下文中被设置为正确的大写或者小写字母。 + + @volatile var totalA = 0 + + val text = Future { + "na" * 16 + "BATMAN!!!" + } + + text onSuccess { + case txt => totalA += txt.count(_ == 'a') + } + + text onSuccess { + case txt => totalA += txt.count(_ == 'A') + } + +以上,这两个回调(callbacks)可能是一个接一个地执行的,这样变量totalA得到的预期值为18。然而,它们也可能是并发执行的,于是totalA最终可能是16或2,因为+= 是一个不可分割的操作符(即它是由一个读和一个写的步骤组成,这样就可能使其与其他的读和写任意交错执行)。 + +考虑到完整性,回调的使用情景列在这儿: + +- 在future中注册onComplete回调的时候要确保最后future执行完成之后调用相应的终止回调。 + +- 注册onSuccess或者onFailure回调时也和注册onComplete一样,不同之处在于future执行成功或失败分别调用onSuccess或onSuccess的对应的闭包。 + +- 注册一个已经完成的future的回调最后将导致此回调一直处于执行状态(1所隐含的)。 + +- 在future中注册多个回调的情况下,这些回调的执行顺序是不确定的。事实上,这些回调也许是同时执行的,然而,特定的ExecutionContext执行可能导致明确的顺序。 + +- 在一些回调抛出异常的情况下,其他的回调的执行不受影响。 + +- 在一些情况下,回调函数永远不能结束(例如,这些回调处于无限循环中),其他回调可能完全不会执行。在这种情况下,对于那些潜在的阻塞回调要使用阻塞的构造(例子如下)。 + +- 一旦执行完,回调将从future对象中移除,这样更适合JVM的垃圾回收机制(GC)。 + +### 函数组合(Functional Composition)和For解构(For-Comprehensions) + +尽管前文所展示的回调机制已经足够把future的结果和后继计算结合起来的,但是有些时候回调机制并不易于使用,且容易造成冗余的代码。我们可以通过一个例子来说明。假设我们有一个用于进行货币交易服务的API,我们想要在有盈利的时候购进一些美元。让我们先来看看怎样用回调来解决这个问题: + + val rateQuote = Future { + connection.getCurrentValue(USD) + } + + rateQuote onSuccess { case quote => + val purchase = Future { + if (isProfitable(quote)) connection.buy(amount, quote) + else throw new Exception("not profitable") + } + + purchase onSuccess { + case _ => println("Purchased " + amount + " USD") + } + } + +首先,我们创建一个名为rateQuote的future对象并获得当前的汇率。在服务器返回了汇率且该future对象成功完成了之后,计算操作才会从onSuccess回调中执行,这时我们就可以开始判断买还是不买了。所以我们创建了另一个名为purchase的future对象,用来在可盈利的情况下做出购买决定,并在稍后发送一个请求。最后,一旦purchase运行结束,我们会在标准输出中打印一条通知消息。 + +这确实是可行的,但是有两点原因使这种做法并不方便。其一,我们不得不使用onSuccess,且不得不在其中嵌套purchase future对象。试想一下,如果在purchase执行完成之后我们可能会想要卖掉一些其他的货币。这时我们将不得不在onSuccess的回调中重复这个模式,从而可能使代码过度嵌套,过于冗长,并且难以理解。 + +其二,purchase只是定义在局部范围内--它只能被来自onSuccess内部的回调响应。这也就是说,这个应用的其他部分看不到purchase,而且不能为它注册其他的onSuccess回调,比如说卖掉些别的货币。 + +为解决上述的两个问题,futures提供了组合器(combinators)来使之具有更多易用的组合形式。映射(map)是最基本的组合器之一。试想给定一个future对象和一个通过映射来获得该future值的函数,映射方法将创建一个新Future对象,一旦原来的Future成功完成了计算操作,新的Future会通过该返回值来完成自己的计算。你能够像理解容器(collections)的map一样来理解future的map。 + +让我们用map的方法来重构一下前面的例子: + + val rateQuote = Future { + connection.getCurrentValue(USD) + } + + val purchase = rateQuote map { quote => + if (isProfitable(quote)) connection.buy(amount, quote) + else throw new Exception("not profitable") + } + + purchase onSuccess { + case _ => println("Purchased " + amount + " USD") + } + +通过对rateQuote的映射我们减少了一次onSuccess的回调,更重要的是避免了嵌套。这时如果我们决定出售一些货币就可以再次使用purchase方法上的映射了。 + +可是如果isProfitable方法返回了false将会发生些什么?会引发异常?这种情况下,purchase的确会因为异常而失败。不仅仅如此,想象一下,链接的中断和getCurrentValue方法抛出异常会使rateQuote的操作失败。在这些情况下映射将不会返回任何值,而purchase也会会自动的以和rateQuote相同的异常而执行失败。 + +总之,如果原Future的计算成功完成了,那么返回的Future将会使用原Future的映射值来完成计算。如果映射函数抛出了异常则Future也会带着该异常完成计算。如果原Future由于异常而计算失败,那么返回的Future也会包含相同的异常。这种异常的传导方式也同样适用于其他的组合器(combinators)。 + +使之能够在For-comprehensions原则下使用,是设计Future的目的之一。也正是因为这个原因,Future还拥有flatMap,filter和foreach等组合器。其中flatMap方法可以构造一个函数,它可以把值映射到一个姑且称为g的新future,然后返回一个随g的完成而完成的Future对象。 + +让我们假设我们想把一些美元兑换成瑞士法郎。我们必须为这两种货币报价,然后再在这两个报价的基础上确定交易。下面是一个在for-comprehensions中使用flatMap和withFilter的例子: + + val usdQuote = Future { connection.getCurrentValue(USD) } + val chfQuote = Future { connection.getCurrentValue(CHF) } + + val purchase = for { + usd <- usdQuote + chf <- chfQuote + if isProfitable(usd, chf) + } yield connection.buy(amount, chf) + + purchase onSuccess { + case _ => println("Purchased " + amount + " CHF") + } + +purchase只有当usdQuote和chfQuote都完成计算以后才能完成-- 它以其他两个Future的计算值为前提所以它自己的计算不能更早的开始。 + +上面的for-comprhension将被转换为: + + val purchase = usdQuote flatMap { + usd => + chfQuote + .withFilter(chf => isProfitable(usd, chf)) + .map(chf => connection.buy(amount, chf)) + } + +这的确是比for-comprehension稍微难以把握一些,但是我们这样分析有助于您更容易的理解flatMap的操作。FlatMap操作会把自身的值映射到其他future对象上,并随着该对象计算完成的返回值一起完成计算。在我们的例子里,flatMap用usdQuote的值把chfQuote的值映射到第三个futrue对象里,该对象用于发送一定量瑞士法郎的购入请求。只有当通过映射返回的第三个future对象完成了计算,purchase才能完成计算。 + +这可能有些难以置信,但幸运的是faltMap操作在for-comprhensions模式以外很少使用,因为for-comprehensions本身更容易理解和使用。 + +再说说filter,它可以用于创建一个新的future对象,该对象只有在满足某些特定条件的前提下才会得到原始future的计算值,否则就会抛出一个NoSuchElementException的异常而失败。调用了filter的future,其效果与直接调用withFilter完全一样。 + +作为组合器的collect同filter之间的关系有些类似容器(collections)API里的那些方法之间的关系。 + +值得注意的是,调用foreach组合器并不会在计算值可用的时候阻塞当前的进程去获取计算值。恰恰相反,只有当future对象成功计算完成了,foreach所迭代的函数才能够被异步的执行。这意味着foreach与onSuccess回调意义完全相同。 + +由于Future trait(译注: trait有点类似java中的接口(interface)的概念)从概念上看包含两种类型的返回值(计算结果和异常),所以组合器会有一个处理异常的需求。 + +比方说我们准备在rateQuote的基础上决定购入一定量的货币,那么`connection.buy`方法需要知道购入的数量和期望的报价值,最终完成购买的数量将会被返回。假如报价值偏偏在这个节骨眼儿改变了,那buy方法将会抛出一个`QuoteChangedExecption`,并且不会做任何交易。如果我们想让我们的Future对象返回0而不是抛出那个该死的异常,那我们需要使用recover组合器: + + val purchase: Future[Int] = rateQuote map { + quote => connection.buy(amount, quote) + } recover { + case QuoteChangedException() => 0 + } + +这里用到的recover能够创建一个新future对象,该对象当计算完成时持有和原future对象一样的值。如果执行不成功则偏函数的参数会被传递给使原Future失败的那个Throwable异常。如果它把Throwable映射到了某个值,那么新的Future就会成功完成并返回该值。如果偏函数没有定义在Throwable中,那么最终产生结果的future也会失败并返回同样的Throwable。 + +组合器recoverWith能够创建一个新future对象,当原future对象成功完成计算时,新future对象包含有和原future对象相同的计算结果。若原future失败或异常,偏函数将会返回造成原future失败的相同的Throwable异常。如果此时Throwable又被映射给了别的future,那么新Future就会完成并返回这个future的结果。recoverWith同recover的关系跟flatMap和map之间的关系很像。 + +fallbackTo组合器生成的future对象可以在该原future成功完成计算时返回结果,如果原future失败或异常返回future参数对象的成功值。在原future和参数future都失败的情况下,新future对象会完成并返回原future对象抛出的异常。正如下面的例子中,本想打印美元的汇率,但是在获取美元汇率失败的情况下会打印出瑞士法郎的汇率: + + val usdQuote = Future { + connection.getCurrentValue(USD) + } map { + usd => "Value: " + usd + "$" + } + val chfQuote = Future { + connection.getCurrentValue(CHF) + } map { + chf => "Value: " + chf + "CHF" + } + + al anyQuote = usdQuote fallbackTo chfQuote + + anyQuote onSuccess { println(_) } + +组合器andThen的用法是出于纯粹的side-effecting目的。经andThen返回的新Future无论原Future成功或失败都会返回与原Future一模一样的结果。一旦原Future完成并返回结果,andThen后跟的代码块就会被调用,且新Future将返回与原Future一样的结果,这确保了多个andThen调用的顺序执行。正如下例所示,这段代码可以从社交网站上把近期发出的帖子收集到一个可变集合里,然后把它们都打印在屏幕上: + + val allposts = mutable.Set[String]() + + Future { + session.getRecentPosts + } andThen { + case Success(posts) => allposts ++= posts + } andThen { + case _ => + clearAll() + for (post <- allposts) render(post) + } + +综上所述,Future的组合器功能是纯函数式的,每种组合器都会返回一个与原Future相关的新Future对象。 + +### 投影(Projections) + +为了确保for解构(for-comprehensions)能够返回异常,futures也提供了投影(projections)。如果原future对象失败了,失败的投影(projection)会返回一个带有Throwable类型返回值的future对象。如果原Future成功了,失败的投影(projection)会抛出一个NoSuchElementException异常。下面就是一个在屏幕上打印出异常的例子: + + val f = Future { + 2 / 0 + } + for (exc <- f.failed) println(exc) + +下面的例子不会在屏幕上打印出任何东西: + + val f = Future { + 4 / 2 + } + for (exc <- f.failed) println(exc) + +### Future的扩展 + +用更多的实用方法来对Futures API进行扩展支持已经被提上了日程,这将为很多外部框架提供更多专业工具。 + +## Blocking + +正如前面所说的,在future的blocking非常有效地缓解性能和预防死锁。虽然在futures中使用这些功能方面的首选方式是Callbacks和combinators,但在某些处理中也会需要用到blocking,并且它也是被Futures and Promises API所支持的。 + +在之前的并发交易(concurrency trading)例子中,在应用的最后有一处用到block来确定是否所有的futures已经完成。这有个如何使用block来处理一个future结果的例子: + + import scala.concurrent._ + import scala.concurrent.duration._ + + def main(args: Array[String]) { + val rateQuote = Future { + connection.getCurrentValue(USD) + } + + val purchase = rateQuote map { quote => + if (isProfitable(quote)) connection.buy(amount, quote) + else throw new Exception("not profitable") + } + + Await.result(purchase, 0 nanos) + } + +在这种情况下这个future是不成功的,这个调用者转发出了该future对象不成功的异常。它包含了失败的投影(projection)-- 阻塞(blocking)该结果将会造成一个NoSuchElementException异常在原future对象被成功计算的情况下被抛出。 + +相反的,调用`Await.ready`来等待这个future直到它已完成,但获不到它的结果。同样的方式,调用那个方法时如果这个future是失败的,它将不会抛出异常。 + +The Future trait实现了Awaitable trait还有其`ready()`和`result()`方法。这些方法不能被客户端直接调用,它们只能通过执行环境上下文来进行调用。 + +为了允许程序调用可能是阻塞式的第三方代码,而又不必实现Awaitable特质,原函数可以用如下的方式来调用: + + blocking { + potentiallyBlockingCall() + } + +这段blocking代码也可以抛出一个异常。在这种情况下,这个异常会转发给调用者。 + +## 异常(Exceptions) + +当异步计算抛出未处理的异常时,与那些计算相关的futures就失败了。失败的futures存储了一个Throwable的实例,而不是返回值。Futures提供onFailure回调方法,它用一个PartialFunction去表示一个Throwable。下列特殊异常的处理方式不同: + +`scala.runtime.NonLocalReturnControl[_]` --此异常保存了一个与返回相关联的值。通常情况下,在方法体中的返回结构被调用去抛出这个异常。相关联的值将会存储到future或一个promise中,而不是一直保存在这个异常中。 + +ExecutionException-当因为一个未处理的中断异常、错误或者`scala.util.control.ControlThrowable`导致计算失败时会被存储起来。这种情况下,ExecutionException会为此具有未处理的异常。这些异常会在执行失败的异步计算线程中重新抛出。这样做的目的,是为了防止正常情况下没有被客户端代码处理过的那些关键的、与控制流相关的异常继续传播下去,同时告知客户端其中的future对象是计算失败的。 + +更精确的语义描述请参见 [NonFatal]。 + +## Promises + +到目前为止,我们仅考虑了通过异步计算的方式创建future对象来使用future的方法。尽管如此,futures也可以使用promises来创建。 + +如果说futures是为了一个还没有存在的结果,而当成一种只读占位符的对象类型去创建,那么promise就被认为是一个可写的,可以实现一个future的单一赋值容器。这就是说,promise通过这种success方法可以成功去实现一个带有值的future。相反的,因为一个失败的promise通过failure方法就会实现一个带有异常的future。 + +一个promise p通过p.future方式返回future。 这个futrue对象被指定到promise p。根据这种实现方式,可能就会出现p.future与p相同的情况。 + +考虑下面的生产者 - 消费者的例子,其中一个计算产生一个值,并把它转移到另一个使用该值的计算。这个传递中的值通过一个promise来完成。 + + import scala.concurrent.{ Future, Promise } + import scala.concurrent.ExecutionContext.Implicits.global + + val p = Promise[T]() + val f = p.future + + val producer = Future { + val r = produceSomething() + p success r + continueDoingSomethingUnrelated() + } + + val consumer = Future { + startDoingSomething() + f onSuccess { + case r => doSomethingWithResult() + } + } + +在这里,我们创建了一个promise并利用它的future方法获得由它实现的Future。然后,我们开始了两种异步计算。第一种做了某些计算,结果值存放在r中,通过执行promise p,这个值被用来完成future对象f。第二种做了某些计算,然后读取实现了future f的计算结果值r。需要注意的是,在生产者完成执行`continueDoingSomethingUnrelated()` 方法这个任务之前,消费者可以获得这个结果值。 + +正如前面提到的,promises具有单赋值语义。因此,它们仅能被实现一次。在一个已经计算完成的promise或者failed的promise上调用success方法将会抛出一个IllegalStateException异常。 + +下面的这个例子显示了如何fail a promise。 + + val p = promise[T] + val f = p.future + + val producer = Future { + val r = someComputation + if (isInvalid(r)) + p failure (new IllegalStateException) + else { + val q = doSomeMoreComputation(r) + p success q + } + } + +如上,生产者计算出一个中间结果值r,并判断它的有效性。如果它不是有效的,它会通过返回一个异常实现promise p的方式fails the promise,关联的future f是failed。否则,生产者会继续它的计算,最终使用一个有效的结果值实现future f,同时实现 promise p。 + +Promises也能通过一个complete方法来实现,这个方法采用了一个`potential value Try[T]`,这个值要么是一个类型为`Failure[Throwable]`的失败的结果值,要么是一个类型为`Success[T]`的成功的结果值。 + +类似success方法,在一个已经完成(completed)的promise对象上调用failure方法和complete方法同样会抛出一个IllegalStateException异常。 + +应用前面所述的promises和futures方法的一个优点是,这些方法是单一操作的并且是没有副作用(side-effects)的,因此程序是具有确定性的(deterministic)。确定性意味着,如果该程序没有抛出异常(future的计算值被获得),无论并行的程序如何调度,那么程序的结果将会永远是一样的。 + +在一些情况下,客户端也许希望能够只在promise没有完成的情况下完成该promise的计算(例如,如果有多个HTTP请求被多个不同的futures对象来执行,并且客户端只关心地一个HTTP应答(response),该应答对应于地一个完成该promise的future)。因为这个原因,future提供了tryComplete,trySuccess和tryFailure方法。客户端需要意识到调用这些的结果是不确定的,调用的结果将以来从程序执行的调度。 + +completeWith方法将用另外一个future完成promise计算。当该future结束的时候,该promise对象得到那个future对象同样的值,如下的程序将打印1: + + val f = Future { 1 } + val p = promise[Int] + + p completeWith f + + p.future onSuccess { + case x => println(x) + } + +当让一个promise以异常失败的时候,三总子类型的Throwable异常被分别的处理。如果中断该promise的可抛出(Throwable)一场是`scala.runtime.NonLocalReturnControl`,那么该promise将以对应的值结束;如果是一个Error的实例,`InterruptedException`或者`scala.util.control.ControlThrowable`,那么该可抛出(Throwable)异常将会封装一个ExecutionException异常,该ExectionException将会让该promise以失败结束。 + +通过使用promises,futures的onComplete方法和future的构造方法,你能够实现前文描述的任何函数式组合组合器(compition combinators)。让我们来假设一下你想实现一个新的组合起,该组合器首先使用两个future对象f和,产生第三个future,该future能够用f或者g来完成,但是只在它能够成功完成的情况下。 + +这里有个关于如何去做的实例: + + def first[T](f: Future[T], g: Future[T]): Future[T] = { + val p = promise[T] + + f onSuccess { + case x => p.trySuccess(x) + } + + g onSuccess { + case x => p.trySuccess(x) + } + + p.future + } + +注意,在这种实现方式中,如果f与g都不是成功的,那么`first(f, g)`将不会实现(即返回一个值或者返回一个异常)。 + +## 工具(Utilities) + +为了简化在并发应用中处理时序(time)的问题,`scala.concurrent`引入了Duration抽象。Duration不是被作为另外一个通常的时间抽象存在的。他是为了用在并发(concurrency)库中使用的,Duration位于`scala.concurrent`包中。 + +Duration是表示时间长短的基础类,其可以是有限的或者无限的。有限的duration用FiniteDuration类来表示,并通过时间长度`(length)`和`java.util.concurrent.TimeUnit`来构造。无限的durations,同样扩展了Duration,只在两种情况下存在,`Duration.Inf`和`Duration.MinusInf`。库中同样提供了一些Durations的子类用来做隐式的转换,这些子类不应被直接使用。 + +抽象的Duration类包含了如下方法: + +到不同时间单位的转换`(toNanos, toMicros, toMillis, toSeconds, toMinutes, toHours, toDays and toUnit(unit: TimeUnit))`。 +durations的比较`(<,<=,>和>=)`。 +算术运算符`(+, -, *, / 和单值运算_-)` +duration的最大最小方法`(min,max)`。 +测试duration是否是无限的方法`(isFinite)`。 +Duration能够用如下方法实例化`(instantiated)`: + +隐式的通过Int和Long类型转换得来 `val d = 100 millis`。 +通过传递一个`Long length`和`java.util.concurrent.TimeUnit`。例如`val d = Duration(100, MILLISECONDS)`。 +通过传递一个字符串来表示时间区间,例如 `val d = Duration("1.2 µs")`。 +Duration也提供了unapply方法,因此可以i被用于模式匹配中,例如: + + import scala.concurrent.duration._ + import java.util.concurrent.TimeUnit._ + + // instantiation + val d1 = Duration(100, MILLISECONDS) // from Long and TimeUnit + val d2 = Duration(100, "millis") // from Long and String + val d3 = 100 millis // implicitly from Long, Int or Double + val d4 = Duration("1.2 µs") // from String + + // pattern matching + val Duration(length, unit) = 5 millis diff --git a/_zh-cn/overviews/core/implicit-classes.md b/_zh-cn/overviews/core/implicit-classes.md new file mode 100644 index 0000000000..12c6f5f555 --- /dev/null +++ b/_zh-cn/overviews/core/implicit-classes.md @@ -0,0 +1,83 @@ +--- +layout: singlepage-overview +title: Implicit Classes + +partof: implicit-classes + +language: zh-cn + +discourse: false +--- + +**Josh Suereth 著** + +## 介绍 + +Scala 2.10引入了一种叫做隐式类的新特性。隐式类指的是用implicit关键字修饰的类。在对应的作用域内,带有这个关键字的类的主构造函数可用于隐式转换。 + +隐式类型是在[SIP-13](http://docs.scala-lang.org/sips/pending/implicit-classes.html)中提出的。 + +## 用法 + +创建隐式类时,只需要在对应的类前加上implicit关键字。比如: + + object Helpers { + implicit class IntWithTimes(x: Int) { + def times[A](f: => A): Unit = { + def loop(current: Int): Unit = + if(current > 0) { + f + loop(current - 1) + } + loop(x) + } + } + } + +这个例子创建了一个名为IntWithTimes的隐式类。这个类包含一个int值和一个名为times的方法。要使用这个类,只需将其导入作用域内并调用times方法。比如: + + scala> import Helpers._ + import Helpers._ + + scala> 5 times println("HI") + HI + HI + HI + HI + HI + +使用隐式类时,类名必须在当前作用域内可见且无歧义,这一要求与隐式值等其他隐式类型转换方式类似。 + +## 限制条件 + +隐式类有以下限制条件: + +1. 只能在别的trait/类/对象内部定义。 + +```` + object Helpers { + implicit class RichInt(x: Int) // 正确! + } + implicit class RichDouble(x: Double) // 错误! +```` + +2. 构造函数只能携带一个非隐式参数。 +```` + implicit class RichDate(date: java.util.Date) // 正确! + implicit class Indexer[T](collecton: Seq[T], index: Int) // 错误! + implicit class Indexer[T](collecton: Seq[T])(implicit index: Index) // 正确! +```` + +虽然我们可以创建带有多个非隐式参数的隐式类,但这些类无法用于隐式转换。 + +3. 在同一作用域内,不能有任何方法、成员或对象与隐式类同名。 + +注意:这意味着隐式类不能是case class。 + + object Bar + implicit class Bar(x: Int) // 错误! + + val x = 5 + implicit class x(y: Int) // 错误! + + implicit case class Baz(x: Int) // 错误! diff --git a/_zh-cn/overviews/core/string-interpolation.md b/_zh-cn/overviews/core/string-interpolation.md new file mode 100644 index 0000000000..91fed4b818 --- /dev/null +++ b/_zh-cn/overviews/core/string-interpolation.md @@ -0,0 +1,121 @@ +--- +layout: singlepage-overview +title: 字符串插值 + +partof: string-interpolation + +language: zh-cn + +discourse: false +--- + +**Josh Suereth 著** + +## 简介 + +自2.10.0版本开始,Scala提供了一种新的机制来根据数据生成字符串:字符串插值。字符串插值允许使用者将变量引用直接插入处理过的字面字符中。如下例: + + val name="James" + println(s"Hello,$name")//Hello,James + +在上例中, s"Hello,$name" 是待处理字符串字面,编译器会对它做额外的工作。待处理字符串字面通过“号前的字符来标示(例如:上例中是s)。字符串插值的实现细节在 [SIP-11](http://docs.scala-lang.org/sips/pending/string-interpolation.html) 中有全面介绍。 + +## 用法 + +Scala 提供了三种创新的字符串插值方法:s,f 和 raw. + +### s 字符串插值器 + +在任何字符串前加上s,就可以直接在串中使用变量了。你已经见过这个例子: + + val name="James" + println(s"Hello,$name")//Hello,James +此例中,$name嵌套在一个将被s字符串插值器处理的字符串中。插值器知道在这个字符串的这个地方应该插入这个name变量的值,以使输出字符串为Hello,James。使用s插值器,在这个字符串中可以使用任何在处理范围内的名字。 + +字符串插值器也可以处理任意的表达式。例如: + + println(s"1+1=${1+1}") +将会输出字符串1+1=2。任何表达式都可以嵌入到${}中。 + +### f 插值器 + +在任何字符串字面前加上 f,就可以生成简单的格式化串,功能相似于其他语言中的 printf 函数。当使用 f 插值器的时候,所有的变量引用都应当后跟一个printf-style格式的字符串,如%d。看下面这个例子: + + val height=1.9d + val name="James" + println(f"$name%s is $height%2.2f meters tall")//James is 1.90 meters tall +f 插值器是类型安全的。如果试图向只支持 int 的格式化串传入一个double 值,编译器则会报错。例如: + + val height:Double=1.9d + + scala>f"$height%4d" + :9: error: type mismatch; + found : Double + required: Int + f"$height%4d" + ^ +f 插值器利用了java中的字符串数据格式。这种以%开头的格式在 [Formatter javadoc] 中有相关概述。如果在具体变量后没有%,则格式化程序默认使用 %s(串型)格式。 + +### raw 插值器 + +除了对字面值中的字符不做编码外,raw 插值器与 s 插值器在功能上是相同的。如下是个被处理过的字符串: + + scala>s"a\nb" + res0:String= + a + b +这里,s 插值器用回车代替了\n。而raw插值器却不会如此处理。 + + scala>raw"a\nb" + res1:String=a\nb +当不想输入\n被转换为回车的时候,raw 插值器是非常实用的。 + +除了以上三种字符串插值器外,使用者可以自定义插值器。 + +### 高级用法 + +在Scala中,所有处理过的字符串字面值都进行了简单编码转换。任何时候编译器遇到一个如下形式的字符串字面值: + + id"string content" +它都会被转换成一个StringContext实例的call(id)方法。这个方法在隐式范围内仍可用。只需要简单得 +建立一个隐类,给StringContext实例增加一个新方法,便可以定义我们自己的字符串插值器。如下例: + + //注意:为了避免运行时实例化,我们从AnyVal中继承。 + //更多信息请见值类的说明 + implicit class JsonHelper(val sc:StringContext) extends AnyVal{ + def json(args:Any*):JSONObject=sys.error("TODO-IMPLEMENT") + } + + def giveMeSomeJson(x:JSONObject):Unit=... + + giveMeSomeJson(json"{name:$name,id:$id}") +在这个例子中,我们试图通过字符串插值生成一个JSON文本语法。隐类 JsonHelper 作用域内使用该语法,且这个JSON方法需要一个完整的实现。只不过,字符串字面值格式化的结果不是一个字符串,而是一个JSON对象。 + +当编译器遇到"{name:$name,id:$id"}",它将会被重写成如下表达式: + + new StringContext("{name:",",id:","}").json(name,id) + +隐类则被重写成如下形式 + + new JsonHelper(new StringContext("{name:",",id:","}")).json(name,id) + +所以,JSON方法可以访问字符串的原生片段而每个表达式都是一个值。这个方法的一个简单但又令人迷惑的例子: + + implicit class JsonHelper(val sc:StringContext) extends AnyVal{ + def json(args:Any*):JSONObject={ + val strings=sc.parts.iterator + val expressions=args.iterator + var buf=new StringBuffer(strings.next) + while(strings.hasNext){ + buf append expressions.next + buf append strings.next + } + parseJson(buf) + } + } + +被处理过的字符串的每部分都是StringContext的成员。每个表达式的值都将传入到JSON方法的args参数。JSON方法接受这些值并合成一个大字符串,然后再解析成JSON格式。有一种更复杂的实现可以避免合成字符串的操作,它只是简单的直接通过原生字符串和表达式值构建JSON。 + +## 限制 + +字符串插值目前对模式匹配语句不适用。此特性将在2.11版本中生效。 diff --git a/_zh-cn/overviews/core/value-classes.md b/_zh-cn/overviews/core/value-classes.md new file mode 100644 index 0000000000..85014abdb8 --- /dev/null +++ b/_zh-cn/overviews/core/value-classes.md @@ -0,0 +1,260 @@ +--- +layout: singlepage-overview +title: Value Classes and Universal Traits + +partof: value-classes + +language: zh-cn + +discourse: false +--- + +**Mark Harrah 著** + +## 引言 + +Value classes是在[SIP-15](http://docs.scala-lang.org/sips/pending/value-classes.html)中提出的一种通过继承AnyVal类来避免运行时对象分配的新机制。以下是一个最简的value class。 + + class Wrapper(val underlying: Int) extends AnyVal + +它仅有一个被用作运行时底层表示的公有val参数。在编译期,其类型为Wrapper,但在运行时,它被表示为一个Int。Value class可以带有def定义,但不能再定义额外的val、var,以及内嵌的trait、class或object: + + class Wrapper(val underlying: Int) extends AnyVal { + def foo: Wrapper = new Wrapper(underlying * 19) + } + +Value class只能继承universal traits,但其自身不能再被继承。所谓universal trait就是继承自Any的、只有def成员,且不作任何初始化工作的trait。继承自某个universal trait的value class同时继承了该trait的方法,但是(调用这些方法)会带来一定的对象分配开销。例如: + + trait Printable extends Any { + def print(): Unit = println(this) + } + class Wrapper(val underlying: Int) extends AnyVal with Printable + + val w = new Wrapper(3) + w.print() // 这里实际上会生成一个Wrapper类的实例 + +本文后续篇幅将介绍相关用例和与对象分配时机相关的细节,并给出一些有关value class自身限制的具体实例。 + +## 扩展方法 + +关于value类的一个用例,是将它们和隐含类联合([SIP-13](http://docs.scala-lang.org/sips/pending/implicit-classes.html))以获得免分配扩展方法。使用隐含类可以提供便捷的语法来定义扩展方法,同时 value 类移除运行时开销。一个好的例子是在标准库里的RichInt类。RichInt 继承自Int类型并附带一些方法。由于它是一个 value类,使用RichInt 方法时不需要创建一个RichInt 的实例。 + +下面有关RichInt的代码片段示范了RichInt是如何继承Int来允许3.toHexString的表达式: + + implicit class RichInt(val self: Int) extends AnyVal { + def toHexString: String = java.lang.Integer.toHexString(self) + } + +在运行时,表达式3.toHexString 被优化并等价于静态对象的方法调用 (RichInt$.MODULE$.extension$toHexString(3)),而不是创建一个新实例对象,再调用其方法。 + +## 正确性 + +关于value类的另一个用例是:不增加运行时开销的同时,获得数据类型的类型安全。例如,一个数据类型片断代表一个距离 ,如: + + class Meter(val value: Double) extends AnyVal { + def +(m: Meter): Meter = new Meter(value + m.value) + } + +代码:对两个距离进行相加,例如: + + val x = new Meter(3.4) + val y = new Meter(4.3) + val z = x + y + +实际上不会分配任何Meter实例,而是在运行时仅使用原始双精浮点数(double) 。 + +注意:在实践中,可以使用条件类(case)and/or 扩展方法来让语句更清晰。 + +## 必须进行分配的情况 + +由于JVM不支持value类,Scala 有时需要真正实例化value类。详细细节见[SIP-15]。 + +### 分配概要 + +value类在以下情况下,需要真正实例化: + +1. value类作为另一种类型使用时。 +2. value类被赋值给数组。 +3. 执行运行时类型测试,例如模式匹配。 + +### 分配细节 + +无论何时,将value类作为另一种类型进行处理时(包括universal trait),此value类实例必须被实例化。例如,value类Meter : + + trait Distance extends Any + case class Meter(val value: Double) extends AnyVal with Distance + +接收Distance类型值的方法需要一个正真的Meter实例。下面的例子中,Meter类真正被实例化。 + + def add(a: Distance, b: Distance): Distance = ... + add(Meter(3.4), Meter(4.3)) + +如果替换add方法的签名: + + def add(a: Meter, b: Meter): Meter = ... + +那么就不必进行分配了。此规则的另一个例子是value类作为类型参数使用。例如:即使是调用identity方法,也必须创建真正的Meter实例。 + + def identity[T](t: T): T = t + identity(Meter(5.0)) + +必须进行分配的另一种情况是:将它赋值给数组。即使这个数组就是value类数组,例如: + + val m = Meter(5.0) + val array = Array[Meter](m) + +数组中包含了真正的Meter 实例,并不只是底层基本类型double。 + +最后是类型测试。例如,模式匹配中的处理以及asInstanceOf方法都要求一个真正的value类实例: + + case class P(val i: Int) extends AnyVal + + val p = new P(3) + p match { // 在这里,新的P实例被创建 + case P(3) => println("Matched 3") + case P(x) => println("Not 3") + } + +## 限制 + +目前Value类有一些限制,部分原因是JVM不提供value类概念的原生支持。value类的完整实现细节及其限制见[SIP-15]。 + +### 限制概要 + +一个value类 ... + +1. ... 必须只有一个public的构造函数。并有且只有一个public的,类型不为value类的val参数。 +2. ... 不能有特殊的类型参数. +3. ... 不能有嵌套或本地类、trait或对象。 +4. ... 不能定义equals或hashCode方法。 +5. ... 必须是一个顶级类,或静态访问对象的一个成员 +6. ... 仅能有def为成员。尤其是,成员不能有惰性val、val或者var 。 +7. ... 不能被其它类继承。 + +### 限制示例 + +本章节列出了许多限制下具体影响, 而在“必要分配”章节已提及的部分则不再敖述。 + +构造函数不允许有多个参数: + + class Complex(val real: Double, val imag: Double) extends AnyVal + +则Scala编译器将生成以下的错误信息: + + Complex.scala:1: error: value class needs to have exactly one public val parameter + (Complex.scala:1: 错误:value类只能有一个public的val参数。) + (译者注:鉴于实际中编译器输出的可能是英文信息,在此提供双语。) + class Complex(val real: Double, val imag: Double) extends AnyVal + ^ + +由于构造函数参数必须是val,而不能是一个按名(by-name)参数: + + NoByName.scala:1: error: `val' parameters may not be call-by-name + (NoByName.scala:1: 错误: `val' 不能为 call-by-name) + class NoByName(val x: => Int) extends AnyVal + ^ + +Scala不允许惰性val作为构造函数参数, 所以value类也不允许。并且不允许多个构造函数。 + + class Secondary(val x: Int) extends AnyVal { + def this(y: Double) = this(y.toInt) + } + + Secondary.scala:2: error: value class may not have secondary constructors + (Secondary.scala:2: 错误:value类不能有第二个构造函数。) + def this(y: Double) = this(y.toInt) + ^ + +value class不能将惰性val或val作为成员,也不能有嵌套类、trait或对象。 + + class NoLazyMember(val evaluate: () => Double) extends AnyVal { + val member: Int = 3 + lazy val x: Double = evaluate() + object NestedObject + class NestedClass + } + + Invalid.scala:2: error: this statement is not allowed in value class: private[this] val member: Int = 3 + (Invalid.scala:2: 错误: value类中不允许此表达式:private [this] val member: Int = 3) + val member: Int = 3 + ^ + Invalid.scala:3: error: this statement is not allowed in value class: lazy private[this] var x: Double = NoLazyMember.this.evaluate.apply() + (Invalid.scala:3: 错误:value类中不允许此表达式: lazy private[this] var x: Double = NoLazyMember.this.evaluate.apply()) + lazy val x: Double = evaluate() + ^ + Invalid.scala:4: error: value class may not have nested module definitions + (Invalid.scala:4: 错误: value类中不能定义嵌套模块) + object NestedObject + ^ + Invalid.scala:5: error: value class may not have nested class definitions + (Invalid.scala:5: 错误:value类中不能定义嵌套类) + class NestedClass + ^ + +注意:value类中也不允许出现本地类、trait或对象,如下: + + class NoLocalTemplates(val x: Int) extends AnyVal { + def aMethod = { + class Local + ... + } + } + +在目前value类实现的限制下,value类不能嵌套: + + class Outer(val inner: Inner) extends AnyVal + class Inner(val value: Int) extends AnyVal + + Nested.scala:1: error: value class may not wrap another user-defined value class + (Nested.scala:1:错误:vlaue类不能包含另一个用户定义的value类) + class Outer(val inner: Inner) extends AnyVal + ^ + +此外,结构类型不能使用value类作为方法的参数或返回值类型。 + + class Value(val x: Int) extends AnyVal + object Usage { + def anyValue(v: { def value: Value }): Value = + v.value + } + + Struct.scala:3: error: Result type in structural refinement may not refer to a user-defined value class + (Struct.scala:3: 错误: 结构细化中的结果类型不适用于用户定义的value类) + def anyValue(v: { def value: Value }): Value = + ^ + +value类不能继承non-universal trait,并且其本身不能被继承: + + trait NotUniversal + class Value(val x: Int) extends AnyVal with notUniversal + class Extend(x: Int) extends Value(x) + + Extend.scala:2: error: illegal inheritance; superclass AnyVal + is not a subclass of the superclass Object + of the mixin trait NotUniversal + (Extend.scala:2: 错误:非法继承:父类AnyVal不是一个父类对象(混入trait NotUniversal)的子类) + class Value(val x: Int) extends AnyVal with NotUniversal + ^ + Extend.scala:3: error: illegal inheritance from final class Value + (Extend.scala:3: 错误: 从Value类(final类)非法继承) + class Extend(x: Int) extends Value(x) + ^ + +第二条错误信息显示:虽然value类没有显式地用final关键字修饰,但依然认为value类是final类。 + +另一个限制是:一个类仅支持单个参数的话,则value类必须是顶级类,或静态访问对象的成员。这是由于嵌套value类需要第二个参数来引用封闭类。所以不允许下述代码: + + class Outer { + class Inner(val x: Int) extends AnyVal + } + + Outer.scala:2: error: value class may not be a member of another class + (Outer.scala:2: 错误:value类不能作为其它类的成员) + class Inner(val x: Int) extends AnyVal + ^ + +但允许下述代码,因为封闭对象是顶级类: + + object Outer { + class Inner(val x: Int) extends AnyVal + } From 370d0ad4e847c16b468514e3ea3a3076196100d4 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Wed, 26 Jul 2017 21:29:15 +0200 Subject: [PATCH 064/112] Rest of the single page overviews (pt 2) --- es/overviews/core/actors.md | 497 ---------------- es/overviews/core/parallel-collections.md | 9 - es/overviews/core/string-interpolation.md | 133 ----- .../overviews/core/actors-migration-guide.md | 466 --------------- zh-cn/overviews/core/actors.md | 307 ---------- .../core/architecture-of-scala-collections.md | 541 ------------------ zh-cn/overviews/core/futures.md | 504 ---------------- zh-cn/overviews/core/implicit-classes.md | 83 --- zh-cn/overviews/core/string-interpolation.md | 122 ---- zh-cn/overviews/core/value-classes.md | 261 --------- 10 files changed, 2923 deletions(-) delete mode 100644 es/overviews/core/actors.md delete mode 100644 es/overviews/core/parallel-collections.md delete mode 100644 es/overviews/core/string-interpolation.md delete mode 100644 zh-cn/overviews/core/actors-migration-guide.md delete mode 100644 zh-cn/overviews/core/actors.md delete mode 100644 zh-cn/overviews/core/architecture-of-scala-collections.md delete mode 100644 zh-cn/overviews/core/futures.md delete mode 100644 zh-cn/overviews/core/implicit-classes.md delete mode 100644 zh-cn/overviews/core/string-interpolation.md delete mode 100644 zh-cn/overviews/core/value-classes.md diff --git a/es/overviews/core/actors.md b/es/overviews/core/actors.md deleted file mode 100644 index 92f5d6e291..0000000000 --- a/es/overviews/core/actors.md +++ /dev/null @@ -1,497 +0,0 @@ ---- -layout: overview -title: API de actores en Scala -label-color: success -label-text: Available -language: es -overview: actors - -discourse: false ---- - -**Philipp Haller and Stephen Tu** - -**Traducción e interpretación: Miguel Ángel Pastor Olivar** - -## Introducción - -La presente guía describe el API del paquete `scala.actors` de Scala 2.8/2.9. El documento se estructura en diferentes grupos lógicos. La jerarquía de "traits" es tenida en cuenta para llevar a cabo la estructuración de las secciones individuales. La atención se centra en el comportamiento exhibido en tiempo de ejecución por varios de los métodos presentes en los traits anteriores, complementando la documentación existente en el Scaladoc API. - -## Traits de actores: Reactor, ReplyReactor, y Actor - -### The Reactor trait - -`Reactor` es el padre de todos los traits relacionados con los actores. Heredando de este trait podremos definir actores con una funcionalidad básica de envío y recepción de mensajes. - -El comportamiento de un `Reactor` se define mediante la implementación de su método `act`. Este método es ejecutado una vez el `Reactor` haya sido iniciado mediante la invocación del método `start`, retornando el `Reactor`. El método `start`es *idempotente*, lo cual significa que la invocación del mismo sobre un actor que ya ha sido iniciado no surte ningún efecto. - -El trait `Reactor` tiene un parámetro de tipo `Msg` el cual determina el tipo de mensajes que un actor es capaz de recibir. - -La invocación del método `!` de un `Reactor` envía un mensaje al receptor. La operación de envío de un mensaje mediante el operador `!` es asíncrona por lo que el actor que envía el mensaje no se bloquea esperando a que el mensaje sea recibido sino que su ejecución continua de manera inmediata. Por ejemplo, `a ! msg` envia `msg` a `a`. Todos los actores disponen de un *buzón* encargado de regular los mensajes entrantes hasta que son procesados. - -El trait `Reactor` trait también define el método `forward`. Este método es heredado de `OutputChannel` y tiene el mismo efecto que el método `!`. Aquellos traits que hereden de `Reactor`, en particular el trait `ReplyActor`, sobreescriben este método para habilitar lo que comunmente se conocen como *"implicit reply destinations"* (ver a continuación) - -Un `Reactor` recibe mensajes utilizando el método `react`. Este método espera un argumento de tipo `PartialFunction[Msg, Unit]` el cual define cómo los mensajes de tipo `Msg` son tratados una vez llegan al buzón de un actor. En el siguiente ejemplo, el actor espera recibir la cadena "Hello", para posteriomente imprimir un saludo: - - react { - case "Hello" => println("Hi there") - } - -La invocación del método `react` nunca retorna. Por tanto, cualquier código que deba ejecutarse tras la recepción de un mensaje deberá ser incluido dentro de la función parcial pasada al método `react`. Por ejemplo, dos mensajes pueden ser recibidos secuencialmente mediante la anidación de dos llamadas a `react`: - - react { - case Get(from) => - react { - case Put(x) => from ! x - } - } - -El trait `Reactor` también ofrece una serie de estructuras de control que facilitan la programación utilizando el mecanismo de `react`. - -#### Terminación y estados de ejecución - -La ejecución de un `Reactor` finaliza cuando el cuerpo del método `act` ha sido completado. Un `Reactor` también pueden terminarse a si mismo de manera explícita mediante el uso del método `exit`. El tipo de retorno de `exit` es `Nothing`, dado que `exit` siempre dispara una excepción. Esta excepción únicamente se utiliza de manera interna y nunca debería ser capturada. - -Un `Reactor` finalizado pueden ser reiniciado mediante la invocación de su método `restart`. La invocación del método anterior sobre un `Reactor` que no ha terminado su ejecución lanza una excepción de tipo `IllegalStateException`. El reinicio de un actor que ya ha terminado provoca que el método `act` se ejecute nuevamente. - -El tipo `Reactor` define el método `getState`, el cual retorna, como un miembro de la enumeración `Actor.State`, el estado actual de la ejecución del actor. Un actor que todavía no ha sido iniciado se encuentra en el estado `Actor.State.New`. Si el actor se está ejecutando pero no está esperando por ningún mensaje su estado será `Actor.State.Runnable`. En caso de que el actor haya sido suspendido mientras espera por un mensaje estará en el estado `Actor.State.Suspended`. Por último, un actor ya terminado se encontrará en el estado `Actor.State.Terminated`. - -#### Manejo de excepciones - -El miembro `exceptionHandler` permite llevar a cabo la definición de un manejador de excepciones que estará habilitado durante toda la vida del `Reactor`: - - def exceptionHandler: PartialFunction[Exception, Unit] - -Este manejador de excepciones (`exceptionHandler`) retorna una función parcial que se utiliza para gestionar excepciones que no hayan sido tratadas de ninguna otra manera. Siempre que una excepción se propague fuera del método `act` de un `Reactor` el manejador anterior será aplicado a dicha excepción, permitiendo al actor ejecutar código de limpieza antes de que se termine. Nótese que la visibilidad de `exceptionHandler` es `protected`. - -El manejo de excepciones mediante el uso de `exceptionHandler` encaja a la perfección con las estructuras de control utilizadas para programas con el método `react`. Siempre que una excepción es manejada por la función parcial retornada por `excepctionHandler`, la ejecución continua con la "closure" actual: - - loop { - react { - case Msg(data) => - if (cond) // process data - else throw new Exception("cannot process data") - } - } - -Assumiendo que `Reactor` sobreescribe el atributo `exceptionHandler`, tras el lanzamiento de una excepción en el cuerpo del método `react`, y una vez ésta ha sido gestionada, la ejecución continua con la siguiente iteración del bucle. - -### The ReplyReactor trait - -El trait `ReplyReactor` extiende `Reactor[Any]` y sobrescribe y/o añade los siguientes métodos: - -- El método `!` es sobrescrito para obtener una referencia al actor - actual (el emisor). Junto al mensaje actual, la referencia a dicho - emisor es enviada al buzón del actor receptor. Este último dispone de - acceso al emisor del mensaje mediante el uso del método `sender` (véase más abajo). - -- El método `forward` es sobrescrito para obtener una referencia al emisor - del mensaje que actualmente está siendo procesado. Junto con el mensaje - actual, esta referencia es enviada como el emisor del mensaje actual. - Como consuencia de este hecho, `forward` nos permite reenviar mensajes - en nombre de actores diferentes al actual. - -- El método (añadido) `sender` retorna el emisor del mensaje que está siendo - actualmente procesado. Puesto que un mensaje puede haber sido reenviado, - `sender` podría retornar un actor diferente al que realmente envió el mensaje. - -- El método (añadido) `reply` envía una respuesta al emisor del último mensaje. - `reply` también es utilizado para responder a mensajes síncronos o a mensajes - que han sido enviados mediante un "future" (ver más adelante). - -- El método (añadido) `!?` ofrece un *mecanismo síncrono de envío de mensajes*. - La invocación de `!?` provoca que el actor emisor del mensaje se bloquee hasta - que se recibe una respuesta, momento en el cual retorna dicha respuesta. Existen - dos variantes sobrecargadas. La versión con dos parámetros recibe un argumento - adicional que representa el tiempo de espera (medido en milisegundos) y su tipo - de retorno es `Option[Any]` en lugar de `Any`. En caso de que el emisor no - reciba una respuesta en el periodo de espera establecido, el método `!?` retornará - `None`; en otro caso retornará la respuesta recibida recubierta con `Some`. - -- Los métodos (añadidos) `!!` son similares al envío síncrono de mensajes en el sentido de - que el receptor puede enviar una respuesta al emisor del mensaje. Sin embargo, en lugar - de bloquear el actor emisor hasta que una respuesta es recibida, retornan una instancia de - `Future`. Esta última puede ser utilizada para recuperar la respuesta del receptor una - vez se encuentre disponible; asimismo puede ser utilizada para comprobar si la respuesta - está disponible sin la necesidad de bloquear el emisor. Existen dos versiones sobrecargadas. - La versión que acepta dos parámetros recibe un argumento adicional de tipo - `PartialFunction[Any, A]`. Esta función parcial es utilizada para realizar el post-procesado de - la respuesta del receptor. Básicamente, `!!` retorna un "future" que aplicará la anterior - función parcial a la repuesta (una vez recibida). El resultado del "future" es el resultado - de este post-procesado. - -- El método (añadido) `reactWithin` permite llevar a cabo la recepción de mensajes en un periodo - determinado de tiempo. En comparación con el método `react`, recibe un parámetro adicional, - `msec`, el cual representa el periodo de tiempo, expresado en milisegundos, hasta que el patrón `TIMEOUT` - es satisfecho (`TIMEOUT` es un "case object" presente en el paquete `scala.actors`). Ejemplo: - - reactWithin(2000) { - case Answer(text) => // process text - case TIMEOUT => println("no answer within 2 seconds") - } - -- El método `reactWithin` también permite realizar accesos no bloqueantes al buzón. Si - especificamos un tiempo de espera de 0 milisegundos, primeramente el buzón será escaneado - en busca de un mensaje que concuerde. En caso de que no exista ningún mensaje concordante - tras el primer escaneo, el patrón `TIMEOUT` será satisfecho. Por ejemplo, esto nos permite - recibir determinado tipo de mensajes donde unos tienen una prioridad mayor que otros: - - reactWithin(0) { - case HighPriorityMsg => // ... - case TIMEOUT => - react { - case LowPriorityMsg => // ... - } - } - - En el ejemplo anterior, el actor procesa en primer lugar los mensajes `HighPriorityMsg` aunque - exista un mensaje `LowPriorityMsg` más antiguo en el buzón. El actor sólo procesará mensajes - `LowPriorityMsg` en primer lugar en aquella situación donde no exista ningún `HighProrityMsg` - en el buzón. - -Adicionalmente, el tipo `ReplyActor` añade el estado de ejecución `Actor.State.TimedSuspended`. Un actor suspendido, esperando la recepción de un mensaje mediante el uso de `reactWithin` se encuentra en dicho estado. - -### El trait Actor - -El trait `Actor` extiende de `ReplyReactor` añadiendo y/o sobrescribiendo los siguientes miembros: - -- El método (añadido) `receive` se comporta del mismo modo que `react`, con la excepción - de que puede retornar un resultado. Este hecho se ve reflejado en la definición del tipo, - que es polimórfico en el tipo del resultado: `def receive[R](f: PartialFunction[Any, R]): R`. - Sin embargo, la utilización de `receive` hace que el uso del actor - sea más pesado, puesto que el hilo subyacente es bloqueado mientras - el actor está esperando por la respuesta. El hilo bloqueado no está - disponible para ejecutar otros actores hasta que la invocación del - método `receive` haya retornado. - -- El método (añadido) `link` permite a un actor enlazarse y desenlazarse de otro - actor respectivamente. El proceso de enlazado puede utilizarse para monitorizar - y responder a la terminación de un actor. En particular, el proceso de enlazado - afecta al comportamiento mostrado en la ejecución del método `exit` tal y como - se escribe en el la documentación del API del trait `Actor`. - -- El atributo `trapExit` permite responder a la terminación de un actor enlazado, - independientemente de los motivos de su terminación (es decir, carece de importancia - si la terminación del actor es normal o no). Si `trapExit` toma el valor cierto en - un actor, este nunca terminará por culpa de los actores enlazados. En cambio, siempre - y cuando uno de sus actores enlazados finalice, recibirá un mensaje de tipo `Exit`. - `Exit` es una "case class" que presenta dos atributos: `from` referenciando al actor - que termina y `reason` conteniendo los motivos de la terminación. - -#### Terminación y estados de ejecución - -Cuando la ejecución de un actor finaliza, el motivo de dicha terminación puede ser -establecida de manera explícita mediante la invocación de la siguiente variante -del método `exit`: - - def exit(reason: AnyRef): Nothing - -Un actor cuyo estado de terminación es diferente del símbolo `'normal` propaga -los motivos de su terminación a todos aquellos actores que se encuentren enlazados -a él. Si el motivo de la terminación es una excepción no controlada, el motivo de -finalización será una instancia de la "case class" `UncaughtException`. - -El trait `Actor` incluye dos nuevos estados de ejecución. Un actor que se encuentra -esperando la recepción de un mensaje mediante la utilización del método `receive` se -encuentra en el método `Actor.State.Blocked`. Un actor esperado la recepción de un -mensaje mediante la utilización del método `receiveWithin` se encuentra en el estado -`Actor.State.TimeBlocked`. - -## Estructuras de control - -El trait `Reactor` define una serie de estructuras de control que simplifican el mecanismo -de programación con la función sin retorno `react`. Normalmente, una invocación al método -`react` no retorna nunca. Si el actor necesita ejecutar código a continuación de la invocación -anterior, tendrá que pasar, de manera explícita, dicho código al método `react` o utilizar -algunas de las estructuras que encapsulan este comportamiento. - -La estructura de control más basica es `andThen`. Permite registrar una `closure` que será -ejecutada una vez el actor haya terminado la ejecución de todo lo demas. - - actor { - { - react { - case "hello" => // processing "hello" - }: Unit - } andThen { - println("hi there") - } - } - -Por ejemplo, el actor anterior imprime un saludo tras realizar el procesado -del mensaje `hello`. Aunque la invocación del método `react` no retorna, -podemos utilizar `andThen` para registrar el código encargado de imprimir -el saludo a continuación de la ejecución del actor. - -Nótese que existe una *atribución de tipo* a continuación de la invocación -de `react` (`:Unit`). Básicamente, nos permite tratar el resultado de -`react` como si fuese de tipo `Unit`, lo cual es legal, puesto que el resultado -de una expresión siempre se puede eliminar. Es necesario llevar a cabo esta operación -dado que `andThen` no puede ser un miembro del tipo `Unit`, que es el tipo del resultado -retornado por `react`. Tratando el tipo de resultado retornado por `react` como -`Unit` permite llevar a cabo la aplicación de una conversión implícita la cual -hace que el miembro `andThen` esté disponible. - -El API ofrece unas cuantas estructuras de control adicionales: - -- `loop { ... }`. Itera de manera indefinidia, ejecutando el código entre -las llaves en cada una de las iteraciones. La invocación de `react` en el -cuerpo del bucle provoca que el actor se comporte de manera habitual ante -la llegada de un nuevo mensaje. Posteriormente a la recepción del mensaje, -la ejecución continua con la siguiente iteración del bucle actual. - -- `loopWhile (c) { ... }`. Ejecuta el código entre las llaves mientras la -condición `c` tome el valor `true`. La invocación de `react` en el cuerpo -del bucle ocasiona el mismo efecto que en el caso de `loop`. - -- `continue`. Continua con la ejecución de la closure actual. La invocación -de `continue` en el cuerpo de un `loop`o `loopWhile` ocasionará que el actor -termine la iteración en curso y continue con la siguiente. Si la iteración en -curso ha sido registrada utilizando `andThen`, la ejecución continua con la -segunda "closure" pasada como segundo argumento a `andThen`. - -Las estructuras de control pueden ser utilizadas en cualquier parte del cuerpo -del método `act` y en los cuerpos de los métodos que, transitivamente, son -llamados por `act`. Aquellos actores creados utilizando la sintáxis `actor { ... }` -pueden importar las estructuras de control desde el objeto `Actor`. - -#### Futures - -Los traits `RepyActor` y `Actor` soportan operaciones de envío de mensajes -(métodos `!!`) que, de manera inmediata, retornan un *future*. Un *future*, -es una instancia del trait `Future` y actúa como un manejador que puede -ser utilizado para recuperar la respuesta a un mensaje "send-with-future". - -El emisor de un mensaje "send-with-future" puede esperar por la respuesta del -future *aplicando* dicha future. Por ejemplo, el envío de un mensaje mediante -`val fut = a !! msg` permite al emisor esperar por el resultado del future -del siguiente modo: `val res = fut()`. - -Adicionalmente, utilizando el método `isSet`, un `Future` puede ser consultado -de manera no bloqueante para comprobar si el resultado está disponible. - -Un mensaje "send-with-future" no es el único modo de obtener una referencia a -un future. Estos pueden ser creados utilizando el método `future`. En el siguiente -ejemplo, `body` se ejecuta de manera concurrente, retornando un future como -resultado. - - val fut = Future { body } - // ... - fut() // wait for future - -Lo que hace especial a los futures en el contexto de los actores es la posibilidad -de recuperar su resultado utilizando las operaciones estándar de actores de -recepción de mensajes como `receive`, etc. Además, es posible utilizar las operaciones -basadas en eventos `react`y `reactWithin`. Esto permite a un actor esperar por el -resultado de un future sin la necesidad de bloquear el hilo subyacente. - -Las operaciones de recepción basadas en actores están disponibles a través del -atributo `inputChannel` del future. Dado un future de tipo `Future[T]`, el tipo -de `inputChannel` es `InputChannel[T]`. Por ejemplo: - - val fut = a !! msg - // ... - fut.inputChannel.react { - case Response => // ... - } - -## Canales - -Los canales pueden ser utilizados para simplificar el manejo de mensajes -que presentan tipos diferentes pero que son enviados al mismo actor. La -jerarquía de canales se divide en `OutputChannel` e `InputChannel`. - -Los `OutputChannel` pueden ser utilizados para enviar mensajes. Un -`OutputChannel` `out` soporta las siguientes operaciones: - -- `out ! msg`. Envía el mensaje `msg` a `out` de manera asíncrona. Cuando `msg` - es enviado directamente a un actor se incluye un referencia al actor emisor - del mensaje. - -- `out forward msg`. Reenvía el mensaje `msg` a `out` de manera asíncrona. - El actor emisor se determina en el caso en el que `msg` es reenviado a - un actor. - -- `out.receiver`. Retorna el único actor que está recibiendo mensajes que están - siendo enviados al canal `out`. - -- `out.send(msg, from)`. Envía el mensaje `msg` a `out` de manera asíncrona, - proporcionando a `from` como el emisor del mensaje. - -Nótese que el trait `OutputChannel` tiene un parámetro de tipo que especifica el -tipo de los mensajes que pueden ser enviados al canal (utilizando `!`, `forward`, -y `send`). Este parámetro de tipo es contra-variante: - - trait OutputChannel[-Msg] - -Los actores pueden recibir mensajes de un `InputChannel`. Del mismo modo que -`OutputChannel`, el trait `InputChannel` presenta un parámetro de tipo que -especifica el tipo de mensajes que pueden ser recibidos por el canal. En este caso, -el parámetro de tipo es covariante: - - trait InputChannel[+Msg] - -Un `InputChannel[Msg]` `in` soportal las siguientes operaciones. - -- `in.receive { case Pat1 => ... ; case Patn => ... }` (y de manera similar, - `in.receiveWithin`) recibe un mensaje proveniente de `in`. La invocación - del método `receive` en un canal de entrada presenta la misma semántica - que la operación estándar de actores `receive`. La única diferencia es que - la función parcial pasada como argumento tiene tipo `PartialFunction[Msg, R]` - donde `R` es el tipo de retorno de `receive`. - -- `in.react { case Pat1 => ... ; case Patn => ... }` (y de manera similar, - `in.reactWithin`). Recibe un mensaje de `in` utilizando la operación basada en - eventos `react`. Del mismo modo que la operación `react` en actores, el tipo - de retorno es `Nothing`, indicando que las invocaciones de este método nunca - retornan. Al igual que la operación `receive` anterior, la función parcial - que se pasa como argumento presenta un tipo más específico: - - PartialFunction[Msg, Unit] - -### Creando y compartiendo canales - -Los canales son creados utilizando la clase concreta `Channel`. Esta clase extiende -de `InputChannel` y `OutputChannel`. Un canal pueden ser compartido haciendo dicho -canal visible en el ámbito de múltiples actores o enviándolo como mensaje. - -El siguiente ejemplo muestra la compartición mediante publicación en ámbitos: - - actor { - var out: OutputChannel[String] = null - val child = actor { - react { - case "go" => out ! "hello" - } - } - val channel = new Channel[String] - out = channel - child ! "go" - channel.receive { - case msg => println(msg.length) - } - } - -La ejecución de este ejemplo imprime la cadena "5" en la consola. Nótese que el -actor `child` únicamente tiene acceso a `out`, que es un `OutputChannel[String]`. -La referencia al canal, la cual puede ser utilizada para llevar a cabo la recepción -de mensajes, se encuentra oculta. Sin embargo, se deben tomar precauciones y -asegurarse que el canal de salida es inicializado con un canal concreto antes de que -`child` le envíe ningún mensaje. En el ejemplo que nos ocupa, esto es llevado a cabo -mediante el mensaje "go". Cuando se está recibiendo de `channel` utilizando el método -`channel.receive` podemos hacer uso del hecho que `msg` es de tipo `String`, y por -lo tanto tiene un miembro `length`. - -Una alternativa a la compartición de canales es enviarlos a través de mensajes. -El siguiente fragmento de código muestra un sencillo ejemplo de aplicación: - - case class ReplyTo(out: OutputChannel[String]) - - val child = actor { - react { - case ReplyTo(out) => out ! "hello" - } - } - - actor { - val channel = new Channel[String] - child ! ReplyTo(channel) - channel.receive { - case msg => println(msg.length) - } - } - -La "case class" `ReplyTo` es un tipo de mensajes que utilizamos para distribuir -una referencia a un `OutputChannel[String]`. Cuando el actor `child` recibe un -mensaje de tipo `ReplyTo` éste envía una cadena a su canal de salida. El segundo -actor recibe en el canal del mismo modo que anteriormente. - -## Planificadores - -Un `Reactor`(o una instancia de uno de sus subtipos) es ejecutado utilizando un -*planificador*. El trait `Reactor` incluye el miembro `scheduler` el cual retorna el -planificador utilizado para ejecutar sus instancias: - - def scheduler: IScheduler - -La plataforma de ejecución ejecuta los actores enviando tareas al planificador mediante -el uso de los métodos `execute` definidos en el trait `IScheduler`. La mayor parte -del resto de métodos definidos en este trait únicamente adquieren cierto protagonismo -cuando se necesita implementar un nuevo planificador desde cero; algo que no es necesario -en muchas ocasiones. - -Los planificadores por defecto utilizados para ejecutar instancias de `Reactor` y -`Actor` detectan cuando los actores han finalizado su ejecución. En el momento que esto -ocurre, el planificador se termina a si mismo (terminando con cualquier hilo que estuviera -en uso por parte del planificador). Sin embargo, algunos planificadores como el -`SingleThreadedScheduler` (definido en el paquete `scheduler`) necesita ser terminado de -manera explícita mediante la invocación de su método `shutdown`). - -La manera más sencilla de crear un planificador personalizado consisten en extender la clase -`SchedulerAdapter`, implementando el siguiente método abstracto: - - def execute(fun: => Unit): Unit - -Por norma general, una implementación concreata utilizaría un pool de hilos para llevar a cabo -la ejecución del argumento por nombre `fun`. - -## Actores remotos - -Esta sección describe el API de los actores remotos. Su principal interfaz es el objecto -[`RemoteActor`](http://www.scala-lang.org/api/2.9.1/scala/actors/remote/RemoteActor$.html) definido -en el paquete `scala.actors.remote`. Este objeto facilita el conjunto de métodos necesarios para crear -y establecer conexiones a instancias de actores remotos. En los fragmentos de código que se muestran a -continuación se asume que todos los miembros de `RemoteActor` han sido importados; la lista completa -de importaciones utilizadas es la siguiente: - - import scala.actors._ - import scala.actors.Actor._ - import scala.actors.remote._ - import scala.actors.remote.RemoteActor._ - -### Iniciando actores remotos - -Un actore remot es identificado de manera unívoca por un -[`Symbol`](http://www.scala-lang.org/api/2.9.1/scala/Symbol.html). Este símbolo es único para la instancia -de la máquina virual en la que se está ejecutando un actor. Un actor remoto identificado con el nombre -`myActor` puede ser creado del siguiente modo. - - class MyActor extends Actor { - def act() { - alive(9000) - register('myActor, self) - // ... - } - } - -Nótese que el nombre únicamente puede ser registrado con un único actor al mismo tiempo. -Por ejemplo, para registrar el actor *A* como `'myActor` y posteriormente registrar otro -actor *B* como `'myActor`, debería esperar hasta que *A* haya finalizado. Este requisito -aplica a lo largo de todos los puertos, por lo que registrando a *B* en un puerto diferente -no sería suficiente. - -### Connecting to remote actors - -Establecer la conexión con un actor remoto es un proceso simple. Para obtener una referencia remota -a un actor remoto que está ejecutándose en la máquina `myMachine` en el puerto 8000 con el nombre -`'anActor`, tendremos que utilizar `select`del siguiente modo: - - val myRemoteActor = select(Node("myMachine", 8000), 'anActor) - -El actor retornado por `select` es de tipo `AbstractActor`, que proporciona esencialmente el mismo -interfaz que un actor normal, y por lo tanto es compatible con las habituales operaciones de envío -de mensajes: - - myRemoteActor ! "Hello!" - receive { - case response => println("Response: " + response) - } - myRemoteActor !? "What is the meaning of life?" match { - case 42 => println("Success") - case oops => println("Failed: " + oops) - } - val future = myRemoteActor !! "What is the last digit of PI?" - -Nótese que la operación `select` es perezosa; no inicializa ninguna conexión de red. Simplemente crea -una nueva instancia de `AbstractActor` que está preparada para iniciar una nueva conexión de red en el -momento en que sea necesario (por ejemplo cuando el método '!' es invocado). diff --git a/es/overviews/core/parallel-collections.md b/es/overviews/core/parallel-collections.md deleted file mode 100644 index d731520cef..0000000000 --- a/es/overviews/core/parallel-collections.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: overview -overview: parallel-collections -partof: parallel-collections -language: es -title: Las Colecciones Paralelizadas - -discourse: false ---- diff --git a/es/overviews/core/string-interpolation.md b/es/overviews/core/string-interpolation.md deleted file mode 100644 index b1a63abfd1..0000000000 --- a/es/overviews/core/string-interpolation.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -layout: overview -title: Interpolación de cadenas -discourse: true -label-color: success -label-text: New in 2.10 -language: es -overview: string-interpolation - -discourse: false ---- - -**Josh Suereth** - -**Traducción e interpretación: Miguel Ángel Pastor Olivar** - -## Introducción - -Desde la versión 2.10.0, Scala ofrece un nuevo mecanismo para la creación de cadenas a partir de nuestros datos mediante la técnica de interpolación de cadenas. -Este nuevo mecanismo permite a los usuarios incluir referencias a variables de manera directa en cadenas de texto "procesadas". Por ejemplo: - - val name = "James" - println(s"Hello, $name") // Hello, James - -En el ejemplo anterior, el literal `s"Hello, $name"` es una cadena "procesada". Esto significa que el compilador debe realizar un trabajo adicional durante el tratamiento de dicha cadena. Una cadena "procesada" se denota mediante un conjunto de caracteres que preceden al símbolo `"`. La interpolación de cadenas ha sido introducida por [SIP-11](http://docs.scala-lang.org/sips/pending/string-interpolation.html), el cual contiene todos los detalles de implementación. - -## Uso - -Scala ofrece tres métodos de interpolación de manera nativa: `s`, `f` and `raw`. - -### Interpolador `s` - -El uso del prefijo `s` en cualquier cadena permite el uso de variables de manera directa dentro de la propia cadena. Ya hemos visto el ejemplo anterior: - - val name = "James" - println(s"Hello, $name") // Hello, James - -`$name` se anida dentro de la cadena "procesada" de tipo `s`. El interpolador `s` sabe como insertar el valor de la variable `name` en lugar indicado, dando como resultado la cadena `Hello, James`. Mediante el uso del interpolador `s`, cualquier nombre disponible en el ámbito puede ser utilizado dentro de la cadena. - -Las interpolaciones pueden recibir expresiones arbitrarias. Por ejemplo: - - println(s"1 + 1 = ${1 + 1}") - -imprimirá la cadena `1 + 1 = 2`. Cualquier expresión puede ser embebida en `${}` - -### Interpolador `f` - -Prefijando `f` a cualquier cadena permite llevar a cabo la creación de cadenas formateadas, del mismo modo que `printf` es utilizado en otros lenguajes. Cuando utilizamos este interpolador, todas las referencias a variables deben estar seguidas por una cadena de formateo que siga el formato `printf-`, como `%d`. Veamos un ejemplo: - - val height = 1.9d - val name = "James" - println(f"$name%s is $height%2.2f meters tall") // James is 1.90 meters tall - -El interpolador `f` es seguro respecto a tipos. Si pasamos un número real a una cadena de formateo que sólo funciona con números enteros, el compilador emitirá un error. Por ejemplo: - - val height: Double = 1.9d - - scala> f"$height%4d" - :9: error: type mismatch; - found : Double - required: Int - f"$height%4d" - ^ - -El interpolador `f` hace uso de las utilidades de formateo de cadenas disponibles en java. Los formatos permitidos tras el carácter `%` son descritos en [Formatter javadoc](http://docs.oracle.com/javase/1.6.0/docs/api/java/util/Formatter.html#detail). Si el carácter `%` no aparece tras la definición de una variable, `%s` es utilizado por defecto. - -### Interpolador `raw` - -El interpolador `raw` difiere del interpolador `s` en que el primero no realiza el escapado de literales contenidos en la cadena. A continuación se muestra un ejemplo de una cadena procesada: - - scala> s"a\nb" - res0: String = - a - b - -En el ejemplo anterior, el interpolador `s` ha reemplazado los caracteres `\n` con un salto de linea. El interpolador `raw` no llevará a cabo esta acción: - - scala> raw"a\nb" - res1: String = a\nb - -Esta cadena de interpolación es muy útil cuando se desea evitar que expresiones como `\n` se conviertan en un salto de línea. - -Adicionalmente a los interpoladores ofrecidos de serie por Scala, nosotros podremos definir nuestras propias cadenas de interpolación. - -## Uso avanzado - -En Scala, todas las cadenas "procesadas" son simples transformaciones de código. En cualquier punto en el que el compilador encuentra una cadena de texto con la forma: - - id"string content" - -la transforma en la llamada a un método (`id`) sobre una instancia de [StringContext](http://www.scala-lang.org/api/current/index.html#scala.StringContext). Este método también puede estar disponible en un ámbito implícito. Para definiir nuestra propia cadena de interpolación simplemente necesitamos crear una clase implícita que añada un nuevo método a la clase `StringContext`. A continuación se muestra un ejemplo: - - // Note: We extends AnyVal to prevent runtime instantiation. See - // value class guide for more info. - implicit class JsonHelper(val sc: StringContext) extends AnyVal { - def json(args: Any*): JSONObject = sys.error("TODO - IMPLEMENT") - } - - def giveMeSomeJson(x: JSONObject): Unit = ... - - giveMeSomeJson(json"{ name: $name, id: $id }") - -En este ejemplo, estamos intentando crear una cadena JSON mediante el uso de la interpolación de cadenas. La clase implícita `JsonHelper` debe estar disponible en el ámbito donde deseemos utilizar esta sintaxis, y el método `json` necesitaría ser implementado completamente. Sin embargo, el resutlado de dicha cadena de formateo no sería una cadena sino un objeto de tipo `JSONObject` - -Cuando el compilador encuentra la cadena `json"{ name: $name, id: $id }"` reescribe la siguiente expresión: - - new StringContext("{ name: ", ", id: ", " }").json(name, id) - -La clase implícita es utilizada para reescribir el fragmento anterior de la siguiente forma: - - new JsonHelper(new StringContext("{ name: ", ", id: ", " }")).json(name, id) - -De este modo, el método `json` tiene acceso a las diferentes partes de las cadenas así como cada una de las expresiones. Una implementación simple, y con errores, de este método podría ser: - - implicit class JsonHelper(val sc: StringContext) extends AnyVal { - def json(args: Any*): JSONObject = { - val strings = sc.parts.iterator - val expressions = args.iterator - var buf = new StringBuffer(strings.next) - while(strings.hasNext) { - buf append expressions.next - buf append strings.next - } - parseJson(buf) - } - } - -Cada una de las diferentes partes de la cadena "procesada" son expuestas en el atributo `parts` de la clase `StringContext`. Cada uno de los valores de la expresión se pasa en el argumento `args` del método `json`. Este método acepta dichos argumentos y genera una gran cadena que posteriormente convierte en un objecto de tipo JSON. Una implementación más sofisticada podría evitar la generación de la cadena anterior y llevar a cabo de manera directa la construcción del objeto JSON a partir de las cadenas y los valores de la expresión. - - -## Limitaciones - -La interpolación de cadenas no funciona con sentencias "pattern matching". Esta funcionalidad está planificada para su inclusión en la versión 2.11 de Scala. diff --git a/zh-cn/overviews/core/actors-migration-guide.md b/zh-cn/overviews/core/actors-migration-guide.md deleted file mode 100644 index 641dc5b61b..0000000000 --- a/zh-cn/overviews/core/actors-migration-guide.md +++ /dev/null @@ -1,466 +0,0 @@ ---- -layout: overview -language: zh-cn -label-color: success -label-text: New in 2.10 -overview: actors-migration-guide -title: Scala Actors迁移指南 - -discourse: false ---- - -**Vojin Jovanovic 和 Philipp Haller 著** - -## 概述 - -从Scala的2.11.0版本开始,Scala的Actors库已经过时了。早在Scala2.10.0的时候,默认的actor库即是Akka。 - -为了方便的将Scala Actors迁移到Akka,我们提供了Actor迁移工具包(AMK)。通过在一个项目的类路径中添加scala-actors-migration.jar,AMK包含了一个针对Scala Actors扩展。此外,Akka 2.1包含一些特殊功能,比如ActorDSL singleton,可以实现更简单的转换功能,使Scala Actors代码变成Akka代码。本章内容的目的是用来指导用户完成迁移过程,并解释如何使用AMK。 - -本指南包括以下内容:在“迁移工具的局限性”章节中,我们在此概述了迁移工具的主要局限性。在“迁移概述”章节中我们描述了迁移过程和谈论了Scala的变化分布,使得迁移成为一种可能。最后,在“一步一步指导迁移到Akka”章节里,我们展示了一些迁移工作的例子,以及各个步骤,如果需要从Scala Actors迁移至Akka's actors,本节是推荐阅读的。 - -免责声明:并发代码是臭名昭著的,当出现bug时很难调试和修复。由于两个actor的不同实现,这种差异导致可能出现错误。迁移过程没一步后都建议进行完全的代码测试。 - -## 迁移工具的局限性 - -由于Akka和Scala的actor模型的完整功能不尽相同导致两者之间不能平滑地迁移。下面的列表解释了很难迁移的部分行为: - -1. 依靠终止原因和双向行为链接方法 - Scala和Akka actors有不同的故障处理和actor monitoring模型。在Scala actors模型中,如果一个相关联部分异常终止,相关联的actors终止。如果终止是显式跟踪(通过self.trapExit),actor可以从失败的actor收到终止的原因。通过Akka这个功能不能迁移到AMK。AMK允许迁移的只是[Akka monitoring](http://doc.akka.io/docs/akka/2.1.0/general/supervision.html#What_Lifecycle_Monitoring_Means)机制。Monitoring不同于连接,因为它是单向(unindirectional)的并且终止的原因是现在已知的。如果仅仅是monitoring机制是无法满足需求的,迁移的链接必须推迟到最后一刻(步骤5的迁移)。然后,当迁移到Akka,用户必须创建一个[监督层次(supervision hierarchy)](http://doc.akka.io/docs/akka/2.1.0/general/supervision.html),处理故障。 - -2. 使用restart方法——Akka不提供显式的重启actors,因此上述例子我们不能提供平滑迁移。用户必须更改系统,所以没有使用重启方法(restart method)。 - -3. 使用getState方法 - Akka actors没有显式状态,此功能无法迁移。用户代码必须没有getState调用。 - -4. 实例化后没有启动actors - Akka actors模型会在实例化后自动启动actors,所以用户不需要重塑系统来显式的在实例化后启动actors。 - -5. mailboxSize方法不存在Akka中,因此不能迁移。这种方法很少使用,很容易被删除。 - -## 迁移概述 - -### 迁移工具 - -在Scal 2.10.0 actors 是在[Scala distribution](http://www.scala-lang.org/downloads)中作为一个单独包(scala-actors.jar)存在的,并且他们的接口已被弃用。这种分布也包含在Akka actors的akka-actor.jar里。AMK同时存在Scala actors 和 akka-actor.jar之中。未来的主要版本的Scala将不包含Scala actors和AMK。 - -开始迁移,用户需要添加scala-actors.jar和scala-actors-migration.jar来构建他们的项目。添加scala-actors.jar和scala-actors-migration.jar允许使用下面描述的AMK。这些jar位于[Scala Tools](https://oss.sonatype.org/content/groups/scala-tools/org/scala-lang/)库和[Scala distribution](http://www.scala-lang.org/downloads)库中。 - -### 一步一步来迁移 - -Actor迁移工具使用起来应该有5步骤。每一步都设计为引入的基于代码的最小变化。在前四个迁移步骤的代码中将使用Scala actors来实现,并在该步完成后运行所有的系统测试。然而,方法和类的签名将被转换为与Akka相似。迁移工具在Scal方面引入了一种新的actor类型(ActWithStash)和强制执行actors的ActorRef接口。 - -该结果同样强制通过一个特殊的方法在ActorDSL 对象上创建actors。在这些步骤可以每次迁移一个actor。这降低了在同一时刻引入多个bug的可能性,同样降低了bug的复杂程度。 - -在Scala方面迁移完成后,用户应该改变import语句并变成使用Akka库。在Akka方面,ActorDSL和ActWithStash允许对Scala Actors和他们的生态系的react construct进行建模。这个步骤迁移所有actors到Akka的后端,会在系统中引入bug。一旦代码迁移到Akka,用户将能够使用Akka的所有的功能的。 - -### 一步一步指导迁移到Akka - -在这一章中,我们将通过actor迁移的5个步骤。在每一步之后的代码都要为可能的错误进行检测。在前4个步骤中可以一边迁移一个actor和一边测试功能。然而,最后一步迁移所有actors到Akka后它只能作为一个整体进行测试。在这个步骤之后系统应该具有和之前一样相同的功能,不过它将使用Akka actor库。 - -### 步骤1——万物皆是Actor - -Scala actors库提供了公共访问多个类型的actors。他们被组织在类层次结构和每个子类提供了稍微更丰富的功能。为了进一步的使迁移步骤更容易,我们将首先更改Actor类型系统中的每一个actor。这种迁移步骤很简单,因为Actor类位于层次结构的底部,并提供了广泛的功能。 - -来自Scala库的Actors应根据以下规则进行迁移: - -1. class MyServ extends Reactor[T] -> class MyServ extends Actor - -注意,反应器提供了一个额外的类型参数代表了类型的消息收到。如果用户代码中使用这些信息,那么一个需要:i)应用模式匹配与显式类型,或者ii)做一个向下的消息来自任何泛型T。 - -1. class MyServ extends ReplyReactor -> class MyServ extends Actor - -2. class MyServ extends DaemonActor -> class MyServ extends Actor - -为了为DaemonActor提供配对功能,将下列代码添加到类的定义。 - - override def scheduler: IScheduler = DaemonScheduler - -### 步骤2 - 实例化 - -在Akka中,actors可以访问只有通过ActorRef接口。ActorRef的实例可以通过在ActorDSL对象上调用actor方法或者通过调用ActorRefFactory实例的actorOf方法来获得。在Scala的AMK工具包中,我们提供了Akka ActorRef和ActorDSL的一个子集,该子集实际上是Akka库的一个单例对象(singleton object)。 - -这一步的迁移使所有actors访问通过ActorRefs。首先,我们现实如何迁移普通模式的实例化Sacla Actors。然后,我们将展示如何分别克服问题的ActorRef和Actor的不同接口。 - -#### Actor实例化 - -actor实例的转换规则(以下规则需要import scala.actors.migration._): - -1. 构造器调用实例化 - - val myActor = new MyActor(arg1, arg2) - myActor.start() - -应该被替换 - - ActorDSL.actor(new MyActor(arg1, arg2)) - -2. 用于创建Actors的DSL(译注:领域专用语言(Domain Specific Language)) - - val myActor = actor { - // actor 定义 - } -应该被替换 - - val myActor = ActorDSL.actor(new Actor { - def act() { - // actor 定义 - } - }) - -3. 从Actor Trait扩展来的对象 - - object MyActor extends Actor { - // MyActor 定义 - } - MyActor.start() -应该被替换 - - class MyActor extends Actor { - // MyActor 定义 - } - - object MyActor { - val ref = ActorDSL.actor(new MyActor) - } -所有的MyActor地想都应该被替换成MyActor.ref。 - -需要注意的是Akka actors在实例化的同时开始运行。actors创建并开始在迁移的系统的情况下,actors在不同的位置以及改变这可能会影响系统的行为,用户需要更改代码,以使得actors在实例化后立即开始执行。 - -远程actors也需要被获取作为ActorRefs。为了得到一个远程actor ActorRef需使用方法selectActorRef。 - -#### 不同的方法签名(signatures) - -至此为止我们已经改变了所有的actor实例化,返回ActorRefs,然而,我们还没有完成迁移工作。有不同的接口在ActorRefs和Actors中,因此我们需要改变在每个迁移实例上触发的方法。不幸的是,Scala Actors提供的一些方法不能迁移。对下列方法的用户需要找到一个解决方案: - -1. getState()——Akka中的actors 默认情况下由其监管actors(supervising actors)负责管理和重启。在这种情况下,一个actor的状态是不相关的。 - -2. restart() - 显式的重启一个Scala actor。在Akka中没有相应的功能。 - -所有其他Actor方法需要转换为两个ActorRef中的方法。转换是通过下面描述的规则。请注意,所有的规则需要导入以下内容: - - import scala.concurrent.duration._ - import scala.actors.migration.pattern.ask - import scala.actors.migration._ - import scala.concurrent._ -额外规则1-3的作用域定义在无限的时间需要一个隐含的超时。然而,由于Akka不允许无限超时,我们会使用100年。例如: - - implicit val timeout = Timeout(36500 days) - -规则: - -1. !!(msg: Any): Future[Any] 被?替换。这条规则会改变一个返回类型到scala.concurrent.Future这可能导致类型不匹配。由于scala.concurrent.Future比过去的返回值具有更广泛的功能,这种类型的错误可以很容易地固定在与本地修改: - - actor !! message -> respActor ? message - -2. !![A] (msg: Any, handler: PartialFunction[Any, A]): Future[A] 被?取代。处理程序可以提取作为一个单独的函数,并用来生成一个future对象结果。处理的结果应给出另一个future对象结果,就像在下面的例子: - - val handler: PartialFunction[Any, T] = ... // handler - actor !! (message, handler) -> (respActor ? message) map handler - -3. !? (msg: Any):任何被?替换都将阻塞在返回的future对象上 - - actor !? message -> - Await.result(respActor ? message, Duration.Inf) - -4. !? (msec: Long, msg: Any): Option[Any]任何被?替换都将显式的阻塞在future对象 - - actor !? (dur, message) -> - val res = respActor.?(message)(Timeout(dur milliseconds)) - val optFut = res map (Some(_)) recover { case _ => None } - Await.result(optFut, Duration.Inf) - -这里没有提到的公共方法是为了actors DSL被申明为公共的。他们只能在定义actor时使用,所以他们的这一步迁移是不相关的。 - -###第3步 - 从Actor 到 ActWithStash - -到目前为止,所有的控制器都继承自Actor trait。我们通过指定的工厂方法来实例化控制器,所有的控制器都可以通过接口ActorRef 来进行访问。现在我们需要把所有的控制器迁移的AMK 的 ActWithStash 类上。这个类的行为方式和Scala的Actor几乎完全一致,它提供了另外一些方法,对应于Akka的Actor trait。这使得控制器更易于逐步的迁移到Akka。 - -为了达到这个目的,所有的从Actor继承的类,按照下列的方式,需要改为继承自ActWithStash: - - class MyActor extends Actor -> class MyActor extends ActWithStash - -经过这样修改以后,代码会无法通过编译。因为ActWithStash中的receive 方法不能在act中像原来那样使用。要使代码通过编译,需要在所有的 receive 调用中加上类型参数。例如: - - receive { case x: Int => "Number" } -> - receive[String] { case x: Int => "Number" } - -另外,要使代码通过编译,还要在act方法前加上 override关键字,并且定义一个空的receive方法。act方法需要被重写,因为它在ActWithStash 的实现中模拟了Akka的消息处理循环。需要修改的地方请看下面的例子: - - class MyActor extends ActWithStash { - - // 空的 receive 方法 (现在还没有用) - def receive = {case _ => } - - override def act() { - // 原来代码中的 receive 方法改为 react。 - } - } -ActWithStash 的实例中,变量trapExit 的缺省值是true。如果希望改变,可以在初始化方法中把它设置为false。 - -远程控制器在ActWithStash 下无法直接使用,register('name, this)方法需要被替换为: - - registerActorRef('name, self) - -在后面的步骤中, registerActorRef 和 alive 方法的调用与其它方法一样。 - -现在,用户可以测试运行,整个系统的运行会和原来一样。ActWithStash 和Actor 拥有相同的基本架构,所以系统的运行会与原来没有什么区别。 - -### 第4步 - 去掉act 方法 - -在这一节,我们讨论怎样从ActWithStash中去掉act方法,以及怎样修改其他方法,使它与Akka更加契合. 这一环节会比较繁杂,所以我们建议最好一次只修改一个控制器。在Scala中,控制器的行为主要是在act方法的中定义。逻辑上来说,控制器是一个并发执行act方法的过程,执行完成后过程终止。在Akka中,控制器用一个全局消息处理器来依次处理它的的消息队列中的消息。这个消息处理器是一个receive函数返回的偏函数(partial function),该偏函数被应用与每一条消息上。 - -因为ActWithStash中Akka方法的行为依赖于移除的act方法,所以我们首先要做的是去掉act方法。然后,我们需要按照给定的规则修改scala.actors.Actor中每个方法的。 - -#### 怎样去除act 方法 - -在下面的列表中,我们给出了通用消息处理模式的修改规则。这个列表并不包含所有的模式,它只是覆盖了其中一些通用的模式。然而用户可以通过参考这些规则,通过扩展简单规则,将act方法移植到Akka。 - -嵌套调用react/reactWithin需要注意:消息处理偏函数需要做结构扩展,使它更接近Akka模式。尽管这种修改会很复杂,但是它允许任何层次的嵌套被移植。下面有相关的例子。 - -在复杂控制流中使用receive/receiveWithin需要注意:这个移植会比较复杂,因为它要求重构act方法。在消息处理偏函数中使用react 和 andThen可以使receive的调用模型化。下面是一些简单的例子。 - -1. 如果在act方法中有一些代码在第一个包含react的loop之前被执行,那么这些代码应该被放在preStart方法中。 - - def act() { - //初始化的代码放在这里 - loop { - react { ... } - } - } -应该被替换 - - override def preStart() { - //初始化的代码放在这里 - } - - def act() { - loop { - react{ ... } - } - } -其他的模式,如果在第一个react 之前有一些代码,也可以使用这个规则。 - -2. 当act 的形式为:一个简单loop循环嵌套react,用下面的方法。 - - def act() = { - loop { - react { - // body - } - } - } -应该被替换 - - def receive = { - // body - } - -3. 当act包含一个loopWhile 结构,用下面的方法。 - - def act() = { - loopWhile(c) { - react { - case x: Int => - // do task - if (x == 42) { - c = false - } - } - } - } -应该被替换 - - def receive = { - case x: Int => - // do task - if (x == 42) { - context.stop(self) - } - } - -4. 当act包含嵌套的react,用下面的规则: - - def act() = { - var c = true - loopWhile(c) { - react { - case x: Int => - // do task - if (x == 42) { - c = false - } else { - react { - case y: String => - // do nested task - } - } - } - } - } -应该被替换 - - def receive = { - case x: Int => - // do task - if (x == 42) { - context.stop(self) - } else { - context.become(({ - case y: String => - // do nested task - }: Receive).andThen(x => { - unstashAll() - context.unbecome() - }).orElse { case x => stash(x) }) - } - } - -5. reactWithin方法使用下面的修改规则: - - loop { - reactWithin(t) { - case TIMEOUT => // timeout processing code - case msg => // message processing code - } - } -应该被替换 - - import scala.concurrent.duration._ - - context.setReceiveTimeout(t millisecond) - def receive = { - case ReceiveTimeout => // timeout processing code - case msg => // message processing code - } - -6. 在Akka中,异常处理用另一种方式完成。如果要模拟Scala控制器的方式,那就用下面的方法 - - def act() = { - loop { - react { - case msg => - // 可能会失败的代码 - } - } - } - - override def exceptionHandler = { - case x: Exception => println("got exception") - } -应该被替换 - - def receive = PFCatch({ - case msg => - // 可能会失败的代码 - }, { case x: Exception => println("got exception") }) - PFCatch 的定义 - - class PFCatch(f: PartialFunction[Any, Unit], - handler: PartialFunction[Exception, Unit]) - extends PartialFunction[Any, Unit] { - - def apply(x: Any) = { - try { - f(x) - } catch { - case e: Exception if handler.isDefinedAt(e) => - handler(e) - } - } - - def isDefinedAt(x: Any) = f.isDefinedAt(x) - } - - object PFCatch { - def apply(f: PartialFunction[Any, Unit], - handler: PartialFunction[Exception, Unit]) = - new PFCatch(f, handler) - } - -PFCatch并不包含在AMK之中,所以它可以保留在移植代码中,AMK将会在下一版本中被删除。当整个移植完成后,错误处理也可以改由Akka来监管。 - -#### 修改Actor的方法 - -当我们移除了act方法以后,我们需要替换在Akka中不存在,但是有相似功能的方法。在下面的列表中,我们给出了两者的区别和替换方法: - -1. exit()/exit(reason) - 需要由 context.stop(self) 替换 - -2. receiver - 需要由 self 替换 - -3. reply(msg) - 需要由 sender ! msg 替换 - -4. link(actor) - 在Akka中,控制器之间的链接一部分由[supervision](http://doc.akka.io/docs/akka/2.1.0/general/supervision.html#What_Supervision_Means)来完成,一部分由[actor monitoring](http://doc.akka.io/docs/akka/2.1.0/general/supervision.html#What_Lifecycle_Monitoring_Means)来完成。在AMK中,我们只支持监测方法。因此,这部分Scala功能可以被完整的移植。 - -linking 和 watching 之间的区别在于:watching actor总是接受结束通知。然而,不像Scala的Exit消息包含结束的原因,Akka的watching 返回Terminated(a: ActorRef)消息,只包含ActorRef。获取结束原因的功能无法被移植。在Akka中,这一步骤可以在第4步之后,通过组织控制器的监管层级 [supervision hierarchy](http://doc.akka.io/docs/akka/2.1.0/general/supervision.html)来完成。 - -如果watching actors收到的消息不撇陪结束消息,控制器会被终止并抛出DeathPactException异常。注意就算watching actors正常的结束,也会发生这种情况。在Scala中,linked actors只要一方不正常的终止,另一方就会以相同的原因终止。 - -如果系统不能单独的用 watch actors来 移植,用户可以像原来那样用link和exit(reason)来使用。然而,因为act()重载了Exit消息,需要做如下的修改: - - case Exit(actor, reason) => - println("sorry about your " + reason) - ... -应该被替换 - - case t @ Terminated(actorRef) => - println("sorry about your " + t.reason) - ... -注意:在Scala和Akka的actor之间有另一种细微的区别:在Scala, link/watch 到已经终止的控制器不会有任何影响。在Akka中,看管已经终止的控制器会导致发送终止消息。这会在系统移植的第5 步导致不可预料的结果。 - -### 第5步 - Akka后端的移植 - -到目前为止,用户代码已经做好了移植到Akka actors的准备工作。现在我们可以把Scala actors迁移到Akka actor上。为了完成这一目标,需要配置build,去掉scala-actors.jar 和 scala-actors-migration.jar,把 akka-actor.jar 和 typesafe-config.jar加进来。AMK只能在Akka actor 2.1下正常工作,Akka actor 2.1已经包含在分发包 [Scala distribution](http://www.scala-lang.org/downloads)中, 可以用这样的方法配置。 - -经过这一步骤以后,因为包名的不同和API之间的细微差别,编译会失败。我们必须将每一个导入的actor从scala 修改为Akka。下列是部分需要修改的包名: - - scala.actors._ -> akka.actor._ - scala.actors.migration.ActWithStash -> akka.actor.ActorDSL._ - scala.actors.migration.pattern.ask -> akka.pattern.ask - scala.actors.migration.Timeout -> akka.util.Timeout - -当然,ActWithStash 中方法的声明 def receive = 必须加上前缀override。 - -在Scala actor中,stash 方法需要一个消息做为参数。例如: - - def receive = { - ... - case x => stash(x) - } - -在Akka中,只有当前处理的消息可以被隐藏(stashed)。因此,上面的例子可以替换为: - - def receive = { - ... - case x => stash() - } - -#### 添加Actor System - -Akka actor 组织在[Actor systems](http://doc.akka.io/docs/akka/2.1.0/general/actor-systems.html)系统中。每一个被实例化的actor必须属于某一个ActorSystem。因此,要添加一个ActorSystem 实例作为每个actor 实例调用的第一个参数。下面给出了例子。 - -为了完成该转换,你需要有一个actor system 实例。例如: - - val system = ActorSystem("migration-system") - -然后,做如下转换: - - ActorDSL.actor(...) -> ActorDSL.actor(system)(...) - -如果对actor 的调用都使用同一个ActorSystem ,那么它可以作为隐式参数来传递。例如: - - ActorDSL.actor(...) -> - import project.implicitActorSystem - ActorDSL.actor(...) - -当所有的主线程和actors结束后,Scala程序会终止。迁移到Akka后,当所有的主线程结束,所有的actor systems关闭后,程序才会结束。Actor systems 需要在程序退出前明确的中止。这需要通过在Actor system中调用shutdown 方法来完成。 - -#### 远程 Actors - -当代码迁移到Akka,远程actors就不再工作了。 registerActorFor 和 alive 方法需要被移除。 在Akka中,远程控制通过配置独立的完成。更多细节请参考[Akka remoting documentation](http://doc.akka.io/docs/akka/2.1.0/scala/remoting.html)。 - -#### 样例和问题 - -这篇文档中的所有程序片段可以在[Actors Migration test suite](http://github.com/scala/actors-migration/tree/master/src/test/)中找到,这些程序做为测试文件,前缀为actmig。 - -这篇文档和Actor移植组件由 [Vojin Jovanovic](http://people.epfl.ch/vojin.jovanovic)和[Philipp Haller](http://lampwww.epfl.ch/~phaller/)编写。 - -如果你发现任何问题或不完善的地方,请把它们报告给 [Scala Bugtracker](https://github.com/scala/actors-migration/issues)。 - diff --git a/zh-cn/overviews/core/actors.md b/zh-cn/overviews/core/actors.md deleted file mode 100644 index 01809d1e1e..0000000000 --- a/zh-cn/overviews/core/actors.md +++ /dev/null @@ -1,307 +0,0 @@ ---- -layout: overview -title: The Scala Actors API -overview: actors -language: zh-cn - -discourse: false ---- - -**Philipp Haller 和 Stephen Tu 著** - -## 简介 - -本指南介绍了Scala 2.8和2.9中`scala.actors`包的API。这个包的内容因为逻辑上相通,所以放到了同一个类型的包内。这个trait在每个章节里面都会有所涉及。这章的重点在于这些traits所定义的各种方法在运行状态时的行为,由此来补充现有的Scala基础API。 - -注意:在Scala 2.10版本中这个Actors库将是过时的,并且在未来Scala发布的版本中将会被移除。开发者应该使用在`akka.actor`包中[Akka](http://akka.io/) actors来替代它。想了解如何将代码从Scala actors迁移到Akka请参考[Actors 迁移指南](http://docs.scala-lang.org/overviews/core/actors-migration-guide.html)章节。 - -## Actor trait:Reactor, ReplyReactor和Actor - -### Reactor trait - -Reactor 是所有`actor trait`的父级trait。扩展这个trait可以定义actor,其具有发送和接收消息的基本功能。 - -Reactor的行为通过实现其act方法来定义。一旦调用start方法启动Reactor,这个act方法便会执行,并返回这个Reactor对象本身。start方法是具有等幂性的,也就是说,在一个已经启动了的actor对象上调用它(start方法)是没有作用的。 - -Reactor trait 有一个Msg 的类型参数,这个参数指明这个actor所能接收的消息类型。 - -调用Reactor的!方法来向接收者发送消息。用!发送消息是异步的,这样意味着不会等待消息被接收——它在发送消息后便立刻往下执行。例如:`a ! msg`表示向`a`发送`msg`。每个actor都有各自的信箱(mailbox)作为缓冲来存放接收到的消息,直至这些消息得到处理。 - -Reactor trait中也定义了一个forward方法,这个方法继承于OutputChannel。它和!(感叹号,发送方法)有同样的作用。Reactor的SubTrait(子特性)——特别是`ReplyReactor trait`——覆写了此方法,使得它能够隐式地回复目标。(详细地看下面的介绍) - -一个Reactor用react方法来接收消息。react方法需要一个PartialFunction[Msg, Unit]类型的参数,当消息到达actor的邮箱之后,react方法根据这个参数来确定如何处理消息。在下面例子中,当前的actor等待接收一个“Hello”字符串,然后打印一句问候。 - - react { - case "Hello" => println("Hi there") - } - -调用react没有返回值。因此,在接收到一条消息后,任何要执行的代码必须被包含在传递给react方法的偏函数(partial function)中。举个例子,通过嵌套两个react方法调用可以按顺序接收到两条消息: - - react { - case Get(from) => - react { - case Put(x) => from ! x - } - } - -Reactor trait 也提供了控制结构,简化了react方法的代码。 - -### 终止和执行状态 - -当Reactor的act方法完整执行后, Reactor则随即终止执行。Reactor也可以显式地使用exit方法来终止自身。exit方法的返回值类型为Nothing,因为它总是会抛出异常。这个异常仅在内部使用,并且不应该去捕捉这个异常。 - -一个已终止的Reactor可以通过它的restart方法使它重新启动。对一个未终止的Reactor调用restart方法则会抛出`IllegalStateException`异常。重新启动一个已终止的actor则会使它的act方法重新运行。 - -Reactor定义了一个getState方法,这个方法可以将actor当前的运行状态作为Actor.State枚举的一个成员返回。一个尚未运行的actor处于`Actor.State.New`状态。一个能够运行并且不在等待消息的actor处于`Actor.State.Runnable`状态。一个已挂起,并正在等待消息的actor处于`Actor.State.Suspended`状态。一个已终止的actor处于`Actor.State.Terminated`状态。 - -### 异常处理 - -exceptionHandler成员允许定义一个异常处理程序,其在Reactor的整个生命周期均可用。 - - def exceptionHandler: PartialFunction[Exception, Unit] - -exceptionHandler返回一个偏函数,它用来处理其他没有被处理的异常。每当一个异常被传递到Reactor的act方法体之外时,这个成员函数就被应用到该异常,以允许这个actor在它结束前执行清理代码。注意:`exceptionHandler`的可见性为protected。 - -用exceptionHandler来处理异常并使用控制结构对与react的编程是非常有效的。每当exceptionHandler返回的偏函数处理完一个异常后,程序会以当前的后续闭包(continuation closure)继续执行。 - - loop { - react { - case Msg(data) => - if (cond) // 数据处理代码 - else throw new Exception("cannot process data") - } - } - -假设Reactor覆写了exceptionHandler,在处理完一个在react方法体内抛出的异常后,程序将会执行下一个循环迭代。 - -### ReplyReactor trait - -`ReplyReactor trait`扩展了`Reactor[Any]`并且增加或覆写了以下方法: - -!方法被覆写以获得一个当前actor对象(发送方)的引用,并且,这个发送方引用和实际的消息一起被传递到接收actor的信箱(mail box)中。接收方通过其sender方法访问消息的发送方(见下文)。 - -forward方法被覆写以获得一个引用,这个引用指向正在被处理的消息的发送方。引用和实际的消息一起作为当前消息的发送方传递。结果,forward方法允许代表不同于当前actor对象的actor对象转发消息。 - -增加的sender方法返回正被处理的消息的发送方。考虑到一个消息可能已经被转发,发送方可能不会返回实际发送消息的actor对象。 - -增加的reply方法向最后一个消息的发送方回复消息。reply方法也被用作回复一个同步消息发送或者一个使用future的消息发送(见下文)。 - -增加的!?方法提供同步消息发送。调用!?方法会引起发送方actor对象等待,直到收到一个响应,然后返回这个响应。重载的变量有两个。这个双参数变量需要额外的超时参数(以毫秒计),并且,它的返回类型是Option[Any]而不是Any。如果发送方在指定的超时期间没有收到一个响应,!?方法返回None,否则它会返回由Some包裹的响应。 - -增加的!!方法与同步消息发送的相似点在于,它们都允许从接收方传递一个响应。然而,它们返回Future实例,而不是阻塞发送中的actor对象直到接收响应。一旦Future对象可用,它可以被用来重新获得接收方的响应,还可以在不阻塞发送方的情况下,用于获知响应是否可用。重载的变量有两个。双参数变量需要额外的PartialFunction[Any,A]类型的参数。这个偏函数用于对接收方响应进行后处理。本质上,!!方法返回一个future对象,一旦响应被接收,这个future对象把偏函数应用于响应。future对象的结果就是后处理的结果。 - -增加的reactWithin方法允许在一段给定的时间段内接收消息。相对于react方法,这个方法需要一个额外的msec参数,用来指示在这个时间段(以毫秒计)直到匹配指定的TIMEOUT模式为止(TIMEOUT是包scala.actors中的用例对象(case object))。例如: - -reactWithin(2000) { case Answer(text) => // process text case TIMEOUT => println("no answer within 2 seconds") } - -reactWithin方法也允许以非阻塞方式访问信箱。当指定一个0毫秒的时间段时,首先会扫描信箱以找到一个匹配消息。如果在第一次扫描后没有匹配的消息,这个TIMEOUT模式将会匹配。例如,这使得接收某些消息会比其他消息有较高的优先级: - -reactWithin(0) { case HighPriorityMsg => // ... case TIMEOUT => react { case LowPriorityMsg => // ... } } - -在上述例子中,即使在信箱里有一个先到达的低优先级的消息,actor对象也会首先处理下一个高优先级的消息。actor对象只有在信箱里没有高优先级消息时才会首先处理一个低优先级的消息。 - -另外,ReplyReactor 增加了`Actor.State.TimedSuspended`执行状态。一个使用`reactWithin`方法等待接收消息而挂起的actor对象,处在` Actor.State.TimedSuspended `状态。 - -### Actor trait - -Actor trait扩展了`ReplyReactor`并增加或覆写了以下成员: - -增加的receive方法的行为类似react方法,但它可以返回一个结果。这可以在它的类型上反映——它的结果是多态的:def receive[R](f: PartialFunction[Any, R]): R。然而,因为actor对象挂起并等待消息时,receive方法会阻塞底层线程(underlying thread),使用receive方法使actor对象变得更加重量级。直到receive方法的调用返回,阻塞的线程将不能够执行其他actor对象。 - -增加的link和unlink方法允许一个actor对象将自身链接到另一个actor对象,或将自身从另一个actor对象断开链接。链接可以用来监控或对另一个actor对象的终止做出反应。特别要注意的是,正如在Actor trait的API文档中的解释,链接影响调用exit方法的行为。 - -trapExit成员允许对链接的actor对象的终止做出反应而无关其退出的原因(即,无关是否正常退出)。如果一个actor对象的trapExit成员被设置为true,则这个actor对象会因链接的actor对象而永远不会终止。相反,每当其中一个链接的actor对象个终止了,它将会收到类型为Exit的消息。这个Exit case class 有两个成员:from指终止的actor对象;reason指退出原因。 - -### 终止和执行状态 - -当终止一个actor对象的执行时,可以通过调用以下exit方法的变体,显式地设置退出原因: - - def exit(reason: AnyRef): Nothing -当一个actor对象以符号'normal以外的原因退出,会向所有链接到它的atocr对象传递其退出原因。如果一个actor对象由于一个未捕获异常终止,它的退出原因则为一个UncaughtException case class的实例。 - -Actor trait增加了两个新的执行状态。使用receive方法并正在等待接收消息的actor处在`Actor.State.Blocked`状态。使用receiveWithin方法并正在等待接收消息的actor处在`Actor.State.TimedBlocked`状态。 - -## 控制结构 - -Reactor trait定义了控制结构,它简化了无返回的react操作的编程。一般来说,一个react方法调用并不返回。如果actor对象随后应当执行代码,那么,或者显式传递actor对象的后续代码给react方法,或者可以使用下述控制结构,达到隐藏这些延续代码的目的。 - -最基础的控制结构是andThen,它允许注册一个闭包。一旦actor对象的所有其他代码执行完毕,闭包就会被执行。 - - actor { - { - react { - case "hello" => // 处理 "hello" - }: Unit - } andThen { - println("hi there") - } - } - -例如,上述actor实例在它处理了“hello”消息之后,打印一句问候。虽然调用react方法不会返回,我们仍然可以使用andThen来注册这段输出问候的代码(作为actor的延续)。 - -注意:在react方法的调用(: Unit)中存在一种类型归属。总而言之,既然表达式的结果经常可以被忽略,react方法的结果就可以合法地作为Unit类型来处理。andThen方法无法成为Nothing类型(react方法的结果类型)的一个成员,所以在这里有必要这样做。把react方法的结果类型当作Unit,允许实现一个隐式转换的应用,这个隐式转换使得andThen成员可用。 - -API还提供一些额外的控制结构: - -loop { ... }。无限循环,在每一次迭代中,执行括号中的代码。调用循环体内的react方法,actor对象同样会对消息做出反应。而后,继续执行这个循环的下次迭代。 - -loopWhile (c) { ... }。当条件c返回true,执行括号中的代码。调用循环体中的react方法和使用loop时的效果一样。 - -continue。继续执行当前的接下来的后续闭包(continuation closure)。在loop或loopWhile循环体内调用continue方法将会使actor对象结束当前的迭代并继续执行下次迭代。如果使用andThen注册了当前的后续代码,这个闭包会作为第二个参数传给andThen,并以此继续执行。 - -控制结构可以在Reactor对象的act方法中,以及在act方法(传递地)调用的方法中任意处使用。对于用actor{...}这样的缩略形式创建的actor,控制结构可以从Actor对象导入。 - -### Future - -ReplyReactor和Actor trait支持发送带有结果的消息(!!方法),其立即返回一个future实例。一个future即Future trait的一个实例,即可以用来重新获取一个send-with-future消息的响应的句柄。 - -一个send-with-future消息的发送方可以通过应用future来等待future的响应。例如,使用val fut = a !! msg 语句发送消息,允许发送方等待future的结果。如:val res = fut()。 - -另外,一个Future可以在不阻塞的情况下,通过isSet方法来查询并获知其结果是否可用。 - -send-with-future的消息并不是获得future的唯一的方法。future也可以通过future方法计算而得。下述例子中,计算体会被并行地启动运行,并返回一个future实例作为其结果: - - val fut = Future { body } - // ... - fut() // 等待future - -能够通过基于actor的标准接收操作(例如receive方法等)来取回future的结果,使得future实例在actor上下文中变得特殊。此外,也能够通过使用基于事件的操作(react方法和ractWithin方法)。这使得一个actor实例在等待一个future实例结果时不用阻塞它的底层线程。 - -通过future的inputChannel,使得基于actor的接收操作方法可用。对于一个类型为`Future[T]`的future对象而言,它的类型是`InputChannel[T]`。例如: - - val fut = a !! msg - // ... - fut.inputChannel.react { - case Response => // ... - } - -## Channel(通道) - -channnel可以用来对发送到同一actor的不同类型消息的处理进行简化。channel的层级被分为OutputChannel和InputChannel。 - -OutputChannel可用于发送消息。OutputChannel的out方法支持以下操作。 - -out ! msg。异步地向out方法发送msg。当msg直接发送给一个actor,一个发送中的actor的引用会被传递。 - -out forward msg。异步地转发msg给out方法。当msg被直接转发给一个actor,发送中的actor会被确定。 - -out.receiver。返回唯一的actor,其接收发送到out channel(通道)的消息。 - -out.send(msg, from)。异步地发送msg到out,并提供from作为消息的发送方。 - -注意:OutputChannel trait有一个类型参数,其指定了可以被发送到channel(通道)的消息类型(使用!、forward和send)。这个类型参数是逆变的: - - trait OutputChannel[-Msg] - -Actor能够从InputChannel接收消息。就像OutputChannel,InputChannel trait也有一个类型参数,用于指定可以从channel(通道)接收的消息类型。这个类型参数是协变的: - - trait InputChannel[+Msg] - -An` InputChannel[Msg] `in支持下列操作。 - -in.receive { case Pat1 => ... ; case Patn => ... }(以及类似的 in.receiveWithin)。从in接收一个消息。在一个输入channel(通道)上调用receive方法和actor的标准receive操作具有相同的语义。唯一的区别是,作为参数被传递的偏函数具有PartialFunction[Msg, R]类型,此处R是receive方法的返回类型。 - -in.react { case Pat1 => ... ; case Patn => ... }(以及类似的in.reactWithin)通过基于事件的react操作,从in方法接收一个消息。就像actor的react方法,返回类型是Nothing。这意味着此方法的调用不会返回。就像之前的receive操作,作为参数传递的偏函数有一个更具体的类型:PartialFunction[Msg, Unit] - -### 创建和共享channel - -channel通过使用具体的Channel类创建。它同时扩展了InputChannel和OutputChannel。使channel在多个actor的作用域(Scope)中可见,或者将其在消息中发送,都可以实现channel的共享。 - -下面的例子阐述了基于作用域(scope)的共享。 - - actor { - var out: OutputChannel[String] = null - val child = actor { - react { - case "go" => out ! "hello" - } - } - val channel = new Channel[String] - out = channel - child ! "go" - channel.receive { - case msg => println(msg.length) - } - } - -运行这个例子将输出字符串“5”到控制台。注意:子actor对out(一个OutputChannel[String])具有唯一的访问权。而用于接收消息的channel的引用则被隐藏了。然而,必须要注意的是,在子actor向输出channel发送消息之前,确保输出channel被初始化到一个具体的channel。通过使用“go”消息来完成消息发送。当使用channel.receive来从channel接收消息时,因为消息是String类型的,可以使用它提供的length成员。 - -另一种共享channel的可行的方法是在消息中发送它们。下面的例子对此作了阐述。 - - case class ReplyTo(out: OutputChannel[String]) - - val child = actor { - react { - case ReplyTo(out) => out ! "hello" - } - } - - actor { - val channel = new Channel[String] - child ! ReplyTo(channel) - channel.receive { - case msg => println(msg.length) - } - } - -ReplyTo case class是一个消息类型,用于分派一个引用到OutputChannel[String]。当子actor接收一个ReplyTo消息时,它向它的输出channel发送一个字符串。第二个actor则像以前一样接收那个channel上的消息。 - -## Scheduler - -scheduler用于执行一个Reactor实例(或子类型的一个实例)。Reactor trait引入了scheduler成员,其返回常用于执行Reactor实例的scheduler。 - - def scheduler: IScheduler - -运行时系统通过使用在IScheduler trait中定义的execute方法之一,向scheduler提交任务来执行actor。只有在完整实现一个新的scheduler时(但没有必要),此trait的大多数其他方法才是相关的。 - -默认的scheduler常用于执行Reactor实例,而当所有的actor完成其执行时,Actor则会检测环境。当这发生时,scheduler把它自己关闭(终止scheduler使用的任何线程)。然而,一些scheduler,比如SingleThreadedScheduler(位于scheduler包)必须要通过调用它们的shutdown方法显式地关闭。 - -创建自定义scheduler的最简单方法是通过扩展SchedulerAdapter,实现下面的抽象成员: - - def execute(fun: => Unit): Unit - -典型情况下,一个具体的实现将会使用线程池来执行它的按名参数fun。 - -## 远程Actor - -这一段描述了远程actor的API。它的主要接口是scala.actors.remote包中的RemoteActor对象。这个对象提供各种方法来创建和连接到远程actor实例。在下面的代码段中我们假设所有的RemoteActor成员都已被导入,所使用的完整导入列表如下: - - import scala.actors._ - import scala.actors.Actor._ - import scala.actors.remote._ - import scala.actors.remote.RemoteActor._ - -### 启动远程Actor - -远程actor由一个Symbol唯一标记。在这个远程Actor所执行JVM上,这个符号对于JVM实例是唯一的。由名称'myActor标记的远程actor可按如下方法创建。 - - class MyActor extends Actor { - def act() { - alive(9000) - register('myActor, self) - // ... - } - } - -记住:一个名字一次只能标记到一个单独的(存活的)actor。例如,想要标记一个actorA为'myActor,然后标记另一个actorB为'myActor。这种情况下,必须等待A终止。这个要求适用于所有的端口,因此简单地将B标记到不同的端口来作为A是不能满足要求的。 - -### 连接到远程Actor - -连接到一个远程actor也同样简单。为了获得一个远程Actor的远程引用(运行于机器名为myMachine,端口为8000,名称为'anActor),可按下述方式使用select方法: - - val myRemoteActor = select(Node("myMachine", 8000), 'anActor) - -从select函数返回的actor具有类型AbstractActor,这个类型本质上提供了和通常actor相同的接口,因此支持通常的消息发送操作: - - myRemoteActor ! "Hello!" - receive { - case response => println("Response: " + response) - } - myRemoteActor !? "What is the meaning of life?" match { - case 42 => println("Success") - case oops => println("Failed: " + oops) - } - val future = myRemoteActor !! "What is the last digit of PI?" - -记住:select方法是惰性的,它不实际初始化任何网络连接。仅当必要时(例如,调用!时),它会单纯地创建一个新的,准备好初始化新网络连接的AbstractActor实例。 - diff --git a/zh-cn/overviews/core/architecture-of-scala-collections.md b/zh-cn/overviews/core/architecture-of-scala-collections.md deleted file mode 100644 index b7b254dec9..0000000000 --- a/zh-cn/overviews/core/architecture-of-scala-collections.md +++ /dev/null @@ -1,541 +0,0 @@ ---- -layout: overview -title: Scala容器类体系结构 -overview: architecture-of-scala-collections -language: zh-cn - -discourse: false ---- - -**Martin Odersky 和 Lex Spoon 著** - -本篇详细的介绍了Scala 容器类(collections)框架。通过与 [Scala 2.8 的 Collection API](http://docs.scala-lang.org/overviews/collections/introduction.html) 的对比,你会了解到更多框架的内部运作方式,同时你也将学习到如何通过几行代码复用这个容器类框架的功能来定义自己的容器类。 - -[Scala 2.8 容器API](http://docs.scala-lang.org/overviews/collections/introduction.html) 中包含了大量的 容器(collection)操作,这些操作在不同的许多容器类上表现为一致。假设,为每种 Collection 类型都用不同的方法代码实现,那么将导致代码的异常臃肿,很多代码将会仅仅是别处代码的拷贝。随着时间的推移,这些重复的代码也会带来不一致的问题,试想,相同的代码,在某个地方被修改了,而另外的地方却被遗漏了。而新的 容器类(collections)框架的设计原则目标就是尽量的避免重复,在尽可能少的地方定义操作(理想情况下,只在一处定义,当然也会有例外的情况存在)。设计中使用的方法是,在 Collection 模板中实现大部分的操作,这样就可以灵活的从独立的基类和实现中继承。后面的部分,我们会来详细阐述框架的各组成部分:模板(templates)、类(classes)以及trait(译注:类似于java里接口的概念),也会说明他们所支持的构建原则。 - -## Builders - -Builder类概要: - - package scala.collection.mutable - - class Builder[-Elem, +To] { - def +=(elem: Elem): this.type - def result(): To - def clear(): Unit - def mapResult[NewTo](f: To => NewTo): Builder[Elem, NewTo] = ... - } - -几乎所有的 Collection 操作都由遍历器(traversals)和构建器 (builders)来完成。Traversal 用可遍历类的foreach方法来实现,而构建新的 容器(collections)是由构建器类的实例来完成。上面的代码就是对这个类的精简描述。 - -我们用 b += x 来表示为构建器 b 加上元素 x。也可以一次加上多个元素,例如: b += (x, y) 及 b ++= x ,这类似于缓存(buffers)的工作方式(实际上,缓存就是构建器的增强版)。构建器的 result() 方法会返回一个collection。在获取了结果之后,构建器的状态就变成未定义,调用它的 clear() 方法可以把状态重置成空状态。构建器是通用元素类型,它适用于元素,类型,及它所返回的Collection。 - -通常,一个builder可以使用其他的builder来组合一个容器的元素,但是如果想要把其他builder返回的结果进行转换,例如,转成另一种类型,就需要使用Builder类的mapResult方法。假设,你有一个数组buffer,名叫 buf。一个ArrayBuffer的builder 的 result() 返回它自身。如果想用它去创建一个新的ArrayBuffer的builder,就可以使用 mapResult : - - scala> val buf = new ArrayBuffer[Int] - buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer() - - scala> val bldr = buf mapResult (_.toArray) - bldr: scala.collection.mutable.Builder[Int,Array[Int]] - = ArrayBuffer() - -结果值 bldr,是使用 buf 来收集元素的builder。当调用 bldr 的result时,其实是调用的 buf 的result,结果是返回的buf本身。接着这个数组buffer用 _.toArray 映射成了一个数组,结果 bldr 也就成了一个数组的 builder. - -## 分解(factoring out)通用操作 - -### TraversableLike类概述 - - package scala.collection - - class TraversableLike[+Elem, +Repr] { - def newBuilder: Builder[Elem, Repr] // deferred - def foreach[U](f: Elem => U) // deferred - ... - def filter(p: Elem => Boolean): Repr = { - val b = newBuilder - foreach { elem => if (p(elem)) b += elem } - b.result - } - } - -Collection库重构的主要设计目标是在拥有自然类型的同时又尽可能的共享代码实现。Scala的Collection 遵从“结果类型相同”的原则:只要可能,容器上的转换方法最后都会生成相同类型的Collection。例如,过滤操作对各种Collection类型都应该产生相同类型的实例。在List上应用过滤器应该获得List,在Map上应用过滤器,应该获得Map,如此等等。在下面的章节中,会告诉大家该原则的实现方法。 - -Scala的 Collection 库通过在 trait 实现中使用通用的构建器(builders)和遍历器(traversals)来避免代码重复、实现“结果类型相同”的原则。这些Trait的名字都有Like后缀。例如:IndexedSeqLike 是 IndexedSeq 的 trait 实现,再如,TraversableLike 是 Traversable 的 trait 实现。和普通的 Collection 只有一个类型参数不同,trait实现有两个类型参数。他们不仅参数化了容器的成员类型,也参数化了 Collection 所代表的类型,就像下面的 Seq[I] 或 List[T]。下面是TraversableLike开头的描述: - - trait TraversableLike[+Elem, +Repr] { ... } - -类型参数Elem代表Traversable的元素类型,参数Repr代表它自身。Repr上没有限制,甚至,Repr可以是非Traversable子类的实例。这就意味这,非容器子类的类,例如String和Array也可以使用所有容器实现trait中包含的操作。 - -以过滤器为例,这个操作只在TraversableLike定义了一次,就使得它适用于所有的容器类(collections)。通过查看前面 TraversableLike类的概述中相关的代码描述,我们知道,该trait声明了两个抽象方法,newBuilder 和 foreach,这些抽象方法在具体的collection类中实现。过滤器也是用这两个方法,通过相同的方式来实现的。首先,它用 newBuiler 方法构造一个新的builder,类型为 Repr 的类型。然后,使用 foreach 来遍历当前 collection 中的所有元素。一旦某个元素 x 满足谓词 p (即,p(x)为真),那么就把x加入到builder中。最后,用 builder 的 result 方法返回类型同 Repr 的 collection,里面的元素就是上面收集到的满足条件的所有元素。 - -容器(collections)上的映射操作就更复杂。例如:如果 f 是一个以一个String类型为参数并返回一个Int类型的函数,xs 是一个 List[String],那么在xs上使用该映射函数 f 应该会返回 List[Int]。同样,如果 ys 是一个 Array[String],那么通过 f 映射,应该返回 Array[Int]。 这里的难点在于,如何实现这样的效果而又不用分别针对 list 和 array 写重复的代码。TraversableLike 类的 newBuilder/foreach 也完成不了这个任务,因为他们需要生成一个完全相同类型的容器(collection)类型,而映射需要的是生成一个相同类型但内部元素类型却不同的容器。 - -很多情况下,甚至像map的构造函数的结果类型都可能是不那么简单的,因而需要依靠于其他参数类型,比如下面的例子: - - scala> import collection.immutable.BitSet - import collection.immutable.BitSet - - scala> val bits = BitSet(1, 2, 3) - bits: scala.collection.immutable.BitSet = BitSet(1, 2, 3) - - scala> bits map (_ * 2) - res13: scala.collection.immutable.BitSet = BitSet(2, 4, 6) - - scala> bits map (_.toFloat) - res14: scala.collection.immutable.Set[Float] - = Set(1.0, 2.0, 3.0) - -在一个BitSet上使用倍乘映射 _*2,会得到另一个BitSet。然而,如果在相同的BitSet上使用映射函数 (_.toFloat) 结果会得到一个 Set[Float]。这样也很合理,因为 BitSet 中只能放整型,而不能存放浮点型。 - -因此,要提醒大家注意,映射(map)的结果类型是由传进来的方法的类型决定的。如果映射函数中的参数会得到Int类型的值,那么映射的结果就是 BitSet。但如果是其他类型,那么映射的结果就是 Set 类型。后面会让大家了解 Scala 这种灵活的类型适应是如何实现的。 - -类似 BitSet 的问题不是唯一的,这里还有在map类型上应用map函数的交互式例子: - - scala> Map("a" -> 1, "b" -> 2) map { case (x, y) => (y, x) } - res3: scala.collection.immutable.Map[Int,java.lang.String] - = Map(1 -> a, 2 -> b) - - scala> Map("a" -> 1, "b" -> 2) map { case (x, y) => y } - res4: scala.collection.immutable.Iterable[Int] - = List(1, 2) - -第一个函数用于交换两个键值对。这个函数映射的结果是一个类似的Map,键和值颠倒了。事实上,地一个表达式产生了一个键值颠倒的map类型(在原map可颠倒的情况下)。然而,第二个函数,把键值对映射成一个整型,即成员变成了具体的值。在这种情况下,我们不可能把结果转换成Map类型,因此处理成,把结果转换成Map的一个可遍历的超类,这里是List。 - -你可能会问,哪为什么不强制让映射都返回相同类型的Collection呢?例如:BitSet上的映射只能接受整型到整型的函数,而Map上的映射只能接受键值对到键值对的函数。但这种约束从面向对象的观点来看是不能接受的,它会破坏里氏替换原则(Liskov substitution principle),即:Map是可遍历类,因此所有在可遍历类上的合法的操作都必然在Map中合法。 - -Scala通过重载来解决这个问题:Scala中的重载并非简单的复制Java的实现(Java的实现不够灵活),它使用隐式参数所提供的更加系统化的重载方式。 - -TraversableLike 中映射(map)的实现: - - def map[B, That](p: Elem => B) - (implicit bf: CanBuildFrom[B, That, This]): That = { - val b = bf(this) - for (x <- this) b += f(x) - b.result - } - -上面的代码展示了TraversableLike如何实现映射的trait。看起来非常类似于TraversableLike类的过滤器的实现。主要的区别在于,过滤器使用TraversableLike类的抽象方法 newBuilder,而映射使用的是Builder工场,它作为CanBuildFrom类型的一个额外的隐式参数传入。 - -CanBuildFrom trait: - - package scala.collection.generic - - trait CanBuildFrom[-From, -Elem, +To] { - // 创建一个新的构造器(builder) - def apply(from: From): Builder[Elem, To] - } - -上面的代码是 trait CanBuildFrom 的定义,它代表着构建者工场。它有三个参数:Elem是要创建的容器(collection)的元素的类型,To是要构建的容器(collection)的类型,From是该构建器工场适用的类型。通过定义适合的隐式定义的构建器工场,你就可以构建出符合你需要的类型转换行为。以 BitSet 类为例,它的伴生对象包含一个 CanBuildFrom[BitSet, Int, BitSet] 类型的构建器工场。这就意味着,当在一个 BitSet 上执行操作的时候,你可以创建另一个元素类型为整型的 BitSet。如果你需要的类型不同,那么,你还可以使用其他的隐式构建器工场,它们在Set的伴生对象中实现。下面就是一个更通用的构建器,A是通用类型参数: - - CanBuildFrom[Set[_], A, Set[A]] - -这就意味着,当操作一个任意Set(用现有的类型 Set[] 表述),我们可以再次创建一个 Set,并且无需关心它的元素类型A是什么。给你两个 CanBuildFrom 的隐式实例,你有可以利用 Scala 的隐式解析(implicit resolution)规则去挑选出其中最契合的一个。 - -所以说,隐式解析(implicit resolution)为类似映射的比较棘手的Collection操作提供了正确的静态类型。但是动态类型又怎么办呢?特别是,假设你有一个List,作为静态类型它有遍历方法,你在它上面使用一些映射(map)方法: - - scala> val xs: Iterable[Int] = List(1, 2, 3) - xs: Iterable[Int] = List(1, 2, 3) - - scala> val ys = xs map (x => x * x) - ys: Iterable[Int] = List(1, 4, 9) - -上述ys的静态类型是可遍历的(Iterable)类型。但是它的动态类型仍然必须是List类型的!此行为是间接被实现的。在CanBuildFrom的apply方法被作为参数传递源容器中。大多数的builder工厂仿制traversables(除建造工厂意外所有的叶子类型(leaf classes))将调用转发到集合的方法genericBuilder。反过来genericBuilder方法调用属于在定义它收集的建设者。所以Scala使用静态隐式解析,以解决map类型的限制问题,以及分派挑选对应于这些约束最佳的动态类型。 - -## 集成新容器 - -如果想要集成一个新的容器(Collection)类,以便受益于在正确类型上预定义的操作,需要做些什么呢?在下面几页中,将通过两个例子来进行演示。 - -### 集成序列(Sequence) - -RNA(核糖核酸)碱基(译者注:RNA链即很多不同RNA碱基的序列,RNA参考资料:http://zh.wikipedia.org/wiki/RNA): - - abstract class Base - case object A extends Base - case object T extends Base - case object G extends Base - case object U extends Base - - object Base { - val fromInt: Int => Base = Array(A, T, G, U) - val toInt: Base => Int = Map(A -> 0, T -> 1, G -> 2, U -> 3) - } - -假设需要为RNA链建立一个新的序列类型,这些RNA链是由碱基A(腺嘌呤)、T(胸腺嘧啶)、G(鸟嘌呤)、U(尿嘧啶)组成的序列。如上述列出的RNA碱基,很容易建立碱基的定义。 - -每个碱基都定义为一个具体对象(case object),该对象继承自一个共同的抽象类Base(碱基)。这个Base类具有一个伴生对象(companion object),该伴生对象定义了描述碱基和整数(0到3)之间映射的2个函数。可以从例子中看到,有两种不同的方式来使用容器(Collection)来实现这些函数。toInt函数通过一个从Base值到整数之间的映射(map)来实现。而它的逆函数fromInt则通过数组来实现。以上这些实现方法都基于一个事实,即“映射和数组都是函数”。因为他们都继承自Function1 trait。 - -下一步任务,便是为RNA链定义一个类。从概念上来看,一个RNA链就是一个简单的Seq[Base]。然而,RNA链可以很长,所以值的去花点时间来简化RNA链的表现形式。因为只有4种碱基,所以每个碱基可以通过2个比特位来区别。因此,在一个integer中,可以保存16个由2位比特标示的碱基。即构造一个Seq[Base]的特殊子类,并使用这种压缩的表示(packed representation)方式。 - -#### RNA链类的第一个版本 - - import collection.IndexedSeqLike - import collection.mutable.{Builder, ArrayBuffer} - import collection.generic.CanBuildFrom - - final class RNA1 private (val groups: Array[Int], - val length: Int) extends IndexedSeq[Base] { - - import RNA1._ - - def apply(idx: Int): Base = { - if (idx < 0 || length <= idx) - throw new IndexOutOfBoundsException - Base.fromInt(groups(idx / N) >> (idx % N * S) & M) - } - } - - object RNA1 { - - // 表示一组所需要的比特数 - private val S = 2 - - // 一个Int能够放入的组数 - private val N = 32 / S - - // 分离组的位掩码(bitmask) - private val M = (1 << S) - 1 - - def fromSeq(buf: Seq[Base]): RNA1 = { - val groups = new Array[Int]((buf.length + N - 1) / N) - for (i <- 0 until buf.length) - groups(i / N) |= Base.toInt(buf(i)) << (i % N * S) - new RNA1(groups, buf.length) - } - - def apply(bases: Base*) = fromSeq(bases) - } - -上面的RNA链类呈现出这个类的第一个版本,它将在以后被细化。类RNA1有一个构造函数,这个构造函数将int数组作为第一个参数。而这个数组包含打包压缩后的RNA数据,每个数组元素都有16个碱基,而最后一个元素则只有一部分有数据。第二个参数是长度,指定了数组中(和序列中)碱基的总数。RNA1类扩展了IndexedSeq[Base]。而IndexedSeq来自scala.collection.immutable,IndexedSeq定义了两个抽象方法:length和apply。这方法些需要在具体的子类中实现。类RNA1通过定义一个相同名字的参数字段来自动实现length。同时,通过类RNA1中给出的代码实现了索引方法apply。实质上,apply方法首先从数组中提取出一个整数值,然后再对这个整数中使用右移位(>>)和掩码(&)提取出正确的两位比特。私有常数S、N来自RNA1的伴生对象,S指定了每个包的尺寸(也就是2),N指定每个整数的两位比特包的数量,而M则是一个比特掩码,分离出一个字(word)的低S位。 - -注意,RNA1类的构造函数是一个私有函数。这意味着用户端无法通过调用new函数来创建RNA1序列的实例。这是有意义的,因为这能对用户隐藏RNA1序列包装数组的实现。如果用户端无法看到RNA序列的具体实现,以后任何时候,就可以做到改变RNA序列具体实现的同时,不影响到用户端代码。换句话说,这种设计实现了RNA序列的接口和实现之间解藕。然而,如果无法通过new来创建一个RNA序列,那就必须存在其他方法来创建它,否则整个类就变得毫无用处。事实上,有两种建立RNA序列的替代途径,两者都由RNA1的伴生对象(companion object)提供。第一个途径是fromSeq方法,这个方法将一个给定的碱基序列(也就是一个Seq[Base]类型的值)转换成RNA1类的实例。fromSeq方法将所有其序列参数内的碱基打包进一个数组。然后,将这个数组以及原序列的长度作为参数,调用RNA1的私有构造函数。这利用了一个事实:一个类的私有构造函数对于其伴生对象(companion object)是可见的。 - -创建RNA1实例的第二种途径由RNA1对象中的apply方法提供。它使用一个可变数量的Base类参数,并简单地将其作为序列指向fromSeq方法。这里是两个创建RNA实例的实际方案。 - - scala> val xs = List(A, G, T, A) - xs: List[Product with Base] = List(A, G, T, A) - - scala> RNA1.fromSeq(xs) - res1: RNA1 = RNA1(A, G, T, A) - - scala> val rna1 = RNA1(A, U, G, G, T) - rna1: RNA1 = RNA1(A, U, G, G, T) - -## 控制RNA类型中方法的返回值 - -这里有一些和RNA1抽象之间更多的交互操作 - - scala> rna1.length - res2: Int = 5 - - scala> rna1.last - res3: Base = T - - scala> rna1.take(3) - res4: IndexedSeq[Base] = Vector(A, U, G) - -前两个返回值正如预期,但最后一个——从rna1中获得前3个元素——的返回值则未必如预期。实际上,我们知道一个IndexedSeq[Base]作为返回值的静态类型而一个Vector作为返回值的动态类型,但我们更想看到一个RNA1的值。但这是无法做到的,因为之前在RNA1类中所做的一切仅仅是让RNA1扩展IndexedSeq。换句话说,IndexedSeq类具有一个take方法,其返回一个IndexedSeq。并且,这个方法是根据 IndexedSeq 的默认是用Vector来实现的。所以,这就是上一个交互中最后一行上所能看到的。 - -#### RNA链类的第二个版本 - - final class RNA2 private ( - val groups: Array[Int], - val length: Int - ) extends IndexedSeq[Base] with IndexedSeqLike[Base, RNA2] { - - import RNA2._ - - override def newBuilder: Builder[Base, RNA2] = - new ArrayBuffer[Base] mapResult fromSeq - - def apply(idx: Int): Base = // as before - } - -现在,明白了本质之后,下一个问题便是如何去改变它们。一种途径便是覆写(override)RNA1类中的take方法,可能如下所示: - - def take(count: Int): RNA1 = RNA1.fromSeq(super.take(count)) - -这对take函数有效,但drop、filter或者init又如何呢?事实上,序列(Sequence)中有超过50个方法同样返回序列。为了保持一致,所有这些方法都必须被覆写。这看起来越来越不像一个有吸引力的选择。幸运的是,有一种更简单的途径来达到同样的效果。RNA类不仅需要继承自IndexedSeq类,同时继承自它的实现trait(特性)IndexedSeqLike。如上面的RNA2所示。新的实现在两个方面与之前不同。第一个,RNA2类现在同样扩展自IndexedSeqLike[Base, RNA2]。这个IndexedSeqLike trait(特性)以可扩展的方式实现了所有IndexedSeq的具体方法。比如,如take、drop、filer或init的返回值类型即是传给IndexedSeqLike类的第二个类型参数,也就是说,在RNA2中的是RNA2本身。 - -为了能够做,IndexedSeqLike将自身建立在newBuilder抽象上,这个抽象能够创建正确类型的builder。IndexedSeqLike trait(特性)的子类必须覆写newBuilder以返回一个它们自身类型的容器。在RNA2类中,newBuilder方法返回一个Builder[Base, RNA2]类型的builder。 - -为了构造这个builder,首先创建一个ArrayBuffer,其自身就是一个Builder[Base, ArrayBuffer]。然后通过调用其mapResult方法来将这个ArrayBuffer转换为一个RNA2 builder。mapResult方法需要一个从ArrayBuffer到RNA2的转换函数来作为其参数。转换函数仅仅提供RNA2.fromSeq,其将一个任意的碱基序列转换为RNA2值(之前提到过,数组缓冲是一种序列,所以RNA2.fromSeq可对其使用)。 - -如果忘记声明newBuilder,将会得到一个如下的错误信息: - - RNA2.scala:5: error: overriding method newBuilder in trait - TraversableLike of type => scala.collection.mutable.Builder[Base,RNA2]; - method newBuilder in trait GenericTraversableTemplate of type - => scala.collection.mutable.Builder[Base,IndexedSeq[Base]] has - incompatible type - class RNA2 private (val groups: Array[Int], val length: Int) ^ - - one error found(发现一个错误) - -错误信息非常地长,并且很复杂,体现了容器(Collection)库错综复杂的组合。所以,最好忽略有关这些方法来源的信息,因为在这种情况下,它更多得是分散人的精力。而剩下的,则说明需要声明一个具有返回类型Builder[Base, RNA2]的newBuilder方法,但无法找到一个具有返回类型Builder[Base,IndexedSeq[Base]]的newBuilder方法。后者并不覆写前者。第一个方法——返回值类型为Builder[Base, RNA2]——是一个抽象方法,其在RNA2类中通过传递RNA2的类型参数给IndexedSeqLike,来以这种类型实例化。第二个方法的返回值类型为Builder[Base,IndexedSeq[Base]]——是由继承后的IndexedSeq类提供的。换句话说,如果没有声明一个以第一个返回值类型为返回值的newBuilder,RNA2类就是非法的。 - -改善了RNA2类中的实现之后,take、drop或filter方法现在便会按照预期执行: - - scala> val rna2 = RNA2(A, U, G, G, T) - rna2: RNA2 = RNA2(A, U, G, G, T) - - scala> rna2 take 3 - res5: RNA2 = RNA2(A, U, G) - - scala> rna2 filter (U !=) - res6: RNA2 = RNA2(A, G, G, T) - -### 使用map(映射)和friends(友元) - -然而,在容器中存在没有被处理的其他类别的方法。这些方法就不总会返回容器类型。它们可能返回同一类型的容器,但包含不同类型的元素。典型的例子就是map方法。如果s是一个Int的序列(Seq[Int]),f是将Int转换为String的方法,那么,s.map(f)将返回一个String的序列(Seq[String])。这样,元素类型在接收者和结果之间发生了改变,但容器的类型还是保持一致。 - -有一些其他的方法的行为与map类似,比如说flatMap、collect等,但另一些则不同。例如:++这个追加方法,它也可能因参数返回一个不同类型的结果——向Int类型的列表拼接一个String类型的列表将会得到一个Any类型的列表。至于这些方法如何适应RNA链,理想情况下应认为,在RNA链上进行碱基到碱基的映射将产生另外一个RNA链。(译者注:碱基为RNA链的“元素”) - - scala> val rna = RNA(A, U, G, G, T) - rna: RNA = RNA(A, U, G, G, T) - - scala> rna map { case A => T case b => b } - res7: RNA = RNA(T, U, G, G, T) - -同样,用 ++ 方法来拼接两个RNA链应该再次产生另外一个RNA链。 - - scala> rna ++ rna - res8: RNA = RNA(A, U, G, G, T, A, U, G, G, T) - -另一方面,在RNA链上进行碱基(类型)到其他类型的映射无法产生另外一个RNA链,因为新的元素有错误的类型。它只能产生一个序列而非RNA链。同样,向RNA链追加非Base类型的元素可以产生一个普通序列,但无法产生另一个RNA链。 - - scala> rna map Base.toInt - res2: IndexedSeq[Int] = Vector(0, 3, 2, 2, 1) - - scala> rna ++ List("missing", "data") - res3: IndexedSeq[java.lang.Object] = - Vector(A, U, G, G, T, missing, data) - -这就是在理想情况下应认为结果。但是,RNA2类并不提供这样的处理。事实上,如果你用RNA2类的实例来运行前两个例子,结果则是: - - scala> val rna2 = RNA2(A, U, G, G, T) - rna2: RNA2 = RNA2(A, U, G, G, T) - - scala> rna2 map { case A => T case b => b } - res0: IndexedSeq[Base] = Vector(T, U, G, G, T) - - scala> rna2 ++ rna2 - res1: IndexedSeq[Base] = Vector(A, U, G, G, T, A, U, G, G, T) - -所以,即使生成的容器元素类型是Base,map和++的结果也永远不会是RNA链。如需改善,则需要仔细查看map方法的签名(或++,它也有类似的方法签名)。map的方法最初在scala.collection.TraversableLike类中定义,具有如下签名: - - def map[B, That](f: A => B) - (隐含CBF:CanBuildFrom[修订版,B]): - -这里的A是一个容器元素的类型,而Repr是容器本身的类型,即传递给实现类(例如 TraversableLike和IndexedSeqLike)的第二个参数。map方法有两个以上的参数,B和That。参数B表示映射函数的结果类型,同时也是新容器中的元素类型。That作为map的结果类型。所以,That表示所创建的新容器的类型。 - -对于That类型如何确定,事实上,它是根据隐式参数cbf(CanBuildFrom[Repr,B,That]类型)被链接到其他类型。这些隐式CanBuildFrom由独立的容器类定义。大体上,CanBuildFrom[From,Elem,To]类型的值可以描述为:“有这么一种方法,由给定的From类型的容器,使用Elem类型,建立To的容器。” - -#### RNA链类的最终版本 - - final class RNA private (val groups: Array[Int], val length: Int) - extends IndexedSeq[Base] with IndexedSeqLike[Base, RNA] { - - import RNA._ - - // 在IndexedSeq中必须重新实现newBuilder - override protected[this] def newBuilder: Builder[Base, RNA] = - RNA.newBuilder - - // 在IndexedSeq中必须实现apply - def apply(idx: Int): Base = { - if (idx < 0 || length <= idx) - throw new IndexOutOfBoundsException - Base.fromInt(groups(idx / N) >> (idx % N * S) & M) - } - - // (可选)重新实现foreach, - // 来提高效率 - override def foreach[U](f: Base => U): Unit = { - var i = 0 - var b = 0 - while (i < length) { - b = if (i % N == 0) groups(i / N) else b >>> S - f(Base.fromInt(b & M)) - i += 1 - } - } - } - -#### RNA伴生对象的最终版本 - - object RNA { - - private val S = 2 // group中的比特(bit)数 - private val M = (1 << S) - 1 // 用于隔离group的比特掩码 - private val N = 32 / S // 一个Int中的group数 - - def fromSeq(buf: Seq[Base]): RNA = { - val groups = new Array[Int]((buf.length + N - 1) / N) - for (i <- 0 until buf.length) - groups(i / N) |= Base.toInt(buf(i)) << (i % N * S) - new RNA(groups, buf.length) - } - - def apply(bases: Base*) = fromSeq(bases) - - def newBuilder: Builder[Base, RNA] = - new ArrayBuffer mapResult fromSeq - - implicit def canBuildFrom: CanBuildFrom[RNA, Base, RNA] = - new CanBuildFrom[RNA, Base, RNA] { - def apply(): Builder[Base, RNA] = newBuilder - def apply(from: RNA): Builder[Base, RNA] = newBuilder - } - } - -现在在RNA2序列链上的 map 和 ++ 的行为变得更加清晰了。由于没有能够创建RNA2序列的CanBuildFrom实例,因此在从trait InexedSeq继承的伴生对象上得到的CanBuildFrom成为了第二选择。这隐式地创建了IndexedSeqs,也是在应用map到RNA2的时候发生的情况。 - -为了解决这个缺点,你需要在RNA类的同伴对象里定义CanBuildFrom的隐式实例。该实例的类型应该是CanBuildFrom [RNA, Base, RNA] 。即,这个实例规定,给定一个RNA链和新元素类型Base,可以建立另一个RNA链容器。上述有关RNA链的两个代码以及其伴生对象展示了细节。相较于类RNA2有两个重要的区别。首先, newBuilder的实现,从RNA类移到了它的伴生对象中。RNA类中新的newBuilder方法只是转发这个定义。其次,在RNA对象现在有个隐式的CanBuildFrom值。要创建这样的对象你需要在CanBuildFrom trait中定义两个apply方法。同时,为容器RNA创建一个新的builder,但参数列表不同。在apply()方法只是简单地以正确的类型创建builder。相比之下,apply(from)方法将原来的容器作为参数。在适应动态类型的builder的返回值与接收者的动态类型一致非常有用。在RNA的情况下,这不会起作用,因为RNA是final类,所以静态类型的RNA任何接收者同时具有RNA作为其动态类型。这就是为什么apply(from)也只是简单地调用newBuilder ,忽略其参数。 - -这样,RNA类(final)以原本的类型实现了所有容器方法。它的实现需要一些协议支持。从本质上讲,你需要知道newBuilder 工厂放在哪里以及canBuildFrom的隐式实现。在有利方面,能够以相对较少的代码,得到大量自动定义的方法。另外,如果不打算在容器中扩展take,drop,map或++这样操作,可以不写额外的代码,并在类RNA1所示的实现上结束工作。 - -到目前为止,讨论集中在以定义新序列所需的最少方法来获得特定类型。但在实践中,可能需要在序列上添加新的功能,或重写现有的方法,以获得更好的效果。其中一个例子就是重写RNA类的foreach方法。foreach是RNA本身的一个重要方法,因为它实现了遍历容器。此外,容器的许多其他方法的实现依赖foreach。因此,投入一些精力来做优化方法的实现有意义。IndexedSeq的foreach方法的标准实现仅仅使用aplly来选取容器的中的第i个元素(i从0到容器长度-1)。因此,对RNA链的每一个元素,标准实现选择一个数组元素,并从中解开一个碱基(base)。而RNA类上重写的foreach要聪明得多。对于每一个选定的数组元素,它立刻对其中所包含的所有碱基应用给定的方法。因此,数组选择和位拆包的工作大大减少。 - -### 整合 sets与 map - -在第二个实例中,将介绍如何将一个新的map类型整合到容器框架中的。其方式是通过使用关键字“Patricia trie”,实现以String作为类型的可变映射(mutable map)。术语“Patricia“实际上就是"Practical Algorithm to Retrieve Information Coded in Alphanumeric."(检索字母数字编码信息的实用算法) 的缩写。思想是以树的形式存储一个set或者map,在这种树中,后续字符作为子树可以用唯一确定的关键字查找。例如,一个 Patricia trie存储了三个字符串 "abc", "abd", "al", "all", "xy" 。如下: - -patricia 树的例子: - -![patricia.png](/resources/images/patricia.png) - -为了能够在trie中查找与字符串”abc“匹配的节点,只要沿着标记为”a“的子树,查找到标记为”b“的子树,最后到达标记为”c“的子树。如果 Patricia trie作为map使用,键所对应的值保存在一个可通过键定位的节点上。如果作为set,只需保存一个标记,说明set中存在这个节点。 - -使用Patricia tries的prefix map实现方式: - - import collection._ - - class PrefixMap[T] - extends mutable.Map[String, T] - with mutable.MapLike[String, T, PrefixMap[T]] { - - var suffixes: immutable.Map[Char, PrefixMap[T]] = Map.empty - var value: Option[T] = None - - def get(s: String): Option[T] = - if (s.isEmpty) value - else suffixes get (s(0)) flatMap (_.get(s substring 1)) - - def withPrefix(s: String): PrefixMap[T] = - if (s.isEmpty) this - else { - val leading = s(0) - suffixes get leading match { - case None => - suffixes = suffixes + (leading -> empty) - case _ => - } - suffixes(leading) withPrefix (s substring 1) - } - - override def update(s: String, elem: T) = - withPrefix(s).value = Some(elem) - - override def remove(s: String): Option[T] = - if (s.isEmpty) { val prev = value; value = None; prev } - else suffixes get (s(0)) flatMap (_.remove(s substring 1)) - - def iterator: Iterator[(String, T)] = - (for (v <- value.iterator) yield ("", v)) ++ - (for ((chr, m) <- suffixes.iterator; - (s, v) <- m.iterator) yield (chr +: s, v)) - - def += (kv: (String, T)): this.type = { update(kv._1, kv._2); this } - - def -= (s: String): this.type = { remove(s); this } - - override def empty = new PrefixMap[T] - } - -Patricia tries支持非常高效的查找和更新。另一个良好的特点是,支持通过前缀查找子容器。例如,在上述的patricia tree中,你可以从树根处按照“a”链接进行查找,获得所有以”a“为开头的键所组成的子容器。 - -依据这些思想,来看一下作为Patricia trie的映射实现方式。这种map称为PrefixMap。PrefixMap提供了withPrefix方法,这个方法根据给定的前缀查找子映射(submap),其包含了所有匹配该前缀的键。首先,使用键来定义一个prefix map,执行如下。 - - scala> val m = PrefixMap("abc" -> 0, "abd" -> 1, "al" -> 2, - "all" -> 3, "xy" -> 4) - m: PrefixMap[Int] = Map((abc,0), (abd,1), (al,2), (all,3), (xy,4)) - -然后,在m中调用withPrefix方法将产生另一个prefix map: - - scala> m withPrefix "a" - res14: PrefixMap[Int] = Map((bc,0), (bd,1), (l,2), (ll,3)) - -上面展示的代码表明了PrefixMap的定义方式。这个类以关联值T参数化,并扩展mutable.Map[String, T] 与 mutable.MapLike[String, T, PrefixMap[T]]。在RNA链的例子中对序列的处理也使用了这种模式,然后,为转换(如filter)继承实现类(如MapLike),用以获得正确的结果类型。 - -一个prefix map节点中含有两个可变字段:suffixes与value。value字段包含关联此节点的任意值,其初始化为None。suffixes字段包含从字符到PrefixMap值的映射(map),其初始化为空的map。 - -为什么要选择一种不可变map作为suffixes的实现方式?既然PrefixMap在整体上也是可变的,使用可变映射(map)是否更符合标准吗?这个问题的答案是,因为仅包含少量元素的不可变map,在空间和执行时间都非常高效。例如,包含有少于5个元素的map代表一个单独的对象。相比之下,就标准的可变map中的HashMap来说,即使为空时,也至少占用80bytes的空间。因此,如果普遍使用小容器,不可变的容器就优于可变的容器。在Patricia tries的例子中,预想除了树顶端节点之外,大部分节点仅包含少量的successor。所以在不可变映射(map)中存储这些successor效率很可能会更高。 - -现在看看映射(map)中第一个方法的实现:get。算法如下:为了获取prefix map里面和空字符串相关的值,简单地选取存储在树根节点上的任意值。另外,如果键字符串非空,尝试选取字符串的首字符匹配的子映射。如果产生一个map,继续去寻找map里面首个字符的之后的剩余键字符串。如果选取失败,键没有存储在map里面,则返回None。使用flatmap可以优雅地表示任意值上的联合选择。当对任意值ov,以及闭包f(其转而会返回任的值)进行应用时,如果ov和f都返回已定义的值,那么ov flatmap f将执行成功,否则ov flatmap f将返回None。 - -可变映射(map)之后的两个方法的实现是+=和-=。在prefixmap的实现中,它们按照其它两种方法定义:update和remove。 - -remove方法和get方法非常类似,除了在返回任何关联值之前,保存这个值的字段被设置为None。update方法首先会调用 withPrefix 方法找到需要被更新的树节点,然后将给定的值赋值给该节点的value字段。withPrefix 方法遍历整个树,如果在这个树中没有发现以这些前缀字符为节点的路径,它会根据需要创建子映射(sub-map)。 - -可变map最后一个需要实现的抽象方法是iterator。这个方法需要创建一个能够遍历map中所有键值对的迭代器iterator。对于任何给出的 prefix map,iterator 由如下几部分组成:首先,如果这个map中,在树根节点的value字段包含一个已定义的值Some(x),那么("", x)应为从iterator返回的第一个元素。此外,iterator需要串联存储在suffixes字段上的所有submap的iterator,并且还需要在这些返回的iterator每个键字符串的前面加上一个字符。进一步说,如果m是通过一个字符chr链接到根节点的submap ,并且(s, v)是一个从m.iterator返回的元素,那么这个根节点的iterator 将会转而返回(chr +: s, v)。在PrefixMap的iterator方法的实现中,这个逻辑可以非常简明地用两个for表达式实现。第一个for表达式在value.iterate上迭代。这表明Option值定义一个迭代器方法,如果Option值为None,则不返回任何元素。如果Option值为Some(x),则返回一个确切的元素x。 - -prefix map的伴生对象: - - import scala.collection.mutable.{Builder, MapBuilder} - import scala.collection.generic.CanBuildFrom - - object PrefixMap extends { - def empty[T] = new PrefixMap[T] - - def apply[T](kvs: (String, T)*): PrefixMap[T] = { - val m: PrefixMap[T] = empty - for (kv <- kvs) m += kv - m - } - - def newBuilder[T]: Builder[(String, T), PrefixMap[T]] = - new MapBuilder[String, T, PrefixMap[T]](empty) - - implicit def canBuildFrom[T] - : CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] = - new CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] { - def apply(from: PrefixMap[_]) = newBuilder[T] - def apply() = newBuilder[T] - } - } - -请注意,在PrefixMap中没有newBuilder方法的定义。这是没有必要的,因为maps和sets有默认的构造器,即MapBuilder类的实例。对可变映射来说,其默认的构造器初始时是一个空映射,然后使用映射的+= 方法连续增加元素。可变集合也类似。非可变映射和非可变集合的默认构造器则不同,它们使用无损的元素添加方法+,而非+=方法。 - -然而,为了构建合适的集合或映射,你都需要从一个空的集合或映射开始。empty方法提供了这样的功能,它是PrefixMap中最后定义的方法,该方法简单的返回一个新的PrefixMap。 - -现在我们来看看PrefixMap的伴生对象。事实上,并不是非要定义这种伴生对象,类PrefixMap自己就可以很好的完成它的功能。PrefixMap 对象的主要作用,是定义一些方便的工厂方法。它也定义了一个 CanBuildFrom,该方法可以让输入工作完成的更好。 - -其中有两个方法值得一提,它们是 empty 和 apply。同样的方法,在Scala的容器框架中的其他容器中都存在,因此在PrefixMap中定义它们也很合理。用这两种方法,你可以像写其他容器一样的编写PrefixMap: - - scala> PrefixMap("hello" -> 5, "hi" -> 2) - res0: PrefixMap[Int] = Map((hello,5), (hi,2)) - - scala> PrefixMap.empty[String] - res2: PrefixMap[String] = Map() - -另一个PrefixMap对象的成员是内置CanBuildFrom实例。它和上一节定义的CanBuildFrom目的相同:使得类似map等方法能返回最合适的类型。以PrefixMap的键值对映射函数为例,只要该函数生成串型和另一种类型组成的键值对,那么结果又会是一个PrefixMap。这里有一个例子: - - scala> res0 map { case (k, v) => (k + "!", "x" * v) } - res8: PrefixMap[String] = Map((hello!,xxxxx), (hi!,xx)) - -给出的函数参数是一个PrefixMap res0的键值对绑定,最终生成串型值对。map的结果是一个PrefixMap,只是String类型替代了int类型。如果在PrefixMap中没有内置的canBuildFrom ,那么结果将是一个普通的可变映射,而不是一个PrefixMap。 - -### 小结 - -总而言之,如果你想要将一个新的collection类完全的融入到框架中,需要注意以下几点: - -1. 决定容器应该是可变的,还是非可变的。 -2. 为容器选择正确的基类trait -3. 确保容器继承自适合的trait实现,这样它就能具有大多数的容器操作。 -4. 如果你想要map及类似的操作去返回你的容器类型的实例,那么就需要在类的伴生对象中提供一个隐式CanBuildFrom。 - -你现在已经了解Scala容器如何构建和如何构建新的容器类型。由于Scala丰富的抽象支持,新容器类型无需写代码就可以拥有大量的方法实现。 - -### 致谢 - -这些页面的素材改编自,由Odersky,Spoon和Venners编写的[Scala编程](http://www.artima.com/shop/programming_in_scala)第2版 。感谢Artima 对于出版的大力支持。 diff --git a/zh-cn/overviews/core/futures.md b/zh-cn/overviews/core/futures.md deleted file mode 100644 index fe46c6be9e..0000000000 --- a/zh-cn/overviews/core/futures.md +++ /dev/null @@ -1,504 +0,0 @@ ---- -layout: overview -label-color: success -label-text: New in 2.10 -overview: futures -language: zh-cn -title: Future和Promise - -discourse: false ---- - -**Philipp Haller, Aleksandar Prokopec, Heather Miller, Viktor Klang, Roland Kuhn, and Vojin Jovanovic 著** - -## 简介 - -Future提供了一套高效便捷的非阻塞并行操作管理方案。其基本思想很简单,所谓Future,指的是一类占位符对象,用于指代某些尚未完成的计算的结果。一般来说,由Future指代的计算都是并行执行的,计算完毕后可另行获取相关计算结果。以这种方式组织并行任务,便可以写出高效、异步、非阻塞的并行代码。 - -默认情况下,future和promise并不采用一般的阻塞操作,而是依赖回调进行非阻塞操作。为了在语法和概念层面更加简明扼要地使用这些回调,Scala还提供了flatMap、foreach和filter等算子,使得我们能够以非阻塞的方式对future进行组合。当然,future仍然支持阻塞操作——必要时,可以阻塞等待future(不过并不鼓励这样做)。 - -## Future - -所谓Future,是一种用于指代某个尚未就绪的值的对象。而这个值,往往是某个计算过程的结果: - -- 若该计算过程尚未完成,我们就说该Future未就位; -- 若该计算过程正常结束,或中途抛出异常,我们就说该Future已就位。 - -Future的就位分为两种情况: - -- 当Future带着某个值就位时,我们就说该Future携带计算结果成功就位。 -- 当Future因对应计算过程抛出异常而就绪,我们就说这个Future因该异常而失败。 - -Future的一个重要属性在于它只能被赋值一次。一旦给定了某个值或某个异常,future对象就变成了不可变对象——无法再被改写。 - -创建future对象最简单的方法是调用future方法,该future方法启用异步(asynchronous)计算并返回保存有计算结果的futrue,一旦该future对象计算完成,其结果就变的可用。 - -注意_Future[T]_ 是表示future对象的类型,而future是方法,该方法创建和调度一个异步计算,并返回随着计算结果而完成的future对象。 - -这最好通过一个例子予以说明。 - -假设我们使用某些流行的社交网络的假定API获取某个用户的朋友列表,我们将打开一个新对话(session),然后发送一个请求来获取某个特定用户的好友列表。 - - import scala.concurrent._ - import ExecutionContext.Implicits.global - - val session = socialNetwork.createSessionFor("user", credentials) - val f: Future[List[Friend]] = Future { - session.getFriends() - } - -以上,首先导入scala.concurrent 包使得Future类型和future构造函数可见。我们将马上解释第二个导入。 - -然后我们初始化一个session变量来用作向服务器发送请求,用一个假想的 createSessionFor 方法来返回一个List[Friend]。为了获得朋友列表,我们必须通过网络发送一个请求,这个请求可能耗时很长。这能从调用getFriends方法得到解释。为了更好的利用CPU,响应到达前不应该阻塞(block)程序的其他部分执行,于是在计算中使用异步。future方法就是这样做的,它并行地执行指定的计算块,在这个例子中是向服务器发送请求和等待响应。 - -一旦服务器响应,future f 中的好友列表将变得可用。 - -未成功的尝试可能会导致一个异常(exception)。在下面的例子中,session的值未被正确的初始化,于是在future的计算中将抛出NullPointerException,future f 不会圆满完成,而是以此异常失败。 - - val session = null - val f: Future[List[Friend]] = Future { - session.getFriends - } - -`import ExecutionContext.Implicits.global` 上面的线条导入默认的全局执行上下文(global execution context),执行上下文执行执行提交给他们的任务,也可把执行上下文看作线程池,这对于future方法来说是必不可少的,因为这可以处理异步计算如何及何时被执行。我们可以定义自己的执行上下文,并在future上使用它,但是现在只需要知道你能够通过上面的语句导入默认执行上下文就足够了。 - -我们的例子是基于一个假定的社交网络API,此API的计算包含发送网络请求和等待响应。提供一个涉及到你能试着立即使用的异步计算的例子是公平的。假设你有一个文本文件,你想找出一个特定的关键字第一次出现的位置。当磁盘正在检索此文件内容时,这种计算可能会陷入阻塞,因此并行的执行该操作和程序的其他部分是合理的(make sense)。 - - val firstOccurrence: Future[Int] = Future { - val source = scala.io.Source.fromFile("myText.txt") - source.toSeq.indexOfSlice("myKeyword") - } - -### Callbacks(回调函数) - -现在我们知道如何开始一个异步计算来创建一个新的future值,但是我们没有展示一旦此结果变得可用后如何来使用,以便我们能够用它来做一些有用的事。我们经常对计算结果感兴趣而不仅仅是它的副作用。 - -在许多future的实现中,一旦future的client对future的结果感兴趣,它不得不阻塞它自己的计算直到future完成——然后才能使用future的值继续它自己的计算。虽然这在Scala的Future API(在后面会展示)中是允许的,但是从性能的角度来看更好的办法是一种完全非阻塞的方法,即在future中注册一个回调,future完成后这个回调称为异步回调。如果当注册回调时future已经完成,则回调可能是异步执行的,或在相同的线程中循序执行。 - -注册回调最通常的形式是使用OnComplete方法,即创建一个`Try[T] => U`类型的回调函数。如果future成功完成,回调则会应用到Success[T]类型的值中,否则应用到` Failure[T] `类型的值中。 - - `Try[T]` 和`Option[T]`或 `Either[T, S]`相似,因为它是一个可能持有某种类型值的单子。然而,它是特意设计来保持一个值或某个可抛出(throwable)对象。`Option[T]` 既可以是一个值(如:`Some[T]`)也可以是完全无值(如:`None`),如果`Try[T]`获得一个值则它为`Success[T]` ,否则为`Failure[T]`的异常。 `Failure[T]` 获得更多的关于为什么这儿没值的信息,而不仅仅是None。同时也可以把`Try[T]`看作一种特殊版本的`Either[Throwable, T]`,专门用于左值为可抛出类型(Throwable)的情形。 - -回到我们的社交网络的例子,假设我们想要获取我们最近的帖子并显示在屏幕上,我们通过调用getRecentPosts方法获得一个返回值List[String]——一个近期帖子的列表文本: - - val f: Future[List[String]] = Future { - session.getRecentPosts - } - - f onComplete { - case Success(posts) => for (post <- posts) println(post) - case Success(posts) => for (post <- posts) println(post) - } - -onComplete方法一般在某种意义上它允许客户处理future计算出的成功或失败的结果。对于仅仅处理成功的结果,onSuccess 回调使用如下(该回调以一个偏函数(partial function)为参数): - - val f: Future[List[String]] = Future { - session.getRecentPosts - } - - f onSuccess { - case posts => for (post <- posts) println(post) - } - -对于处理失败结果,onFailure回调使用如下: - - val f: Future[List[String]] = Future { - session.getRecentPosts - } - - f onFailure { - case t => println("An error has occured: " + t.getMessage) - } - - f onSuccess { - case posts => for (post <- posts) println(post) - } - -如果future失败,即future抛出异常,则执行onFailure回调。 - -因为偏函数具有 isDefinedAt方法, onFailure方法只有在特定的Throwable类型对象中被定义才会触发。下面例子中的onFailure回调永远不会被触发: - - val f = Future { - 2 / 0 - } - - f onFailure { - case npe: NullPointerException => - println("I'd be amazed if this printed out.") - } - -回到前面查找某个关键字第一次出现的例子,我们想要在屏幕上打印出此关键字的位置: - - val firstOccurrence: Future[Int] = Future { - val source = scala.io.Source.fromFile("myText.txt") - source.toSeq.indexOfSlice("myKeyword") - } - - firstOccurrence onSuccess { - case idx => println("The keyword first appears at position: " + idx) - } - - firstOccurrence onFailure { - case t => println("Could not process file: " + t.getMessage) - } - - onComplete,、onSuccess 和 onFailure 方法都具有Unit的结果类型,这意味着不能链接使用这些方法的回调。注意这种设计是为了避免暗示而刻意为之的,因为链接回调也许暗示着按照一定的顺序执行注册回调(回调注册在同一个future中是无序的)。 - -也就是说,我们现在应讨论论何时调用callback。因为callback需要future的值是可用的,所有回调只能在future完成之后被调用。然而,不能保证callback在完成future的线程或创建callback的线程中被调用。反而, 回调(callback)会在future对象完成之后的一些线程和一段时间内执行。所以我们说回调(callback)最终会被执行。 - -此外,回调(callback)执行的顺序不是预先定义的,甚至在相同的应用程序中callback的执行顺序也不尽相同。事实上,callback也许不是一个接一个连续的调用,但是可能会在同一时间同时执行。这意味着在下面的例子中,变量totalA也许不能在计算上下文中被设置为正确的大写或者小写字母。 - - @volatile var totalA = 0 - - val text = Future { - "na" * 16 + "BATMAN!!!" - } - - text onSuccess { - case txt => totalA += txt.count(_ == 'a') - } - - text onSuccess { - case txt => totalA += txt.count(_ == 'A') - } - -以上,这两个回调(callbacks)可能是一个接一个地执行的,这样变量totalA得到的预期值为18。然而,它们也可能是并发执行的,于是totalA最终可能是16或2,因为+= 是一个不可分割的操作符(即它是由一个读和一个写的步骤组成,这样就可能使其与其他的读和写任意交错执行)。 - -考虑到完整性,回调的使用情景列在这儿: - -- 在future中注册onComplete回调的时候要确保最后future执行完成之后调用相应的终止回调。 - -- 注册onSuccess或者onFailure回调时也和注册onComplete一样,不同之处在于future执行成功或失败分别调用onSuccess或onSuccess的对应的闭包。 - -- 注册一个已经完成的future的回调最后将导致此回调一直处于执行状态(1所隐含的)。 - -- 在future中注册多个回调的情况下,这些回调的执行顺序是不确定的。事实上,这些回调也许是同时执行的,然而,特定的ExecutionContext执行可能导致明确的顺序。 - -- 在一些回调抛出异常的情况下,其他的回调的执行不受影响。 - -- 在一些情况下,回调函数永远不能结束(例如,这些回调处于无限循环中),其他回调可能完全不会执行。在这种情况下,对于那些潜在的阻塞回调要使用阻塞的构造(例子如下)。 - -- 一旦执行完,回调将从future对象中移除,这样更适合JVM的垃圾回收机制(GC)。 - -### 函数组合(Functional Composition)和For解构(For-Comprehensions) - -尽管前文所展示的回调机制已经足够把future的结果和后继计算结合起来的,但是有些时候回调机制并不易于使用,且容易造成冗余的代码。我们可以通过一个例子来说明。假设我们有一个用于进行货币交易服务的API,我们想要在有盈利的时候购进一些美元。让我们先来看看怎样用回调来解决这个问题: - - val rateQuote = Future { - connection.getCurrentValue(USD) - } - - rateQuote onSuccess { case quote => - val purchase = Future { - if (isProfitable(quote)) connection.buy(amount, quote) - else throw new Exception("not profitable") - } - - purchase onSuccess { - case _ => println("Purchased " + amount + " USD") - } - } - -首先,我们创建一个名为rateQuote的future对象并获得当前的汇率。在服务器返回了汇率且该future对象成功完成了之后,计算操作才会从onSuccess回调中执行,这时我们就可以开始判断买还是不买了。所以我们创建了另一个名为purchase的future对象,用来在可盈利的情况下做出购买决定,并在稍后发送一个请求。最后,一旦purchase运行结束,我们会在标准输出中打印一条通知消息。 - -这确实是可行的,但是有两点原因使这种做法并不方便。其一,我们不得不使用onSuccess,且不得不在其中嵌套purchase future对象。试想一下,如果在purchase执行完成之后我们可能会想要卖掉一些其他的货币。这时我们将不得不在onSuccess的回调中重复这个模式,从而可能使代码过度嵌套,过于冗长,并且难以理解。 - -其二,purchase只是定义在局部范围内--它只能被来自onSuccess内部的回调响应。这也就是说,这个应用的其他部分看不到purchase,而且不能为它注册其他的onSuccess回调,比如说卖掉些别的货币。 - -为解决上述的两个问题,futures提供了组合器(combinators)来使之具有更多易用的组合形式。映射(map)是最基本的组合器之一。试想给定一个future对象和一个通过映射来获得该future值的函数,映射方法将创建一个新Future对象,一旦原来的Future成功完成了计算操作,新的Future会通过该返回值来完成自己的计算。你能够像理解容器(collections)的map一样来理解future的map。 - -让我们用map的方法来重构一下前面的例子: - - val rateQuote = Future { - connection.getCurrentValue(USD) - } - - val purchase = rateQuote map { quote => - if (isProfitable(quote)) connection.buy(amount, quote) - else throw new Exception("not profitable") - } - - purchase onSuccess { - case _ => println("Purchased " + amount + " USD") - } - -通过对rateQuote的映射我们减少了一次onSuccess的回调,更重要的是避免了嵌套。这时如果我们决定出售一些货币就可以再次使用purchase方法上的映射了。 - -可是如果isProfitable方法返回了false将会发生些什么?会引发异常?这种情况下,purchase的确会因为异常而失败。不仅仅如此,想象一下,链接的中断和getCurrentValue方法抛出异常会使rateQuote的操作失败。在这些情况下映射将不会返回任何值,而purchase也会会自动的以和rateQuote相同的异常而执行失败。 - -总之,如果原Future的计算成功完成了,那么返回的Future将会使用原Future的映射值来完成计算。如果映射函数抛出了异常则Future也会带着该异常完成计算。如果原Future由于异常而计算失败,那么返回的Future也会包含相同的异常。这种异常的传导方式也同样适用于其他的组合器(combinators)。 - -使之能够在For-comprehensions原则下使用,是设计Future的目的之一。也正是因为这个原因,Future还拥有flatMap,filter和foreach等组合器。其中flatMap方法可以构造一个函数,它可以把值映射到一个姑且称为g的新future,然后返回一个随g的完成而完成的Future对象。 - -让我们假设我们想把一些美元兑换成瑞士法郎。我们必须为这两种货币报价,然后再在这两个报价的基础上确定交易。下面是一个在for-comprehensions中使用flatMap和withFilter的例子: - - val usdQuote = Future { connection.getCurrentValue(USD) } - val chfQuote = Future { connection.getCurrentValue(CHF) } - - val purchase = for { - usd <- usdQuote - chf <- chfQuote - if isProfitable(usd, chf) - } yield connection.buy(amount, chf) - - purchase onSuccess { - case _ => println("Purchased " + amount + " CHF") - } - -purchase只有当usdQuote和chfQuote都完成计算以后才能完成-- 它以其他两个Future的计算值为前提所以它自己的计算不能更早的开始。 - -上面的for-comprhension将被转换为: - - val purchase = usdQuote flatMap { - usd => - chfQuote - .withFilter(chf => isProfitable(usd, chf)) - .map(chf => connection.buy(amount, chf)) - } - -这的确是比for-comprehension稍微难以把握一些,但是我们这样分析有助于您更容易的理解flatMap的操作。FlatMap操作会把自身的值映射到其他future对象上,并随着该对象计算完成的返回值一起完成计算。在我们的例子里,flatMap用usdQuote的值把chfQuote的值映射到第三个futrue对象里,该对象用于发送一定量瑞士法郎的购入请求。只有当通过映射返回的第三个future对象完成了计算,purchase才能完成计算。 - -这可能有些难以置信,但幸运的是faltMap操作在for-comprhensions模式以外很少使用,因为for-comprehensions本身更容易理解和使用。 - -再说说filter,它可以用于创建一个新的future对象,该对象只有在满足某些特定条件的前提下才会得到原始future的计算值,否则就会抛出一个NoSuchElementException的异常而失败。调用了filter的future,其效果与直接调用withFilter完全一样。 - -作为组合器的collect同filter之间的关系有些类似容器(collections)API里的那些方法之间的关系。 - -值得注意的是,调用foreach组合器并不会在计算值可用的时候阻塞当前的进程去获取计算值。恰恰相反,只有当future对象成功计算完成了,foreach所迭代的函数才能够被异步的执行。这意味着foreach与onSuccess回调意义完全相同。 - -由于Future trait(译注: trait有点类似java中的接口(interface)的概念)从概念上看包含两种类型的返回值(计算结果和异常),所以组合器会有一个处理异常的需求。 - -比方说我们准备在rateQuote的基础上决定购入一定量的货币,那么`connection.buy`方法需要知道购入的数量和期望的报价值,最终完成购买的数量将会被返回。假如报价值偏偏在这个节骨眼儿改变了,那buy方法将会抛出一个`QuoteChangedExecption`,并且不会做任何交易。如果我们想让我们的Future对象返回0而不是抛出那个该死的异常,那我们需要使用recover组合器: - - val purchase: Future[Int] = rateQuote map { - quote => connection.buy(amount, quote) - } recover { - case QuoteChangedException() => 0 - } - -这里用到的recover能够创建一个新future对象,该对象当计算完成时持有和原future对象一样的值。如果执行不成功则偏函数的参数会被传递给使原Future失败的那个Throwable异常。如果它把Throwable映射到了某个值,那么新的Future就会成功完成并返回该值。如果偏函数没有定义在Throwable中,那么最终产生结果的future也会失败并返回同样的Throwable。 - -组合器recoverWith能够创建一个新future对象,当原future对象成功完成计算时,新future对象包含有和原future对象相同的计算结果。若原future失败或异常,偏函数将会返回造成原future失败的相同的Throwable异常。如果此时Throwable又被映射给了别的future,那么新Future就会完成并返回这个future的结果。recoverWith同recover的关系跟flatMap和map之间的关系很像。 - -fallbackTo组合器生成的future对象可以在该原future成功完成计算时返回结果,如果原future失败或异常返回future参数对象的成功值。在原future和参数future都失败的情况下,新future对象会完成并返回原future对象抛出的异常。正如下面的例子中,本想打印美元的汇率,但是在获取美元汇率失败的情况下会打印出瑞士法郎的汇率: - - val usdQuote = Future { - connection.getCurrentValue(USD) - } map { - usd => "Value: " + usd + "$" - } - val chfQuote = Future { - connection.getCurrentValue(CHF) - } map { - chf => "Value: " + chf + "CHF" - } - - al anyQuote = usdQuote fallbackTo chfQuote - - anyQuote onSuccess { println(_) } - -组合器andThen的用法是出于纯粹的side-effecting目的。经andThen返回的新Future无论原Future成功或失败都会返回与原Future一模一样的结果。一旦原Future完成并返回结果,andThen后跟的代码块就会被调用,且新Future将返回与原Future一样的结果,这确保了多个andThen调用的顺序执行。正如下例所示,这段代码可以从社交网站上把近期发出的帖子收集到一个可变集合里,然后把它们都打印在屏幕上: - - val allposts = mutable.Set[String]() - - Future { - session.getRecentPosts - } andThen { - case Success(posts) => allposts ++= posts - } andThen { - case _ => - clearAll() - for (post <- allposts) render(post) - } - -综上所述,Future的组合器功能是纯函数式的,每种组合器都会返回一个与原Future相关的新Future对象。 - -### 投影(Projections) - -为了确保for解构(for-comprehensions)能够返回异常,futures也提供了投影(projections)。如果原future对象失败了,失败的投影(projection)会返回一个带有Throwable类型返回值的future对象。如果原Future成功了,失败的投影(projection)会抛出一个NoSuchElementException异常。下面就是一个在屏幕上打印出异常的例子: - - val f = Future { - 2 / 0 - } - for (exc <- f.failed) println(exc) - -下面的例子不会在屏幕上打印出任何东西: - - val f = Future { - 4 / 2 - } - for (exc <- f.failed) println(exc) - -### Future的扩展 - -用更多的实用方法来对Futures API进行扩展支持已经被提上了日程,这将为很多外部框架提供更多专业工具。 - -## Blocking - -正如前面所说的,在future的blocking非常有效地缓解性能和预防死锁。虽然在futures中使用这些功能方面的首选方式是Callbacks和combinators,但在某些处理中也会需要用到blocking,并且它也是被Futures and Promises API所支持的。 - -在之前的并发交易(concurrency trading)例子中,在应用的最后有一处用到block来确定是否所有的futures已经完成。这有个如何使用block来处理一个future结果的例子: - - import scala.concurrent._ - import scala.concurrent.duration._ - - def main(args: Array[String]) { - val rateQuote = Future { - connection.getCurrentValue(USD) - } - - val purchase = rateQuote map { quote => - if (isProfitable(quote)) connection.buy(amount, quote) - else throw new Exception("not profitable") - } - - Await.result(purchase, 0 nanos) - } - -在这种情况下这个future是不成功的,这个调用者转发出了该future对象不成功的异常。它包含了失败的投影(projection)-- 阻塞(blocking)该结果将会造成一个NoSuchElementException异常在原future对象被成功计算的情况下被抛出。 - -相反的,调用`Await.ready`来等待这个future直到它已完成,但获不到它的结果。同样的方式,调用那个方法时如果这个future是失败的,它将不会抛出异常。 - -The Future trait实现了Awaitable trait还有其`ready()`和`result()`方法。这些方法不能被客户端直接调用,它们只能通过执行环境上下文来进行调用。 - -为了允许程序调用可能是阻塞式的第三方代码,而又不必实现Awaitable特质,原函数可以用如下的方式来调用: - - blocking { - potentiallyBlockingCall() - } - -这段blocking代码也可以抛出一个异常。在这种情况下,这个异常会转发给调用者。 - -## 异常(Exceptions) - -当异步计算抛出未处理的异常时,与那些计算相关的futures就失败了。失败的futures存储了一个Throwable的实例,而不是返回值。Futures提供onFailure回调方法,它用一个PartialFunction去表示一个Throwable。下列特殊异常的处理方式不同: - -`scala.runtime.NonLocalReturnControl[_]` --此异常保存了一个与返回相关联的值。通常情况下,在方法体中的返回结构被调用去抛出这个异常。相关联的值将会存储到future或一个promise中,而不是一直保存在这个异常中。 - -ExecutionException-当因为一个未处理的中断异常、错误或者`scala.util.control.ControlThrowable`导致计算失败时会被存储起来。这种情况下,ExecutionException会为此具有未处理的异常。这些异常会在执行失败的异步计算线程中重新抛出。这样做的目的,是为了防止正常情况下没有被客户端代码处理过的那些关键的、与控制流相关的异常继续传播下去,同时告知客户端其中的future对象是计算失败的。 - -更精确的语义描述请参见 [NonFatal]。 - -## Promises - -到目前为止,我们仅考虑了通过异步计算的方式创建future对象来使用future的方法。尽管如此,futures也可以使用promises来创建。 - -如果说futures是为了一个还没有存在的结果,而当成一种只读占位符的对象类型去创建,那么promise就被认为是一个可写的,可以实现一个future的单一赋值容器。这就是说,promise通过这种success方法可以成功去实现一个带有值的future。相反的,因为一个失败的promise通过failure方法就会实现一个带有异常的future。 - -一个promise p通过p.future方式返回future。 这个futrue对象被指定到promise p。根据这种实现方式,可能就会出现p.future与p相同的情况。 - -考虑下面的生产者 - 消费者的例子,其中一个计算产生一个值,并把它转移到另一个使用该值的计算。这个传递中的值通过一个promise来完成。 - - import scala.concurrent.{ Future, Promise } - import scala.concurrent.ExecutionContext.Implicits.global - - val p = Promise[T]() - val f = p.future - - val producer = Future { - val r = produceSomething() - p success r - continueDoingSomethingUnrelated() - } - - val consumer = Future { - startDoingSomething() - f onSuccess { - case r => doSomethingWithResult() - } - } - -在这里,我们创建了一个promise并利用它的future方法获得由它实现的Future。然后,我们开始了两种异步计算。第一种做了某些计算,结果值存放在r中,通过执行promise p,这个值被用来完成future对象f。第二种做了某些计算,然后读取实现了future f的计算结果值r。需要注意的是,在生产者完成执行`continueDoingSomethingUnrelated()` 方法这个任务之前,消费者可以获得这个结果值。 - -正如前面提到的,promises具有单赋值语义。因此,它们仅能被实现一次。在一个已经计算完成的promise或者failed的promise上调用success方法将会抛出一个IllegalStateException异常。 - -下面的这个例子显示了如何fail a promise。 - - val p = promise[T] - val f = p.future - - val producer = Future { - val r = someComputation - if (isInvalid(r)) - p failure (new IllegalStateException) - else { - val q = doSomeMoreComputation(r) - p success q - } - } - -如上,生产者计算出一个中间结果值r,并判断它的有效性。如果它不是有效的,它会通过返回一个异常实现promise p的方式fails the promise,关联的future f是failed。否则,生产者会继续它的计算,最终使用一个有效的结果值实现future f,同时实现 promise p。 - -Promises也能通过一个complete方法来实现,这个方法采用了一个`potential value Try[T]`,这个值要么是一个类型为`Failure[Throwable]`的失败的结果值,要么是一个类型为`Success[T]`的成功的结果值。 - -类似success方法,在一个已经完成(completed)的promise对象上调用failure方法和complete方法同样会抛出一个IllegalStateException异常。 - -应用前面所述的promises和futures方法的一个优点是,这些方法是单一操作的并且是没有副作用(side-effects)的,因此程序是具有确定性的(deterministic)。确定性意味着,如果该程序没有抛出异常(future的计算值被获得),无论并行的程序如何调度,那么程序的结果将会永远是一样的。 - -在一些情况下,客户端也许希望能够只在promise没有完成的情况下完成该promise的计算(例如,如果有多个HTTP请求被多个不同的futures对象来执行,并且客户端只关心地一个HTTP应答(response),该应答对应于地一个完成该promise的future)。因为这个原因,future提供了tryComplete,trySuccess和tryFailure方法。客户端需要意识到调用这些的结果是不确定的,调用的结果将以来从程序执行的调度。 - -completeWith方法将用另外一个future完成promise计算。当该future结束的时候,该promise对象得到那个future对象同样的值,如下的程序将打印1: - - val f = Future { 1 } - val p = promise[Int] - - p completeWith f - - p.future onSuccess { - case x => println(x) - } - -当让一个promise以异常失败的时候,三总子类型的Throwable异常被分别的处理。如果中断该promise的可抛出(Throwable)一场是`scala.runtime.NonLocalReturnControl`,那么该promise将以对应的值结束;如果是一个Error的实例,`InterruptedException`或者`scala.util.control.ControlThrowable`,那么该可抛出(Throwable)异常将会封装一个ExecutionException异常,该ExectionException将会让该promise以失败结束。 - -通过使用promises,futures的onComplete方法和future的构造方法,你能够实现前文描述的任何函数式组合组合器(compition combinators)。让我们来假设一下你想实现一个新的组合起,该组合器首先使用两个future对象f和,产生第三个future,该future能够用f或者g来完成,但是只在它能够成功完成的情况下。 - -这里有个关于如何去做的实例: - - def first[T](f: Future[T], g: Future[T]): Future[T] = { - val p = promise[T] - - f onSuccess { - case x => p.trySuccess(x) - } - - g onSuccess { - case x => p.trySuccess(x) - } - - p.future - } - -注意,在这种实现方式中,如果f与g都不是成功的,那么`first(f, g)`将不会实现(即返回一个值或者返回一个异常)。 - -## 工具(Utilities) - -为了简化在并发应用中处理时序(time)的问题,`scala.concurrent`引入了Duration抽象。Duration不是被作为另外一个通常的时间抽象存在的。他是为了用在并发(concurrency)库中使用的,Duration位于`scala.concurrent`包中。 - -Duration是表示时间长短的基础类,其可以是有限的或者无限的。有限的duration用FiniteDuration类来表示,并通过时间长度`(length)`和`java.util.concurrent.TimeUnit`来构造。无限的durations,同样扩展了Duration,只在两种情况下存在,`Duration.Inf`和`Duration.MinusInf`。库中同样提供了一些Durations的子类用来做隐式的转换,这些子类不应被直接使用。 - -抽象的Duration类包含了如下方法: - -到不同时间单位的转换`(toNanos, toMicros, toMillis, toSeconds, toMinutes, toHours, toDays and toUnit(unit: TimeUnit))`。 -durations的比较`(<,<=,>和>=)`。 -算术运算符`(+, -, *, / 和单值运算_-)` -duration的最大最小方法`(min,max)`。 -测试duration是否是无限的方法`(isFinite)`。 -Duration能够用如下方法实例化`(instantiated)`: - -隐式的通过Int和Long类型转换得来 `val d = 100 millis`。 -通过传递一个`Long length`和`java.util.concurrent.TimeUnit`。例如`val d = Duration(100, MILLISECONDS)`。 -通过传递一个字符串来表示时间区间,例如 `val d = Duration("1.2 µs")`。 -Duration也提供了unapply方法,因此可以i被用于模式匹配中,例如: - - import scala.concurrent.duration._ - import java.util.concurrent.TimeUnit._ - - // instantiation - val d1 = Duration(100, MILLISECONDS) // from Long and TimeUnit - val d2 = Duration(100, "millis") // from Long and String - val d3 = 100 millis // implicitly from Long, Int or Double - val d4 = Duration("1.2 µs") // from String - - // pattern matching - val Duration(length, unit) = 5 millis - diff --git a/zh-cn/overviews/core/implicit-classes.md b/zh-cn/overviews/core/implicit-classes.md deleted file mode 100644 index a68d2c2758..0000000000 --- a/zh-cn/overviews/core/implicit-classes.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -layout: overview -overview: implicit-classes -label-color: success -label-text: Available -language: zh-cn -title: Implicit Classes - -discourse: false ---- - -**Josh Suereth 著** - -## 介绍 - -Scala 2.10引入了一种叫做隐式类的新特性。隐式类指的是用implicit关键字修饰的类。在对应的作用域内,带有这个关键字的类的主构造函数可用于隐式转换。 - -隐式类型是在[SIP-13](http://docs.scala-lang.org/sips/pending/implicit-classes.html)中提出的。 - -## 用法 - -创建隐式类时,只需要在对应的类前加上implicit关键字。比如: - - object Helpers { - implicit class IntWithTimes(x: Int) { - def times[A](f: => A): Unit = { - def loop(current: Int): Unit = - if(current > 0) { - f - loop(current - 1) - } - loop(x) - } - } - } - -这个例子创建了一个名为IntWithTimes的隐式类。这个类包含一个int值和一个名为times的方法。要使用这个类,只需将其导入作用域内并调用times方法。比如: - - scala> import Helpers._ - import Helpers._ - - scala> 5 times println("HI") - HI - HI - HI - HI - HI - -使用隐式类时,类名必须在当前作用域内可见且无歧义,这一要求与隐式值等其他隐式类型转换方式类似。 - -## 限制条件 - -隐式类有以下限制条件: - -1. 只能在别的trait/类/对象内部定义。 - -```` - object Helpers { - implicit class RichInt(x: Int) // 正确! - } - implicit class RichDouble(x: Double) // 错误! -```` - -2. 构造函数只能携带一个非隐式参数。 -```` - implicit class RichDate(date: java.util.Date) // 正确! - implicit class Indexer[T](collecton: Seq[T], index: Int) // 错误! - implicit class Indexer[T](collecton: Seq[T])(implicit index: Index) // 正确! -```` - -虽然我们可以创建带有多个非隐式参数的隐式类,但这些类无法用于隐式转换。 - -3. 在同一作用域内,不能有任何方法、成员或对象与隐式类同名。 - -注意:这意味着隐式类不能是case class。 - - object Bar - implicit class Bar(x: Int) // 错误! - - val x = 5 - implicit class x(y: Int) // 错误! - - implicit case class Baz(x: Int) // 错误! diff --git a/zh-cn/overviews/core/string-interpolation.md b/zh-cn/overviews/core/string-interpolation.md deleted file mode 100644 index 971b557d01..0000000000 --- a/zh-cn/overviews/core/string-interpolation.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -layout: overview -discourse: true -language: zh-cn -label-color: success -label-text: New in 2.10 -overview: string-interpolation -title: 字符串插值 - -discourse: false ---- - -**Josh Suereth 著** - -## 简介 - -自2.10.0版本开始,Scala提供了一种新的机制来根据数据生成字符串:字符串插值。字符串插值允许使用者将变量引用直接插入处理过的字面字符中。如下例: - - val name="James" - println(s"Hello,$name")//Hello,James - -在上例中, s"Hello,$name" 是待处理字符串字面,编译器会对它做额外的工作。待处理字符串字面通过“号前的字符来标示(例如:上例中是s)。字符串插值的实现细节在 [SIP-11](http://docs.scala-lang.org/sips/pending/string-interpolation.html) 中有全面介绍。 - -## 用法 - -Scala 提供了三种创新的字符串插值方法:s,f 和 raw. - -### s 字符串插值器 - -在任何字符串前加上s,就可以直接在串中使用变量了。你已经见过这个例子: - - val name="James" - println(s"Hello,$name")//Hello,James -此例中,$name嵌套在一个将被s字符串插值器处理的字符串中。插值器知道在这个字符串的这个地方应该插入这个name变量的值,以使输出字符串为Hello,James。使用s插值器,在这个字符串中可以使用任何在处理范围内的名字。 - -字符串插值器也可以处理任意的表达式。例如: - - println(s"1+1=${1+1}") -将会输出字符串1+1=2。任何表达式都可以嵌入到${}中。 - -### f 插值器 - -在任何字符串字面前加上 f,就可以生成简单的格式化串,功能相似于其他语言中的 printf 函数。当使用 f 插值器的时候,所有的变量引用都应当后跟一个printf-style格式的字符串,如%d。看下面这个例子: - - val height=1.9d - val name="James" - println(f"$name%s is $height%2.2f meters tall")//James is 1.90 meters tall -f 插值器是类型安全的。如果试图向只支持 int 的格式化串传入一个double 值,编译器则会报错。例如: - - val height:Double=1.9d - - scala>f"$height%4d" - :9: error: type mismatch; - found : Double - required: Int - f"$height%4d" - ^ -f 插值器利用了java中的字符串数据格式。这种以%开头的格式在 [Formatter javadoc] 中有相关概述。如果在具体变量后没有%,则格式化程序默认使用 %s(串型)格式。 - -### raw 插值器 - -除了对字面值中的字符不做编码外,raw 插值器与 s 插值器在功能上是相同的。如下是个被处理过的字符串: - - scala>s"a\nb" - res0:String= - a - b -这里,s 插值器用回车代替了\n。而raw插值器却不会如此处理。 - - scala>raw"a\nb" - res1:String=a\nb -当不想输入\n被转换为回车的时候,raw 插值器是非常实用的。 - -除了以上三种字符串插值器外,使用者可以自定义插值器。 - -### 高级用法 - -在Scala中,所有处理过的字符串字面值都进行了简单编码转换。任何时候编译器遇到一个如下形式的字符串字面值: - - id"string content" -它都会被转换成一个StringContext实例的call(id)方法。这个方法在隐式范围内仍可用。只需要简单得 -建立一个隐类,给StringContext实例增加一个新方法,便可以定义我们自己的字符串插值器。如下例: - - //注意:为了避免运行时实例化,我们从AnyVal中继承。 - //更多信息请见值类的说明 - implicit class JsonHelper(val sc:StringContext) extends AnyVal{ - def json(args:Any*):JSONObject=sys.error("TODO-IMPLEMENT") - } - - def giveMeSomeJson(x:JSONObject):Unit=... - - giveMeSomeJson(json"{name:$name,id:$id}") -在这个例子中,我们试图通过字符串插值生成一个JSON文本语法。隐类 JsonHelper 作用域内使用该语法,且这个JSON方法需要一个完整的实现。只不过,字符串字面值格式化的结果不是一个字符串,而是一个JSON对象。 - -当编译器遇到"{name:$name,id:$id"}",它将会被重写成如下表达式: - - new StringContext("{name:",",id:","}").json(name,id) - -隐类则被重写成如下形式 - - new JsonHelper(new StringContext("{name:",",id:","}")).json(name,id) - -所以,JSON方法可以访问字符串的原生片段而每个表达式都是一个值。这个方法的一个简单但又令人迷惑的例子: - - implicit class JsonHelper(val sc:StringContext) extends AnyVal{ - def json(args:Any*):JSONObject={ - val strings=sc.parts.iterator - val expressions=args.iterator - var buf=new StringBuffer(strings.next) - while(strings.hasNext){ - buf append expressions.next - buf append strings.next - } - parseJson(buf) - } - } - -被处理过的字符串的每部分都是StringContext的成员。每个表达式的值都将传入到JSON方法的args参数。JSON方法接受这些值并合成一个大字符串,然后再解析成JSON格式。有一种更复杂的实现可以避免合成字符串的操作,它只是简单的直接通过原生字符串和表达式值构建JSON。 - -## 限制 - -字符串插值目前对模式匹配语句不适用。此特性将在2.11版本中生效。 diff --git a/zh-cn/overviews/core/value-classes.md b/zh-cn/overviews/core/value-classes.md deleted file mode 100644 index 5661e08c23..0000000000 --- a/zh-cn/overviews/core/value-classes.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -layout: overview -language: zh-cn -label-color: success -label-text: New in 2.10 -overview: value-classes -title: Value Classes and Universal Traits - -discourse: false ---- - -**Mark Harrah 著** - -## 引言 - -Value classes是在[SIP-15](http://docs.scala-lang.org/sips/pending/value-classes.html)中提出的一种通过继承AnyVal类来避免运行时对象分配的新机制。以下是一个最简的value class。 - - class Wrapper(val underlying: Int) extends AnyVal - -它仅有一个被用作运行时底层表示的公有val参数。在编译期,其类型为Wrapper,但在运行时,它被表示为一个Int。Value class可以带有def定义,但不能再定义额外的val、var,以及内嵌的trait、class或object: - - class Wrapper(val underlying: Int) extends AnyVal { - def foo: Wrapper = new Wrapper(underlying * 19) - } - -Value class只能继承universal traits,但其自身不能再被继承。所谓universal trait就是继承自Any的、只有def成员,且不作任何初始化工作的trait。继承自某个universal trait的value class同时继承了该trait的方法,但是(调用这些方法)会带来一定的对象分配开销。例如: - - trait Printable extends Any { - def print(): Unit = println(this) - } - class Wrapper(val underlying: Int) extends AnyVal with Printable - - val w = new Wrapper(3) - w.print() // 这里实际上会生成一个Wrapper类的实例 - -本文后续篇幅将介绍相关用例和与对象分配时机相关的细节,并给出一些有关value class自身限制的具体实例。 - -## 扩展方法 - -关于value类的一个用例,是将它们和隐含类联合([SIP-13](http://docs.scala-lang.org/sips/pending/implicit-classes.html))以获得免分配扩展方法。使用隐含类可以提供便捷的语法来定义扩展方法,同时 value 类移除运行时开销。一个好的例子是在标准库里的RichInt类。RichInt 继承自Int类型并附带一些方法。由于它是一个 value类,使用RichInt 方法时不需要创建一个RichInt 的实例。 - -下面有关RichInt的代码片段示范了RichInt是如何继承Int来允许3.toHexString的表达式: - - implicit class RichInt(val self: Int) extends AnyVal { - def toHexString: String = java.lang.Integer.toHexString(self) - } - -在运行时,表达式3.toHexString 被优化并等价于静态对象的方法调用 (RichInt$.MODULE$.extension$toHexString(3)),而不是创建一个新实例对象,再调用其方法。 - -## 正确性 - -关于value类的另一个用例是:不增加运行时开销的同时,获得数据类型的类型安全。例如,一个数据类型片断代表一个距离 ,如: - - class Meter(val value: Double) extends AnyVal { - def +(m: Meter): Meter = new Meter(value + m.value) - } - -代码:对两个距离进行相加,例如: - - val x = new Meter(3.4) - val y = new Meter(4.3) - val z = x + y - -实际上不会分配任何Meter实例,而是在运行时仅使用原始双精浮点数(double) 。 - -注意:在实践中,可以使用条件类(case)and/or 扩展方法来让语句更清晰。 - -## 必须进行分配的情况 - -由于JVM不支持value类,Scala 有时需要真正实例化value类。详细细节见[SIP-15]。 - -### 分配概要 - -value类在以下情况下,需要真正实例化: - -1. value类作为另一种类型使用时。 -2. value类被赋值给数组。 -3. 执行运行时类型测试,例如模式匹配。 - -### 分配细节 - -无论何时,将value类作为另一种类型进行处理时(包括universal trait),此value类实例必须被实例化。例如,value类Meter : - - trait Distance extends Any - case class Meter(val value: Double) extends AnyVal with Distance - -接收Distance类型值的方法需要一个正真的Meter实例。下面的例子中,Meter类真正被实例化。 - - def add(a: Distance, b: Distance): Distance = ... - add(Meter(3.4), Meter(4.3)) - -如果替换add方法的签名: - - def add(a: Meter, b: Meter): Meter = ... - -那么就不必进行分配了。此规则的另一个例子是value类作为类型参数使用。例如:即使是调用identity方法,也必须创建真正的Meter实例。 - - def identity[T](t: T): T = t - identity(Meter(5.0)) - -必须进行分配的另一种情况是:将它赋值给数组。即使这个数组就是value类数组,例如: - - val m = Meter(5.0) - val array = Array[Meter](m) - -数组中包含了真正的Meter 实例,并不只是底层基本类型double。 - -最后是类型测试。例如,模式匹配中的处理以及asInstanceOf方法都要求一个真正的value类实例: - - case class P(val i: Int) extends AnyVal - - val p = new P(3) - p match { // 在这里,新的P实例被创建 - case P(3) => println("Matched 3") - case P(x) => println("Not 3") - } - -## 限制 - -目前Value类有一些限制,部分原因是JVM不提供value类概念的原生支持。value类的完整实现细节及其限制见[SIP-15]。 - -### 限制概要 - -一个value类 ... - -1. ... 必须只有一个public的构造函数。并有且只有一个public的,类型不为value类的val参数。 -2. ... 不能有特殊的类型参数. -3. ... 不能有嵌套或本地类、trait或对象。 -4. ... 不能定义equals或hashCode方法。 -5. ... 必须是一个顶级类,或静态访问对象的一个成员 -6. ... 仅能有def为成员。尤其是,成员不能有惰性val、val或者var 。 -7. ... 不能被其它类继承。 - -### 限制示例 - -本章节列出了许多限制下具体影响, 而在“必要分配”章节已提及的部分则不再敖述。 - -构造函数不允许有多个参数: - - class Complex(val real: Double, val imag: Double) extends AnyVal - -则Scala编译器将生成以下的错误信息: - - Complex.scala:1: error: value class needs to have exactly one public val parameter - (Complex.scala:1: 错误:value类只能有一个public的val参数。) - (译者注:鉴于实际中编译器输出的可能是英文信息,在此提供双语。) - class Complex(val real: Double, val imag: Double) extends AnyVal - ^ - -由于构造函数参数必须是val,而不能是一个按名(by-name)参数: - - NoByName.scala:1: error: `val' parameters may not be call-by-name - (NoByName.scala:1: 错误: `val' 不能为 call-by-name) - class NoByName(val x: => Int) extends AnyVal - ^ - -Scala不允许惰性val作为构造函数参数, 所以value类也不允许。并且不允许多个构造函数。 - - class Secondary(val x: Int) extends AnyVal { - def this(y: Double) = this(y.toInt) - } - - Secondary.scala:2: error: value class may not have secondary constructors - (Secondary.scala:2: 错误:value类不能有第二个构造函数。) - def this(y: Double) = this(y.toInt) - ^ - -value class不能将惰性val或val作为成员,也不能有嵌套类、trait或对象。 - - class NoLazyMember(val evaluate: () => Double) extends AnyVal { - val member: Int = 3 - lazy val x: Double = evaluate() - object NestedObject - class NestedClass - } - - Invalid.scala:2: error: this statement is not allowed in value class: private[this] val member: Int = 3 - (Invalid.scala:2: 错误: value类中不允许此表达式:private [this] val member: Int = 3) - val member: Int = 3 - ^ - Invalid.scala:3: error: this statement is not allowed in value class: lazy private[this] var x: Double = NoLazyMember.this.evaluate.apply() - (Invalid.scala:3: 错误:value类中不允许此表达式: lazy private[this] var x: Double = NoLazyMember.this.evaluate.apply()) - lazy val x: Double = evaluate() - ^ - Invalid.scala:4: error: value class may not have nested module definitions - (Invalid.scala:4: 错误: value类中不能定义嵌套模块) - object NestedObject - ^ - Invalid.scala:5: error: value class may not have nested class definitions - (Invalid.scala:5: 错误:value类中不能定义嵌套类) - class NestedClass - ^ - -注意:value类中也不允许出现本地类、trait或对象,如下: - - class NoLocalTemplates(val x: Int) extends AnyVal { - def aMethod = { - class Local - ... - } - } - -在目前value类实现的限制下,value类不能嵌套: - - class Outer(val inner: Inner) extends AnyVal - class Inner(val value: Int) extends AnyVal - - Nested.scala:1: error: value class may not wrap another user-defined value class - (Nested.scala:1:错误:vlaue类不能包含另一个用户定义的value类) - class Outer(val inner: Inner) extends AnyVal - ^ - -此外,结构类型不能使用value类作为方法的参数或返回值类型。 - - class Value(val x: Int) extends AnyVal - object Usage { - def anyValue(v: { def value: Value }): Value = - v.value - } - - Struct.scala:3: error: Result type in structural refinement may not refer to a user-defined value class - (Struct.scala:3: 错误: 结构细化中的结果类型不适用于用户定义的value类) - def anyValue(v: { def value: Value }): Value = - ^ - -value类不能继承non-universal trait,并且其本身不能被继承: - - trait NotUniversal - class Value(val x: Int) extends AnyVal with notUniversal - class Extend(x: Int) extends Value(x) - - Extend.scala:2: error: illegal inheritance; superclass AnyVal - is not a subclass of the superclass Object - of the mixin trait NotUniversal - (Extend.scala:2: 错误:非法继承:父类AnyVal不是一个父类对象(混入trait NotUniversal)的子类) - class Value(val x: Int) extends AnyVal with NotUniversal - ^ - Extend.scala:3: error: illegal inheritance from final class Value - (Extend.scala:3: 错误: 从Value类(final类)非法继承) - class Extend(x: Int) extends Value(x) - ^ - -第二条错误信息显示:虽然value类没有显式地用final关键字修饰,但依然认为value类是final类。 - -另一个限制是:一个类仅支持单个参数的话,则value类必须是顶级类,或静态访问对象的成员。这是由于嵌套value类需要第二个参数来引用封闭类。所以不允许下述代码: - - class Outer { - class Inner(val x: Int) extends AnyVal - } - - Outer.scala:2: error: value class may not be a member of another class - (Outer.scala:2: 错误:value类不能作为其它类的成员) - class Inner(val x: Int) extends AnyVal - ^ - -但允许下述代码,因为封闭对象是顶级类: - - object Outer { - class Inner(val x: Int) extends AnyVal - } - From cfe9168e712ad117c65135676f431f984007f6f8 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 09:55:34 +0200 Subject: [PATCH 065/112] Scala tour formatting, language switch, and es & ba translations --- .../tour/abstract-types.md | 37 +- .../tour/annotations.md | 20 +- .../tour/anonymous-function-syntax.md | 8 +- .../tour/automatic-closures.md | 8 +- .../tour/case-classes.md | 8 +- .../tour/classes.md | 2 +- .../tour/compound-types.md | 8 +- .../tour/currying.md | 8 +- .../tour/default-parameter-values.md | 8 +- .../tour/explicitly-typed-self-references.md | 8 +- .../tour/extractor-objects.md | 8 +- .../tour/generic-classes.md | 8 +- .../tour/higher-order-functions.md | 8 +- .../tour/implicit-conversions.md | 8 +- .../tour/implicit-parameters.md | 8 +- .../tour/inner-classes.md | 8 +- .../tour/local-type-inference.md | 8 +- .../tour/lower-type-bounds.md | 8 +- .../tour/mixin-class-composition.md | 8 +- .../tour/named-parameters.md | 8 +- .../tour/nested-functions.md | 8 +- .../tour/operators.md | 8 +- .../tour/pattern-matching.md | 8 +- .../tour/polymorphic-methods.md | 8 +- .../tour/regular-expression-patterns.md | 8 +- .../tour/sequence-comprehensions.md | 8 +- .../tour/singleton-objects.md | 8 +- .../tour/tour-of-scala.md | 8 +- .../tour/traits.md | 8 +- .../tour/unified-types.md | 8 +- .../tour/upper-type-bounds.md | 8 +- .../tour/variances.md | 8 +- _config.yml | 15 + _data/docnames.yml | 5 + _data/translations.yml | 2 + .../tour/abstract-types.md | 26 +- .../tour/annotations.md | 6 +- .../tour/anonymous-function-syntax.md | 8 +- .../tour/automatic-closures.md | 6 +- .../tour/case-classes.md | 6 +- .../tour/classes.md | 6 +- .../tour/compound-types.md | 6 +- .../tour/currying.md | 12 +- .../tour/default-parameter-values.md | 6 +- .../tour/explicitly-typed-self-references.md | 7 +- .../tour/extractor-objects.md | 8 +- .../tour/generic-classes.md | 6 +- .../tour/higher-order-functions.md | 12 +- .../tour/implicit-conversions.md | 6 +- .../tour/implicit-parameters.md | 8 +- .../tour/inner-classes.md | 18 +- .../tour/local-type-inference.md | 10 +- .../tour/lower-type-bounds.md | 7 +- .../tour/mixin-class-composition.md | 22 +- .../tour/named-parameters.md | 6 +- .../tour/nested-functions.md | 6 +- .../tour/operators.md | 6 +- .../tour/pattern-matching.md | 6 +- .../tour/polymorphic-methods.md | 8 +- .../tour/regular-expression-patterns.md | 6 +- .../tour/sequence-comprehensions.md | 13 +- .../tour/singleton-objects.md | 6 +- .../tour/tour-of-scala.md | 6 +- .../tour/traits.md | 16 +- .../tour/unified-types.md | 6 +- .../tour/upper-type-bounds.md | 8 +- .../tour/variances.md | 6 +- _includes/sidebar-toc-tour-overview.html | 49 + _layouts/inner-page-parent-dropdown.html | 2 + _layouts/multipage-overview.html | 2 +- _layouts/tour.html | 11 +- _overviews/tutorials/01-post.png | Bin 0 -> 172693 bytes _overviews/tutorials/02-post.png | Bin 0 -> 114529 bytes _overviews/tutorials/03-fork.png | Bin 0 -> 46172 bytes _overviews/tutorials/04-submit.png | Bin 0 -> 166407 bytes _overviews/tutorials/05-review.png | Bin 0 -> 29682 bytes _overviews/tutorials/FAQ/breakout.md | 234 ++++ .../tutorials/FAQ/chaining-implicits.md | 109 ++ _overviews/tutorials/FAQ/collections.md | 377 +++++ _overviews/tutorials/FAQ/context-bounds.md | 105 ++ _overviews/tutorials/FAQ/finding-implicits.md | 328 +++++ _overviews/tutorials/FAQ/finding-symbols.md | 205 +++ .../tutorials/FAQ/initialization-order.md | 164 +++ .../tutorials/FAQ/stream-view-iterator.md | 46 + _overviews/tutorials/FAQ/yield.md | 158 +++ _overviews/tutorials/index.md | 47 + ...scala-for-csharp-programmers.disabled.html | 1237 +++++++++++++++++ .../tutorials/scala-for-java-programmers.md | 723 ++++++++++ _overviews/tutorials/scala-with-maven.md | 302 ++++ _tour/Makefile | 4 + _tour/abstract-types.md | 73 + _tour/annotations.md | 126 ++ _tour/basics.md | 311 +++++ _tour/by-name-parameters.md | 40 + _tour/case-classes.md | 57 + _tour/classes.md | 111 ++ _tour/compound-types.md | 52 + _tour/currying.md | 41 + _tour/default-parameter-values.md | 47 + _tour/dot-hot-reload.sh | 1 + _tour/extractor-objects.md | 58 + _tour/for-comprehensions.md | 51 + _tour/generic-classes.md | 57 + _tour/higher-order-functions.md | 42 + _tour/implicit-conversions.md | 59 + _tour/implicit-parameters.md | 58 + _tour/inner-classes.md | 81 ++ _tour/local-type-inference.md | 62 + _tour/lower-type-bounds.md | 67 + _tour/mixin-class-composition.md | 81 ++ _tour/named-arguments.md | 35 + _tour/nested-functions.md | 34 + _tour/operators.md | 79 ++ _tour/pattern-matching.md | 149 ++ _tour/polymorphic-methods.md | 33 + _tour/regular-expression-patterns.md | 61 + _tour/self-types.md | 37 + _tour/sequence-comprehensions.md | 69 + _tour/singleton-objects.md | 74 + _tour/tour-of-scala.md | 48 + _tour/traits.md | 82 ++ _tour/type-casting-diagram.dot | 14 + _tour/type-casting-diagram.svg | 91 ++ _tour/unified-types-diagram.dot | 16 + _tour/unified-types-diagram.svg | 277 ++++ _tour/unified-types.md | 80 ++ _tour/upper-type-bounds.md | 49 + _tour/variances.md | 152 ++ 128 files changed, 7260 insertions(+), 296 deletions(-) rename ba/tutorials/tour/_posts/2017-02-13-abstract-types.md => _ba/tour/abstract-types.md (93%) rename ba/tutorials/tour/_posts/2017-02-13-annotations.md => _ba/tour/annotations.md (94%) rename ba/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md => _ba/tour/anonymous-function-syntax.md (92%) rename ba/tutorials/tour/_posts/2017-02-13-automatic-closures.md => _ba/tour/automatic-closures.md (96%) rename ba/tutorials/tour/_posts/2017-02-13-case-classes.md => _ba/tour/case-classes.md (97%) rename ba/tutorials/tour/_posts/2017-02-13-classes.md => _ba/tour/classes.md (98%) rename ba/tutorials/tour/_posts/2017-02-13-compound-types.md => _ba/tour/compound-types.md (95%) rename ba/tutorials/tour/_posts/2017-02-13-currying.md => _ba/tour/currying.md (92%) rename ba/tutorials/tour/_posts/2017-02-13-default-parameter-values.md => _ba/tour/default-parameter-values.md (97%) rename ba/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md => _ba/tour/explicitly-typed-self-references.md (98%) rename ba/tutorials/tour/_posts/2017-02-13-extractor-objects.md => _ba/tour/extractor-objects.md (96%) rename ba/tutorials/tour/_posts/2017-02-13-generic-classes.md => _ba/tour/generic-classes.md (95%) rename ba/tutorials/tour/_posts/2017-02-13-higher-order-functions.md => _ba/tour/higher-order-functions.md (93%) rename ba/tutorials/tour/_posts/2017-02-13-implicit-conversions.md => _ba/tour/implicit-conversions.md (96%) rename ba/tutorials/tour/_posts/2017-02-13-implicit-parameters.md => _ba/tour/implicit-parameters.md (95%) rename ba/tutorials/tour/_posts/2017-02-13-inner-classes.md => _ba/tour/inner-classes.md (97%) rename ba/tutorials/tour/_posts/2017-02-13-local-type-inference.md => _ba/tour/local-type-inference.md (96%) rename ba/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md => _ba/tour/lower-type-bounds.md (96%) rename ba/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md => _ba/tour/mixin-class-composition.md (96%) rename ba/tutorials/tour/_posts/2017-02-13-named-parameters.md => _ba/tour/named-parameters.md (93%) rename ba/tutorials/tour/_posts/2017-02-13-nested-functions.md => _ba/tour/nested-functions.md (91%) rename ba/tutorials/tour/_posts/2017-02-13-operators.md => _ba/tour/operators.md (94%) rename ba/tutorials/tour/_posts/2017-02-13-pattern-matching.md => _ba/tour/pattern-matching.md (95%) rename ba/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md => _ba/tour/polymorphic-methods.md (92%) rename ba/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md => _ba/tour/regular-expression-patterns.md (95%) rename ba/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md => _ba/tour/sequence-comprehensions.md (96%) rename ba/tutorials/tour/_posts/2017-02-13-singleton-objects.md => _ba/tour/singleton-objects.md (97%) rename ba/tutorials/tour/_posts/2017-02-13-tour-of-scala.md => _ba/tour/tour-of-scala.md (98%) rename ba/tutorials/tour/_posts/2017-02-13-traits.md => _ba/tour/traits.md (95%) rename ba/tutorials/tour/_posts/2017-02-13-unified-types.md => _ba/tour/unified-types.md (96%) rename ba/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md => _ba/tour/upper-type-bounds.md (94%) rename ba/tutorials/tour/_posts/2017-02-13-variances.md => _ba/tour/variances.md (97%) create mode 100644 _data/docnames.yml create mode 100644 _data/translations.yml rename es/tutorials/tour/_posts/2017-02-13-abstract-types.md => _es/tour/abstract-types.md (97%) rename es/tutorials/tour/_posts/2017-02-13-annotations.md => _es/tour/annotations.md (99%) rename es/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md => _es/tour/anonymous-function-syntax.md (92%) rename es/tutorials/tour/_posts/2017-02-13-automatic-closures.md => _es/tour/automatic-closures.md (97%) rename es/tutorials/tour/_posts/2017-02-13-case-classes.md => _es/tour/case-classes.md (98%) rename es/tutorials/tour/_posts/2017-02-13-classes.md => _es/tour/classes.md (96%) rename es/tutorials/tour/_posts/2017-02-13-compound-types.md => _es/tour/compound-types.md (96%) rename es/tutorials/tour/_posts/2017-02-13-currying.md => _es/tour/currying.md (94%) rename es/tutorials/tour/_posts/2017-02-13-default-parameter-values.md => _es/tour/default-parameter-values.md (97%) rename es/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md => _es/tour/explicitly-typed-self-references.md (98%) rename es/tutorials/tour/_posts/2017-02-13-extractor-objects.md => _es/tour/extractor-objects.md (96%) rename es/tutorials/tour/_posts/2017-02-13-generic-classes.md => _es/tour/generic-classes.md (96%) rename es/tutorials/tour/_posts/2017-02-13-higher-order-functions.md => _es/tour/higher-order-functions.md (94%) rename es/tutorials/tour/_posts/2017-02-13-implicit-conversions.md => _es/tour/implicit-conversions.md (73%) rename es/tutorials/tour/_posts/2017-02-13-implicit-parameters.md => _es/tour/implicit-parameters.md (96%) rename es/tutorials/tour/_posts/2017-02-13-inner-classes.md => _es/tour/inner-classes.md (97%) rename es/tutorials/tour/_posts/2017-02-13-local-type-inference.md => _es/tour/local-type-inference.md (96%) rename es/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md => _es/tour/lower-type-bounds.md (97%) rename es/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md => _es/tour/mixin-class-composition.md (96%) rename es/tutorials/tour/_posts/2017-02-13-named-parameters.md => _es/tour/named-parameters.md (94%) rename es/tutorials/tour/_posts/2017-02-13-nested-functions.md => _es/tour/nested-functions.md (92%) rename es/tutorials/tour/_posts/2017-02-13-operators.md => _es/tour/operators.md (95%) rename es/tutorials/tour/_posts/2017-02-13-pattern-matching.md => _es/tour/pattern-matching.md (96%) rename es/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md => _es/tour/polymorphic-methods.md (95%) rename es/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md => _es/tour/regular-expression-patterns.md (96%) rename es/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md => _es/tour/sequence-comprehensions.md (96%) rename es/tutorials/tour/_posts/2017-02-13-singleton-objects.md => _es/tour/singleton-objects.md (98%) rename es/tutorials/tour/_posts/2017-02-13-tour-of-scala.md => _es/tour/tour-of-scala.md (98%) rename es/tutorials/tour/_posts/2017-02-13-traits.md => _es/tour/traits.md (95%) rename es/tutorials/tour/_posts/2017-02-13-unified-types.md => _es/tour/unified-types.md (97%) rename es/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md => _es/tour/upper-type-bounds.md (95%) rename es/tutorials/tour/_posts/2017-02-13-variances.md => _es/tour/variances.md (97%) create mode 100644 _includes/sidebar-toc-tour-overview.html create mode 100644 _overviews/tutorials/01-post.png create mode 100644 _overviews/tutorials/02-post.png create mode 100644 _overviews/tutorials/03-fork.png create mode 100644 _overviews/tutorials/04-submit.png create mode 100644 _overviews/tutorials/05-review.png create mode 100644 _overviews/tutorials/FAQ/breakout.md create mode 100644 _overviews/tutorials/FAQ/chaining-implicits.md create mode 100644 _overviews/tutorials/FAQ/collections.md create mode 100644 _overviews/tutorials/FAQ/context-bounds.md create mode 100644 _overviews/tutorials/FAQ/finding-implicits.md create mode 100644 _overviews/tutorials/FAQ/finding-symbols.md create mode 100644 _overviews/tutorials/FAQ/initialization-order.md create mode 100644 _overviews/tutorials/FAQ/stream-view-iterator.md create mode 100644 _overviews/tutorials/FAQ/yield.md create mode 100644 _overviews/tutorials/index.md create mode 100644 _overviews/tutorials/scala-for-csharp-programmers.disabled.html create mode 100644 _overviews/tutorials/scala-for-java-programmers.md create mode 100644 _overviews/tutorials/scala-with-maven.md create mode 100644 _tour/Makefile create mode 100644 _tour/abstract-types.md create mode 100644 _tour/annotations.md create mode 100644 _tour/basics.md create mode 100644 _tour/by-name-parameters.md create mode 100644 _tour/case-classes.md create mode 100644 _tour/classes.md create mode 100644 _tour/compound-types.md create mode 100644 _tour/currying.md create mode 100644 _tour/default-parameter-values.md create mode 100755 _tour/dot-hot-reload.sh create mode 100644 _tour/extractor-objects.md create mode 100644 _tour/for-comprehensions.md create mode 100644 _tour/generic-classes.md create mode 100644 _tour/higher-order-functions.md create mode 100644 _tour/implicit-conversions.md create mode 100644 _tour/implicit-parameters.md create mode 100644 _tour/inner-classes.md create mode 100644 _tour/local-type-inference.md create mode 100644 _tour/lower-type-bounds.md create mode 100644 _tour/mixin-class-composition.md create mode 100644 _tour/named-arguments.md create mode 100644 _tour/nested-functions.md create mode 100644 _tour/operators.md create mode 100644 _tour/pattern-matching.md create mode 100644 _tour/polymorphic-methods.md create mode 100644 _tour/regular-expression-patterns.md create mode 100644 _tour/self-types.md create mode 100644 _tour/sequence-comprehensions.md create mode 100644 _tour/singleton-objects.md create mode 100644 _tour/tour-of-scala.md create mode 100644 _tour/traits.md create mode 100644 _tour/type-casting-diagram.dot create mode 100644 _tour/type-casting-diagram.svg create mode 100644 _tour/unified-types-diagram.dot create mode 100644 _tour/unified-types-diagram.svg create mode 100644 _tour/unified-types.md create mode 100644 _tour/upper-type-bounds.md create mode 100644 _tour/variances.md diff --git a/ba/tutorials/tour/_posts/2017-02-13-abstract-types.md b/_ba/tour/abstract-types.md similarity index 93% rename from ba/tutorials/tour/_posts/2017-02-13-abstract-types.md rename to _ba/tour/abstract-types.md index 40e1f0cbe8..97d531910c 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-abstract-types.md +++ b/_ba/tour/abstract-types.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Apstraktni tipovi discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 22 -outof: 33 + language: ba next-page: compound-types @@ -15,37 +15,37 @@ previous-page: inner-classes --- U Scali, klase su parameterizovane vrijednostima (parameteri konstruktora) i tipovima (ako su [generičke](generic-classes.html)). -Zbog dosljednosti, ne samo da je moguće imati vrijednosti kao članove objekta već i tipove. +Zbog dosljednosti, ne samo da je moguće imati vrijednosti kao članove objekta već i tipove. Nadalje, obje forme članova mogu biti konkretne ili apstraktne. Slijedi primjer koji sadrži obje forme: apstraktnu vrijednost i apstraktni tip kao članove [klase](traits.html) `Buffer`. - + trait Buffer { type T val element: T } - + *Apstraktni tipovi* su tipovi čiji identitet nije precizno definisan. -U gornjem primjeru, poznato je samo da svaki objekat klase `Buffer` ima tip-član `T`, -ali definicija klase `Buffer` ne kazuje kojem konkretno tipu odgovara `T`. +U gornjem primjeru, poznato je samo da svaki objekat klase `Buffer` ima tip-član `T`, +ali definicija klase `Buffer` ne kazuje kojem konkretno tipu odgovara `T`. Kao i definicije vrijednosti, možemo redefinisati (override) definicije tipova u podklasama. Ovo nam omogućuje da otkrijemo više informacija o apstraktnom tipu sužavanjem granica tipa (koje opisuju moguće konkretne instance apstraktnog tipa). -U sljedećem programu izvodimo klasu `SeqBuffer` koja omogućuje čuvanje samo sekvenci u baferu kazivanjem da tip `T` +U sljedećem programu izvodimo klasu `SeqBuffer` koja omogućuje čuvanje samo sekvenci u baferu kazivanjem da tip `T` mora biti podtip `Seq[U]` za neki novi apstraktni tip `U`: - + abstract class SeqBuffer extends Buffer { type U type T <: Seq[U] def length = element.length } - -Trejtovi (trait) ili [klase](classes.html) s apstraktnim tip-članovima se često koriste u kombinaciji s instanciranjem anonimnih klasa. + +Trejtovi (trait) ili [klase](classes.html) s apstraktnim tip-članovima se često koriste u kombinaciji s instanciranjem anonimnih klasa. Radi ilustracije, pogledaćemo program koji radi s sekvencijalnim baferom koji sadrži listu integera: - + abstract class IntSeqBuffer extends SeqBuffer { type U = Int } - + object AbstractTypeTest1 extends App { def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer = new IntSeqBuffer { @@ -56,14 +56,14 @@ Radi ilustracije, pogledaćemo program koji radi s sekvencijalnim baferom koji s println("length = " + buf.length) println("content = " + buf.element) } - + Povratni tip metode `newIntSeqBuf` odnosi se na specijalizaciju trejta `Buffer` u kom je tip `U` sada jednak `Int`u. Imamo sličan alijas tip u anonimnoj instanci klase u tijelu metode `newIntSeqBuf`. Ovdje kreiramo novu instancu `IntSeqBuffer` u kojoj se tip `T` odnosi na `List[Int]`. Imajte na umu da je često moguće pretvoriti apstraktni tip-član u tipski parametar klase i obrnuto. Slijedi verzija gornjeg koda koji koristi tipske parametre: - + abstract class Buffer[+T] { val element: T } @@ -79,8 +79,7 @@ Slijedi verzija gornjeg koda koji koristi tipske parametre: println("length = " + buf.length) println("content = " + buf.element) } - + Primijetite da moramo koristiti [anotacije za varijansu](variances.html) ovdje; inače ne bismo mogli sakriti konkretni tip sekvencijalne implementacije objekta vraćenog iz metode `newIntSeqBuf`. Nadalje, postoje slučajevi u kojima nije moguće zamijeniti apstraktne tipove tip parametrima. - diff --git a/ba/tutorials/tour/_posts/2017-02-13-annotations.md b/_ba/tour/annotations.md similarity index 94% rename from ba/tutorials/tour/_posts/2017-02-13-annotations.md rename to _ba/tour/annotations.md index 41bcc5d6e4..27da913fd4 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-annotations.md +++ b/_ba/tour/annotations.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Anotacije discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 31 -outof: 33 + language: ba next-page: default-parameter-values @@ -16,11 +16,11 @@ previous-page: case-classes Anotacije pridružuju meta-informacije definicijama. -Jednostavna anotacija ima formu `@C` ili `@C(a1, .., an)`. Ovdje je `C` konstruktor klase `C`, koja mora naslijediti klasu `scala.Annotation`. -Svi argumenti konstruktora `a1, .., an` moraju biti konstante (npr. izrazi ili numeričke primitive, stringovi, primitive klasa, +Jednostavna anotacija ima formu `@C` ili `@C(a1, .., an)`. Ovdje je `C` konstruktor klase `C`, koja mora naslijediti klasu `scala.Annotation`. +Svi argumenti konstruktora `a1, .., an` moraju biti konstante (npr. izrazi ili numeričke primitive, stringovi, primitive klasa, Java enumeracije i jednodimenzionalni nizovi (`Array`) navedenih). -Anotacijska klauza (ili više njih) primjenjuje se na prvu definiciju ili deklaraciju koja slijedi nakon nje. +Anotacijska klauza (ili više njih) primjenjuje se na prvu definiciju ili deklaraciju koja slijedi nakon nje. Redoslijed anotacijskih klauza nije bitan. Značenje anotacijskih klauza je _implementacijski-nezavisno_. Na Java platformi, sljedeće Scala anotacije imaju standardno značenje. @@ -40,7 +40,7 @@ Značenje anotacijskih klauza je _implementacijski-nezavisno_. Na Java platformi U sljedećem primjeru dodajemo `throws` anotaciju definiciji metode `read` da bi uhvatili izuzetak (exception) u Java main programu. -> Java kompajler provjerava da li program sadrži rukovatelje (handler) za [provjereni izuzetak (checked exception)](http://docs.oracle.com/javase/specs/jls/se5.0/html/exceptions.html) +> Java kompajler provjerava da li program sadrži rukovatelje (handler) za [provjereni izuzetak (checked exception)](http://docs.oracle.com/javase/specs/jls/se5.0/html/exceptions.html) analizom koji se izuzeci mogu dobiti izvršenjem neke metode ili konstruktora. Za svaki mogući provjereni izuzetak **throws** klauza metodi ili konstruktora _mora_ navesti klasu izuzetka ili neku nadklasu izuzetka. > Pošto Scala nema provjerene izuzetke, Scala metode _moraju_ imati jednu ili više `throws` anotacija kako bi Java kod mogao uhvatiti iste. @@ -102,7 +102,7 @@ Primjena anotacije u Scali izgleda kao poziv konstruktora, dok se za instanciran mail = "support@coders.com") class MyScalaClass ... -Ova sintaksa je ponekad naporna, npr. ako anotacija ima samo jedan element (bez podrazumijevane vrijednosti), pa po konvenciji, +Ova sintaksa je ponekad naporna, npr. ako anotacija ima samo jedan element (bez podrazumijevane vrijednosti), pa po konvenciji, ako se koristi naziv `value` onda se u Javi može koristiti i konstruktor-sintaksa: @interface SourceURL { @@ -120,7 +120,7 @@ U ovom slučaju, Scala omogućuje istu sintaksu: @SourceURL("http://coders.com/") class MyScalaClass ... -Element `mail` je specificiran s podrazumijevanom vrijednošću tako da ne moramo eksplicitno navoditi vrijednost za njega. +Element `mail` je specificiran s podrazumijevanom vrijednošću tako da ne moramo eksplicitno navoditi vrijednost za njega. Međutim, ako trebamo, ne možemo miješati dva Javina stila: @SourceURL(value = "http://coders.com/", diff --git a/ba/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md b/_ba/tour/anonymous-function-syntax.md similarity index 92% rename from ba/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md rename to _ba/tour/anonymous-function-syntax.md index 7028c1e047..649fafe17f 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md +++ b/_ba/tour/anonymous-function-syntax.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Sintaksa anonimnih funkcija discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 6 -outof: 33 + language: ba next-page: higher-order-functions diff --git a/ba/tutorials/tour/_posts/2017-02-13-automatic-closures.md b/_ba/tour/automatic-closures.md similarity index 96% rename from ba/tutorials/tour/_posts/2017-02-13-automatic-closures.md rename to _ba/tour/automatic-closures.md index de63acf310..251b826950 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-automatic-closures.md +++ b/_ba/tour/automatic-closures.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Automatska konstrukcija tipno zavisnih closura (zatvarajućih funkcija) discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 30 -outof: 33 + language: ba next-page: case-classes diff --git a/ba/tutorials/tour/_posts/2017-02-13-case-classes.md b/_ba/tour/case-classes.md similarity index 97% rename from ba/tutorials/tour/_posts/2017-02-13-case-classes.md rename to _ba/tour/case-classes.md index d4cc962188..bac4d64e2b 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-case-classes.md +++ b/_ba/tour/case-classes.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Case klase discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 30 -outof: 33 + language: ba next-page: annotations diff --git a/ba/tutorials/tour/_posts/2017-02-13-classes.md b/_ba/tour/classes.md similarity index 98% rename from ba/tutorials/tour/_posts/2017-02-13-classes.md rename to _ba/tour/classes.md index 83ea0f87d5..ffe1a8931a 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-classes.md +++ b/_ba/tour/classes.md @@ -1,5 +1,5 @@ --- -layout: inner-page-no-masthead +layout: tour title: Klase discourse: false diff --git a/ba/tutorials/tour/_posts/2017-02-13-compound-types.md b/_ba/tour/compound-types.md similarity index 95% rename from ba/tutorials/tour/_posts/2017-02-13-compound-types.md rename to _ba/tour/compound-types.md index 03c1d8fa96..6a682eafa0 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-compound-types.md +++ b/_ba/tour/compound-types.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Složeni tipovi discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 23 -outof: 33 + language: ba next-page: explicitly-typed-self-references diff --git a/ba/tutorials/tour/_posts/2017-02-13-currying.md b/_ba/tour/currying.md similarity index 92% rename from ba/tutorials/tour/_posts/2017-02-13-currying.md rename to _ba/tour/currying.md index f8defffe49..43f0a35893 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-currying.md +++ b/_ba/tour/currying.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Curry-jevanje discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 9 -outof: 33 + language: ba next-page: pattern-matching diff --git a/ba/tutorials/tour/_posts/2017-02-13-default-parameter-values.md b/_ba/tour/default-parameter-values.md similarity index 97% rename from ba/tutorials/tour/_posts/2017-02-13-default-parameter-values.md rename to _ba/tour/default-parameter-values.md index 5a1ab03f35..6e1d3cf2bd 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-default-parameter-values.md +++ b/_ba/tour/default-parameter-values.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Podrazumijevane vrijednosti parametara discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 32 -outof: 33 + language: ba next-page: named-parameters diff --git a/ba/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md b/_ba/tour/explicitly-typed-self-references.md similarity index 98% rename from ba/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md rename to _ba/tour/explicitly-typed-self-references.md index bd04a40486..12cfffcff3 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md +++ b/_ba/tour/explicitly-typed-self-references.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Eksplicitno tipizirane samo-reference discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 24 -outof: 33 + language: ba next-page: implicit-parameters diff --git a/ba/tutorials/tour/_posts/2017-02-13-extractor-objects.md b/_ba/tour/extractor-objects.md similarity index 96% rename from ba/tutorials/tour/_posts/2017-02-13-extractor-objects.md rename to _ba/tour/extractor-objects.md index c482c42e8a..3b701d13ea 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-extractor-objects.md +++ b/_ba/tour/extractor-objects.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Ekstraktor objekti discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 15 -outof: 33 + language: ba next-page: sequence-comprehensions diff --git a/ba/tutorials/tour/_posts/2017-02-13-generic-classes.md b/_ba/tour/generic-classes.md similarity index 95% rename from ba/tutorials/tour/_posts/2017-02-13-generic-classes.md rename to _ba/tour/generic-classes.md index 0401137f13..9399d391bd 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-generic-classes.md +++ b/_ba/tour/generic-classes.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Generičke klase discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 17 -outof: 33 + language: ba next-page: variances diff --git a/ba/tutorials/tour/_posts/2017-02-13-higher-order-functions.md b/_ba/tour/higher-order-functions.md similarity index 93% rename from ba/tutorials/tour/_posts/2017-02-13-higher-order-functions.md rename to _ba/tour/higher-order-functions.md index 51315461e5..2eef4bc5a1 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-higher-order-functions.md +++ b/_ba/tour/higher-order-functions.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Funkcije višeg reda discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 7 -outof: 33 + language: ba next-page: nested-functions diff --git a/ba/tutorials/tour/_posts/2017-02-13-implicit-conversions.md b/_ba/tour/implicit-conversions.md similarity index 96% rename from ba/tutorials/tour/_posts/2017-02-13-implicit-conversions.md rename to _ba/tour/implicit-conversions.md index 5d833e3afb..39212f99a0 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-implicit-conversions.md +++ b/_ba/tour/implicit-conversions.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Implicitne konverzije discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 26 -outof: 33 + language: ba next-page: polymorphic-methods diff --git a/ba/tutorials/tour/_posts/2017-02-13-implicit-parameters.md b/_ba/tour/implicit-parameters.md similarity index 95% rename from ba/tutorials/tour/_posts/2017-02-13-implicit-parameters.md rename to _ba/tour/implicit-parameters.md index 5625f765a2..12439f74d1 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-implicit-parameters.md +++ b/_ba/tour/implicit-parameters.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Implicitni parametri discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 25 -outof: 33 + language: ba next-page: implicit-conversions diff --git a/ba/tutorials/tour/_posts/2017-02-13-inner-classes.md b/_ba/tour/inner-classes.md similarity index 97% rename from ba/tutorials/tour/_posts/2017-02-13-inner-classes.md rename to _ba/tour/inner-classes.md index 9a75d18bc8..a860deec8c 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-inner-classes.md +++ b/_ba/tour/inner-classes.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Unutarnje klase discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 21 -outof: 33 + language: ba next-page: abstract-types diff --git a/ba/tutorials/tour/_posts/2017-02-13-local-type-inference.md b/_ba/tour/local-type-inference.md similarity index 96% rename from ba/tutorials/tour/_posts/2017-02-13-local-type-inference.md rename to _ba/tour/local-type-inference.md index 6baf6c2dde..aa2c46665a 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-local-type-inference.md +++ b/_ba/tour/local-type-inference.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Lokalno zaključivanje tipova (type inference) discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 28 -outof: 33 + language: ba next-page: operators diff --git a/ba/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md b/_ba/tour/lower-type-bounds.md similarity index 96% rename from ba/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md rename to _ba/tour/lower-type-bounds.md index f158d20587..91714c6c0c 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md +++ b/_ba/tour/lower-type-bounds.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Donja granica tipa discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 20 -outof: 33 + language: ba next-page: inner-classes diff --git a/ba/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md b/_ba/tour/mixin-class-composition.md similarity index 96% rename from ba/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md rename to _ba/tour/mixin-class-composition.md index c0973eb7d2..379d464211 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md +++ b/_ba/tour/mixin-class-composition.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Kompozicija mixin klasa discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 5 -outof: 33 + language: ba next-page: anonymous-function-syntax diff --git a/ba/tutorials/tour/_posts/2017-02-13-named-parameters.md b/_ba/tour/named-parameters.md similarity index 93% rename from ba/tutorials/tour/_posts/2017-02-13-named-parameters.md rename to _ba/tour/named-parameters.md index 86afdcc36e..0690d22a48 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-named-parameters.md +++ b/_ba/tour/named-parameters.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Imenovani parametri discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 33 -outof: 33 + language: ba previous-page: default-parameter-values diff --git a/ba/tutorials/tour/_posts/2017-02-13-nested-functions.md b/_ba/tour/nested-functions.md similarity index 91% rename from ba/tutorials/tour/_posts/2017-02-13-nested-functions.md rename to _ba/tour/nested-functions.md index f50997e548..b9a7b81755 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-nested-functions.md +++ b/_ba/tour/nested-functions.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Ugniježdene funkcije discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 8 -outof: 33 + language: ba next-page: currying diff --git a/ba/tutorials/tour/_posts/2017-02-13-operators.md b/_ba/tour/operators.md similarity index 94% rename from ba/tutorials/tour/_posts/2017-02-13-operators.md rename to _ba/tour/operators.md index 632ddb2c01..82f4b04110 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-operators.md +++ b/_ba/tour/operators.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Operatori discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 29 -outof: 33 + language: ba next-page: automatic-closures diff --git a/ba/tutorials/tour/_posts/2017-02-13-pattern-matching.md b/_ba/tour/pattern-matching.md similarity index 95% rename from ba/tutorials/tour/_posts/2017-02-13-pattern-matching.md rename to _ba/tour/pattern-matching.md index d4d622770d..e9364697e4 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-pattern-matching.md +++ b/_ba/tour/pattern-matching.md @@ -1,14 +1,14 @@ --- -layout: inner-page-no-masthead +layout: tour title: Podudaranje uzoraka (pattern matching) discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 11 -outof: 33 + language: ba next-page: singleton-objects diff --git a/ba/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md b/_ba/tour/polymorphic-methods.md similarity index 92% rename from ba/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md rename to _ba/tour/polymorphic-methods.md index 25c948ac7c..81fbf42b3c 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md +++ b/_ba/tour/polymorphic-methods.md @@ -1,14 +1,14 @@ --- -layout: inner-page-no-masthead +layout: tour title: Polimorfne metode discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 27 -outof: 33 + language: ba next-page: local-type-inference diff --git a/ba/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md b/_ba/tour/regular-expression-patterns.md similarity index 95% rename from ba/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md rename to _ba/tour/regular-expression-patterns.md index e88fe604f5..6d2a68d3ef 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md +++ b/_ba/tour/regular-expression-patterns.md @@ -1,14 +1,14 @@ --- -layout: inner-page-no-masthead +layout: tour title: Regularni izrazi discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 14 -outof: 33 + language: ba next-page: extractor-objects diff --git a/ba/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md b/_ba/tour/sequence-comprehensions.md similarity index 96% rename from ba/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md rename to _ba/tour/sequence-comprehensions.md index 9b006e966c..169a0cbe8e 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md +++ b/_ba/tour/sequence-comprehensions.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Komprehensije sekvenci discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 16 -outof: 33 + language: ba next-page: generic-classes diff --git a/ba/tutorials/tour/_posts/2017-02-13-singleton-objects.md b/_ba/tour/singleton-objects.md similarity index 97% rename from ba/tutorials/tour/_posts/2017-02-13-singleton-objects.md rename to _ba/tour/singleton-objects.md index d80b0ed415..9874cd887b 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-singleton-objects.md +++ b/_ba/tour/singleton-objects.md @@ -1,14 +1,14 @@ --- -layout: inner-page-no-masthead +layout: tour title: Singlton objekti discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 12 -outof: 33 + language: ba next-page: regular-expression-patterns diff --git a/ba/tutorials/tour/_posts/2017-02-13-tour-of-scala.md b/_ba/tour/tour-of-scala.md similarity index 98% rename from ba/tutorials/tour/_posts/2017-02-13-tour-of-scala.md rename to _ba/tour/tour-of-scala.md index 9fa8c01ea7..2557e3956f 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-tour-of-scala.md +++ b/_ba/tour/tour-of-scala.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Uvod discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 1 -outof: 33 + language: ba next-page: unified-types diff --git a/ba/tutorials/tour/_posts/2017-02-13-traits.md b/_ba/tour/traits.md similarity index 95% rename from ba/tutorials/tour/_posts/2017-02-13-traits.md rename to _ba/tour/traits.md index 0815d5327b..fa286e2714 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-traits.md +++ b/_ba/tour/traits.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Trejtovi discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 4 -outof: 33 + language: ba next-page: mixin-class-composition diff --git a/ba/tutorials/tour/_posts/2017-02-13-unified-types.md b/_ba/tour/unified-types.md similarity index 96% rename from ba/tutorials/tour/_posts/2017-02-13-unified-types.md rename to _ba/tour/unified-types.md index 9d33ec3ed8..d5dc8f7a9a 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-unified-types.md +++ b/_ba/tour/unified-types.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Sjedinjeni tipovi discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 2 -outof: 33 + language: ba next-page: classes diff --git a/ba/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md b/_ba/tour/upper-type-bounds.md similarity index 94% rename from ba/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md rename to _ba/tour/upper-type-bounds.md index a56fe82e17..eb8fa5ef1e 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md +++ b/_ba/tour/upper-type-bounds.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Gornja granica tipa discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 19 -outof: 33 + language: ba next-page: lower-type-bounds diff --git a/ba/tutorials/tour/_posts/2017-02-13-variances.md b/_ba/tour/variances.md similarity index 97% rename from ba/tutorials/tour/_posts/2017-02-13-variances.md rename to _ba/tour/variances.md index 5b9a1c5577..f8bcb83f0d 100644 --- a/ba/tutorials/tour/_posts/2017-02-13-variances.md +++ b/_ba/tour/variances.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Varijanse discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 18 -outof: 33 + language: ba next-page: upper-type-bounds diff --git a/_config.yml b/_config.yml index bee78be1fd..826a28def4 100644 --- a/_config.yml +++ b/_config.yml @@ -22,6 +22,9 @@ collections: output: true overviews: output: true + tour: + output: true + permalink: /:collection/:path.html books: output: false ja: # Japanese translations @@ -36,6 +39,18 @@ collections: es: # Spanish translations output: true permalink: /:collection/:path.html + ba: # Bosnian translations + output: true + permalink: /:collection/:path.html + + +defaults: + - + scope: + path: "" + type: "tour" + values: + overview-name: "Tour of Scala" highlighter: rouge permalink: /:categories/:title.html:output_ext diff --git a/_data/docnames.yml b/_data/docnames.yml new file mode 100644 index 0000000000..3511026f14 --- /dev/null +++ b/_data/docnames.yml @@ -0,0 +1,5 @@ +scala-tour: + name: "Tour of Scala" + +futures: + name: "Futures" diff --git a/_data/translations.yml b/_data/translations.yml new file mode 100644 index 0000000000..afbfdbb6f4 --- /dev/null +++ b/_data/translations.yml @@ -0,0 +1,2 @@ +tour: + languages: [ba, es, ko, pt-br, pl] diff --git a/es/tutorials/tour/_posts/2017-02-13-abstract-types.md b/_es/tour/abstract-types.md similarity index 97% rename from es/tutorials/tour/_posts/2017-02-13-abstract-types.md rename to _es/tour/abstract-types.md index 30fd2a0373..ac54a11ca7 100644 --- a/es/tutorials/tour/_posts/2017-02-13-abstract-types.md +++ b/_es/tour/abstract-types.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Tipos Abstractos discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 2 -outof: 33 + language: es next-page: annotations @@ -16,28 +16,28 @@ previous-page: tour-of-scala En Scala, las cases son parametrizadas con valores (los parámetros de construcción) y con tipos (si las clases son [genéricas](generic-classes.html)). Por razones de consistencia, no es posible tener solo valores como miembros de objetos; tanto los tipos como los valores son miembros de objetos. Además, ambos tipos de miembros pueden ser concretos y abstractos. A continuación un ejemplo el cual define de forma conjunta una asignación de valor tardía y un tipo abstracto como miembros del [trait](traits.html) `Buffer`. - + trait Buffer { type T val element: T } - + Los *tipos abstractos* son tipos los cuales su identidad no es precisamente conocida. En el ejemplo anterior, lo único que sabemos es que cada objeto de la clase `Buffer` tiene un miembro de tipo `T`, pero la definición de la clase `Buffer` no revela qué tipo concreto se corresponde con el tipo `T`. Tal como las definiciones de valores, es posible sobrescribir las definiciones de tipos en subclases. Esto permite revelar más información acerca de un tipo abstracto al acotar el tipo ligado (el cual describe las posibles instancias concretas del tipo abstracto). En el siguiente programa derivamos la clase `SeqBuffer` la cual nos permite almacenar solamente sequencias en el buffer al estipular que el tipo `T` tiene que ser un subtipo de `Seq[U]` para un nuevo tipo abstracto `U`: - + abstract class SeqBuffer extends Buffer { type U type T <: Seq[U] def length = element.length } - + Traits o [clases](classes.html) con miembros de tipos abstractos son generalmente usados en combinación con instancias de clases anónimas. Para ilustrar este concepto veremos un programa el cual trata con un buffer de sequencia que se remite a una lista de enteros. - + abstract class IntSeqBuffer extends SeqBuffer { type U = Int } - + object AbstractTypeTest1 extends App { def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer = new IntSeqBuffer { @@ -48,11 +48,11 @@ Traits o [clases](classes.html) con miembros de tipos abstractos son generalment println("length = " + buf.length) println("content = " + buf.element) } - + El tipo retornado por el método `newIntSeqBuf` está ligado a la especialización del trait `Buffer` en el cual el tipo `U` es ahora equivalente a `Int`. Existe un tipo alias similar en la instancia de la clase anónima dentro del cuerpo del método `newIntSeqBuf`. En ese lugar se crea una nueva instancia de `IntSeqBuffer` en la cual el tipo `T` está ligado a `List[Int]`. Es necesario notar que generalmente es posible transformar un tipo abstracto en un tipo paramétrico de una clase y viceversa. A continuación se muestra una versión del código anterior el cual solo usa tipos paramétricos. - + abstract class Buffer[+T] { val element: T } @@ -68,5 +68,5 @@ Es necesario notar que generalmente es posible transformar un tipo abstracto en println("length = " + buf.length) println("content = " + buf.element) } - + Nótese que es necesario usar [variance annotations](variances.html) aquí; de otra manera no sería posible ocultar el tipo implementado por la secuencia concreta del objeto retornado por `newIntSeqBuf`. Además, existen casos en los cuales no es posible remplazar tipos abstractos con tipos parametrizados. diff --git a/es/tutorials/tour/_posts/2017-02-13-annotations.md b/_es/tour/annotations.md similarity index 99% rename from es/tutorials/tour/_posts/2017-02-13-annotations.md rename to _es/tour/annotations.md index bbd14dd490..2f9dea008d 100644 --- a/es/tutorials/tour/_posts/2017-02-13-annotations.md +++ b/_es/tour/annotations.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Anotaciones discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 3 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md b/_es/tour/anonymous-function-syntax.md similarity index 92% rename from es/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md rename to _es/tour/anonymous-function-syntax.md index a5d6626cd6..9801b51b6a 100644 --- a/es/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md +++ b/_es/tour/anonymous-function-syntax.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Sintaxis de funciones anónimas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 14 language: es @@ -27,7 +27,7 @@ También es posible definir funciones con múltiples parámetros: (x: Int, y: Int) => "(" + x + ", " + y + ")" -o sin parámetros: +o sin parámetros: () => { System.getProperty("user.dir") } diff --git a/es/tutorials/tour/_posts/2017-02-13-automatic-closures.md b/_es/tour/automatic-closures.md similarity index 97% rename from es/tutorials/tour/_posts/2017-02-13-automatic-closures.md rename to _es/tour/automatic-closures.md index 8d5850d967..a04500bca2 100644 --- a/es/tutorials/tour/_posts/2017-02-13-automatic-closures.md +++ b/_es/tour/automatic-closures.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Construcción de closures automáticas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 16 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-case-classes.md b/_es/tour/case-classes.md similarity index 98% rename from es/tutorials/tour/_posts/2017-02-13-case-classes.md rename to _es/tour/case-classes.md index f3c622bdc6..4e4a203b20 100644 --- a/es/tutorials/tour/_posts/2017-02-13-case-classes.md +++ b/_es/tour/case-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Clases Case discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 5 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-classes.md b/_es/tour/classes.md similarity index 96% rename from es/tutorials/tour/_posts/2017-02-13-classes.md rename to _es/tour/classes.md index 1d4c7690fa..1ed2c731dc 100644 --- a/es/tutorials/tour/_posts/2017-02-13-classes.md +++ b/_es/tour/classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Clases discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 4 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-compound-types.md b/_es/tour/compound-types.md similarity index 96% rename from es/tutorials/tour/_posts/2017-02-13-compound-types.md rename to _es/tour/compound-types.md index e160fffb0a..c9f43d3eee 100644 --- a/es/tutorials/tour/_posts/2017-02-13-compound-types.md +++ b/_es/tour/compound-types.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Tipos Compuestos discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 6 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-currying.md b/_es/tour/currying.md similarity index 94% rename from es/tutorials/tour/_posts/2017-02-13-currying.md rename to _es/tour/currying.md index 7604c506de..1e0fdac05e 100644 --- a/es/tutorials/tour/_posts/2017-02-13-currying.md +++ b/_es/tour/currying.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Currying discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 15 language: es @@ -20,14 +20,14 @@ Los métodos pueden definir múltiples listas de parámetros. Cuando un método Aquí se muestra un ejemplo: object CurryTest extends App { - + def filter(xs: List[Int], p: Int => Boolean): List[Int] = if (xs.isEmpty) xs else if (p(xs.head)) xs.head :: filter(xs.tail, p) else filter(xs.tail, p) - + def modN(n: Int)(x: Int) = ((x % n) == 0) - + val nums = List(1, 2, 3, 4, 5, 6, 7, 8) println(filter(nums, modN(2))) println(filter(nums, modN(3))) diff --git a/es/tutorials/tour/_posts/2017-02-13-default-parameter-values.md b/_es/tour/default-parameter-values.md similarity index 97% rename from es/tutorials/tour/_posts/2017-02-13-default-parameter-values.md rename to _es/tour/default-parameter-values.md index efe2201bbe..eb4b70911d 100644 --- a/es/tutorials/tour/_posts/2017-02-13-default-parameter-values.md +++ b/_es/tour/default-parameter-values.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Valores de parámetros por defecto discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 34 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md b/_es/tour/explicitly-typed-self-references.md similarity index 98% rename from es/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md rename to _es/tour/explicitly-typed-self-references.md index 6cfbe837a9..ff8545c75b 100644 --- a/es/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md +++ b/_es/tour/explicitly-typed-self-references.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Autorefrencias explicitamente tipadas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 27 language: es @@ -103,4 +103,3 @@ Aquí hay un ejemplo de uso de la clase `GrafoDirigidoConcreto`: n2.conectarCon(n3) n1.conectarCon(n3) } - diff --git a/es/tutorials/tour/_posts/2017-02-13-extractor-objects.md b/_es/tour/extractor-objects.md similarity index 96% rename from es/tutorials/tour/_posts/2017-02-13-extractor-objects.md rename to _es/tour/extractor-objects.md index df5fa2d365..9278a2e276 100644 --- a/es/tutorials/tour/_posts/2017-02-13-extractor-objects.md +++ b/_es/tour/extractor-objects.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Objetos Extractores discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 8 language: es @@ -19,7 +19,7 @@ En Scala pueden ser definidos patrones independientemente de las clases Caso (en def apply(x: Int): Int = x * 2 def unapply(z: Int): Option[Int] = if (z%2 == 0) Some(z/2) else None } - + object TwiceTest extends App { val x = Twice(21) x match { case Twice(n) => Console.println(n) } // imprime 21 diff --git a/es/tutorials/tour/_posts/2017-02-13-generic-classes.md b/_es/tour/generic-classes.md similarity index 96% rename from es/tutorials/tour/_posts/2017-02-13-generic-classes.md rename to _es/tour/generic-classes.md index fd7022f3ea..949baba87c 100644 --- a/es/tutorials/tour/_posts/2017-02-13-generic-classes.md +++ b/_es/tour/generic-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Clases genéricas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 9 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-higher-order-functions.md b/_es/tour/higher-order-functions.md similarity index 94% rename from es/tutorials/tour/_posts/2017-02-13-higher-order-functions.md rename to _es/tour/higher-order-functions.md index ee842e06f6..899dca2139 100644 --- a/es/tutorials/tour/_posts/2017-02-13-higher-order-functions.md +++ b/_es/tour/higher-order-functions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Funciones de orden superior discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 18 language: es @@ -20,17 +20,17 @@ Scala permite la definición de funciones de orden superior. Estas funciones son _Nota: los métodos son automáticamente tomados como funciones si el contexto lo requiere._ Otro ejemplo: - + class Decorator(left: String, right: String) { def layout[A](x: A) = left + x.toString() + right } - + object FunTest extends App { def apply(f: Int => String, v: Int) = f(v) val decorator = new Decorator("[", "]") println(apply(decorator.layout, 7)) } - + La ejecución da como valor el siguiente resultado: [7] diff --git a/es/tutorials/tour/_posts/2017-02-13-implicit-conversions.md b/_es/tour/implicit-conversions.md similarity index 73% rename from es/tutorials/tour/_posts/2017-02-13-implicit-conversions.md rename to _es/tour/implicit-conversions.md index 3a236c49c8..1979510108 100644 --- a/es/tutorials/tour/_posts/2017-02-13-implicit-conversions.md +++ b/_es/tour/implicit-conversions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Implicit Conversions discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 32 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-implicit-parameters.md b/_es/tour/implicit-parameters.md similarity index 96% rename from es/tutorials/tour/_posts/2017-02-13-implicit-parameters.md rename to _es/tour/implicit-parameters.md index 0d119e0250..581593a7c3 100644 --- a/es/tutorials/tour/_posts/2017-02-13-implicit-parameters.md +++ b/_es/tour/implicit-parameters.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Parámetros implícitos discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 10 language: es @@ -20,7 +20,7 @@ Los argumentos reales que son elegibles para ser pasados a un parámetro implíc * Segunda, además son elegibles todos los miembros de modulos `companion` (ver [objetos companion] (singleton-objects.html) ) del tipo de parámetro implicito que tienen la etiqueta `implicit`. En el siguiente ejemplo definimos un método `sum` el cual computa la suma de una lista de elementos usando las operaciones `add` y `unit` de `Monoid`. Note que los valores implícitos no pueden ser de nivel superior (top-level), deben ser miembros de una plantilla. - + abstract class SemiGroup[A] { def add(x: A, y: A): A } diff --git a/es/tutorials/tour/_posts/2017-02-13-inner-classes.md b/_es/tour/inner-classes.md similarity index 97% rename from es/tutorials/tour/_posts/2017-02-13-inner-classes.md rename to _es/tour/inner-classes.md index 48ad7de9f6..c072021dc8 100644 --- a/es/tutorials/tour/_posts/2017-02-13-inner-classes.md +++ b/_es/tour/inner-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Clases Internas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 11 language: es @@ -14,7 +14,7 @@ previous-page: implicit-parameters --- En Scala es posible que las clases tengan como miembro otras clases. A diferencia de lenguajes similares a Java donde ese tipo de clases internas son miembros de las clases que las envuelven, en Scala esas clases internas están ligadas al objeto externo. Para ilustrar esta diferencia, vamos a mostrar rápidamente una implementación del tipo grafo: - + class Graph { class Node { var connectedNodes: List[Node] = Nil @@ -42,9 +42,9 @@ En nuestro programa, los grafos son representados mediante una lista de nodos. E n1.connectTo(n2) n3.connectTo(n1) } - + Ahora vamos a completar el ejemplo con información relacionada al tipado para definir explicitamente de qué tipo son las entidades anteriormente definidas: - + object GraphTest extends App { val g: Graph = new Graph val n1: g.Node = g.newNode @@ -57,7 +57,7 @@ Ahora vamos a completar el ejemplo con información relacionada al tipado para d El código anterior muestra que al tipo del nodo le es prefijado con la instancia superior (que en nuestro ejemplo es `g`). Si ahora tenemos dos grafos, el sistema de tipado de Scala no nos permite mezclar nodos definidos en un grafo con nodos definidos en otro, ya que los nodos del otro grafo tienen un tipo diferente. Aquí está el programa ilegal: - + object IllegalGraphTest extends App { val g: Graph = new Graph val n1: g.Node = g.newNode @@ -67,9 +67,9 @@ Aquí está el programa ilegal: val n3: h.Node = h.newNode n1.connectTo(n3) // ilegal! } - + Por favor note que en Java la última linea del ejemplo anterior hubiese sido correcta. Para los nodos de ambos grafos, Java asignaría el mismo tipo `Graph.Node`; es decir, `Node` es prefijado con la clase `Graph`. En Scala un tipo similar también puede ser definido, pero es escrito `Graph#Node`. Si queremos que sea posible conectar nodos de distintos grafos, es necesario modificar la implementación inicial del grafo de la siguiente manera: - + class Graph { class Node { var connectedNodes: List[Graph#Node] = Nil // Graph#Node en lugar de Node diff --git a/es/tutorials/tour/_posts/2017-02-13-local-type-inference.md b/_es/tour/local-type-inference.md similarity index 96% rename from es/tutorials/tour/_posts/2017-02-13-local-type-inference.md rename to _es/tour/local-type-inference.md index 219847df4f..b236ca118a 100644 --- a/es/tutorials/tour/_posts/2017-02-13-local-type-inference.md +++ b/_es/tour/local-type-inference.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Inferencia de tipos Local discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 29 language: es @@ -13,7 +13,7 @@ next-page: unified-types previous-page: explicitly-typed-self-references --- -Scala tiene incorporado un mecanismo de inferencia de tipos el cual permite al programador omitir ciertos tipos de anotaciones. Por ejemplo, generalmente no es necesario especificar el tipo de una variable, ya que el compilador puede deducir el tipo mediante la expresión de inicialización de la variable. También puede generalmente omitirse los tipos de retorno de métodos ya que se corresponden con el tipo del cuerpo, que es inferido por el compilador. +Scala tiene incorporado un mecanismo de inferencia de tipos el cual permite al programador omitir ciertos tipos de anotaciones. Por ejemplo, generalmente no es necesario especificar el tipo de una variable, ya que el compilador puede deducir el tipo mediante la expresión de inicialización de la variable. También puede generalmente omitirse los tipos de retorno de métodos ya que se corresponden con el tipo del cuerpo, que es inferido por el compilador. Aquí hay un ejemplo: @@ -22,7 +22,7 @@ Aquí hay un ejemplo: val y = x.toString() // el tipo de y es String def succ(x: Int) = x + 1 // el método succ retorna valores Int } - + Para métodos recursivos, el compilador no es capaz de inferir el tipo resultado. A continuación mostramos un programa el cual falla por esa razón: object InferenceTest2 { diff --git a/es/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md b/_es/tour/lower-type-bounds.md similarity index 97% rename from es/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md rename to _es/tour/lower-type-bounds.md index 407d1018cc..0819ecde6f 100644 --- a/es/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md +++ b/_es/tour/lower-type-bounds.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Límite de tipado inferior discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 26 language: es @@ -49,4 +49,3 @@ Este código ilustra el concepto: .prepend("world") val anyList: ListNode[Any] = strList.prepend(12345) } - diff --git a/es/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md b/_es/tour/mixin-class-composition.md similarity index 96% rename from es/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md rename to _es/tour/mixin-class-composition.md index 8303f566f2..5a48fc0a8b 100644 --- a/es/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md +++ b/_es/tour/mixin-class-composition.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Composición de clases mixin discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 12 language: es @@ -15,30 +15,30 @@ previous-page: inner-classes _Nota de traducción: La palabra `mixin` puede ser traducida como mezcla, dando título a esta sección de: Composición de clases Mezcla, pero es preferible utilizar la notación original_ A diferencia de lenguajes que solo soportan _herencia simple_, Scala tiene una notación más general de la reutilización de clases. Scala hace posible reutilizar la _nueva definición de miembros de una clase_ (es decir, el delta en relación a la superclase) en la definición de una nueva clase. Esto es expresado como una _composición de clases mixin_. Considere la siguiente abstracción para iteradores. - + abstract class AbsIterator { type T def hasNext: Boolean def next: T } - + A continuación, considere una clase mezcla la cual extiende `AbsIterator` con un método `foreach` el cual aplica una función dada a cada elemento retornado por el iterador. Para definir una clase que puede usarse como una clase mezcla usamos la palabra clave `trait`. - + trait RichIterator extends AbsIterator { def foreach(f: T => Unit) { while (hasNext) f(next) } } - + Aquí se muestra una clase iterador concreta, la cual retorna caracteres sucesivos de una cadena de caracteres dada: - + class StringIterator(s: String) extends AbsIterator { type T = Char private var i = 0 def hasNext = i < s.length() def next = { val ch = s charAt i; i += 1; ch } } - + Nos gustaría combinar la funcionalidad de `StringIterator` y `RichIterator` en una sola clase. Solo con herencia simple e interfaces esto es imposible, ya que ambas clases contienen implementaciones para sus miembros. Scala nos ayuda con sus _compisiciones de clases mezcladas_. Permite a los programadores reutilizar el delta de la definición de una clase, esto es, todas las nuevas definiciones que no son heredadas. Este mecanismo hace posible combinar `StringIterator` con `RichIterator`, como es hecho en el siguiente programa, el cual imprime una columna de todos los caracteres de una cadena de caracteres dada. - + object StringIteratorTest { def main(args: Array[String]) { class Iter extends StringIterator(args(0)) with RichIterator @@ -46,5 +46,5 @@ Nos gustaría combinar la funcionalidad de `StringIterator` y `RichIterator` en iter foreach println } } - + La clase `Iter` en la función `main` es construida de una composición mixin de los padres `StringIterator` y `RichIterator` con la palabra clave `with`. El primera padre es llamado la _superclase_ de `Iter`, mientras el segundo padre (y cualquier otro que exista) es llamada un _mixin_. diff --git a/es/tutorials/tour/_posts/2017-02-13-named-parameters.md b/_es/tour/named-parameters.md similarity index 94% rename from es/tutorials/tour/_posts/2017-02-13-named-parameters.md rename to _es/tour/named-parameters.md index 6827e8e0a4..6ecb8c877d 100644 --- a/es/tutorials/tour/_posts/2017-02-13-named-parameters.md +++ b/_es/tour/named-parameters.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Parámetros nombrados discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 35 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-nested-functions.md b/_es/tour/nested-functions.md similarity index 92% rename from es/tutorials/tour/_posts/2017-02-13-nested-functions.md rename to _es/tour/nested-functions.md index a73ae19882..c592b30c65 100644 --- a/es/tutorials/tour/_posts/2017-02-13-nested-functions.md +++ b/_es/tour/nested-functions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Funciones Anidadas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 13 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-operators.md b/_es/tour/operators.md similarity index 95% rename from es/tutorials/tour/_posts/2017-02-13-operators.md rename to _es/tour/operators.md index c6c8393267..fa7c55cf65 100644 --- a/es/tutorials/tour/_posts/2017-02-13-operators.md +++ b/_es/tour/operators.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Operadores discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 17 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-pattern-matching.md b/_es/tour/pattern-matching.md similarity index 96% rename from es/tutorials/tour/_posts/2017-02-13-pattern-matching.md rename to _es/tour/pattern-matching.md index 8b0509f506..d20d83bf56 100644 --- a/es/tutorials/tour/_posts/2017-02-13-pattern-matching.md +++ b/_es/tour/pattern-matching.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Reconocimiento de patrones discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 20 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md b/_es/tour/polymorphic-methods.md similarity index 95% rename from es/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md rename to _es/tour/polymorphic-methods.md index 2fa92e125e..c14ca2a18d 100644 --- a/es/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md +++ b/_es/tour/polymorphic-methods.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Métodos polimórficos discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 21 language: es @@ -16,7 +16,7 @@ previous-page: pattern-matching Los métodos en Scala pueden ser parametrizados tanto con valores como con tipos. Como a nivel de clase, parámetros de valores son encerrados en un par de paréntesis, mientras que los parámetros de tipo son declarados dentro de un par de corchetes. Aquí hay un ejemplo: - + object PolyTest extends App { def dup[T](x: T, n: Int): List[T] = if (n == 0) Nil diff --git a/es/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md b/_es/tour/regular-expression-patterns.md similarity index 96% rename from es/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md rename to _es/tour/regular-expression-patterns.md index 9743620a85..bb0571a27a 100644 --- a/es/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md +++ b/_es/tour/regular-expression-patterns.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Patrones basados en expresiones regulares discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 22 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md b/_es/tour/sequence-comprehensions.md similarity index 96% rename from es/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md rename to _es/tour/sequence-comprehensions.md index 82f8de3c66..203a01b4c8 100644 --- a/es/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md +++ b/_es/tour/sequence-comprehensions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Sequencias por Comprensión discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 7 language: es @@ -16,7 +16,7 @@ previous-page: compound-types Scala cuenta con una notación ligera para expresar *sequencias por comprensión* (*sequence comprehensions*). Las comprensiones tienen la forma `for (enumeradores) yield e`, donde `enumeradores` se refiere a una lista de enumeradores separados por el símbolo punto y coma (;). Un *enumerador* puede ser tanto un generador el cual introduce nuevas variables, o un filtro. La comprensión evalúa el cuerpo `e` por cada paso (o ciclo) generado por los enumeradores y retorna una secuencia de estos valores. Aquí hay un ejemplo: - + object ComprehensionTest1 extends App { def pares(desde: Int, hasta: Int): List[Int] = for (i <- List.range(desde, hasta) if i % 2 == 0) yield i @@ -29,7 +29,7 @@ El programa produce los siguientes valores List(0, 2, 4, 6, 8, 10, 12, 14, 16, 18) -Aquí se muestra un ejemplo más complicado que computa todos los pares de números entre `0` y `n-1` cuya suma es igual a un número dado `v`: +Aquí se muestra un ejemplo más complicado que computa todos los pares de números entre `0` y `n-1` cuya suma es igual a un número dado `v`: object ComprehensionTest2 extends App { def foo(n: Int, v: Int) = @@ -52,10 +52,9 @@ Esta es la salida del programa: (16, 16) Existe también una forma especial de comprensión de secuencias la cual retorna `Unit`. En este caso las variables que son creadas por la lista de generadores y filtros son usados para realizar tareas con efectos colaterales (modificaciones de algún tipo). El programador tiene que omitir la palabra reservada `yield` para usar una comprensión de este tipo. - + object ComprehensionTest3 extends App { for (i <- Iterator.range(0, 20); j <- Iterator.range(i, 20) if i + j == 32) println("(" + i + ", " + j + ")") } - diff --git a/es/tutorials/tour/_posts/2017-02-13-singleton-objects.md b/_es/tour/singleton-objects.md similarity index 98% rename from es/tutorials/tour/_posts/2017-02-13-singleton-objects.md rename to _es/tour/singleton-objects.md index 643b81d2f1..8c15cf50f4 100644 --- a/es/tutorials/tour/_posts/2017-02-13-singleton-objects.md +++ b/_es/tour/singleton-objects.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Singleton Objects discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 12 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-tour-of-scala.md b/_es/tour/tour-of-scala.md similarity index 98% rename from es/tutorials/tour/_posts/2017-02-13-tour-of-scala.md rename to _es/tour/tour-of-scala.md index 867c80d53a..ba2fe0eeaf 100644 --- a/es/tutorials/tour/_posts/2017-02-13-tour-of-scala.md +++ b/_es/tour/tour-of-scala.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Introducción discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 1 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-traits.md b/_es/tour/traits.md similarity index 95% rename from es/tutorials/tour/_posts/2017-02-13-traits.md rename to _es/tour/traits.md index e6cd048591..363fee8148 100644 --- a/es/tutorials/tour/_posts/2017-02-13-traits.md +++ b/_es/tour/traits.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Traits discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 24 language: es @@ -15,16 +15,16 @@ previous-page: regular-expression-patterns _Nota de traducción: La palabra `trait` en inglés puede traducirse literalmente como `rasgo` o `caracteristica`. Preferimos la designación original trait por ser una característica muy natural de Scala._ -De forma similar a las interfaces de Java, los traits son usados para definir tipos de objetos al especificar el comportamiento mediante los métodos provistos. A diferencia de Java, Scala permite a los traits ser parcialmente implementados, esto es, es posible definir implementaciones por defecto para algunos métodos. En contraste con las clases, los traits no pueden tener parámetros de constructor. +De forma similar a las interfaces de Java, los traits son usados para definir tipos de objetos al especificar el comportamiento mediante los métodos provistos. A diferencia de Java, Scala permite a los traits ser parcialmente implementados, esto es, es posible definir implementaciones por defecto para algunos métodos. En contraste con las clases, los traits no pueden tener parámetros de constructor. A continuación se muestra un ejemplo: - + trait Similarity { def isSimilar(x: Any): Boolean def isNotSimilar(x: Any): Boolean = !isSimilar(x) } - + Este trait consiste de dos métodos `isSimilar` y `isNotSimilar`. Mientras `isSimilar` no provee una implementación concreta del método (es abstracto en la terminología Java), el método `isNotSimilar` define una implementación concreta. Consecuentemente, las clases que integren este trait solo tienen que proveer una implementación concreta para `isSimilar`. El comportamiento de `isNotSimilar` es directamente heredado del trait. Los traits típicamente son integrados a una clase (u otros traits) mediante una [Composición de clases mixin](mixin-class-composition.html): - + class Point(xc: Int, yc: Int) extends Similarity { var x: Int = xc var y: Int = yc @@ -40,7 +40,7 @@ Este trait consiste de dos métodos `isSimilar` y `isNotSimilar`. Mientras `isSi println(p1.isNotSimilar(p3)) println(p1.isNotSimilar(2)) } - + Esta es la salida del programa: false diff --git a/es/tutorials/tour/_posts/2017-02-13-unified-types.md b/_es/tour/unified-types.md similarity index 97% rename from es/tutorials/tour/_posts/2017-02-13-unified-types.md rename to _es/tour/unified-types.md index 5593469d7b..3bdc3541af 100644 --- a/es/tutorials/tour/_posts/2017-02-13-unified-types.md +++ b/_es/tour/unified-types.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Tipos Unificados discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 30 language: es diff --git a/es/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md b/_es/tour/upper-type-bounds.md similarity index 95% rename from es/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md rename to _es/tour/upper-type-bounds.md index 95db89f78b..84124c8944 100644 --- a/es/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md +++ b/_es/tour/upper-type-bounds.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Límite de tipado superior discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 25 language: es @@ -32,6 +32,6 @@ Aquí se muestra un ejemplo el cual se basa en un límite de tipado superior par val list: List[MyInt] = List(MyInt(1), MyInt(2), MyInt(3)) println(findSimilar[MyInt](MyInt(4), list)) println(findSimilar[MyInt](MyInt(2), list)) - } + } Sin la anotación del límite de tipado superior no sería posible llamar al método `isSimilar` en el método `findSimilar`. El uso de los límites de tipado inferiores se discute [aquí](lower-type-bounds.html). diff --git a/es/tutorials/tour/_posts/2017-02-13-variances.md b/_es/tour/variances.md similarity index 97% rename from es/tutorials/tour/_posts/2017-02-13-variances.md rename to _es/tour/variances.md index 106277d090..ad4a8fc3c2 100644 --- a/es/tutorials/tour/_posts/2017-02-13-variances.md +++ b/_es/tour/variances.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Varianzas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 31 language: es diff --git a/_includes/sidebar-toc-tour-overview.html b/_includes/sidebar-toc-tour-overview.html new file mode 100644 index 0000000000..28a1a1636a --- /dev/null +++ b/_includes/sidebar-toc-tour-overview.html @@ -0,0 +1,49 @@ +
    + +
    diff --git a/_layouts/inner-page-parent-dropdown.html b/_layouts/inner-page-parent-dropdown.html index 8074a0413c..d2e5029f68 100644 --- a/_layouts/inner-page-parent-dropdown.html +++ b/_layouts/inner-page-parent-dropdown.html @@ -14,6 +14,8 @@
    {% if page.overview-name %}
    {{ page.overview-name }}
    + {% elsif site.data.docnames[page.partof].name %} +
    {{ site.data.docnames[page.partof].name }}
    {% else %}
     
    {% endif %} diff --git a/_layouts/multipage-overview.html b/_layouts/multipage-overview.html index cf7c0b5a32..8ba00e63a8 100644 --- a/_layouts/multipage-overview.html +++ b/_layouts/multipage-overview.html @@ -13,6 +13,6 @@
    - {% include sidebar-toc-multipage-overview.html %} + {% include sidebar-toc-tour-overview.html %} diff --git a/_layouts/tour.html b/_layouts/tour.html index 7223c3f55a..8ba00e63a8 100644 --- a/_layouts/tour.html +++ b/_layouts/tour.html @@ -1,17 +1,18 @@ --- -layout: inner-page-parent -permalink: /tutorials/:categories/:title/ +layout: inner-page-parent-dropdown +includeTOC: true +includeCollectionTOC: true --- +
    -
    +
    {{content}} - {% include pager.txt %}
    - {% include tutorial-toc.html %} + {% include sidebar-toc-tour-overview.html %}
    diff --git a/_overviews/tutorials/01-post.png b/_overviews/tutorials/01-post.png new file mode 100644 index 0000000000000000000000000000000000000000..cef3dcaa46d8d4206f3b601ff7157c127d7eb55a GIT binary patch literal 172693 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 V{he5NlyEMeCkpEFC9+1o{{!U6U)2Br literal 0 HcmV?d00001 diff --git a/_overviews/tutorials/02-post.png b/_overviews/tutorials/02-post.png new file mode 100644 index 0000000000000000000000000000000000000000..533ac99538d153d433129e59513a708d02fcc429 GIT binary patch literal 114529 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

    f?AQ{Dupipdlg;C}}=T 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

    5V-fPj1J=6v-5Ui*9vLJ^IReprT1O{yZE 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 z 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 BSkM3f literal 0 HcmV?d00001 diff --git a/_overviews/tutorials/05-review.png b/_overviews/tutorials/05-review.png new file mode 100644 index 0000000000000000000000000000000000000000..330cb519ac2c6945b9e480262568025fe49b7c1f GIT binary patch literal 29682 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 literal 0 HcmV?d00001 diff --git a/_overviews/tutorials/FAQ/breakout.md b/_overviews/tutorials/FAQ/breakout.md new file mode 100644 index 0000000000..bd672df922 --- /dev/null +++ b/_overviews/tutorials/FAQ/breakout.md @@ -0,0 +1,234 @@ +--- +layout: overview +title: What is breakOut, and how does it work? + +discourse: true + +partof: FAQ +num: 5 +--- +You might have encountered some code like the one below, and wonder what is +`breakOut`, and why is it being passed as parameter? + + import scala.collection.breakOut + val map : Map[Int,String] = List("London", "France").map(x => (x.length, x))(breakOut) + + +The answer is found on the definition of `map`: + + def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That + +Note that it has two parameters. The first is your function and the second is +an implicit. If you do not provide that implicit, Scala will choose the most +_specific_ one available. + +### About breakOut + +So, what's the purpose of `breakOut`? Consider the example given at the +beginning , You take a list of strings, transform each string into a tuple +`(Int, String)`, and then produce a `Map` out of it. The most obvious way to do +that would produce an intermediary `List[(Int, String)]` collection, and then +convert it. + +Given that `map` uses a `Builder` to produce the resulting collection, wouldn't +it be possible to skip the intermediary `List` and collect the results directly +into a `Map`? Evidently, yes, it is. To do so, however, we need to pass a +proper `CanBuildFrom` to `map`, and that is exactly what `breakOut` does. + +Let's look, then, at the definition of `breakOut`: + + def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) = + new CanBuildFrom[From, T, To] { + def apply(from: From) = b.apply() ; def apply() = b.apply() + } + +Note that `breakOut` is parameterized, and that it returns an instance of +`CanBuildFrom`. As it happens, the types `From`, `T` and `To` have already been +inferred, because we know that `map` is expecting `CanBuildFrom[List[String], +(Int, String), Map[Int, String]]`. Therefore: + + From = List[String] + T = (Int, String) + To = Map[Int, String] + +To conclude let's examine the implicit received by `breakOut` itself. It is of +type `CanBuildFrom[Nothing,T,To]`. We already know all these types, so we can +determine that we need an implicit of type +`CanBuildFrom[Nothing,(Int,String),Map[Int,String]]`. But is there such a +definition? + +Let's look at `CanBuildFrom`'s definition: + + trait CanBuildFrom[-From, -Elem, +To] + extends AnyRef + +So `CanBuildFrom` is contra-variant on its first type parameter. Because +`Nothing` is a bottom class (ie, it is a subclass of everything), that means +*any* class can be used in place of `Nothing`. + +Since such a builder exists, Scala can use it to produce the desired output. + +### About Builders + +A lot of methods from Scala's collections library consists of taking the +original collection, processing it somehow (in the case of `map`, transforming +each element), and storing the results in a new collection. + +To maximize code reuse, this storing of results is done through a _builder_ +(`scala.collection.mutable.Builder`), which basically supports two operations: +appending elements, and returning the resulting collection. The type of this +resulting collection will depend on the type of the builder. Thus, a `List` +builder will return a `List`, a `Map` builder will return a `Map`, and so on. +The implementation of the `map` method need not concern itself with the type of +the result: the builder takes care of it. + +On the other hand, that means that `map` needs to receive this builder somehow. +The problem faced when designing Scala 2.8 Collections was how to choose the +best builder possible. For example, if I were to write `Map('a' -> +1).map(_.swap)`, I'd like to get a `Map(1 -> 'a')` back. On the other hand, a +`Map('a' -> 1).map(_._1)` can't return a `Map` (it returns an `Iterable`). + +The magic of producing the best possible `Builder` from the known types of the +expression is performed through this `CanBuildFrom` implicit. + +### About CanBuildFrom + +To better explain what's going on, I'll give an example where the collection +being mapped is a `Map` instead of a `List`. I'll go back to `List` later. For +now, consider these two expressions: + + Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length) + Map(1 -> "one", 2 -> "two") map (_._2) + +The first returns a `Map` and the second returns an `Iterable`. The magic of +returning a fitting collection is the work of `CanBuildFrom`. Let's consider +the definition of `map` again to understand it. + +The method `map` is inherited from `TraversableLike`. It is parameterized on +`B` and `That`, and makes use of the type parameters `A` and `Repr`, which +parameterize the class. Let's see both definitions together: + +The class `TraversableLike` is defined as: + + trait TraversableLike[+A, +Repr] + extends HasNewBuilder[A, Repr] with AnyRef + + def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That + + +To understand where `A` and `Repr` come from, let's consider the definition of +`Map` itself: + + trait Map[A, +B] + extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]] + +Because `TraversableLike` is inherited by all traits which extend `Map`, `A` +and `Repr` could be inherited from any of them. The last one gets the +preference, though. So, following the definition of the immutable `Map` and all +the traits that connect it to `TraversableLike`, we have: + + trait Map[A, +B] + extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]] + + trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] + extends MapLike[A, B, This] + + trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] + extends PartialFunction[A, B] with IterableLike[(A, B), This] with Subtractable[A, This] + + trait IterableLike[+A, +Repr] + extends Equals with TraversableLike[A, Repr] + + trait TraversableLike[+A, +Repr] + extends HasNewBuilder[A, Repr] with AnyRef + +If you pass the type parameters of `Map[Int, String]` all the way down the +chain, we find that the types passed to `TraversableLike`, and, thus, used by +`map`, are: + + A = (Int,String) + Repr = Map[Int, String] + +Going back to the example, the first map is receiving a function of type +`((Int, String)) => (Int, Int)` and the second map is receiving a function of +type `((Int, String)) => Int`. I use the double parenthesis to emphasize it is +a tuple being received, as that's the type of `A` as we saw. + +With that information, let's consider the other types. + + map Function.tupled(_ -> _.length): + B = (Int, Int) + + map (_._2): + B = Int + +We can see that the type returned by the first `map` is `Map[Int,Int]`, and the +second is `Iterable[String]`. Looking at `map`'s definition, it is easy to see +that these are the values of `That`. But where do they come from? + +If we look inside the companion objects of the classes involved, we see some +implicit declarations providing them. On object `Map`: + + implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]] + +And on object `Iterable`, whose class is extended by `Map`: + + implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]] + +These definitions provide factories for parameterized `CanBuildFrom`. + +Scala will choose the most specific implicit available. In the first case, it +was the first `CanBuildFrom`. In the second case, as the first did not match, +it chose the second `CanBuildFrom`. + +### Back to the first example + +Let's see the first example, `List`'s and `map`'s definition (again) to +see how the types are inferred: + + val map : Map[Int,String] = List("London", "France").map(x => (x.length, x))(breakOut) + + sealed abstract class List[+A] + extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]] + + trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]] + extends SeqLike[A, Repr] + + trait SeqLike[+A, +Repr] + extends IterableLike[A, Repr] + + trait IterableLike[+A, +Repr] + extends Equals with TraversableLike[A, Repr] + + trait TraversableLike[+A, +Repr] + extends HasNewBuilder[A, Repr] with AnyRef + + def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That + +The type of `List("London", "France")` is `List[String]`, so the types `A` and +`Repr` defined on `TraversableLike` are: + + A = String + Repr = List[String] + +The type for `(x => (x.length, x))` is `(String) => (Int, String)`, so the type +of `B` is: + + B = (Int, String) + +The last unknown type, `That` is the type of the result of `map`, and we +already have that as well: + + val map : Map[Int,String] = + +So, + + That = Map[Int, String] + +That means `breakOut` must, necessarily, return a type or subtype of +`CanBuildFrom[List[String], (Int, String), Map[Int, String]]`. + +This answer was originally submitted in response to [this question on Stack Overflow][1]. + + [1]: http://stackoverflow.com/q/1715681/53013 + diff --git a/_overviews/tutorials/FAQ/chaining-implicits.md b/_overviews/tutorials/FAQ/chaining-implicits.md new file mode 100644 index 0000000000..71424b80d6 --- /dev/null +++ b/_overviews/tutorials/FAQ/chaining-implicits.md @@ -0,0 +1,109 @@ +--- +layout: overview +title: How can I chain/nest implicit conversions? + +discourse: true + +partof: FAQ +num: 6 +--- + +The enrich-my-library pattern allows one to seemingly add a method to a class by +making available an implicit conversion from that class to one that implements +the method. + +Scala does not allow two such implicit conversions taking place, however, so +one cannot got from `A` to `C` using an implicit `A` to `B` and another +implicit `B` to `C`. Is there a way around this restriction? + +Scala has a restriction on automatic conversions to add a method, which is that +it won't apply more than one conversion in trying to find methods. For example: + + class A(val n: Int) + class B(val m: Int, val n: Int) + class C(val m: Int, val n: Int, val o: Int) { + def total = m + n + o + } + + import scala.language.implicitConversions + + // This demonstrates implicit conversion chaining restrictions + object T1 { // to make it easy to test on REPL + implicit def toA(n: Int): A = new A(n) + implicit def aToB(a: A): B = new B(a.n, a.n) + implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n) + + // won't work + println(5.total) + println(new A(5).total) + + // works + println(new B(5, 5).total) + println(new C(5, 5, 10).total) + } + +However, if an implicit definition requires an implicit parameter itself, Scala +_will_ look for additional implicit values for as long as needed. Continuing from +the last example: + + object T2 { + implicit def toA(n: Int): A = new A(n) + implicit def aToB[A1](a: A1)(implicit f: A1 => A): B = + new B(a.n, a.n) + implicit def bToC[B1](b: B1)(implicit f: B1 => B): C = + new C(b.m, b.n, b.m + b.n) + + // works + println(5.total) + println(new A(5).total) + println(new B(5, 5).total) + println(new C(5, 5, 10).total) + } + +_"Magic!"_, you might say. Not so. Here is how the compiler would translate each +one: + + object T1Translated { + implicit def toA(n: Int): A = new A(n) + implicit def aToB(a: A): B = new B(a.n, a.n) + implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n) + + // Scala won't do this + println(bToC(aToB(toA(5))).total) + println(bToC(aToB(new A(5))).total) + + // Just this + println(bToC(new B(5, 5)).total) + + // No implicits required + println(new C(5, 5, 10).total) + } + + object T2Translated { + implicit def toA(n: Int): A = new A(n) + implicit def aToB[A1](a: A1)(implicit f: A1 => A): B = + new B(a.n, a.n) + implicit def bToC[B1](b: B1)(implicit f: B1 => B): C = + new C(b.m, b.n, b.m + b.n) + + // Scala does this + println(bToC(5)(x => aToB(x)(y => toA(y))).total) + println(bToC(new A(5))(x => aToB(x)(identity)).total) + println(bToC(new B(5, 5))(identity).total) + + // no implicits required + println(new C(5, 5, 10).total) + } + +So, while `bToC` is being used as an implicit conversion, `aToB` and `toA` are +being passed as _implicit parameters_, instead of being chained as implicit +conversions. + +See also: + +* [Context bounds](context-bounds.html) +* [A discussion on types, origin and precedence of implicits](finding-implicits.html) + +This question and answer were originally submitted on [Stack Overflow][1]. + + [1]: http://stackoverflow.com/questions/5332801/how-can-i-chain-implicits-in-scala/5332804 diff --git a/_overviews/tutorials/FAQ/collections.md b/_overviews/tutorials/FAQ/collections.md new file mode 100644 index 0000000000..8108915363 --- /dev/null +++ b/_overviews/tutorials/FAQ/collections.md @@ -0,0 +1,377 @@ +--- +layout: overview +title: How are the collections structured? Which one should I choose? + +discourse: true + +partof: FAQ +num: 8 +--- +## Foreword + +There's a [2.8 collection walk-through][1] by Martin Odersky which should +probably be your first reference. It has been supplemented as well with +[architectural notes][2], which will be of particular interest to those who +want to design their own collections. + +The rest of this answer was written way before any such thing existed (in fact, +before 2.8.0 itself was released). + +You can find a paper about it as [Scala SID #3][3]. Other papers in that area +should be interesting as well to people interested in the differences between +Scala 2.7 and 2.8. + +I'll quote from the paper, selectively, and complement with some thoughts of +mine. There are also some images, generated by Matthias at decodified.com, and +the original SVG files can be found [here][4]. + +## The collection classes/traits themselves + +There are actually three hierarchies of traits for the collections: one for +mutable collections, one for immutable collections, and one which doesn't make +any assumptions about the collections. + +There's also a distinction between parallel, serial and maybe-parallel +collections, which was introduced with Scala 2.9. I'll talk about them in the +next section. The hierarchy described in this section refers _exclusively to +non-parallel collections_. + +The following image shows the non-specific hierarchy introduced with Scala 2.8: +![General collection hierarchy][5] + +All elements shown are traits. In the other two hierarchies there are also +classes directly inheriting the traits as well as classes which can be _viewed +as_ belonging in that hierarchy through implicit conversion to wrapper classes. +The legend for these graphs can be found after them. + +Graph for immutable hierarchy: + + +Graph for mutable hierarchy: + + +Legend: + +![Graph legend][8] + +Here's an abbreviated ASCII depiction of the collection hierarchy, for those who can't see the images. + + Traversable + | + | + Iterable + | + +------------------+--------------------+ + Map Set Seq + | | | + | +----+----+ +-----+------+ + Sorted Map SortedSet BitSet Buffer Vector LinearSeq + + +## Parallel Collections + +When Scala 2.9 introduced parallel collections, one of the design goals was to +make their use as seamless as possible. In the simplest terms, one can replace +a non-parallel (serial) collection with a parallel one, and instantly reap the +benefits. + +However, since all collections until then were serial, many algorithms using +them assumed and depended on the fact that they _were_ serial. Parallel +collections fed to the methods with such assumptions would fail. For this +reason, all the hierarchy described in the previous section _mandates serial +processing_. + +Two new hierarchies were created to support the parallel collections. + +The parallel collections hierarchy has the same names for traits, but preceded +with `Par`: `ParIterable`, `ParSeq`, `ParMap` and `ParSet`. Note that there is +no `ParTraversable`, since any collection supporting parallel access is capable +of supporting the stronger `ParIterable` trait. It doesn't have some of the +more specialized traits present in the serial hierarchy either. This whole +hierarchy is found under the directory `scala.collection.parallel`. + +The classes implementing parallel collections also differ, with `ParHashMap` +and `ParHashSet` for both mutable and immutable parallel collections, plus +`ParRange` and `ParVector` implementing `immutable.ParSeq` and `ParArray` +implementing `mutable.ParSeq`. + +Another hierarchy also exists that mirrors the traits of serial and parallel +collections, but with a prefix `Gen`: `GenTraversable`, `GenIterable`, +`GenSeq`, `GenMap` and `GenSet`. These traits are _parents_ to both parallel +and serial collections. This means that a method taking a `Seq` cannot receive +a parallel collection, but a method taking a `GenSeq` is expected to work with +both serial and parallel collections. + +Given the way these hierarchies were structured, code written for Scala 2.8 was +fully compatible with Scala 2.9, and demanded serial behavior. Without being +rewritten, it cannot take advantage of parallel collections, but the changes +required are very small. + +### Using Parallel Collections + +Any collection can be converted into a parallel one by calling the method `par` +on it. Likewise, any collection can be converted into a serial one by calling +the method `seq` on it. + +If the collection was already of the type requested (parallel or serial), no +conversion will take place. If one calls `seq` on a parallel collection or +`par` on a serial collection, however, a new collection with the requested +characteristic will be generated. + +Do not confuse `seq`, which turns a collection into a non-parallel collection, +with `toSeq`, which returns a `Seq` created from the elements of the +collection. Calling `toSeq` on a parallel collection will return a `ParSeq`, +not a serial collection. + +## The Main Traits + +While there are many implementing classes and subtraits, there are some basic +traits in the hierarchy, each of which providing more methods or more specific +guarantees, but reducing the number of classes that could implement them. + +In the following subsections, I'll give a brief description of the main traits +and the idea behind them. + +### Trait TraversableOnce + +This trait is pretty much like trait `Traversable` described below, but with +the limitation that you can only use it _once_. That is, any methods called on +a `TraversableOnce` _may_ render it unusable. + +This limitation makes it possible for the same methods to be shared between the +collections and `Iterator`. This makes it possible for a method that works with +an `Iterator` but not using `Iterator`-specific methods to actually be able to +work with any collection at all, plus iterators, if rewritten to accept +`TraversableOnce`. + +Because `TraversableOnce` unifies collections and iterators, and iterators are +not considered collections, it does not appear in the previous graphs, which +concern themselves only with collections. + +### Trait Traversable + +At the top of the _collection_ hierarchy is trait `Traversable`. Its only +abstract operation is + + def foreach[U](f: Elem => U) + +The operation is meant to traverse all elements of the collection, and apply +the given operation f to each element. The application is done for its side +effect only; in fact any function result of f is discarded by foreach. + +Traversible objects can be finite or infinite. An example of an infinite +traversable object is the stream of natural numbers `Stream.from(0)`. The +method `hasDefiniteSize` indicates whether a collection is possibly infinite. +If `hasDefiniteSize` returns true, the collection is certainly finite. If it +returns false, the collection has not been not fully elaborated yet, so it +might be infinite or finite. + +This class defines methods which can be efficiently implemented in terms of +`foreach` (over 40 of them). + +### Trait Iterable + +This trait declares an abstract method `iterator` that returns an iterator that +yields all the collection’s elements one by one. The `foreach` method in +`Iterable` is implemented in terms of `iterator`. Subclasses of `Iterable` +often override foreach with a direct implementation for efficiency. + +Class `Iterable` also adds some less-often used methods to `Traversable`, which +can be implemented efficiently only if an `iterator` is available. They are +summarized below. + + xs.iterator An iterator that yields every element in xs, in the same order as foreach traverses elements. + xs takeRight n A collection consisting of the last n elements of xs (or, some arbitrary n elements, if no order is defined). + xs dropRight n The rest of the collection except xs takeRight n. + xs sameElements ys A test whether xs and ys contain the same elements in the same order + +### Seq, Set and Map + +After `Iterable` there come three base traits which inherit from it: `Seq`, +`Set`, and `Map`. All three have an `apply` method and all three implement the +`PartialFunction` trait, but the meaning of `apply` is different in each case. + +I trust the meaning of `Seq`, `Set` and `Map` is intuitive. After them, the +classes break up in specific implementations that offer particular guarantees +with regards to performance, and the methods it makes available as a result of +it. Also available are some traits with further refinements, such as +`LinearSeq`, `IndexedSeq` and `SortedSet`. + +## Complete Overview + +### Base Classes and Traits + +* `TraversableOnce` -- All methods and behavior common to collections and iterators. + + * `Traversable` -- Basic collection class. Can be implemented just with `foreach`. + + * `TraversableProxy` -- Proxy for a `Traversable`. Just point `self` to the real collection. + * `TraversableView` -- A Traversable with some non-strict methods. + * `TraversableForwarder` -- Forwards most methods to `underlying`, except `toString`, `hashCode`, `equals`, `stringPrefix`, `newBuilder`, `view` and all calls creating a new iterable object of the same kind. + * `mutable.Traversable` and `immutable.Traversable` -- same thing as `Traversable`, but restricting the collection type. + * Other special-cases `Iterable` classes, such as `MetaData`, exists. + * `Iterable` -- A collection for which an `Iterator` can be created (through `iterator`). + * `IterableProxy`, `IterableView`, `mutable` and `immutable.Iterable`. + + * `Iterator` -- A trait which is not descendant of `Traversable`. Define `next` and `hasNext`. + * `CountedIterator` -- An `Iterator` defining `count`, which returns the elements seen so far. + * `BufferedIterator` -- Defines `head`, which returns the next element without consuming it. + * Other special-cases `Iterator` classes, such as `Source`, exists. + +### The Sequences + +* `Seq` -- A sequence of elements. One assumes a well-defined size and element repetition. Extends `PartialFunction` as well. + + * `IndexedSeq` -- Sequences that support O(1) element access and O(1) length computation. + * `IndexedSeqView` + * `immutable.PagedSeq` -- An implementation of `IndexedSeq` where the elements are produced on-demand by a function passed through the constructor. + * `immutable.IndexedSeq` + + * `immutable.Range` -- A delimited sequence of integers, closed on the lower end, open on the high end, and with a step. + * `immutable.Range.Inclusive` -- A `Range` closed on the high end as well. + * `immutable.NumericRange` -- A more generic version of `Range` which works with any `Integral`. + * `immutable.NumericRange.Inclusive`, `immutable.NumericRange.Exclusive`. + * `immutable.WrappedString`, `immutable.RichString` -- Wrappers which enables seeing a `String` as a `Seq[Char]`, while still preserving the `String` methods. I'm not sure what the difference between them is. + + * `mutable.IndexedSeq` + * `mutable.GenericArray` -- An `Seq`-based array-like structure. Note that the "class" `Array` is Java's `Array`, which is more of a memory storage method than a class. + * `mutable.ResizableArray` -- Internal class used by classes based on resizable arrays. + * `mutable.PriorityQueue`, `mutable.SynchronizedPriorityQueue` -- Classes implementing prioritized queues -- queues where the elements are dequeued according to an `Ordering` first, and order of queueing last. + * `mutable.PriorityQueueProxy` -- an abstract `Proxy` for a `PriorityQueue`. + + * `LinearSeq` -- A trait for linear sequences, with efficient time for `isEmpty`, `head` and `tail`. + + * `immutable.LinearSeq` + * `immutable.List` -- An immutable, singlely-linked, list implementation. + * `immutable.Stream` -- A lazy-list. Its elements are only computed on-demand, but memoized (kept in memory) afterwards. It can be theoretically infinite. + * `mutable.LinearSeq` + * `mutable.DoublyLinkedList` -- A list with mutable `prev`, `head` (`elem`) and `tail` (`next`). + * `mutable.LinkedList` -- A list with mutable `head` (`elem`) and `tail` (`next`). + * `mutable.MutableList` -- A class used internally to implement classes based on mutable lists. + * `mutable.Queue`, `mutable.QueueProxy` -- A data structure optimized for FIFO (First-In, First-Out) operations. + * `mutable.QueueProxy` -- A `Proxy` for a `mutable.Queue`. + + * `SeqProxy`, `SeqView`, `SeqForwarder` + + * `immutable.Seq` + + * `immutable.Queue` -- A class implementing a FIFO-optimized (First-In, First-Out) data structure. There is no common superclass of both `mutable` and `immutable` queues. + * `immutable.Stack` -- A class implementing a LIFO-optimized (Last-In, First-Out) data structure. There is no common superclass of both `mutable` `immutable` stacks. + * `immutable.Vector` -- ? + * `scala.xml.NodeSeq` -- A specialized XML class which extends `immutable.Seq`. + * `immutable.IndexedSeq` -- As seen above. + * `immutable.LinearSeq` -- As seen above. + + * `mutable.ArrayStack` -- A class implementing a LIFO-optimized data structure using arrays. Supposedly significantly faster than a normal stack. + * `mutable.Stack`, `mutable.SynchronizedStack` -- Classes implementing a LIFO-optimized data structure. + * `mutable.StackProxy` -- A `Proxy` for a `mutable.Stack`.. + * `mutable.Seq` + + * `mutable.Buffer` -- Sequence of elements which can be changed by appending, prepending or inserting new members. + * `mutable.ArrayBuffer` -- An implementation of the `mutable.Buffer` class, with constant amortized time for the append, update and random access operations. It has some specialized subclasses, such as `NodeBuffer`. + * `mutable.BufferProxy`, `mutable.SynchronizedBuffer`. + * `mutable.ListBuffer` -- A buffer backed by a list. It provides constant time append and prepend, with most other operations being linear. + * `mutable.ObservableBuffer` -- A *mixin* trait which, when mixed to a `Buffer`, provides notification events through a `Publisher` interfaces. + * `mutable.IndexedSeq` -- As seen above. + * `mutable.LinearSeq` -- As seen above. + +### The Sets + +* `Set` -- A set is a collection that includes at most one of any object. + + * `BitSet` -- A set of integers stored as a bitset. + * `immutable.BitSet` + * `mutable.BitSet` + + * `SortedSet` -- A set whose elements are ordered. + * `immutable.SortedSet` + * `immutable.TreeSet` -- An implementation of a `SortedSet` based on a tree. + + * `SetProxy` -- A `Proxy` for a `Set`. + + * `immutable.Set` + * `immutable.HashSet` -- An implementation of `Set` based on element hashing. + * `immutable.ListSet` -- An implementation of `Set` based on lists. + * Additional set classes exists to provide optimized implementations for sets from 0 to 4 elements. + * `immutable.SetProxy` -- A `Proxy` for an immutable `Set`. + + * `mutable.Set` + * `mutable.HashSet` -- An implementation of `Set` based on element hashing. + * `mutable.ImmutableSetAdaptor` -- A class implementing a mutable `Set` from an immutable `Set`. + * `LinkedHashSet` -- An implementation of `Set` based on lists. + * `ObservableSet` -- A *mixin* trait which, when mixed with a `Set`, provides notification events through a `Publisher` interface. + * `SetProxy` -- A `Proxy` for a `Set`. + * `SynchronizedSet` -- A *mixin* trait which, when mixed with a `Set`, provides notification events through a `Publisher` interface. + +### The Maps + +* `Map` -- An `Iterable` of `Tuple2`, which also provides methods for retrieving a value (the second element of the tuple) given a key (the first element of the tuple). Extends `PartialFunction` as well. + * `MapProxy` -- A `Proxy` for a `Map`. + * `DefaultMap` -- A trait implementing some of `Map`'s abstract methods. + * `SortedMap` -- A `Map` whose keys are sorted. + * `immutable.SortMap` + * `immutable.TreeMap` -- A class implementing `immutable.SortedMap`. + * `immutable.Map` + * `immutable.MapProxy` + * `immutable.HashMap` -- A class implementing `immutable.Map` through key hashing. + * `immutable.IntMap` -- A class implementing `immutable.Map` specialized for `Int` keys. Uses a tree based on the binary digits of the keys. + * `immutable.ListMap` -- A class implementing `immutable.Map` through lists. + * `immutable.LongMap` -- A class implementing `immutable.Map` specialized for `Long` keys. See `IntMap`. + * There are additional classes optimized for an specific number of elements. + * `mutable.Map` + * `mutable.HashMap` -- A class implementing `mutable.Map` through key hashing. + * `mutable.ImmutableMapAdaptor` -- A class implementing a `mutable.Map` from an existing `immutable.Map`. + * `mutable.LinkedHashMap` -- ? + * `mutable.ListMap` -- A class implementing `mutable.Map` through lists. + * `mutable.MultiMap` -- A class accepting more than one distinct value for each key. + * `mutable.ObservableMap` -- A *mixin* which, when mixed with a `Map`, publishes events to observers through a `Publisher` interface. + * `mutable.OpenHashMap` -- A class based on an open hashing algorithm. + * `mutable.SynchronizedMap` -- A *mixin* which should be mixed with a `Map` to provide a version of it with synchronized methods. + * `mutable.MapProxy`. + +## Bonus Questions + +* Why the Like classes exist (e.g. TraversableLike)? + +This was done to achieve maximum code reuse. The concrete *generic* +implementation for classes with a certain structure (a traversable, a map, etc) +is done in the Like classes. The classes intended for general consumption, +then, override selected methods that can be optmized. + +* What the companion methods are for (e.g. List.companion)? + +The builder for the classes, ie, the object which knows how to create instances +of that class in a way that can be used by methods like `map`, is created by a +method in the companion object. So, in order to build an object of type X, I +need to get that builder from the companion object of X. Unfortunately, there +is no way, in Scala, to get from class X to object X. Because of that, there is +a method defined in each instance of X, `companion`, which returns the +companion object of class X. + +While there might be some use for such method in normal programs, its target is +enabling code reuse in the collection library. + +* How I know what implicit objects are in scope at a given point? + +You aren't supposed to care about that. They are implicit precisely so that you +don't need to figure out how to make it work. + +These implicits exists to enable the methods on the collections to be defined +on parent classes but still return a collection of the same type. For example, +the `map` method is defined on `TraversableLike`, but if you used on a `List` +you'll get a `List` back. + +This answer was originally submitted in response to [this question][9] on Stack +Overflow. + + + [1]: http://docs.scala-lang.org/overviews/collections/introduction.html + [2]: http://docs.scala-lang.org/overviews/core/architecture-of-scala-collections.html + [3]: http://www.scala-lang.org/sid/3 + [4]: https://github.com/sirthias/scala-collections-charts/downloads + [5]: http://i.stack.imgur.com/bSVyA.png + [6]: http://i.stack.imgur.com/2fjoA.png + [7]: http://i.stack.imgur.com/Dsptl.png + [8]: http://i.stack.imgur.com/szWUr.png + [9]: http://stackoverflow.com/q/1722137/53013 + diff --git a/_overviews/tutorials/FAQ/context-bounds.md b/_overviews/tutorials/FAQ/context-bounds.md new file mode 100644 index 0000000000..45bad40b92 --- /dev/null +++ b/_overviews/tutorials/FAQ/context-bounds.md @@ -0,0 +1,105 @@ +--- +layout: overview +title: What are Scala context bounds? + +discourse: true + +partof: FAQ +num: 3 +--- + +What is a Context Bound? +------------------------ + +Context bounds were introduced in Scala 2.8.0, and are typically used with the +so-called _type class pattern_, a pattern of code that emulates the +functionality provided by Haskell type classes, though in a more verbose +manner. + +A context bound requires a _parameterized type_, such as `Ordered[A]`, +but unlike `String`. + +A context bound describes an implicit _value_. It is used to declare that for +some type `A`, there is an +implicit value of type `B[A]` available. The syntax goes like this: + + def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A] + +The common example of usage in Scala is this: + + def f[A : ClassTag](n: Int) = new Array[A](n) + +An `Array` initialization on a parameterized type requires a `ClassTag` to +be available, for arcane reasons related to type erasure and the non-erasure +nature of arrays. + +Another very common example in the library is a bit more complex: + + def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b) + +Here, `implicitly` is used to retrive the implicit value we want, one of type +`Ordering[A]`, which class defines the method `compare(a: A, b: A): Int`. + +We'll see another way of doing this below. + +How are Context Bounds implemented? +--------------------------------------------------- + +It shouldn't be surprising that context bounds are +implemented with implicit parameters, given their definition. Actually, the +syntax I showed are syntactic sugars for what really happens. See below how +they de-sugar: + + def g[A : B](a: A) = h(a) + def g[A](a: A)(implicit ev: B[A]) = h(a) + +So, naturally, one can write them in their full syntax, which is specially +useful for context bounds: + + def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b) + +What are Context Bounds used for? +--------------------------------- + +Context bounds are mainly used in what has become known as _typeclass pattern_, +as a reference to Haskell's type classes. Basically, this pattern implements an +alternative to inheritance by making functionality available through a sort of +implicit adapter pattern. + +The classic example is Scala 2.8's `Ordering`. The usage is: + + def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b + +Though you'll usually see that written like this: + + def f[A](a: A, b: A)(implicit ord: Ordering[A]) = { + import ord._ + if (a < b) a else b + } + +Which take advantage of some implicit conversions inside `Ordering` that enable +the traditional operator style. Another example in Scala 2.8 is the `Numeric`: + + def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b) + +A more complex example is the new collection usage of `CanBuildFrom`, but +there's already a very long answer about that, so I'll avoid it here. And, as +mentioned before, there's the `ClassTag` usage, which is required to +initialize new arrays without concrete types. + +Though it has been possible for a long time, the use of context bounds has +really taken off in 2010, and is now found to some degree in most of Scala's +most important libraries and frameworks. The most extreme example of its usage, +though, is the Scalaz library, which brings a lot of the power of Haskell to +Scala. I recommend reading up on typeclass patterns to get more acquainted it +all the ways in which it can be used. + +Related questions of interest: + +* [A discussion on types, origin and precedence of implicits](finding-implicits.html) +* [Chaining implicits](chaining-implicits.html) + +This answer was originally submitted in response to [this question on Stack Overflow][1]. + + [1]: http://stackoverflow.com/q/4465948/53013 + diff --git a/_overviews/tutorials/FAQ/finding-implicits.md b/_overviews/tutorials/FAQ/finding-implicits.md new file mode 100644 index 0000000000..5784fc583a --- /dev/null +++ b/_overviews/tutorials/FAQ/finding-implicits.md @@ -0,0 +1,328 @@ +--- +layout: overview +title: Where does Scala look for implicits? + +discourse: true + +partof: FAQ +num: 7 +--- + +Newcomers to Scala often ask: Where does the compiler look for implicits? + +For example, where do the values for `integral` below come from? + + scala> import scala.math._ + import scala.math._ + + scala> def foo[T](t: T)(implicit integral: Integral[T]): Unit = { + println(integral) + } + foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit + + scala> foo(0) + scala.math.Numeric$IntIsIntegral$@3dbea611 + + scala> foo(0L) + scala.math.Numeric$LongIsIntegral$@48c610af + +The natural continuation of this line of inquiry leads to a second question: How +does the compiler choose which implicit to use, in certain situations of apparent +ambiguity (but that compile anyway)? + +For instance, `scala.Predef` defines two conversions from `String`: one to +`WrappedString` and another to `StringOps`. Both classes, however, share a lot +of methods, so why doesn't Scala complain about ambiguity when, say, calling +`map`? + +**Note:** this question was inspired by [this other question on Stack +Overflow][4], but states the problem in more general terms. The example was +copied from there, because it is referred to in the answer. + +## Types of Implicits + +Implicits in Scala refers to either a value that can be passed "automatically", +so to speak, or a conversion from one type to another that is made +automatically. + +### Implicit Conversion + +Speaking very briefly about the latter type, if one calls a method `m` on an +object `o` of a class `C`, and that class does not support method `m`, then +Scala will look for an implicit conversion from `C` to something that _does_ +support `m`. A simple example would be the method `map` on `String`: + + "abc".map(_.toInt) + +`String` does not support the method `map`, but `StringOps` does, and there's +an implicit conversion from `String` to `StringOps` available (see `implicit +def augmentString` on `Predef`). + +### Implicit Parameters + +The other kind of implicit is the implicit _parameter_. These are passed to +method calls like any other parameter, but the compiler tries to fill them in +automatically. If it can't, it will complain. One _can_ pass these parameters +explicitly, which is how one uses `breakOut`, for example (see question about +`breakOut`, on a day you are feeling up for a challenge). + +In this case, one has to declare the need for an implicit, such as the `foo` +method declaration: + + def foo[T](t: T)(implicit integral: Integral[T]): Unit = { + println(integral) + } + +### Implicit conversions as implicit parameters + +There's one situation where an implicit is both an implicit conversion and an +implicit parameter. For example: + + def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value) + + getIndex("abc", 'a') + +The method `getIndex` can receive any object, as long as there is an implicit +conversion available from its class to `Seq[T]`. Because of that, a `String` can be +passed to `getIndex`, and it will work. + +Behind the scenes, the compiler changes `seq.IndexOf(value)` to +`conv(seq).indexOf(value)`. + +### Context Bounds + +Another common pattern in implicit parameters is the _type class pattern_. This +pattern enables the provision of common interfaces to classes which did not +declare them. It can both serve as a bridge pattern -- gaining separation of +concerns -- and as an adapter pattern. + +The `Integral` class mentioned above is a classic example of type class pattern. +Another example on Scala's standard library is `Ordering`. Scalaz is a library +that makes heavy use of this pattern. + +This is an example of its use: + + def sum[T](list: List[T])(implicit integral: Integral[T]): T = { + import integral._ // get the implicits in question into scope + list.foldLeft(integral.zero)(_ + _) + } + +There is also a syntactic sugar for it, called a _context bound_, which is made +less useful by the need to refer to the implicit. A straight conversion of that +method looks like this: + + def sum[T : Integral](list: List[T]): T = { + val integral = implicitly[Integral[T]] + import integral._ // get the implicits in question into scope + list.foldLeft(integral.zero)(_ + _) + } + +Context bounds are more useful when you just need to _pass_ them to other +methods that use them. For example, the method `sorted` on `Seq` needs an +implicit `Ordering`. To create a method `reverseSort`, one could write: + + def reverseSort[T : Ordering](seq: Seq[T]) = seq.reverse.sorted + +Because `Ordering[T]` was implicitly passed to `reverseSort`, it can then pass +it implicitly to `sorted`. + +## Where do Implicits Come From? + +When the compiler sees the need for an implicit, either because you are calling +a method which does not exist on the object's class, or because you are calling +a method that requires an implicit parameter, it will search for an implicit +that will fit the need. + +This search obeys certain rules that define which implicits are visible and +which are not. The following table showing where the compiler will search for +implicits was taken from an excellent [presentation][1] about implicits by Josh +Suereth, which is heartily recommend to anyone wanting to improve their Scala +knowledge. It has been complemented since then with feedback and updates. + +The implicits available under number 1 below have precedence over the ones under +number 2. Other than that, if there are several eligible arguments which match +the implicit parameter’s type, a most specific one will be chosen using the rules +of static overloading resolution (see [Scala Specification][5] §6.26.4). + +1. First look in current scope + * Implicits defined in current scope + * Explicit imports + * wildcard imports + * Same scope in other files +2. Now look at associated types in + * Companion objects of a type + * Implicit scope of an argument's type **(2.9.1)** + * Implicit scope of type arguments **(2.8.0)** + * Outer objects for nested types + +Let's give examples for them. + +### Implicits Defined in Current Scope + + implicit val n: Int = 5 + def add(x: Int)(implicit y: Int) = x + y + add(5) // takes n from the current scope, res: Int = 10 + +### Explicit Imports + + import scala.collection.JavaConversions.mapAsScalaMap + def env = System.getenv() // Java map + val term = env("TERM") // implicit conversion from Java Map to Scala Map + +### Wildcard Imports + + def sum[T : Integral](list: List[T]): T = { + val integral = implicitly[Integral[T]] + import integral._ // get the implicits in question into scope + list.foldLeft(integral.zero)(_ + _) + } + +### Same Scope in Other Files + +**Edit**: It seems this does not have a different precedence. If you have some +example that demonstrates a precedence distinction, please make a comment. +Otherwise, don't rely on this one. + +This is like the first example, but assuming the implicit definition is in a +different file than its usage. See also how [package objects][2] might be used +in to bring in implicits. + +### Companion Objects of a Type + +There are two object companions of note here. First, the object companion of +the "source" type is looked into. For instance, inside the object `Option` +there is an implicit conversion to `Iterable`, so one can call `Iterable` +methods on `Option`, or pass `Option` to something expecting an `Iterable`. For +example: + + for { + x <- List(1, 2, 3) + y <- Some('x') + } yield (x, y) + +That expression is translated by the compiler into + + List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y))) + +However, `List.flatMap` expects a `TraversableOnce`, which `Option` is not. The +compiler then looks inside `Option`'s object companion and finds the conversion +to `Iterable`, which is a `TraversableOnce`, making this expression correct. + +Second, the companion object of the expected type: + + List(1, 2, 3).sorted + +The method `sorted` takes an implicit `Ordering`. In this case, it looks inside +the object `Ordering`, companion to the class `Ordering`, and finds an implicit +`Ordering[Int]` there. + +Note that companion objects of super classes are also looked into. For example: + + class A(val n: Int) + object A { + implicit def str(a: A) = "A: %d" format a.n + } + class B(val x: Int, y: Int) extends A(y) + val b = new B(5, 2) + val s: String = b // s == "A: 2" + +This is how Scala found the implicit `Numeric[Int]` and `Numeric[Long]` in the +opening example, by the way, as they are found inside `Numeric`, not `Integral`. + +### Implicit scope of an argument's type + +If you have a method with an argument type `A`, then the implicit scope of type +`A` will also be considered. Here "implicit scope" means all these rules +will be applied recursively -- for example, the companion object of `A` will be +searched for implicits, as per the rule above. + +Note that this does not mean the implicit scope of `A` will be searched for +conversions of that parameter alone, but of the whole expression. For example: + + class A(val n: Int) { + def +(other: A) = new A(n + other.n) + } + object A { + implicit def fromInt(n: Int) = new A(n) + } + + // This becomes possible: + 1 + new A(1) + // because it is converted into this: + A.fromInt(1) + new A(1) + +**This available only since Scala 2.9.1.** + +### Implicit scope of type arguments + +This is required to make the type class pattern really work. Consider +`Ordering`, for instance... it comes with some implicits in its companion +object, but you can't add stuff to it. So how can you make an `Ordering` for +your own class that is automatically found? + +Let's start with the implementation: + + class A(val n: Int) + object A { + implicit val ord = new Ordering[A] { + def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n) + } + } + +So, consider what happens when you call + + List(new A(5), new A(2)).sorted + +As we saw, the method `sorted` expects an `Ordering[A]` (actually, it expects +an `Ordering[B]`, where `B >: A`). There isn't any such thing inside +`Ordering`, and there is no "source" type on which to look. Obviously, it is +finding it inside `A`, which is a _type argument_ of `Ordering`. + +This is also how various collection methods expecting `CanBuildFrom` work: the +implicits are found inside companion objects to the type parameters of +`CanBuildFrom`. + +**Note**: `Ordering` is defined as `trait Ordering[T]`, where `T` is a type +parameter. The implicit looked for above is `Ordering[A]`, where +`A` is an actual type, not type parameter: it is a _type argument_ to +`Ordering`. See section 7.2 of the [Scala Specification][6]. + +**This available only since Scala 2.8.0.** + +### Outer Objects for Nested Types + +The principle is simple: + + class A(val n: Int) { + class B(val m: Int) { require(m < n) } + } + object A { + implicit def bToString(b: A#B) = "B: %d" format b.m + } + val a = new A(5) + val b = new a.B(3) + val s: String = b // s == "B: 3" + +A real world example of this would be welcome. Please share your example! + +### Call To Action + +Avoid taking this question as being the final arbiter of what is happening. +If you do notice it has become out-of-date, do [open a ticket about it][7], or, if +you know how to correct it, please fix it. + +Related questions of interest: + +* [Context bounds](context-bounds.html) +* [Chaining implicits](chaining-implicits.html) + +This question and answer were originally submitted on [Stack Overflow][3]. + + [1]: http://jsuereth.com/scala/2011/02/18/2011-implicits-without-tax.html + [2]: https://issues.scala-lang.org/browse/SI-4427 + [3]: http://stackoverflow.com/q/5598085/53013 + [4]: http://stackoverflow.com/questions/5512397/passing-scala-math-integral-as-implicit-parameter + [5]: http://scala-lang.org/files/archive/spec/2.11/06-expressions.html + [6]: http://scala-lang.org/files/archive/spec/2.11/07-implicits.html + [7]: https://github.com/scala/scala.github.com/issues + diff --git a/_overviews/tutorials/FAQ/finding-symbols.md b/_overviews/tutorials/FAQ/finding-symbols.md new file mode 100644 index 0000000000..ea980e95bd --- /dev/null +++ b/_overviews/tutorials/FAQ/finding-symbols.md @@ -0,0 +1,205 @@ +--- +layout: overview +title: How do I find what some symbol means or does? + +discourse: true + +partof: FAQ +num: 1 +outof: 9 +--- +We can divide the operators in Scala, for the purpose of teaching, into four categories: + +* Keywords/reserved symbols +* Normal methods or values +* Methods provided by implicit conversion +* Syntactic sugars/composition + +And let's see some arbitrary examples: + + <- // Keyword + -> // Method provided by implicit conversion + <= // Common method + ++= // Can be a common method or syntactic sugar involving ++ method + :: // Common method or object + _+_ // Not really a single operator; it's parsed as _ + _ + +The exact meaning of most of these methods depends on the class they are defined +on. For example, `<=` on `Int` means _"less than or equal to"_, but it might +mean something else in another class. `::` in an expression is probably the method of the class +`List` but it can also refer to the object of the same name (and in a pattern it +definitely does). + +So, let's discuss these categories. + +Keywords/reserved symbols +------------------------- + +There are a few symbols in Scala that are special and cannot be defined or used as method names. +Two of them are considered proper keywords, while others are just "reserved". They are: + + // Keywords + <- // Used on for-comprehensions, to separate pattern from generator + => // Used for function types, function literals and import renaming + + // Reserved + ( ) // Delimit expressions and parameters + [ ] // Delimit type parameters + { } // Delimit blocks + . // Method call and path separator + // /* */ // Comments + # // Used in type notations + : // Type ascription or context bounds + <: >: // Upper and lower bounds + <% // View bounds (deprecated) + " """ // Strings + ' // Indicate symbols and characters + @ // Annotations and variable binding on pattern matching + ` // Denote constant or enable arbitrary identifiers + , // Parameter separator + ; // Statement separator + _* // vararg expansion + _ // Many different meanings + +These are all _part of the language_, and, as such, can be found in any text +that properly describe the language, such as [Scala Specification][1](PDF) +itself. + +The last one, the underscore, deserve a special description, because it is +widely used, and has different meanings depending on the context. Here's a sample: + + import scala._ // Wild card -- all of Scala is imported + import scala.{ Predef => _, _ } // Exclusion, everything except Predef + def f[M[_]] // Higher kinded type parameter + def f(m: M[_]) // Existential type + _ + _ // Anonymous function placeholder parameter + m _ // Eta expansion of method into method value + m(_) // Partial function application + _ => 5 // Discarded parameter + case _ => // Wild card pattern -- matches anything + f(xs: _*) // Sequence xs is passed as multiple parameters to f(ys: T*) + case Seq(xs @ _*) // Identifier xs is bound to the whole matched sequence + +Common methods +-------------- + +Many symbols are simply methods of a class, a trait, or an object. For instance, if you do + + List(1, 2) ++ List(3, 4) + +You'll find the method `++` right on the Scaladoc for [List][5]. However, +there's one convention that you must be aware when searching for methods. +Methods ending in colon (`:`) bind _to the right_ instead of the left. In other +words, while the above method call is equivalent to: + + List(1, 2).++(List(3, 4)) + +If I had, instead `1 :: List(2, 3)`, that would be equivalent to: + + List(2, 3).::(1) + +So you need to look at the type found _on the right_ when looking for methods +ending in colon. Consider, for instance: + + 1 +: List(2, 3) :+ 4 + +The first method (`+:`) binds to the right, and is found on `List`. The second +method (`:+`) is just a normal method, and binds to the left -- again, on +`List`. + +If the name ends in `=`, look for the method called the same without `=` and +read the last section. + +If you aren't sure what the type of the receiver is, you can look up the symbol +on the Scaladoc [index page for identifiers not starting with letters][2] (for +standard Scala library; of course, third-party libraries can add their own +symbolic methods, for which you should look at the corresponding page of _their_ +Scaladoc). + +Types and objects can also have symbolic names; in particular, it should be mentioned +that for types with two type parameters the name can be written _between_ parameters, +so that e.g. `Int <:< Any` is the same as `<:<[Int, Any]`. + +Methods provided by implicit conversion +--------------------------------------- + +If you did not find the symbol you are looking for in the list of reserved symbols, then +it must be a method, or part of one. But, often, you'll see some symbol and the +documentation for the class will not have that method. When this happens, +either you are looking at a composition of one or more methods with something +else, or the method has been imported into scope, or is available through an +imported implicit conversion. + +These can also be found in Scaladoc's [index][2], as mentioned above. + +All Scala code has three automatic imports: + + // Not necessarily in this order + import java.lang._ + import scala._ + import scala.Predef._ + +The first two only make classes and singleton objects available, none of which +look like operators. [`Predef`][3] is the only interesting one for this post. + +Looking inside `Predef` shows some symbolic names: + + class <:< + class =:= + object =:= + object <%< // removed in Scala 2.10 + def ??? + +There is also `::`, which doesn't appear in the Scaladoc, but is mentioned in the comments. +In addition, `Predef` makes some methods available through _implicit conversions_. Just +look at the methods and classes with `implicit` modifier that receive, as parameter, an +object of type that is receiving the method. For example, consider `"a" -> 1`. We need +to look for an implicit which works on `"a"`, and so it can take `String`, one of its +supertypes (`AnyRef` or `Any`) or a type parameter. In this case, we find +`implicit final class ArrowAssoc[A](private val self: A)` which makes this implicit +avaialable on all types. + +Other implicit conversions may be visible in your scope depending on imports, extended types or +self-type annotations. See [Finding implicits](tutorials/FAQ/finding-implicits.html) for details. + +Syntactic sugars/composition +----------------------------- + +So, here's a few syntactic sugars that may hide a method: + + class Example(arr: Array[Int] = Array.fill(5)(0)) { + def apply(n: Int) = arr(n) + def update(n: Int, v: Int) = arr(n) = v + def a = arr(0); def a_=(v: Int) = arr(0) = v + def b = arr(1); def b_=(v: Int) = arr(1) = v + def c = arr(2); def c_=(v: Int) = arr(2) = v + def d = arr(3); def d_=(v: Int) = arr(3) = v + def e = arr(4); def e_=(v: Int) = arr(4) = v + def +(v: Int) = new Example(arr map (_ + v)) + def unapply(n: Int) = if (arr.indices contains n) Some(arr(n)) else None + } + + val ex = new Example + println(ex(0)) // means ex.apply(0) + ex(0) = 2 // means ex.update(0, 2) + ex.b = 3 // means ex.b_=(3) + val ex(c) = 2 // calls ex.unapply(2) and assigns result to c, if it's Some; throws MatchError if it's None + ex += 1 // means ex = ex + 1; if Example had a += method, it would be used instead + +The last one is interesting, because *any* symbolic method can be combined with `=` in that way. + +And, of course, all of the above can be combined in various combinations, e.g. + + (_+_) // An expression, or parameter, that is an anonymous function with + // two parameters, used exactly where the underscores appear, and + // which calls the "+" method on the first parameter passing the + // second parameter as argument. + +This answer was originally submitted in response to [this question on Stack Overflow][6]. + + [1]: http://scala-lang.org/files/archive/spec/2.11/ + [2]: http://www.scala-lang.org/api/current/index.html#index.index-_ + [3]: http://www.scala-lang.org/api/current/index.html#scala.Predef$ + [4]: http://www.scala-lang.org/api/current/scala/Predef$$ArrowAssoc.html + [5]: http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List + [6]: http://stackoverflow.com/q/7888944/53013 diff --git a/_overviews/tutorials/FAQ/initialization-order.md b/_overviews/tutorials/FAQ/initialization-order.md new file mode 100644 index 0000000000..57374e4352 --- /dev/null +++ b/_overviews/tutorials/FAQ/initialization-order.md @@ -0,0 +1,164 @@ +--- +layout: overview +title: Why is my abstract or overridden val null? + +discourse: true + +partof: FAQ +num: 9 +--- + +## Example +To understand the problem, let's pick the following concrete example. + + abstract class A { + val x1: String + val x2: String = "mom" + + println("A: " + x1 + ", " + x2) + } + class B extends A { + val x1: String = "hello" + + println("B: " + x1 + ", " + x2) + } + class C extends B { + override val x2: String = "dad" + + println("C: " + x1 + ", " + x2) + } + +Let's observe the initialization order through the Scala REPL: + + scala> new C + A: null, null + B: hello, null + C: hello, dad + +Only when we get to the constructor of `C` are both `x1` and `x2` initialized. Therefore, constructors of `A` and `B` risk running into `NullPointerException`s. + +## Explanation +A 'strict' or 'eager' val is one which is not marked lazy. + +In the absence of "early definitions" (see below), initialization of strict vals is done in the following order. + +1. Superclasses are fully initialized before subclasses. +2. Otherwise, in declaration order. + +Naturally when a val is overridden, it is not initialized more than once. So though x2 in the above example is seemingly defined at every point, this is not the case: an overridden val will appear to be null during the construction of superclasses, as will an abstract val. + +There is a compiler flag which can be useful for identifying this situation: + +**-Xcheckinit**: Add runtime check to field accessors. + +It is inadvisable to use this flag outside of testing. It adds significantly to the code size by putting a wrapper around all potentially uninitialized field accesses: the wrapper will throw an exception rather than allow a null (or 0/false in the case of primitive types) to silently appear. Note also that this adds a *runtime* check: it can only tell you anything about code paths which you exercise with it in place. + +Using it on the opening example: + + % scalac -Xcheckinit a.scala + % scala -e 'new C' + scala.UninitializedFieldError: Uninitialized field: a.scala: 13 + at C.x2(a.scala:13) + at A.(a.scala:5) + at B.(a.scala:7) + at C.(a.scala:12) + +### Solutions ### + +Approaches for avoiding null values include: + +#### Use lazy vals #### + + abstract class A { + val x1: String + lazy val x2: String = "mom" + + println("A: " + x1 + ", " + x2) + } + class B extends A { + lazy val x1: String = "hello" + + println("B: " + x1 + ", " + x2) + } + class C extends B { + override lazy val x2: String = "dad" + + println("C: " + x1 + ", " + x2) + } + // scala> new C + // A: hello, dad + // B: hello, dad + // C: hello, dad + +Usually the best answer. Unfortunately you cannot declare an abstract lazy val. If that is what you're after, your options include: + +1. Declare an abstract strict val, and hope subclasses will implement it as a lazy val or with an early definition. If they do not, it will appear to be uninitialized at some points during construction. +2. Declare an abstract def, and hope subclasses will implement it as a lazy val. If they do not, it will be re-evaluated on every access. +3. Declare a concrete lazy val which throws an exception, and hope subclasses override it. If they do not, it will... throw an exception. + +An exception during initialization of a lazy val will cause the right hand side to be re-evaluated on the next access: see SLS 5.2. + +Note that using multiple lazy vals creates a new risk: cycles among lazy vals can result in a stack overflow on first access. + +#### Use early definitions #### + abstract class A { + val x1: String + val x2: String = "mom" + + println("A: " + x1 + ", " + x2) + } + class B extends { + val x1: String = "hello" + } with A { + println("B: " + x1 + ", " + x2) + } + class C extends { + override val x2: String = "dad" + } with B { + println("C: " + x1 + ", " + x2) + } + // scala> new C + // A: hello, dad + // B: hello, dad + // C: hello, dad + +Early definitions are a bit unwieldy, there are limitations as to what can appear and what can be referenced in an early definitions block, and they don't compose as well as lazy vals: but if a lazy val is undesirable, they present another option. They are specified in SLS 5.1.6. + +#### Use constant value definitions #### + abstract class A { + val x1: String + val x2: String = "mom" + + println("A: " + x1 + ", " + x2) + } + class B extends A { + val x1: String = "hello" + final val x3 = "goodbye" + + println("B: " + x1 + ", " + x2) + } + class C extends B { + override val x2: String = "dad" + + println("C: " + x1 + ", " + x2) + } + abstract class D { + val c: C + val x3 = c.x3 // no exceptions! + println("D: " + c + " but " + x3) + } + class E extends D { + val c = new C + println(s"E: ${c.x1}, ${c.x2}, and $x3...") + } + //scala> new E + //D: null but goodbye + //A: null, null + //B: hello, null + //C: hello, dad + //E: hello, dad, and goodbye... + +Sometimes all you need from an interface is a compile-time constant. + +Constant values are stricter than strict and earlier than early definitions and have even more limitations, +as they must be constants. They are specified in SLS 4.1. diff --git a/_overviews/tutorials/FAQ/stream-view-iterator.md b/_overviews/tutorials/FAQ/stream-view-iterator.md new file mode 100644 index 0000000000..6c9413474b --- /dev/null +++ b/_overviews/tutorials/FAQ/stream-view-iterator.md @@ -0,0 +1,46 @@ +--- +layout: overview +title: What is the difference between view, stream and iterator? + +discourse: true + +partof: FAQ +num: 4 +--- +First, they are all _non-strict_. That has a particular mathematical meaning +related to functions, but, basically, means they are computed on-demand instead +of in advance. + +`Stream` is a lazy list indeed. In fact, in Scala, a `Stream` is a `List` whose +`tail` is a `lazy val`. Once computed, a value stays computed and is reused. +Or, as you say, the values are cached. + +An `Iterator` can only be used once because it is a _traversal pointer_ into a +collection, and not a collection in itself. What makes it special in Scala is +the fact that you can apply transformation such as `map` and `filter` and +simply get a new `Iterator` which will only apply these transformations when +you ask for the next element. + +Scala used to provide iterators which could be reset, but that is very hard to +support in a general manner, and they didn't make version 2.8.0. + +Views are meant to be viewed much like a database view. It is a series of +transformation which one applies to a collection to produce a "virtual" +collection. As you said, all transformations are re-applied each time you need +to fetch elements from it. + +Both `Iterator` and views have excellent memory characteristics. `Stream` is +nice, but, in Scala, its main benefit is writing infinite sequences +(particularly sequences recursively defined). One _can_ avoid keeping all of +the `Stream` in memory, though, by making sure you don't keep a reference to +its `head` (for example, by using `def` instead of `val` to define the +`Stream`). + +Because of the penalties incurred by views, one should usually `force` it after +applying the transformations, or keep it as a view if only few elements are +expected to ever be fetched, compared to the total size of the view. + +This answer was originally submitted in response to [this question on Stack Overflow][1]. + + [1]: http://stackoverflow.com/q/5159000/53013 + diff --git a/_overviews/tutorials/FAQ/yield.md b/_overviews/tutorials/FAQ/yield.md new file mode 100644 index 0000000000..cb579ec155 --- /dev/null +++ b/_overviews/tutorials/FAQ/yield.md @@ -0,0 +1,158 @@ +--- +layout: overview +title: How does yield work? + +discourse: true + +partof: FAQ +num: 2 +--- +Though there's a `yield` in other languages such as Python and Ruby, Scala's +`yield` does something very different from them. In Scala, `yield` is part +of for comprehensions -- a generalization of Ruby and Python's list-comprehensions. + +Scala's "for comprehensions" are equivalent to Haskell's "do" notation, and it +is nothing more than a syntactic sugar for composition of multiple monadic +operations. As this statement will most likely not help anyone who needs help, +let's try again... + +Translating for-comprehensions +------------------------------ + +Scala's "for comprehensions" are syntactic sugar for composition of multiple +operations with `foreach`, `map`, `flatMap`, `filter` or `withFilter`. +Scala actually translates a for-expression into calls to those methods, +so any class providing them, or a subset of them, can be used with for comprehensions. + +First, let's talk about the translations. There are very simple rules: + +#### Example 1 + + for(x <- c1; y <- c2; z <-c3) {...} + +is translated into + + c1.foreach(x => c2.foreach(y => c3.foreach(z => {...}))) + +#### Example 2 + + for(x <- c1; y <- c2; z <- c3) yield {...} + +is translated into + + c1.flatMap(x => c2.flatMap(y => c3.map(z => {...}))) + +#### Example 3 + + for(x <- c; if cond) yield {...} + +is translated into + + c.withFilter(x => cond).map(x => {...}) + +with a fallback into + + c.filter(x => cond).map(x => {...}) + +if method `withFilter` is not available but `filter` is. +The next chapter has more information on this. + +#### Example 4 + + for(x <- c; y = ...) yield {...} + +is translated into + + c.map(x => (x, ...)).map((x,y) => {...}) + + +When you look at very simple for comprehensions, the map/foreach alternatives +look, indeed, better. Once you start composing them, though, you can easily get +lost in parenthesis and nesting levels. When that happens, for comprehensions +are usually much clearer. + +I'll show one simple example, and intentionally omit any explanation. You can +decide which syntax is easier to understand. + + l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length)) + +or + + for{ + sl <- l + el <- sl + if el > 0 + } yield el.toString.length + + +About withFilter, and strictness +---------------------------------- + +Scala 2.8 introduced a method called `withFilter`, whose main difference is +that, instead of returning a new, filtered, collection, it filters on-demand. +The `filter` method has its behavior defined based on the strictness of the +collection. To understand this better, let's take a look at some Scala 2.7 with +`List` (strict) and `Stream` (non-strict): + + scala> var found = false + found: Boolean = false + + scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) + 1 + 3 + 7 + 9 + + scala> found = false + found: Boolean = false + + scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) + 1 + 3 + +The difference happens because filter is immediately applied with `List`, +returning a list of odds -- since `found` is `false`. Only then `foreach` is +executed, but, by this time, changing `found` is meaningless, as `filter` has +already executed. + +In the case of `Stream`, the condition is not immediatelly applied. Instead, as +each element is requested by `foreach`, `filter` tests the condition, which +enables `foreach` to influence it through `found`. Just to make it clear, here +is the equivalent for-comprehension code: + + for (x <- List.range(1, 10); if x % 2 == 1 && !found) + if (x == 5) found = true else println(x) + + for (x <- Stream.range(1, 10); if x % 2 == 1 && !found) + if (x == 5) found = true else println(x) + +This caused many problems, because people expected the `if` to be considered +on-demand, instead of being applied to the whole collection beforehand. + +Scala 2.8 introduced `withFilter`, which is _always_ non-strict, no matter the +strictness of the collection. The following example shows `List` with both +methods on Scala 2.8: + + scala> var found = false + found: Boolean = false + + scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) + 1 + 3 + 7 + 9 + + scala> found = false + found: Boolean = false + + scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) + 1 + 3 + +This produces the result most people expect, without changing how `filter` +behaves. As a side note, `Range` was changed from non-strict to strict between +Scala 2.7 and Scala 2.8. + +This answer was originally submitted in response to [this question on Stack Overflow][1]. + + [1]: http://stackoverflow.com/questions/1052476/can-someone-explain-scalas-yield/1052510#1052510 diff --git a/_overviews/tutorials/index.md b/_overviews/tutorials/index.md new file mode 100644 index 0000000000..d4ae517d70 --- /dev/null +++ b/_overviews/tutorials/index.md @@ -0,0 +1,47 @@ +--- +layout: overview +title: Tutorials +--- + +

    + +
    +

    New to Scala?

    +

    Tutorials geared for people coming...

    + +
    + +
    +

    FAQ

    Frequently Asked Questions (and their answers!)

    +
    + {% for pg in site.pages %} + {% if pg.partof == "FAQ" and pg.outof %} + {% assign totalPagesFAQ = pg.outof %} + {% endif %} + {% endfor %} + + {% if totalPagesFAQ %} +
      + {% for i in (1..totalPagesFAQ) %} + {% for pg in site.pages %} + {% if pg.partof == "FAQ" and pg.num and pg.num == i %} +
    • {{ pg.title }}
    • + {% endif %} + {% endfor %} + {% endfor %} +
    + {% else %} **ERROR**. Couldn't find the total number of pages in this set of tutorial articles. Have you declared the `outof` tag in your YAML front matter? + {% endif %} + +
    + +
    +
    +

    A Tour of Scala

    Bite-size pieces of the essentials...

    +
    + {% include tutorial-tour-list.txt %} +
    diff --git a/_overviews/tutorials/scala-for-csharp-programmers.disabled.html b/_overviews/tutorials/scala-for-csharp-programmers.disabled.html new file mode 100644 index 0000000000..593f74779e --- /dev/null +++ b/_overviews/tutorials/scala-for-csharp-programmers.disabled.html @@ -0,0 +1,1237 @@ +--- +layout: overview +title: A Scala Tutorial for C# Programmers + +discourse: true +--- + +By Ivan Towlson + +## Introduction + +Scala is a hybrid of functional and object-oriented languages. +Its functional aspects make it very expressive when writing algorithmic +code, and play nicely with the brave new world of concurrency; its +object-oriented aspects keep it familiar and convenient when creating +business objects or other stateful models. + +## The same concepts + +Scala shares many features and concepts with C#, as well as introducing +many that are new. In fact, some of the capabilities that people talk +about a lot in Scala introductions or Java-to-Scala guides are very +familiar to C# programmers. For example, as a C# programmer, you don't +need to be gently walked through the idea of function types -- you've +been using delegates and lambdas for years. + +However, some Scala features and behaviours are quite different from C#, +and even those which are common usually have different syntax or work +in slightly different ways. So let's start out by covering some Scala +basics from a C# programmer's point of view. + +### Classes + +The basic concept of classes is the same in Scala as in C#. A class +bundles up a bunch of state (member fields) and hides it behind an +interface (member methods). The syntax for declaring classes is just +like C#: + + class Widget { + } + +### Fields + +The syntax for declaring fields is like this: + + class Widget { + val serialNumber = 123 + private var usageCount = 0 + } + +You can probably figure out what's going on here, but let's just note +a few differences from C#: + +* Fields are public by default, rather than private by default. +* You don't need to put a semicolon after a field declaration. You can + write semicolons if you want (or if your muscle memory makes you), + but if you don't, Scala will figure it out for you. +* Scala automatically figures out the type of the field from its initial + value, just like the C# `var` keyword. (Don't be fooled by the appearance + of the second field though -- the Scala `var` keyword doesn't mean the + same thing as the C# `var` keyword.) + +Now, why is one of these fields introduced as `val` and the other as +`var`? In C#, fields are mutable by default. That is, by default, +any code that can access a field can modify it. You can specify *readonly* +to make a field immutable, so that it can't be changed once the object +has been constructed. + +Scala doesn't have a default setting for mutability. You have to engage +brain, and decide whether each field is mutable or immutable. `val` means +a field is immutable (readonly in C#) and `var` means it's mutable +(a normal field in C#). + +So the class above is equivalent to the C#: + + class Widget { + public readonly int serialNumber = 123; + private int usageCount = 0; + } + +Notice that C# makes you write extra code to make things immutable, +and Scala doesn't. This may seem like a small thing, but it's +going to be a really important theme. + +### Methods + +The syntax for declaring methods is like this: + + class Widget { + def reticulate(winding: Int): String = "some value" + } + +This is a more dramatic departure from C# so let's pick it apart. + +The `def` keyword means we're declaring a method rather than a field. +There isn't a direct equivalent to this in C#, which figures out whether +something is a method or a field from context. As with fields, +methods are public by default. + +The method name is reassuringly language-independent. + +Method arguments go in brackets after the method name, just as in C#. +However, the way Scala specifies the argument types is different from C#. +In C# you would write `int winding`; in Scala you write `winding: Int`. + +This is a general principle: in Scala, when you want to specify the +type of something, you write the type after the something, separated +by a colon. (Whereas in C# you write the type before the something, +separated by a space.) + +You can see the same principle in action for the return type of the +function. `reticulate` returns a `String`. + +Finally, the body of the method has been placed after an equals sign, +rather than inside braces. (Braces are only necessary when the method +body consists of multiple expressions/statements.) What's more, +the body of the method is an expression -- that +is, something with a value -- rather than a set of statements amongst which +is a `return`. I'll come back to this when we look at a more realistic +example, but for the time being, let's focus on the trivial example and +translate it into C# syntax: + + class Widget { + public string Reticulate(int winding) { + return "some value"; + } + } + +### Classes and Structs + +In C#, when you define a type, you can decide whether to make it a +reference type (a class) or a value type (a struct). Scala doesn't +allow you to create custom value types. It has only classes, not +structs. This restriction comes from Scala targeting the Java +Virtual Machine. Unlike .NET, the JVM doesn't really have the concept +of value types. Scala tries to disguise this as best it can, but the +lack of custom value types is one place where the implementation +leaks through. Fortunately, Scala makes it easy to define immutable +reference types, which are nearly as good. + +### Base Types + +Scala's base types are pretty much the same as C#'s, except that they +are named with initial capitals, e.g. `Int` instead of `int`. (In fact +every type in Scala starts with an uppercase letter.) There are +no unsigned variants, and booleans are called `Boolean` instead of `bool`. + +Scala's name for `void` is `Unit`, but unlike `void`, `Unit` is a real type. +We'll see why this is important in a moment. + +### Function Types + +In C#, you can have variables that refer to functions instead of data. +These variables have delegate types, such as *Predicate` or +`Func` or `KeyEventHandler` or `Action`. + +Scala has the same concept, but the function types are built into the +language, rather than being library types. Function types are +spelled `(T1, T2, ...) => TR`. For example, a predicate of integers +would be type `(Int) => Boolean`. If there is only one input type, +the parens can be left out like this: `Int => Boolean`. + +Effectively, Scala gets rid of all those weird custom delegate types +like `Predicate` and `Comparer` and has a single family of +function types like the C# `Func<...>` family. + +What if you want to refer to a method that doesn't have a return value? +In C#, you can't write `Func` because void isn't a valid +type; you have to write `Action` instead. But Scala doesn't have +different families of delegate types, it just has the built-in +function types. Fortunately, in Scala, `Unit` is a real type, so you +can write `(Int) => Unit` for a function which takes an integer +and doesn't return a useful value. This means you can pass `void` methods +interchangeably with non-`void` methods, which is a Good Thing. + +### Implementing Methods + +I showed a method above, but only to illustrate the declaration syntax. +Let's take a look at a slightly more substantial method. + + def power4(x: Int): Int = { + var square = x * x // usually wouldn't write this - see below + square * square + } + +This looks pretty similar to C#, except that: + +* We're allowed to leave out semicolons, as mentioned above. +* We don't need a return statement. The method body consists of + an expression, square * square, with some preliminary bits + to define the components of the expression. + The return value of the method is the value of the expression. + +In order to make this method look like C#, I used the `var` keyword +to declare the local variable `square`. But as with fields, the +Scala `var` keyword doesn't work quite the same as the C# `var` keyword: + +In C#, `var` means "work out the type of the variable from the +right hand side". But Scala does that automatically -- you don't +need to ask for it. In Scala, `var` means "allow me to change this +variable (or field) after initialisation". As with fields, you can +also use `val` to mean 'don't allow me to change this variable +after initialisation.' And in fact since we don't need to change +`square` after initialisation, we'd be better off using val: + + def power4(x: Int): Int = { + val square = x * x // val not var + square * square + } + +Incidentally, if you do ever want to explicitly indicate the type +of a variable, you can do it the same way as with function arguments: + + def power4(x: Int): Int = { + val square : Int = x * x + square * square + } + +Notice that you still need `val` or `var`. Telling Scala the type +of the variable is independent of deciding whether the variable should +be mutable or immutable. + +### Tuples + +Everybody hates out parameters. We all know that code like this just +isn't nice: + + Widget widget; + if (widgetDictionary.TryGetValue(widgetKey, out widget)) + { + widget.ReticulateSplines(); + } + +And once you start composing higher-level functions into the mix, it gets +positively nasty. Suppose I want to make a HTTP request. Well, that's +going to produce two outputs in itself, the success code and the response +data. But now suppose I want to time it. Writing the timing code isn't a +problem, but now I have *three* outputs, and to paraphrase *Was Not Was*, +I feel worse than Jamie Zawinski. + +You can get around this in specific situations by creating custom types +like `DictionaryLookupResult` or `TimedHttpRequestResult`, but eventually +the terrible allure wears off, and you just want it to work. + +Enter tuples. A tuple is just a small number of values -- a single value, +a pair of values, a triplet of values, that sort of thing. You can tell +that tuples were named by mathematicians because the name only makes sense +when you look at the cases you hardly ever use (quadruple, quintuple, +sextuple, etc.). Using tuples, our timed HTTP request might look like this: + + public Tuple Time(Func> action) + { + StartTimer(); + var result = action(); + TimeSpan howLong = StopTimer(); + return Tuple.Create(result.First, result.Second, howLong); + } + + var result = Time(() => MakeRequest(uri)); + var succeeded = result.First; + var response = result.Second; + var howLong = result.Third. + Console.WriteLine("it took " + howLong); + +The reason this keeps getting verbose on us is that C# doesn’t provide any +syntatical support for tuples. To C#, a `Tuple<>` is just another generic +type. To us, the readers, a `Tuple<>` is just another generic type with +particularly unhelpful member names. + +Really, what we're really trying to articulate by returning a `Tuple<>` is, +"this method has several outputs." So what do we want to do with those +outputs? We want to access them, for example to stash them in variables, +without having to construct and pick apart the tuple one value at a time. +That means the language has to provide some kind of syntax-level support +for tuples, instead of treating them like every other class the compiler +doesn’t know about. + +Many functional languages have exactly this kind of syntactical support, +and Scala is no exception. Here’s how the above pseudo-C# looks in Scala: + + def time(action: => (Boolean, Stream)): (Boolean, Stream, TimeSpan) = { + startTimer() + val (succeeded, response) = action + (succeeded, response, stopTimer()) + } + + val (succeeded, response, timeTaken) = time(makeRequest) + Console.WriteLine("it took " + timeTaken) + +Notice the multiple variables on the left hand side of the time call? +Notice the `(Boolean, Stream, TimeSpan)` return type of the time method? +That return value is effectively a tuple type, but instead of having to +capture the returned tuple in a `Tuple<>` variable and extract its various +bits by hand, we are getting the Scala compiler to (in the time function) +compose the tuple implicitly for us, without us having to write the +constructor call, and (in the calling code) unpick the tuple into +meaningful, named variables for us without us having to explicitly copy +the values one by one and by name. + +(By the way, a proper implementation of the `time()` method wouldn’t be +restricted to `(Boolean, Stream)` results: we’d be looking to write a +method that could time anything. I’ve skipped that because it would +distract from the point at hand.) + +How would this play with the dictionary example? + + val (found, widget) = widgetDictionary.getValue(key) + if (found) + widget.reticulateSplines() + +We don’t actually save any lines of code, what with having to now capture +the “found” value into a variable and test it separately; and it’s not as +if the original C# version was horribly unreadable anyway. So maybe this is +a matter of taste, but I find this a lot easier to read and to write: all +the outputs are on the left of the equals sign where they belong, instead +of being spread between the assignment result and the parameter list, and +we don’t have that odd Widget declaration at the top. + +## New and different concepts + +Scala's primary platform is the Java virtual machine, and some of the +interest in Scala comes from Java programmers' interest in features such +as type inference, comprehensions and lambdas, with which C# programmers +are already familiar. So what's left that might be of interest +specifically to C# programmers? + +### Mixins and Traits + +#### Motivation + +Interfaces in C# and Java play a dual role. +First, they are a set of capabilities that an implementer has to, well, +implement. Second, they are a feature set that a client can use. + +These two roles can be conflicting: The first means that interfaces want +to be minimal, so that implementers don't have to implement a whole lot +of superfluous and redundant guff. The second means that interfaces want +to be maximal, so that clients don't have to clog themselves up with +boilerplate utility methods. + +Consider, for example, `IEnumerable` (and its sister interface `IEnumerator`). +This is a very minimal interface: implementers just need to be able to +produce values in sequence. But this minimalism means that clients of +`IEnumerable` need to write the same old boilerplate again and again and +again: foreach loops to filter, foreach loops to call a method on each +element of the sequence, foreach loops to aggregate, foreach loops to +check whether all elements meet a criterion, or to find the first member +that meets a criterion, or... + +This is frustrating because the implementations of "filter," "apply", +"aggregate," and so on are always the same. Of course, we could put +these methods into concrete types (`List` includes several), but then +those concrete types will contain duplicate code, and users who only have +an `IEnumerable` will still miss out. And yet we can't put these methods +into the interface because then every implementer of `IEnumerable` would +have to implement them -- and they'd end up writing the same boilerplate, +just now in all the zillions of `IEnumerable` classes instead of their clients. + +#### The C# and Scala Solutions + +We could resolve this tension if we had a way for interfaces to contain +implementation: for example, if `IEnumerable` required the implementer +to provide the class-specific iteration functionality, but then provided +the standard implementations of "filter," "apply", "aggregate" and so on +automatically: + + public pseudo_interface IEnumerable + { + IEnumerator GetEnumerator(); // must be implemented + IEnumerable Filter(Predicate predicate) // comes for free + { + foreach (object o in this) + if (predicate(o)) + yield return o; + } + } + +C# 3 addresses this using extension methods: the methods mentioned above +are all in fact included as extension methods on `IEnumerable` as +part of LINQ. + +This has some advantages over the approach described above: specifically, +the "standard methods" aren't bound up in the interface, so you can add +your own methods instead of being limited to the ones that the interface +author has included. + +On the other hand, it means that method implementations have to be packaged +in a different class from the interface, which feels less than modular. + +Scala takes a different approach. A Scala trait can contain a mix of +abstract methods without implementation as well as concrete methods with +an implementation. (It can also be a pure interface, with only abstract +members.) + +Here's a Scala trait that represents objects that can be compared +and ordered: + + trait Ord { + def < (that: Any): Boolean + def <=(that: Any): Boolean = (this < that) || (this == that) + def > (that: Any): Boolean = !(this <= that) + def >=(that: Any): Boolean = !(this < that) + } + +Orderable objects can extend `Ord`, but only need to implement the +method `<`. They then get the other operators for free, implemented +automatically by Ord in terms of `<`. + + class Date extends Ord { + def < (that: Any): Boolean = /* implementation */ + } + + // can now write: myDate >= yourDate + +A similar trait, called `Ordered` already exists in Scala, so there is no +need to actually define my trait. + +#### Scala Traits vs. C# Extension Methods + +Okay, so Scala has a different way of packaging standard implementations +from C#'s extension methods. It's different, but why is it interesting? +Well, there are a couple of things that you can do with Scala traits that +don't fall nicely out of the extension methods approach. + +First, you can override the default implementations of trait members, +to take advantage of additional information or capabilities available +in the implementing type. + +Let's look at another `IEnumerable` example, recast as a Scala trait: + + trait Enumerable { + def getEnumerator(): Enumerator + def count: Int = { + // Shockingly bad style; for illustrative purposes only + var c = 0 + val e = getEnumerator() + while (e.moveNext()) c = c + 1 + c + } + } + +This (ignoring style issues for now) is the only fully general +implementation we can provide for count: it will work with any `Enumerable`. +But for collections that know their sizes, such as `arrays` or `List`, +it's gruesomely inefficient. It iterates over the entire collection, +counting elements one by one, when it could just consult the `size` member +and return that. + +Let's fix that: + + class MyList extends Enumerable { + private var _size; + def getEnumerator(): Enumerator = /* ... */ + override def count: Int = _size + } + +The `count` member of the `Enumerable` trait works like a virtual method: +it can be overridden in classes which implement/derive from `Enumerable`. + +Compare this to the `Count()` extension method on `IEnumerable` in LINQ. +This achieves the same effect by trying to cast to `ICollection`, which is +fine as far as it goes but isn't extensible. + +Suppose you create an enumerable class that can count itself quickly but +isn't a collection -- for example a natural numbers range object. +With a Scala trait, the `NumberRange` type could provide an efficient +override of `count`, just like any other virtual method; with C# extension +methods, `Enumerable.Count()` would have to somehow know about the +`NumberRange` type in advance, or fall back on counting elements one by one. + +Second, with Scala you can choose a trait implementation when you +instantiate an object, rather than having it baked in at the class level +once and for all. This is called mixin-composition. + +Suppose you're creating a `MyList` instance, but you want it to puff itself +up to look bigger so as to frighten other `MyList` instances off its territory. +(This example would probably work better with fish, but we're stuck with +`Enumerable`s now. Work with me here.) In C#, you'd need to create a +`PuffedUpMyList` class and override the `Count` property. +In Scala, you can just mix in a `PuffedUp` version of the trait: + + trait PuffedUp extends Enumerable { + override def count: Int = super.count + 100 + } + + val normal = new MyList + Console.WriteLine(normal.count) // meh + val puffedUp = new MyList with PuffedUp + Console.WriteLine(puffedUp.count) // eek! + +As you can imagine this gives us much better granularity and composability +of traits and implementations than we get from the extension methods +approach, or indeed from single implementation inheritance type systems +in general. + +So Scala traits have some distinct advantages over extension methods. +The only downside appears to be the inability for clients to add their +own methods to a trait after the fact. + +Fortunately, you can work around this in Scala using so-called implicit +conversions. They enable Scala programmers to enrich existing types with new +functionality. + +### Singletons + +In C#, if you want to create a singleton object, you have to create a class, +then stop evildoers creating their own instances of that class, then create +and provide an instance of that class yourself. + +While this is hardly a Burma Railway of the programming craft, it does +feel like pushing against the grain of the language. Nor is it great for +maintainers, who have to be able to recognise a singleton by its pattern +(private constructor, public static readonly field, ...), or for clients, +who have to use a slightly clumsy multipart syntax to refer to the +singleton (e.g. `Universe.Instance`). + +What would be easier for all concerned would be if you could just declare +objects *as* singletons. That is, instead of writing class `Universe` and a +`public static readonly` instance of it, you could just write `object Universe`. + +And that's exactly what Scala allows you to do: + + object Universe { + def contains(obj: Any): Boolean = true + } + + val v = Universe.contains(42) + +What's going on behind the scenes here? It pretty much goes without saying +that the Scala compiler is creating a new type for the singleton object. +In fact it creates two types, one for the implementation and one for the +interface. The interface looks like a .NET static class (actually, the +.NET 1.x equivalent, a sealed class with only static members). +Thus, a C# program would call the example above as `Universe.contains(42)`. + +Singleton objects are first-class citizens in Scala, so they can for +example derive from classes. This is a nice way of creating special values +with custom behaviour: you don't need to create a whole new type, you just +define an instance and override methods in it: + + abstract class Cat { + def humiliateSelf() + } + + object Slats extends Cat { + def humiliateSelf() { savage(this.tail) } + } + +Obviously this is a frivolous example, but "special singletons" turn out to +be an important part of the functional idiom, for example for bottoming out +recursion. *Scala by Example (PDF)* describes an implementation of a Set class +which is implemented as a tree-like structure ("left subset - member - right +subset"), and methods such as `contains()` work by recursing down to the +child sets. For this to work requires an `EmptySet` whose implementation +(state) and behaviour are quite different from non-empty sets -- e.g. +`contains()` just returns `false` instead of trying to delegate to +non-existent child sets. Since `EmptySet` is logically unique it is both +simpler and more efficient to represent it as a singleton: i.e. to declare +`object EmptySet` instead of `class EmptySet`. + +In fact the whole thing can become alarmingly deep: *Scala by Example* +also includes a description of `Boolean` as an `abstract class`, and +`True` and `False` as singleton objects which extend `Boolean` and provide +appropriate implementations of the `ifThenElse` method. + +And fans of Giuseppe Peano should definitely check out the hypothetical +implementation of `Int`... + +### Pass by Name + +> You're only on chapter 3 and you're already reduced to writing about +> *calling conventions*? You suck! Do another post about chimney sweeps +> being hunted by jars of marmalade!" + +Silence, cur. Pass by name is not as other calling conventions are. +Pass by name, especially in conjunction with some other rather +theoretical-sounding Scala features, is your gateway to the wonderful +world of language extensibility. + +#### What is Passing By Name? + +First, let's talk about what we mean by *calling convention*. A calling +convention describes how stuff gets passed to a method by its caller. +In the good old days, this used to mean exciting things like which +arguments got passed in registers and who was responsible for resetting +the stack pointer. Sadly, the days of being able to refer to "naked fun +calls" are consigned to history: In modern managed environments, the +runtime takes care of all this guff and the main distinction is pass +data by value or by reference. (The situation on the CLR is slightly +complicated by the need to differentiate passing values by value, values +by reference, references by value and references by reference, but I'm +not going to go into that because (a) it's irrelevant to the subject at +hand and (b) that's +[Jon Skeet](http://www.yoda.arachsys.com/csharp/parameters.html)'s turf +and I don't want him to shank me. Again.) + +In *pass by value*, the called method gets a copy of whatever the caller +passed in. Arguments passed by value therefore work like local variables +that are initialised before the method runs: when you do anything to them, +you're doing it to your own copy. + +In *pass by reference*, the called method gets a reference to the caller's +value. When you do anything to a pass-by-reference argument, you're doing +it to the caller's data. + +In *pass by name*, the called method gets... well, it's a bit messy to +explain what the called method gets. But when the called method does +anything to the argument, the argument gets evaluated and the "anything" +is done to that. Crucially, evaluation happens every time the argument +gets mentioned, and only when the argument gets mentioned. + +#### Not Just Another Calling Convention + +Why does this matter? It matters because there are functions you can't +implement using pass by value or pass by reference, but you can implement +using pass by name. + +Suppose, for example, that C# didn't have the `while` keyword. +You'd probably want to write a method that did the job instead: + + public static void While(bool condition, Action body) + { + if (condition) + { + body(); + While(condition, body); + } + } + +What happens when we call this? + + long x = 0; + While(x < 10, () => x = x + 1); + +C# evaluates the arguments to `While` and invokes the `While` method with +the arguments `true` and `() => x = x + 1`. After watching the CPU sit +on 100% for a while you might check on the value of `x` and find it's +somewhere north of a trillion. *Why?* Because the condition argument was +*passed by value*, so whenever the `While` method tests the value of +condition, it's always `true`. The `While` method doesn't know that +condition originally came from the expression `x < 10`; all `While` knows +is that condition is `true`. + +For the `While` method to work, we need it to re-evaluate `x < 10` every +time it hits the condition argument. While needs not the value of the +argument at the call site, nor a reference to the argument at the call +site, but the actual expression that the caller wants it to use to generate +a value. + +Same goes for short-circuit evaluation. If you want short-circuit +evaluation in C#, your only hope if to get on the blower to Anders +Hejlsberg and persuade him to bake it into the language: + + bool result = (a > 0 && Math.Sqrt(a) < 10); + double result = (a < 0 ? Math.Sqrt(-a) : Math.Sqrt(a)); + +You can't write a function like `&&` or `?:` yourself, because C# will +always try to evaluate all the arguments before calling your function. + +Consider a VB exile who wants to reproduce his favourite keywords in C#: + + bool AndAlso(bool condition1, bool condition2) + { + return condition1 && condition2; + } + + T IIf(bool condition, T ifTrue, T ifFalse) + { + if (condition) + return ifTrue; + else + return ifFalse; + } + +But when C# hits one of these: + + bool result = AndAlso(a > 0, Math.Sqrt(a) < 10); + double result = IIf(a < 0, Math.Sqrt(-a), Math.Sqrt(a)); + +it would try to evaluate all the arguments at the call site, and pass the +results of those evaluations to `AndAlso` or `IIf`. There's no +short-circuiting. So the `AndAlso` call would crash if a were negative, +and the `IIf` call if a were anything other than 0. Again, what you want is +for the `condition1`, `condition2`, `ifTrue` and `ifFalse` arguments to be +evaluated by the callee if it needs them, not for the caller to evaluate +them before making the call. + +And that's what *pass by name* does. A parameter passed by name is not +evaluated when it is passed to a method. It is evaluated -- and +re-evaluated -- when the called method evaluates the parameter; +specifically when the called method requests the value of the parameter by +mentioning its name. This might sound weird and academic, but it's the key +to being able to define your own control constructs. + +#### Using Pass By Name in Scala + +Let's see the custom while implementation again, this time with Scala +*pass by name* parameters: + + def myWhile(condition: => Boolean)(body: => Unit): Unit = + if (condition) { + body + myWhile(condition)(body) + } + +We can call this as follows: + + var i = 0 + myWhile (i < 10) { + println(i) + i += 1 + } + +Unlike the C# attempt, this prints out the numbers from 0 to 9 and then +terminates as you'd wish. + +Pass by name also works for short-circuiting: + + import math._ + + def andAlso(condition1: => Boolean, condition2: => Boolean): Boolean = + condition1 && condition2 + + val d = -1.234 + val result = andAlso(d > 0, sqrt(d) < 10) + +The `andAlso` call returns `false` rather than crashing, because +`sqrt(d) < 10` never gets evaluated. + +What's going on here? What's the weird colon-and-pointy-sticks syntax? +What is actually getting passed to `myWhile` and `andAlso` to make this work? + +The answer is a bit surprising. Nothing is going on here. This is the +normal Scala function parameter syntax. There is no *pass by name* in Scala. + +Here's a bog-standard *pass by value* Scala function declaration: + + def myFunc1(i: Int): Unit = ... + +Takes an integer, returns void: easy enough. Here's another: + + def myFunc2(f: Int => Boolean): Unit = ... + +Even if you've not seen this kind of expression before, it's probably not +too hard to guess what this means. This function takes a *function from +`Int` to `Boolean`* as its argument. In C# terms, +`void MyFunc2(Func f)`. We could call this as follows: + + myFunc2 { (i: Int) => i > 0 } + +So now you can guess what this means: + + def myFunc3(f: => Boolean) : Unit = ... + +Well, if `myFunc2` took an *Int-to-Boolean* function, `myFunc3` must be +taking a "blank-to-Boolean" function -- a function that takes no arguments +and returns a `Boolean`. In short, a conditional expression. So we can +call `myFunc3` as follows: + + val j = 123 + myFunc3 { j > 0 } + +The squirly brackets are what we'd expect from an anonymous function, and +because the function has no arguments Scala doesn't make us write +`{ () => j > 0 }`, even though that's what it means really. The anonymous +function has no arguments because `j` is a captured local variable, not an +argument to the function. But there's more. Scala also lets us call +`myFunc3` like this: + + val j = 123 + myFunc3(j > 0) + +This is normal function call syntax, but the Scala compiler realises that +`myFunc3` expects a nullary function (a function with no arguments) rather +than a `Boolean`, and therefore treats `myFunc3(j > 0)` as shorthand for +`myFunc3(() => j > 0)`. This is the same kind of logic that the C# compiler +uses when it decides whether to compile a lambda expression to a delegate +or an expression tree. + +You can probably figure out where it goes from here: + + def myFunc4(f1: => Boolean)(f2: => Unit): Unit = ... + +This takes two functions: a conditional expression, and a function that +takes no arguments and returns no value (in .NET terms, an `Action`). +Using our powers of anticipation, we can imagine how this might be called +using some unholy combination of the two syntaxes we saw for calling +`myFunc3`: + + val j = 123; + myFunc4(j > 0) { println(j); j -= 1; } + +We can mix and match the `()` and `{}` bracketing at whim, except that we +have to use `{}` bracketing if we want to batch up multiple expressions. +For example, you could legally equally well write the following: + + myFunc4 { j > 0 } { println(j); j -= 1; } + myFunc4 { println(j); j > 0 } (j -= 1) + myFunc4 { println(j); j > 0 } { j -= 1 } + +And we'll bow to the inevitable by supplying a body for this function: + + def myFunc5(f1: => Boolean)(f2: => Unit): Unit = + if (f1()) { + f2() + myFunc5(f1)(f2) + } + +Written like this, it's clear that `f1` is getting evaluated each time we +execute the if statement, but is getting passed (as a function) when +`myFunc5` recurses. But Scala allows us to leave the parentheses off +function calls with no arguments, so we can write the above as: + + def myFunc5(f1: => Boolean)(f2: => Unit): Unit = + if (f1) { + f2 + myFunc5(f1)(f2) + } + +Again, type inference allows Scala to distinguish the *evaluation of +`f1`* in the if statement from the *passing of `f1`* in the `myFunc5` +recursion. + +And with a bit of renaming, that's `myWhile`. There's no separate +*pass by name* convention: just the usual closure behaviour of capturing +local variables in an anonymous method or lambda, a bit of syntactic sugar +for nullary functions (functions with no arguments), just like C#'s +syntactic sugar for property getters, and the Scala compiler's ability to +recognise when a closure is required instead of a value. + +In fact, armed with this understanding of the Scala "syntax," we can +easily map it back to C#: + + void While(Func condition, Action body) + { + if (condition()) + { + body(); + While(condition, body); + } + } + + int i = 0; + While(() => i < 10, () => + { + Console.WriteLine(i); + ++i; + }); + +The implementation of the `While` method in C# is, to my eyes, a bit +clearer than the Scala version. However, the syntax for *calling* the +`While` method in C# is clearly way more complicated and less natural than +the syntax for calling `myWhile` in Scala. Calling `myWhile` in Scala was +like using a native language construct. Calling While in C# required a +great deal of clutter at the call site to prevent C# from trying to treat +`i < 10` as a once-and-for-all value, and to express the body at all. + +So that's so-called "pass by name" demystified: The Scala Web site, with +crushing mundanity, demotes it to "automatic type-dependent closure +construction," which is indeed exactly how it works. As we've seen, +however, this technical-sounding feature is actually essential to +creating nice syntax for your own control constructs. We'll shortly see +how this works together with other Scala features to give you even more +flexibility in defining your construct's syntax. + +### Implicits + +Scala implicits offer some features which will be familiar to the C# +programmer, but are much more general in nature and go far beyond what can +be done in C#. + +#### Enriching types in C# and Scala + +Scala, like C#, is statically typed: a class’ methods are compiled into the +class definition and are not open for renegotiation. You cannot, as you +might in Ruby or Python, just go ahead and declare additional methods on an +existing class. + +This is of course very inconvenient. You end up declaring a load of +`FooHelper` or `FooUtils` classes full of static methods, and having to +write verbose calling code such as `if (EnumerableUtils.IsEmpty(sequence))` +rather than the rather more readable `if (sequence.IsEmpty())`. + +C# 3 tries to address this problem by introducing extension methods. +Extension methods are static methods in a `FooHelper` or `FooUtils` kind +of class, except you’re allowed to write them using member syntax. +By defining `IsEmpty` as an extension method on `IEnumerable`, you can +write `if (sequence.IsEmpty())` after all. + +Scala disapproves of static classes and global methods, so it plumps for +an alternative approach. You’ll still write a `FooHelper` or `FooUtils` +kind of class, but instead of taking the `Foo` to be Helped or Utilised as +a method parameter, your class will wrap `Foo` and enrich it with +additional methods. Let’s see this in action as we try to add a method to +the `Double` type: + + class RicherDouble(d : Double) { + def toThe(exp: Double): Double = System.Math.Pow(d, exp) + } + +(We call the class `RicherDouble` because Scala already has a `RichDouble` +class defined which provides further methods to `Double`.) + +Notice that `toThe` is an instance method, and that `RicherDouble` takes a +`Double` as a constructor parameter. This seems pretty grim, because we’d +normally have to access the function like this: + + val result = new DoubleExtensions(2.0).toThe(7.0) + +Hardly readable. To make it look nice, Scala requires us to define an +*implicit conversion* from `Double` to `RicherDouble`: + + object Implicits { + implicit def richerDouble(d: Double) = new RicherDouble(d) + } + +and to bring that implicit conversion into scope: + + import Implicits._ + +Now we can write this: + + val twoToTheSeven = 2.0.toThe(7.0) + +and all will be well. The `Double` type has apparently been successfully +enriched with the `toThe` method. + +This is, of course, just as much an illusion as the C# equivalent. +C# extension methods don’t add methods to a type, and nor do Scala +implicit conversions. What has happened here is that the Scala compiler +has looked around for implicit methods that are applicable to the type of +`2.0` (namely `Double`), and return a type that has a `toThe` method. +Our `Implicits.richerDouble` method fits the bill, so the Scala compiler +silently inserts a call to that method. At runtime, therefore, Scala calls +`Implicits.richerDouble(2.0)` and calls the `toThe` of the resulting +`RicherDouble`. + +If setting this up seems a bit verbose, well, maybe. C# extension methods +are designed to be easily – one might even say implicitly – brought into +scope. That’s very important for operators like the LINQ standard query +operators, but it can result in unwanted extension methods being dragged +into scope and causing havoc. Scala requires the caller to be a bit more +explicit about implicits, which results in a slightly higher setup cost but +gives the caller finer control over which implicit methods are considered. + +But as it happens you can avoid the need for separate definitions of +`Implicits` and `RicherDouble`, and get back to a more concise +representation by using an anonymous class. (As you’d expect, Scala +anonymous classes are fully capable, like Java ones, rather than the +neutered C# version.) Here’s how it looks: + + object Implicits { + implicit def doubleToThe(d1 : Double) = new { + def toThe(d2 : Double) : Double = Math.Pow(d1, d2) + } + } + +Well, big deal. Scala can enrich existing types with new methods just like +C#, but using a different syntax. In related news, Lisp uses a different +kind of bracket: film at eleven. Why should we be interested in Scala +implicits if they’re just another take on extension methods? + +#### Implicit Parameters + +What we saw above was an implicit method – a method which, like a C# +implicit conversion operator, the compiler is allowed to insert a call to +without the programmer writing that call. Scala also has the idea of +implicit parameters – that is, parameters which the compiler is allowed to +insert a value for without the programmer specifying that value. + +That’s just optional parameters with default values, right? Like C++ and +Visual Basic have had since “visual” meant ASCII art on a teletype, and +like C# is about to get? Well, no. + +C++, Visual Basic and C# optional parameters have fixed defaults specified +by the called function. For example, if you have a method like this: + + public void Fie(int a, int b = 123) { … } + +and you call `Fie(456)`, it’s always going to be equivalent to calling +`Fie(456, 123)`. + +A Scala implicit parameter, on the other hand, gets its value from the +calling context. That allows programmer calling the method to control the +implicit parameter value, creating an extensibility point that optional +parameters don’t provide. + +This probably all sounds a bit weird, so let’s look at an example. Consider +the following `Concatenate` method: + + public T Concatenate(IEnumerable sequence, T seed, Func concatenator); + +We pass this guy a sequence, a start value and a function that combines two +values into one, and it returns the result of calling that function across +the sequence. For example, you could pass a sequence of strings, a start +value of `String.Empty`, and `(s1, s2) => s1 + s2`, and it would return you +all the strings concatenated together: + + IEnumerable sequence = new string[] { “mog”, “bites”, “man” }; + string result = Concatenate(sequence, String.Empty, (s1, s2) => s1 + s2); + // result is “mogbitesman” + +But this is a unpleasantly verbose. We’re having to pass in `String.Empty` +and `(s1, s2) => s1 + s2` every time we want to concatenate a sequence of +strings. Not only is this tedious, it also creates the opportunity for +error when the boss decides to “help” and passes the literal +`"String.Empty"` as the seed value instead. (“Oh, and I upgraded all the +semi-colons to colons while I was in there. No, don’t thank me!”) We’d +like to just tell the Concatenate function, “Look, this is how you +concatenate strings,” once and for all. + +Let’s start out by redefining the `Concatenate` method in Scala. +I’m going to factor out the seed and the concatenator method into a trait +because we’ll typically be defining them together. + + trait Concatenator[T] { + def startValue: T + def concat(x: T, y: T): T + } + + object implicitParameters { + def concatenate[T](xs: List[T])(c: Concatenator[T]): T = + if (xs.isEmpty) c.startValue + else c.concat(xs.head, concatenate(xs.tail)(c)) + } + +We can call this as follows: + + object stringConcatenator extends Concatenator[String] { + def startValue: String = "" + def concat(x: String, y: String) = x.concat(y) + } + + object implicitParameters { + def main(args: Array[String]) = { + val result = concatenate(List("mog", "bites", "man"))(stringConcatenator) + println(result) + } + } + +So far, this looks like the C# version except for the factoring out of the +`Concatenator` trait. We’re still having to pass in the +`stringConcatenator` at the point of the call. Let’s fix that: + + def concatenate[T](xs: List[T])(implicit c: Concatenator[T]): T = + if (xs.isEmpty) c.startValue + else c.concat(xs.head, concatenate(xs.tail)) + +We’ve changed two things here. First, we’ve declared c to be an *implicit +parameter*, meaning the caller can leave it out. Second, we’ve left +it out ourselves, in the recursive call to `concatenate(xs.tail)`. + +Well, okay, it’s nice that `concatenate` now doesn’t have to pass the +`Concatenator` explicitly to the recursive call, but we’re still having to +pass in the `stringConcatenator` object to get things started. If only +there were some way to make the `stringConcatenator` object itself implicit… + + object Implicits { + implicit object stringConcatenator extends Concatenator[String] { + def startValue: String = "" + def concat(x: String, y: String) = x.concat(y) + } + } + +Again, we’ve done two things here. First, we’ve declared the +`stringConcatenator` object implicit. Consequently, we’ve had to move it +out of the top level, because Scala doesn’t allow implicits at the top +level (because they’d pollute the global namespace, being in scope even +without an explicit import statement). + +Now we can call `concatenate` like this: + + import Implicits._ + + object implicitParameters { + def main(args: Array[String]) = { + val result = concatenate(List("mog", "bites", "man")) + println(result) + } + } + +And we’ll still get “mogbitesman” as the output. + +Let’s review what’s going on here. The implicit parameter of concatenate +has been set to our `stringConcatenator`, a default value that the +`concatenate` method knew nothing about when it was compiled. This is +somewhere north of what classical optional parameters are capable of, +and we’re not finished yet. Let’s build a `listConcatenator`. + + object Implicits { + class ListConcatenator[T] extends Concatenator[List[T]] { + def startValue: List[T] = Nil + def concat(x: List[T], y: List[T]) = x ::: y + } + implicit object stringListConcatenator extends ListConcatenator[String] { } + } + +This is a bit vexing. `List` in Scala is a generic type, and has a generic +concatenation method called `:::`. But we can’t create a generic object, +because an object is an instance. And implicit parameters have to be objects. +So the best we can do is build a generic `ListConcatenator` class, and then +create trivial implicit objects for each generic parameter type we might +need. + +However, let’s not worry about the implementation, and see how this is used +at the calling end: + + val result = concatenate(List( + List("mog", "bites", "man"), + List("on", "beard") + )) + +This displays `List(mog, bites, man, on, beard)`; that is, it concatenates +the two `List[String]`s into one. Once again, we have not had to pass +`stringListConcatenator` explicitly: the Scala compiler has gone and found +it for us. We can use the exact same calling code to concatenate lists and +strings. + +#### Why Should I Care? + +Isn’t this pointless? At the call site, I have access to +`stringConcatenator` and `listStringConcatenator`. I can easily pass them +in rather than relying on spooky compiler magic to do it for me. +Aren’t implicit parameters just job security for compiler writers? + +Yes, implicit parameters are technically unnecessary. But if we’re going +to play that game, C# is technically unnecessary. You could write all that +code in IL. Extension methods are unnecessary because you could write the +static method out longhand. Optional parameters are unnecessary because +you could read the documentation and pass them in explicitly. +Post-It notes are unnecessary because you could fire up Outlook and create +a Note instead. + +Implicit parameters are about convenience and expressiveness. Implicit +parameters give you a way of describing how a function should handle +different situations, without needing to bake those situations into the +function logic or to specify them every time you call the function. +You don’t want to have to tell the `concatenate` function whether to use +the `List` or `String` concatenator every time you call it: the compiler +knows what you’re concatenating; specifying how to concatenate it just +gives you a chance to get it wrong! + +Consequently, implicit parameters – like implicit conversions – contribute +to Scala’s ability to support internal DSLs. By setting up appropriate +implicits, you can write code that reads much more naturally than if you +had to pepper it with function objects or callbacks. + +#### Conclusion + +Scala’s `implicit` keyword goes beyond C#’s equivalent. As in C#, it is +used for implicit conversions; unlike C#, this is the idiomatic way to add +operations to an existing type, removing the need for the separate +extension method syntax. Implicit parameters have no equivalent in C#. +They are like being able to add default values to a method: just as a C# +using statement bring implicit methods into scope, a Scala import statement +can bring default values into scope. If implicit conversions are a way of +extending classes, then implicit parameters are a way of extending methods, +creating simple, reliable shorthands for complex generic methods, and +making up another piece of the Scala DSL jigsaw. + +#### Method Call Syntax + +C#, like most object-oriented programming languages, is pretty strict about +how you call methods: you use the dot notation, unless the method is a +special ‘operator’ method such as `operator+`, `operator==` or a conversion +operator. The special operator methods are predefined by the compiler: you +can write your own implementation, but you can’t create your own operator +names. You can teach the `+` operator how to handle your custom type, but +you can’t add an exponentiation operator: + + int a = b ** c; + +C# has three problems with this: first, it doesn’t like the method name +`**`; second, it doesn’t like that there’s no `.` before the name; and +third, it doesn’t like that there’s no brackets around the method argument. + +To get around the objection to the name, let’s compromise and call it +`ToThe` for now. So what C# insists on seeing is `a.ToThe(b)`. + +Scala, like many functional languages, isn’t so strict. Scala allows you +to use any method with a single argument in an infix position. Before we +can see this in the exponentiation example, we will enrich the `Double` +type with the `toThe` method as we learned earlier: + + import Implicits._ + import math._ + + class RicherDouble(d: Double) { + def toThe(exp: Double): Double = pow(d, exp) + } + + object Implicits { + implicit def richerDouble(d: Double) = new RicherDouble(d) + } + +Recall that this is just the Scala idiom for extension methods – it’s the +equivalent of writing +`public static ToThe(this double first, double second) { … }` in C#. +(If we were wanting to use infix notation with our own class, we wouldn’t +need all this malarkey.) So now we can write: + + val raised = 2.0.toThe(7.0) + +Okay, so what do we need to do to get this to work in infix position? +Nothing, it turns out. + + val raised = 2.0 toThe 8.0 // it just works + +This still doesn’t look much like a built-in operator, but it turns out +Scala is less fussy than C# about method names too. + + class DoubleExtensions(d : Double) { + def **(exp: Double): Double = Pow(d, exp) + } + + val raised = 2.0 ** 9.0 // it still just works + +Much nicer. + +This sorta-kinda works for postfix operators too: + + class RicherString(s: String) { + def twice: String = s + s + } + + val drivel = "bibble" twice + +Calling methods in infix and postfix nodadion is obviously fairly simple +syntactic sugar over normal dot notation. But this seemingly minor feature +is very important in constructing DSLs, allowing Scala to do in internal +DSLs what many languages can do only using external tools. For example, +where most languages do parsing via an external file format and a tool to +translate that file format into native code (a la `lex` and `yacc`), +Scala’s parser library makes extensive use of infix and postfix methods to +provide a “traditional” syntax for describing a parser, but manages it +entirely within the Scala language. + diff --git a/_overviews/tutorials/scala-for-java-programmers.md b/_overviews/tutorials/scala-for-java-programmers.md new file mode 100644 index 0000000000..b8618b431c --- /dev/null +++ b/_overviews/tutorials/scala-for-java-programmers.md @@ -0,0 +1,723 @@ +--- +layout: overview +title: A Scala Tutorial for Java Programmers +overview: scala-for-java-programmers + +discourse: true +multilingual-overview: true +languages: [es, ko, de, it, zh-tw] +--- + +By Michel Schinz and Philipp Haller + +## Introduction + +This document gives a quick introduction to the Scala language and +compiler. It is intended for people who already have some programming +experience and want an overview of what they can do with Scala. A +basic knowledge of object-oriented programming, especially in Java, is +assumed. + +## A First Example + +As a first example, we will use the standard *Hello world* program. It +is not very fascinating but makes it easy to demonstrate the use of +the Scala tools without knowing too much about the language. Here is +how it looks: + + object HelloWorld { + def main(args: Array[String]) { + println("Hello, world!") + } + } + +The structure of this program should be familiar to Java programmers: +it consists of one method called `main` which takes the command +line arguments, an array of strings, as parameter; the body of this +method consists of a single call to the predefined method `println` +with the friendly greeting as argument. The `main` method does not +return a value (it is a procedure method). Therefore, it is not necessary +to declare a return type. + +What is less familiar to Java programmers is the `object` +declaration containing the `main` method. Such a declaration +introduces what is commonly known as a *singleton object*, that +is a class with a single instance. The declaration above thus declares +both a class called `HelloWorld` and an instance of that class, +also called `HelloWorld`. This instance is created on demand, +the first time it is used. + +The astute reader might have noticed that the `main` method is +not declared as `static` here. This is because static members +(methods or fields) do not exist in Scala. Rather than defining static +members, the Scala programmer declares these members in singleton +objects. + +### Compiling the example + +To compile the example, we use `scalac`, the Scala compiler. `scalac` +works like most compilers: it takes a source file as argument, maybe +some options, and produces one or several object files. The object +files it produces are standard Java class files. + +If we save the above program in a file called +`HelloWorld.scala`, we can compile it by issuing the following +command (the greater-than sign `>` represents the shell prompt +and should not be typed): + + > scalac HelloWorld.scala + +This will generate a few class files in the current directory. One of +them will be called `HelloWorld.class`, and contains a class +which can be directly executed using the `scala` command, as the +following section shows. + +### Running the example + +Once compiled, a Scala program can be run using the `scala` command. +Its usage is very similar to the `java` command used to run Java +programs, and accepts the same options. The above example can be +executed using the following command, which produces the expected +output: + + > scala -classpath . HelloWorld + + Hello, world! + +## Interaction with Java + +One of Scala's strengths is that it makes it very easy to interact +with Java code. All classes from the `java.lang` package are +imported by default, while others need to be imported explicitly. + +Let's look at an example that demonstrates this. We want to obtain +and format the current date according to the conventions used in a +specific country, say France. (Other regions such as the +French-speaking part of Switzerland use the same conventions.) + +Java's class libraries define powerful utility classes, such as +`Date` and `DateFormat`. Since Scala interoperates +seemlessly with Java, there is no need to implement equivalent +classes in the Scala class library--we can simply import the classes +of the corresponding Java packages: + + import java.util.{Date, Locale} + import java.text.DateFormat + import java.text.DateFormat._ + + object FrenchDate { + def main(args: Array[String]) { + val now = new Date + val df = getDateInstance(LONG, Locale.FRANCE) + println(df format now) + } + } + +Scala's import statement looks very similar to Java's equivalent, +however, it is more powerful. Multiple classes can be imported from +the same package by enclosing them in curly braces as on the first +line. Another difference is that when importing all the names of a +package or class, one uses the underscore character (`_`) instead +of the asterisk (`*`). That's because the asterisk is a valid +Scala identifier (e.g. method name), as we will see later. + +The import statement on the third line therefore imports all members +of the `DateFormat` class. This makes the static method +`getDateInstance` and the static field `LONG` directly +visible. + +Inside the `main` method we first create an instance of Java's +`Date` class which by default contains the current date. Next, we +define a date format using the static `getDateInstance` method +that we imported previously. Finally, we print the current date +formatted according to the localized `DateFormat` instance. This +last line shows an interesting property of Scala's syntax. Methods +taking one argument can be used with an infix syntax. That is, the +expression + + df format now + +is just another, slightly less verbose way of writing the expression + + df.format(now) + +This might seem like a minor syntactic detail, but it has important +consequences, one of which will be explored in the next section. + +To conclude this section about integration with Java, it should be +noted that it is also possible to inherit from Java classes and +implement Java interfaces directly in Scala. + +## Everything is an Object + +Scala is a pure object-oriented language in the sense that +*everything* is an object, including numbers or functions. It +differs from Java in that respect, since Java distinguishes +primitive types (such as `boolean` and `int`) from reference +types, and does not enable one to manipulate functions as values. + +### Numbers are objects + +Since numbers are objects, they also have methods. And in fact, an +arithmetic expression like the following: + + 1 + 2 * 3 / x + +consists exclusively of method calls, because it is equivalent to the +following expression, as we saw in the previous section: + + (1).+(((2).*(3))./(x)) + +This also means that `+`, `*`, etc. are valid identifiers +in Scala. + +The parentheses around the numbers in the second version are necessary +because Scala's lexer uses a longest match rule for tokens. +Therefore, it would break the following expression: + + 1.+(2) + +into the tokens `1.`, `+`, and `2`. The reason that +this tokenization is chosen is because `1.` is a longer valid +match than `1`. The token `1.` is interpreted as the +literal `1.0`, making it a `Double` rather than an +`Int`. Writing the expression as: + + (1).+(2) + +prevents `1` from being interpreted as a `Double`. + +### Functions are objects + +Perhaps more surprising for the Java programmer, functions are also +objects in Scala. It is therefore possible to pass functions as +arguments, to store them in variables, and to return them from other +functions. This ability to manipulate functions as values is one of +the cornerstone of a very interesting programming paradigm called +*functional programming*. + +As a very simple example of why it can be useful to use functions as +values, let's consider a timer function whose aim is to perform some +action every second. How do we pass it the action to perform? Quite +logically, as a function. This very simple kind of function passing +should be familiar to many programmers: it is often used in +user-interface code, to register call-back functions which get called +when some event occurs. + +In the following program, the timer function is called +`oncePerSecond`, and it gets a call-back function as argument. +The type of this function is written `() => Unit` and is the type +of all functions which take no arguments and return nothing (the type +`Unit` is similar to `void` in C/C++). The main function of +this program simply calls this timer function with a call-back which +prints a sentence on the terminal. In other words, this program +endlessly prints the sentence "time flies like an arrow" every +second. + + object Timer { + def oncePerSecond(callback: () => Unit) { + while (true) { callback(); Thread sleep 1000 } + } + def timeFlies() { + println("time flies like an arrow...") + } + def main(args: Array[String]) { + oncePerSecond(timeFlies) + } + } + +Note that in order to print the string, we used the predefined method +`println` instead of using the one from `System.out`. + +#### Anonymous functions + +While this program is easy to understand, it can be refined a bit. +First of all, notice that the function `timeFlies` is only +defined in order to be passed later to the `oncePerSecond` +function. Having to name that function, which is only used once, might +seem unnecessary, and it would in fact be nice to be able to construct +this function just as it is passed to `oncePerSecond`. This is +possible in Scala using *anonymous functions*, which are exactly +that: functions without a name. The revised version of our timer +program using an anonymous function instead of *timeFlies* looks +like that: + + object TimerAnonymous { + def oncePerSecond(callback: () => Unit) { + while (true) { callback(); Thread sleep 1000 } + } + def main(args: Array[String]) { + oncePerSecond(() => + println("time flies like an arrow...")) + } + } + +The presence of an anonymous function in this example is revealed by +the right arrow `=>` which separates the function's argument +list from its body. In this example, the argument list is empty, as +witnessed by the empty pair of parenthesis on the left of the arrow. +The body of the function is the same as the one of `timeFlies` +above. + +## Classes + +As we have seen above, Scala is an object-oriented language, and as +such it has a concept of class. (For the sake of completeness, + it should be noted that some object-oriented languages do not have + the concept of class, but Scala is not one of them.) +Classes in Scala are declared using a syntax which is close to +Java's syntax. One important difference is that classes in Scala can +have parameters. This is illustrated in the following definition of +complex numbers. + + class Complex(real: Double, imaginary: Double) { + def re() = real + def im() = imaginary + } + +This `Complex` class takes two arguments, which are the real and +imaginary part of the complex. These arguments must be passed when +creating an instance of class `Complex`, as follows: `new + Complex(1.5, 2.3)`. The class contains two methods, called `re` +and `im`, which give access to these two parts. + +It should be noted that the return type of these two methods is not +given explicitly. It will be inferred automatically by the compiler, +which looks at the right-hand side of these methods and deduces that +both return a value of type `Double`. + +The compiler is not always able to infer types like it does here, and +there is unfortunately no simple rule to know exactly when it will be, +and when not. In practice, this is usually not a problem since the +compiler complains when it is not able to infer a type which was not +given explicitly. As a simple rule, beginner Scala programmers should +try to omit type declarations which seem to be easy to deduce from the +context, and see if the compiler agrees. After some time, the +programmer should get a good feeling about when to omit types, and +when to specify them explicitly. + +### Methods without arguments + +A small problem of the methods `re` and `im` is that, in +order to call them, one has to put an empty pair of parenthesis after +their name, as the following example shows: + + object ComplexNumbers { + def main(args: Array[String]) { + val c = new Complex(1.2, 3.4) + println("imaginary part: " + c.im()) + } + } + +It would be nicer to be able to access the real and imaginary parts +like if they were fields, without putting the empty pair of +parenthesis. This is perfectly doable in Scala, simply by defining +them as methods *without arguments*. Such methods differ from +methods with zero arguments in that they don't have parenthesis after +their name, neither in their definition nor in their use. Our +`Complex` class can be rewritten as follows: + + class Complex(real: Double, imaginary: Double) { + def re = real + def im = imaginary + } + + +### Inheritance and overriding + +All classes in Scala inherit from a super-class. When no super-class +is specified, as in the `Complex` example of previous section, +`scala.AnyRef` is implicitly used. + +It is possible to override methods inherited from a super-class in +Scala. It is however mandatory to explicitly specify that a method +overrides another one using the `override` modifier, in order to +avoid accidental overriding. As an example, our `Complex` class +can be augmented with a redefinition of the `toString` method +inherited from `Object`. + + class Complex(real: Double, imaginary: Double) { + def re = real + def im = imaginary + override def toString() = + "" + re + (if (im < 0) "" else "+") + im + "i" + } + + +## Case Classes and Pattern Matching + +A kind of data structure that often appears in programs is the tree. +For example, interpreters and compilers usually represent programs +internally as trees; XML documents are trees; and several kinds of +containers are based on trees, like red-black trees. + +We will now examine how such trees are represented and manipulated in +Scala through a small calculator program. The aim of this program is +to manipulate very simple arithmetic expressions composed of sums, +integer constants and variables. Two examples of such expressions are +`1+2` and `(x+x)+(7+y)`. + +We first have to decide on a representation for such expressions. The +most natural one is the tree, where nodes are operations (here, the +addition) and leaves are values (here constants or variables). + +In Java, such a tree would be represented using an abstract +super-class for the trees, and one concrete sub-class per node or +leaf. In a functional programming language, one would use an algebraic +data-type for the same purpose. Scala provides the concept of +*case classes* which is somewhat in between the two. Here is how +they can be used to define the type of the trees for our example: + + abstract class Tree + case class Sum(l: Tree, r: Tree) extends Tree + case class Var(n: String) extends Tree + case class Const(v: Int) extends Tree + +The fact that classes `Sum`, `Var` and `Const` are +declared as case classes means that they differ from standard classes +in several respects: + +- the `new` keyword is not mandatory to create instances of + these classes (i.e., one can write `Const(5)` instead of + `new Const(5)`), +- getter functions are automatically defined for the constructor + parameters (i.e., it is possible to get the value of the `v` + constructor parameter of some instance `c` of class + `Const` just by writing `c.v`), +- default definitions for methods `equals` and + `hashCode` are provided, which work on the *structure* of + the instances and not on their identity, +- a default definition for method `toString` is provided, and + prints the value in a "source form" (e.g., the tree for expression + `x+1` prints as `Sum(Var(x),Const(1))`), +- instances of these classes can be decomposed through + *pattern matching* as we will see below. + +Now that we have defined the data-type to represent our arithmetic +expressions, we can start defining operations to manipulate them. We +will start with a function to evaluate an expression in some +*environment*. The aim of the environment is to give values to +variables. For example, the expression `x+1` evaluated in an +environment which associates the value `5` to variable `x`, written +`{ x -> 5 }`, gives `6` as result. + +We therefore have to find a way to represent environments. We could of +course use some associative data-structure like a hash table, but we +can also directly use functions! An environment is really nothing more +than a function which associates a value to a (variable) name. The +environment `{ x -> 5 }` given above can simply be written as +follows in Scala: + + { case "x" => 5 } + +This notation defines a function which, when given the string +`"x"` as argument, returns the integer `5`, and fails with an +exception otherwise. + +Before writing the evaluation function, let us give a name to the type +of the environments. We could of course always use the type +`String => Int` for environments, but it simplifies the program +if we introduce a name for this type, and makes future changes easier. +This is accomplished in Scala with the following notation: + + type Environment = String => Int + +From then on, the type `Environment` can be used as an alias of +the type of functions from `String` to `Int`. + +We can now give the definition of the evaluation function. +Conceptually, it is very simple: the value of a sum of two expressions +is simply the sum of the value of these expressions; the value of a +variable is obtained directly from the environment; and the value of a +constant is the constant itself. Expressing this in Scala is not more +difficult: + + def eval(t: Tree, env: Environment): Int = t match { + case Sum(l, r) => eval(l, env) + eval(r, env) + case Var(n) => env(n) + case Const(v) => v + } + +This evaluation function works by performing *pattern matching* +on the tree `t`. Intuitively, the meaning of the above definition +should be clear: + +1. it first checks if the tree `t` is a `Sum`, and if it + is, it binds the left sub-tree to a new variable called `l` and + the right sub-tree to a variable called `r`, and then proceeds + with the evaluation of the expression following the arrow; this + expression can (and does) make use of the variables bound by the + pattern appearing on the left of the arrow, i.e., `l` and + `r`, +2. if the first check does not succeed, that is, if the tree is not + a `Sum`, it goes on and checks if `t` is a `Var`; if + it is, it binds the name contained in the `Var` node to a + variable `n` and proceeds with the right-hand expression, +3. if the second check also fails, that is if `t` is neither a + `Sum` nor a `Var`, it checks if it is a `Const`, and + if it is, it binds the value contained in the `Const` node to a + variable `v` and proceeds with the right-hand side, +4. finally, if all checks fail, an exception is raised to signal + the failure of the pattern matching expression; this could happen + here only if more sub-classes of `Tree` were declared. + +We see that the basic idea of pattern matching is to attempt to match +a value to a series of patterns, and as soon as a pattern matches, +extract and name various parts of the value, to finally evaluate some +code which typically makes use of these named parts. + +A seasoned object-oriented programmer might wonder why we did not +define `eval` as a *method* of class `Tree` and its +subclasses. We could have done it actually, since Scala allows method +definitions in case classes just like in normal classes. Deciding +whether to use pattern matching or methods is therefore a matter of +taste, but it also has important implications on extensibility: + +- when using methods, it is easy to add a new kind of node as this + can be done just by defining a sub-class of `Tree` for it; on + the other hand, adding a new operation to manipulate the tree is + tedious, as it requires modifications to all sub-classes of + `Tree`, +- when using pattern matching, the situation is reversed: adding a + new kind of node requires the modification of all functions which do + pattern matching on the tree, to take the new node into account; on + the other hand, adding a new operation is easy, by just defining it + as an independent function. + +To explore pattern matching further, let us define another operation +on arithmetic expressions: symbolic derivation. The reader might +remember the following rules regarding this operation: + +1. the derivative of a sum is the sum of the derivatives, +2. the derivative of some variable `v` is one if `v` is the + variable relative to which the derivation takes place, and zero + otherwise, +3. the derivative of a constant is zero. + +These rules can be translated almost literally into Scala code, to +obtain the following definition: + + def derive(t: Tree, v: String): Tree = t match { + case Sum(l, r) => Sum(derive(l, v), derive(r, v)) + case Var(n) if (v == n) => Const(1) + case _ => Const(0) + } + +This function introduces two new concepts related to pattern matching. +First of all, the `case` expression for variables has a +*guard*, an expression following the `if` keyword. This +guard prevents pattern matching from succeeding unless its expression +is true. Here it is used to make sure that we return the constant `1` +only if the name of the variable being derived is the same as the +derivation variable `v`. The second new feature of pattern +matching used here is the *wildcard*, written `_`, which is +a pattern matching any value, without giving it a name. + +We did not explore the whole power of pattern matching yet, but we +will stop here in order to keep this document short. We still want to +see how the two functions above perform on a real example. For that +purpose, let's write a simple `main` function which performs +several operations on the expression `(x+x)+(7+y)`: it first computes +its value in the environment `{ x -> 5, y -> 7 }`, then +computes its derivative relative to `x` and then `y`. + + def main(args: Array[String]) { + val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) + val env: Environment = { case "x" => 5 case "y" => 7 } + println("Expression: " + exp) + println("Evaluation with x=5, y=7: " + eval(exp, env)) + println("Derivative relative to x:\n " + derive(exp, "x")) + println("Derivative relative to y:\n " + derive(exp, "y")) + } + +Executing this program, we get the expected output: + + Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) + Evaluation with x=5, y=7: 24 + Derivative relative to x: + Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0))) + Derivative relative to y: + Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1))) + +By examining the output, we see that the result of the derivative +should be simplified before being presented to the user. Defining a +basic simplification function using pattern matching is an interesting +(but surprisingly tricky) problem, left as an exercise for the reader. + +## Traits + +Apart from inheriting code from a super-class, a Scala class can also +import code from one or several *traits*. + +Maybe the easiest way for a Java programmer to understand what traits +are is to view them as interfaces which can also contain code. In +Scala, when a class inherits from a trait, it implements that trait's +interface, and inherits all the code contained in the trait. + +To see the usefulness of traits, let's look at a classical example: +ordered objects. It is often useful to be able to compare objects of a +given class among themselves, for example to sort them. In Java, +objects which are comparable implement the `Comparable` +interface. In Scala, we can do a bit better than in Java by defining +our equivalent of `Comparable` as a trait, which we will call +`Ord`. + +When comparing objects, six different predicates can be useful: +smaller, smaller or equal, equal, not equal, greater or equal, and +greater. However, defining all of them is fastidious, especially since +four out of these six can be expressed using the remaining two. That +is, given the equal and smaller predicates (for example), one can +express the other ones. In Scala, all these observations can be +nicely captured by the following trait declaration: + + trait Ord { + def < (that: Any): Boolean + def <=(that: Any): Boolean = (this < that) || (this == that) + def > (that: Any): Boolean = !(this <= that) + def >=(that: Any): Boolean = !(this < that) + } + +This definition both creates a new type called `Ord`, which +plays the same role as Java's `Comparable` interface, and +default implementations of three predicates in terms of a fourth, +abstract one. The predicates for equality and inequality do not appear +here since they are by default present in all objects. + +The type `Any` which is used above is the type which is a +super-type of all other types in Scala. It can be seen as a more +general version of Java's `Object` type, since it is also a +super-type of basic types like `Int`, `Float`, etc. + +To make objects of a class comparable, it is therefore sufficient to +define the predicates which test equality and inferiority, and mix in +the `Ord` class above. As an example, let's define a +`Date` class representing dates in the Gregorian calendar. Such +dates are composed of a day, a month and a year, which we will all +represent as integers. We therefore start the definition of the +`Date` class as follows: + + class Date(y: Int, m: Int, d: Int) extends Ord { + def year = y + def month = m + def day = d + override def toString(): String = year + "-" + month + "-" + day + +The important part here is the `extends Ord` declaration which +follows the class name and parameters. It declares that the +`Date` class inherits from the `Ord` trait. + +Then, we redefine the `equals` method, inherited from +`Object`, so that it correctly compares dates by comparing their +individual fields. The default implementation of `equals` is not +usable, because as in Java it compares objects physically. We arrive +at the following definition: + + override def equals(that: Any): Boolean = + that.isInstanceOf[Date] && { + val o = that.asInstanceOf[Date] + o.day == day && o.month == month && o.year == year + } + +This method makes use of the predefined methods `isInstanceOf` +and `asInstanceOf`. The first one, `isInstanceOf`, +corresponds to Java's `instanceof` operator, and returns true +if and only if the object on which it is applied is an instance of the +given type. The second one, `asInstanceOf`, corresponds to +Java's cast operator: if the object is an instance of the given type, +it is viewed as such, otherwise a `ClassCastException` is +thrown. + +Finally, the last method to define is the predicate which tests for +inferiority, as follows. It makes use of another predefined method, +`error`, which throws an exception with the given error message. + + def <(that: Any): Boolean = { + if (!that.isInstanceOf[Date]) + error("cannot compare " + that + " and a Date") + + val o = that.asInstanceOf[Date] + (year < o.year) || + (year == o.year && (month < o.month || + (month == o.month && day < o.day))) + } + +This completes the definition of the `Date` class. Instances of +this class can be seen either as dates or as comparable objects. +Moreover, they all define the six comparison predicates mentioned +above: `equals` and `<` because they appear directly in +the definition of the `Date` class, and the others because they +are inherited from the `Ord` trait. + +Traits are useful in other situations than the one shown here, of +course, but discussing their applications in length is outside the +scope of this document. + +## Genericity + +The last characteristic of Scala we will explore in this tutorial is +genericity. Java programmers should be well aware of the problems +posed by the lack of genericity in their language, a shortcoming which +is addressed in Java 1.5. + +Genericity is the ability to write code parametrized by types. For +example, a programmer writing a library for linked lists faces the +problem of deciding which type to give to the elements of the list. +Since this list is meant to be used in many different contexts, it is +not possible to decide that the type of the elements has to be, say, +`Int`. This would be completely arbitrary and overly +restrictive. + +Java programmers resort to using `Object`, which is the +super-type of all objects. This solution is however far from being +ideal, since it doesn't work for basic types (`int`, +`long`, `float`, etc.) and it implies that a lot of +dynamic type casts have to be inserted by the programmer. + +Scala makes it possible to define generic classes (and methods) to +solve this problem. Let us examine this with an example of the +simplest container class possible: a reference, which can either be +empty or point to an object of some type. + + class Reference[T] { + private var contents: T = _ + def set(value: T) { contents = value } + def get: T = contents + } + +The class `Reference` is parametrized by a type, called `T`, +which is the type of its element. This type is used in the body of the +class as the type of the `contents` variable, the argument of +the `set` method, and the return type of the `get` method. + +The above code sample introduces variables in Scala, which should not +require further explanations. It is however interesting to see that +the initial value given to that variable is `_`, which represents +a default value. This default value is 0 for numeric types, +`false` for the `Boolean` type, `()` for the `Unit` +type and `null` for all object types. + +To use this `Reference` class, one needs to specify which type to use +for the type parameter `T`, that is the type of the element +contained by the cell. For example, to create and use a cell holding +an integer, one could write the following: + + object IntegerReference { + def main(args: Array[String]) { + val cell = new Reference[Int] + cell.set(13) + println("Reference contains the half of " + (cell.get * 2)) + } + } + +As can be seen in that example, it is not necessary to cast the value +returned by the `get` method before using it as an integer. It +is also not possible to store anything but an integer in that +particular cell, since it was declared as holding an integer. + +## Conclusion + +This document gave a quick overview of the Scala language and +presented some basic examples. The interested reader can go on, for example, by +reading the document *Scala By Example*, which +contains much more advanced examples, and consult the *Scala + Language Specification* when needed. diff --git a/_overviews/tutorials/scala-with-maven.md b/_overviews/tutorials/scala-with-maven.md new file mode 100644 index 0000000000..2b22614c52 --- /dev/null +++ b/_overviews/tutorials/scala-with-maven.md @@ -0,0 +1,302 @@ +--- +layout: overview +title: Scala with Maven + +discourse: true +--- + +By Adrian Null + +## Introduction +[Maven][1] is a build/project management tool. It favours "convention over configuration"; it can greatly simplify builds for "standard" projects and a Maven user can usually understand the structure of another Maven project just by looking at its `pom.xml` (Project Object Model). Maven is a plugin-based architecture, making it easy to add new libraries and modules to existing projects. For example, adding a new dependency usually involves only 5 extra lines in the `pom.xml`. These "artifacts" are downloaded from repositories such as [The Central Repository][2]. + +You can also check out the official [example project][36] which uses the same Scala plugin we will show here. + +## Jumping Ahead +If you're familiar with Maven, you can go ahead with the [Scala Maven Plugin][5]. + +## The Scala Maven Plugin +We'll be using the Scala Maven Plugin ([GitHub repo][5], [website][22]) (formerly known as the maven-scala-plugin; renamed to honour the new naming policy where only Maven core plugins are prefixed with "maven"), by far the dominant plugin for Scala projects. *Note: the plugin includes Scala from the Central Repository so there's no need to install it yourself if you're compiling with Maven.* + + +## Getting Maven + +### Linux (Debian) +On Debian and Debian-derivatives, Maven is usually available via `apt-get`. Just do `(sudo) apt-get install maven` and you're good to go. + +### OSX +OSX prior to 10.9 (Mavericks) comes with Maven 3 built in. +If you don't have it, you can get it with the package managers [MacPorts][4], [Homebrew][6], or [Fink][7]. +The Scala Maven Plugin requires Maven 3.0+ + +### Manually (Red Hat Linux, OSX, Windows) +You can download Maven from its [Apache homepage][3]. After extracting it (`tar -zxvf apache-maven-X.X.X-bin.tar.gz`, or use something like [7-zip][8]) to your directory of choice (on Linux and OSX, Unix-like systems, [I like to put them in `/opt/`][9]. On Windows I would probably put this in `C:/`), you need to add Maven to your environment Path variable: + +- Linux/OSX (option 1): Create a symlink to `/usr/bin`, which is already on your Path + - `ln -s /usr/bin/mvn /opt/apache-maven-X.X.X/bin/mvn` +- Linux/OSX (option 2): Add the Maven `bin` folder directly to your path, using your [shell configuration][10] file (e.g. `~/.bash_profile`) + - Add `export PATH=$PATH:/opt/apache-maven-X.X.X/bin` to `.bash_profile` (or whatever profile for the shell you use) + - Example: `echo "export PATH=$PATH:/opt/apache-maven-X.X.X/bin" >> ~/.bash_profile` +- Linux/OSX (option 3): Make a `mvn` shell script in an existing path location + - Example: you have `$HOME/bin` in your path + - Put the folder you extracted in `$HOME/bin` (`mv apache-maven-X.X.X "$HOME/bin/"`) + - Create a file `mvn` in `$HOME/bin` + - Add `"$HOME/bin/apache-maven-X.X.X/bin/mvn" $@` to it, and `chmod u+x mvn` to make it executable + - This is probably the least intrusive way; `$HOME/bin` is usually added to the user's path by default, and if not, it's a useful thing to do/have anyways. The shell script simply invokes the Maven location (which is at some other location) and passes on the arguments +- Windows + - Hit Start. Right click on "My Computer" and go to "Properties" + - This should bring you to "Control Panel -> System and Security -> System", giving an overview of your computer + - On the left sidebar there should be four options; click on "Advanced system settings" (fourth one) + - Under the "Advanced" tab, hit "Environment Variables..." in the bottom right + - Note: I recommend creating/editing your User variables (top box). You can do the same with System variables though (bottom box) + - Create a new variable called "MAVEN3_HOME". Point this to your Maven folder (e.g. `C:\apache-maven-X.X.X`). Use backslashes to be safe, and do not include a trailing slash + - Create a new variable called "MAVEN3_BIN" with this value: `%MAVEN3_HOME%\bin` + - Edit your Path variable: being careful not to change anything else, append `;%MAVEN3_BIN%` to it + - You'll need to restart cmd to see these changes + +## Creating Your First Project +The easiest way to create new projects is using an ["archetype"][11]. An archetype is a general skeleton structure, or template for a project. Think back to "convention over configuration"; in our case, the Scala Maven Plugin provides an archetype for scala projects. + +You run the archetype plugin like this: + + mvn archetype:generate + +If this is your first time, you'll notice that Maven is downloading many jar files. Maven resolves dependencies and downloads them as needed (and only once). Right now, Maven is downloading its core plugins. + +Afterwards, it should give you a list of archetypes (in the hundreds). +The Scala Maven Plugin was 339 on my list: "net.alchim31.maven:scala-archetype-simple (The maven-scala-plugin is used for compiling/testing/running/documenting scala code in maven.)". +You can type "scala" (or something else) to filter the results. +As of 2015 January 27, there you can choose version 3.1.4 or 3.1.5 of this plugin; you should choose the latest + + Choose net.alchim31.maven:scala-archetype-simple version: + 1: 1.4 + 2: 1.5 + +Next, Maven will ask you for a groupId, artifactId, and package. You can read the [guide to naming conventions][12], but in short: + +- groupId: inverted domain name (e.g. com.my-organization) +- artifactId: project name (e.g. playground-project) +- version: anything you want, but I recommend you read and follow the guidelines for [Semantic Versioning][13] (e.g. 0.0.1) +- package: the default is the groupId, but you can change this (e.g. com.my-organization) + +The groupId and artifactId together should serve as a globally unique identifier for your project + +When it's done, you should see a new folder named with the artifactId. `cd` into it and run: + + mvn package + +You'll see Maven downloading dependencies including the Scala library (as mentioned above), [JUnit][14], [ScalaTest][15], and [Specs2][16] (the latter three are test frameworks; the archetype includes an example "Hello world" program, and tests with each of the frameworks). + +### Explaining this Archetype +In your project root, you'll see a `pom.xml`, `src` folder, and `target` folder (target folder only appears after building). *Note: this archetype also includes a `.gitignore`* + +Inside the `src` folder you'll see `main` and `test`; `main` includes your application code, and `test` includes your test suites. Inside each of those you'll find a `scala` folder, followed by your package structure (actually, `test/scala` includes a sample package, but you should replace this with your own package and tests). If you want to mix Scala and Java source code, simply add a `java` folder inside `main` or `test`. + +`target` includes generated/built files, such as `.class` and `.jar` files. You can read about `pom.xml` at the [Maven page][18]. + +Example structure: + +- pom.xml +- src + - main + - scala + - com/my-package/... + *.scala + - java + - com/my-package/... + *.java + - test + - scala + - com/my-package/... + *.scala + - java + - com/my-package/... + *.java +- target + ... + +Again, you can read more about the Scala Maven Plugin at its [website][22]. + +### Creating a Jar +By default the jar created by the Scala Maven Plugin doesn't include a `Main-Class` attribute in the manifest. I had to add the [Maven Assembly Plugin][19] to my `pom.xml` in order to specify custom attributes in the manifest. You can check the latest version of this plugin at the [project summary][20] or at [The Central Repository][21] + + + X.X.X + ... + + ... + + + + ... + + + + ... + + + + ... + + ... + + org.apache.maven.plugins + maven-assembly-plugin + 2.4 + + + jar-with-dependencies + + + + com.your-package.MainClass + + + + + + package + + single + + + + + + + + +After adding this, `mvn package` will also create `[artifactId]-[version]-jar-with-dependencies.jar` under `target`. *Note: this will also copy the Scala library into your Jar. This is normal. Be careful that your dependencies use the same version of Scala, or you will quickly end up with a massive Jar.* + +### Useful commands +- `mvn dependency:copy-dependencies`: copy all libraries and dependencies to the `target/dependency` folder +- `mvn clean` +- `mvn package`: compile, run tests, and create jar + +### Integration with Eclipse ([Scala IDE][24]) +There are instructions at the [Scala Maven Plugin FAQs][23], but I thought I'd expand a bit. The [maven-eclipse-plugin][33] is a core plugin (all plugins prefixed with "maven" are, and are available by default) to generate Eclipse configuration files. However, this plugin does not know about our new Scala source files. We'll be using the [build-helper-maven-plugin][34] to add new source folders: + + ... + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + src/main/scala + + + + + add-test-source + generate-sources + + add-test-source + + + + src/test/scala + + + + + + ... + +After adding that to your `pom.xml`: + +1. Run `mvn eclipse:eclipse` - this generates the Eclipse project files (which are already ignored by our archetype's `.gitignore`) +2. Run `mvn -Declipse.workspace="path/to/your/eclipse/workspace" eclipse:configure-workspace` - this adds an `M2_REPO` classpath variable to Eclipse +3. Run `mvn package` to ensure you have all the dependencies in your local Maven repo + +Unfortunately, the integration isn't perfect. Firstly, open up the generated `.classpath` file (it will be hidden by default as it's a dotfile, but it should be in your project root directory; where you ran `mvn eclipse:eclipse`). You should see something like this near the top. + + + + +Change the `*.java` to `*.scala` (or duplicate the lines and change them to `*.scala` if you also have Java sources). + +Secondly, open the `.project` eclipse file (again, in the same folder). Change `` and `` to look like this. Now Eclipse knows to use the Scala editor and it won't think that everything is a syntax error. + + + + org.scala-ide.sdt.core.scalabuilder + + + + org.scala-ide.sdt.core.scalanature + org.eclipse.jdt.core.javanature + + +Finally, in Eclipse, under "File" choose "Import..." and find your project. + +#### Using [m2eclipse-scala][35] for Eclipse integration +m2eclipse-scala is a work in progress, and their website/repository may have updated information. +It aims to ease integration between m2eclipse and Scala IDE for Eclipse. + +Under "Help -> Install New Software", enter "http://alchim31.free.fr/m2e-scala/update-site" and hit enter. +You should see "Maven Integration for Eclipse -> Maven Integration for Scala IDE". + +Afer it installs, go to "New -> Project -> Other" and select "Maven Project". +Search fo "scala-archetype" choose the one with the group "net.alchim31.maven". +The wizard will more or less guide you through what was done with `mvn archetype:generate` above, and you should end up with a new Scala project! + +To import an existing project, simply go to "File -> Import -> Existing Maven Project" and find the directory containing your project. + +## Adding Dependencies +The first thing I do is look for "Maven" in the project page. For example, Google's [Guava] page includes [Maven Central links][28]. As you can see in the previous link, The Central Repository conveniently includes the snippet you have to add to your `pom.xml` on the left sidebar. + +If you can't find Maven information at the project page, try a Google search for "[project name] maven". Sometimes, you still won't find anything. For [scopt][29] (Scala command line option parser), I couldn't find the latest version from Google. However, [manually searching The Central Repository did][30] + +Afterwards, running + + mvn package + +Will download any new dependencies before packaging + +## Other Useful Reading +I'm not going to explain all of Maven in this tutorial (though I hope to add more in the future, because I do feel that the resources are a bit scattered), so here are some useful articles: +- [Maven Lifecycle][17] (explanation of goals like clean, package, install) + +[1]: http://maven.apache.org +[2]: http://maven.org +[3]: http://maven.apache.org/download.html +[4]: http://macports.org +[5]: https://github.com/davidB/scala-maven-plugin +[6]: http://brew.sh +[7]: http://fink.sf.net +[8]: http://7-zip.org +[9]: http://unix.stackexchange.com/questions/63531/ +[10]: http://wikipedia.org/wiki/Unix_shell#Configuration_files_for_shells +[11]: http://maven.apache.org/archetype/maven-archetype-plugin/ +[12]: http://maven.apache.org/guides/mini/guide-naming-conventions.html +[13]: http://semver.org +[14]: http://junit.org +[15]: http://scalatest.org +[16]: http://specs2.org +[17]: http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html +[18]: http://maven.apache.org/pom.html +[19]: http://maven.apache.org/plugins/maven-assembly-plugin/ +[20]: http://maven.apache.org/plugins/maven-assembly-plugin/project-summary.html +[21]: http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22maven-assembly-plugin%22 +[22]: http://davidb.github.io/scala-maven-plugin +[23]: http://davidb.github.io/scala-maven-plugin/faq.html +[24]: http://scala-ide.org +[25]: http://scala-lang.org/api/current/index.html#scala.App +[26]: http://docs.scala-lang.org/tutorials/tour/polymorphic-methods.html +[27]: https://code.google.com/p/guava-libraries/ +[28]: http://search.maven.org/#artifactdetails%7Ccom.google.guava%7Cguava%7C14.0.1%7Cbundle +[29]: https://github.com/scopt/scopt +[30]: http://search.maven.org/#search%7Cga%7C1%7Cscopt +[31]: http://mvnrepository.com +[32]: http://docs.scala-lang.org/contribute.html +[33]: http://maven.apache.org/plugins/maven-eclipse-plugin/ +[34]: http://mojo.codehaus.org/build-helper-maven-plugin/ +[35]: https://github.com/sonatype/m2eclipse-scala +[36]: https://github.com/scala/scala-module-dependency-sample diff --git a/_tour/Makefile b/_tour/Makefile new file mode 100644 index 0000000000..7e563a4bc8 --- /dev/null +++ b/_tour/Makefile @@ -0,0 +1,4 @@ +unified-types: + dot -Tsvg unified-types-diagram.dot -o unified-types-diagram.svg +type-casting: + dot -Tsvg type-casting-diagram.dot -o type-casting-diagram.svg diff --git a/_tour/abstract-types.md b/_tour/abstract-types.md new file mode 100644 index 0000000000..8479a60a03 --- /dev/null +++ b/_tour/abstract-types.md @@ -0,0 +1,73 @@ +--- +layout: tour +title: Abstract Types + +discourse: true + +partof: scala-tour + +num: 23 +next-page: compound-types +previous-page: inner-classes +prerequisite-knowledge: variance, upper-type-bound +--- + +Traits and abstract classes can have an abstract type member. This means that the concrete implementations define the actual type. Here's an example: + +```tut +trait Buffer { + type T + val element: T +} +``` +Here we have defined an abstract `type T`. It is used to describe the type of `element`. We can extend this trait in an abstract class, adding an upper-type-bound to `T` to make it more specific. + +```tut +abstract class SeqBuffer extends Buffer { + type U + type T <: Seq[U] + def length = element.length +} +``` +Notice how we can use yet another abstract `type U` as an upper-type-bound. This `class SeqBuffer` allows us to store only sequences in the buffer by stating that type `T` has to be a subtype of `Seq[U]` for a new abstract type `U`. + +Traits or [classes](classes.html) with abstract type members are often used in combination with anonymous class instantiations. To illustrate this, we now look at a program which deals with a sequence buffer that refers to a list of integers: + +```tut +abstract class IntSeqBuffer extends SeqBuffer { + type U = Int +} + + +def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer = + new IntSeqBuffer { + type T = List[U] + val element = List(elem1, elem2) + } +val buf = newIntSeqBuf(7, 8) +println("length = " + buf.length) +println("content = " + buf.element) +``` +Here the factory `newIntSeqBuf` uses an anonymous class implementation of `IntSeqBuf` (i.e. `new IntSeqBuffer`), setting `type T` to a `List[Int]`. + +It is also possible to turn abstract type members into type parameters of classes and vice versa. Here is a version of the code above which only uses type parameters: + +```tut +abstract class Buffer[+T] { + val element: T +} +abstract class SeqBuffer[U, +T <: Seq[U]] extends Buffer[T] { + def length = element.length +} + +def newIntSeqBuf(e1: Int, e2: Int): SeqBuffer[Int, Seq[Int]] = + new SeqBuffer[Int, List[Int]] { + val element = List(e1, e2) + } + +val buf = newIntSeqBuf(7, 8) +println("length = " + buf.length) +println("content = " + buf.element) +``` + +Note that we have to use [variance annotations](variances.html) here (`+T <: Seq[U]`) in order to hide the concrete sequence implementation type of the object returned from method `newIntSeqBuf`. Furthermore, there are cases where it is not possible to replace abstract types with type parameters. diff --git a/_tour/annotations.md b/_tour/annotations.md new file mode 100644 index 0000000000..0c00aa2b63 --- /dev/null +++ b/_tour/annotations.md @@ -0,0 +1,126 @@ +--- +layout: tour +title: Annotations + +discourse: true + +partof: scala-tour + +num: 32 +next-page: default-parameter-values +previous-page: by-name-parameters +--- + +Annotations associate meta-information with definitions. For example, the annotation `@deprecated` before a method causes the compiler to print a warning if the method is used. +``` +object DeprecationDemo extends App { + @deprecated + def hello = "hola" + + hello +} +``` +This will compile but the compiler will print a warning: "there was one deprecation warning". + +An annotation clause applies to the first definition or declaration following it. More than one annotation clause may precede a definition and declaration. The order in which these clauses are given does not matter. + + +## Annotations that ensure correctness of encodings +Certain annotations will actually cause compilation to fail if a condition(s) is not met. For example, the annotation `@tailrec` ensures that a method is [tail-recursive](https://en.wikipedia.org/wiki/Tail_call). Tail-recursion can keep memory requirements constant. Here's how it's used in a method which calculates the factorial: +```tut +import scala.annotation.tailrec + +def factorial(x: Int): Int = { + + @tailrec + def factorialHelper(x: Int, accumulator: Int): Int = { + if (x == 1) accumulator else factorialHelper(x - 1, accumulator * x) + } + factorialHelper(x, 1) +} +``` +The `factorialHelper` method has the `@tailrec` which ensures the method is indeed tail-recursive. If we were to change the implementation of `factorialHelper` to the following, it would fail: +``` +import scala.annotation.tailrec + +def factorial(x: Int): Int = { + @tailrec + def factorialHelper(x: Int): Int = { + if (x == 1) 1 else x * factorialHelper(x - 1) + } + factorialHelper(x) +} +``` +We would get the message "Recursive call not in tail position". + + +## Annotations affecting code generation +Some annotations like `@inline` affect the generated code (i.e. your jar file might have different bytes than if you hadn't used the annotation). Inlining means inserting the code in a method's body at the call site. The resulting bytecode is longer, but hopefully runs faster. Using the annotation `@inline` does not ensure that a method will be inlined, but it will cause the compiler to do it if and only if some heuristics about the size of the generated code are met. + +### Java Annotations ### +When writing Scala code which interoperates with Java, there are a few differences in annotation syntax to note. +**Note:** Make sure you use the `-target:jvm-1.8` option with Java annotations. + +Java has user-defined metadata in the form of [annotations](https://docs.oracle.com/javase/tutorial/java/annotations/). A key feature of annotations is that they rely on specifying name-value pairs to initialize their elements. For instance, if we need an annotation to track the source of some class we might define it as + +``` +@interface Source { + public String URL(); + public String mail(); +} +``` + +And then apply it as follows + +``` +@Source(URL = "http://coders.com/", + mail = "support@coders.com") +public class MyClass extends HisClass ... +``` + +An annotation application in Scala looks like a constructor invocation, for instantiating a Java annotation one has to use named arguments: + +``` +@Source(URL = "http://coders.com/", + mail = "support@coders.com") +class MyScalaClass ... +``` + +This syntax is quite tedious if the annotation contains only one element (without default value) so, by convention, if the name is specified as `value` it can be applied in Java using a constructor-like syntax: + +``` +@interface SourceURL { + public String value(); + public String mail() default ""; +} +``` + +And then apply it as follows + +``` +@SourceURL("http://coders.com/") +public class MyClass extends HisClass ... +``` + +In this case, Scala provides the same possibility + +``` +@SourceURL("http://coders.com/") +class MyScalaClass ... +``` + +The `mail` element was specified with a default value so we need not explicitly provide a value for it. However, if we need to do it we can not mix-and-match the two styles in Java: + +``` +@SourceURL(value = "http://coders.com/", + mail = "support@coders.com") +public class MyClass extends HisClass ... +``` + +Scala provides more flexibility in this respect + +``` +@SourceURL("http://coders.com/", + mail = "support@coders.com") + class MyScalaClass ... +``` diff --git a/_tour/basics.md b/_tour/basics.md new file mode 100644 index 0000000000..157b3e2aac --- /dev/null +++ b/_tour/basics.md @@ -0,0 +1,311 @@ +--- +layout: tour +title: Basics + +discourse: true + +partof: scala-tour + +num: 2 +next-page: unified-types +previous-page: tour-of-scala +--- + +In this page, we will cover basics of Scala. + +## Trying Scala in the Browser + +You can run Scala in your browser with ScalaFiddle. + +1. Go to [https://scalafiddle.io](https://scalafiddle.io). +2. Paste `println("Hello, world!")` in the left pane. +3. Hit "Run" button. Output appears in the right pane. + +This is an easy, zero-setup way to experiment with pieces of Scala code. + +## Expressions + +Expressions are computable statements. +``` +1 + 1 +``` +You can output results of expressions using `println`. + +```tut +println(1) // 1 +println(1 + 1) // 2 +println("Hello!") // Hello! +println("Hello," + " world!") // Hello, world! +``` + +### Values + +You can name results of expressions with the `val` keyword. + +```tut +val x = 1 + 1 +println(x) // 2 +``` + +Named results, such as `x` here, are called values. Referencing +a value does not re-compute it. + +Values cannot be re-assigned. + +```tut:nofail +val x = 1 + 1 +x = 3 // This does not compile. +``` + +Types of values can be inferred, but you can also explicitly state the type, like this: + +```tut +val x: Int = 1 + 1 +``` + +Notice how the type declaration `Int` comes after the identifier `x`. You also need a `:`. + +### Variables + +Variables are like values, except you can re-assign them. You can define a variable with the `var` keyword. + +```tut +var x = 1 + 1 +x = 3 // This compiles because "x" is declared with the "var" keyword. +println(x * x) // 9 +``` + +As with values, you can explicitly state the type if you want: + +```tut +var x: Int = 1 + 1 +``` + + +## Blocks + +You can combine expressions by surrounding them with `{}`. We call this a block. + +The result of the last expression in the block is the result of the overall block, too. + +```tut +println({ + val x = 1 + 1 + x + 1 +}) // 3 +``` + +## Functions + +Functions are expressions that take parameters. + +You can define an anonymous function (i.e. no name) that returns a given integer plus one: + +```tut +(x: Int) => x + 1 +``` + +On the left of `=>` is a list of parameters. On the right is an expression involving the parameters. + +You can also name functions. + +```tut +val addOne = (x: Int) => x + 1 +println(addOne(1)) // 2 +``` + +Functions may take multiple parameters. + +```tut +val add = (x: Int, y: Int) => x + y +println(add(1, 2)) // 3 +``` + +Or it can take no parameters. + +```tut +val getTheAnswer = () => 42 +println(getTheAnswer()) // 42 +``` + +We will cover functions in depth [later](anonymous-function-syntax.html). + +## Methods + +Methods look and behave very similar to functions, but there are a few key differences between them. + +Methods are defined with the `def` keyword. `def` is followed by a name, parameter lists, a return type, and a body. + +```tut +def add(x: Int, y: Int): Int = x + y +println(add(1, 2)) // 3 +``` + +Notice how the return type is declared _after_ the parameter list and a colon `: Int`. + +Methods can take multiple parameter lists. + +```tut +def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier +println(addThenMultiply(1, 2)(3)) // 9 +``` + +Or no parameter lists at all. + +```tut +def name: String = System.getProperty("name") +println("Hello, " + name + "!") +``` + +There are some other differences, but for now, you can think of them as something similar to functions. + +Methods can have multi-line expressions as well. +```tut +def getSquareString(input: Double): String = { + val square = input * input + square.toString +} +``` +The last expression in the body is the method's return value. (Scala does have a `return` keyword, but it's rarely used.) + +## Classes + +You can define classes with the `class` keyword followed by its name and constructor parameters. + +```tut +class Greeter(prefix: String, suffix: String) { + def greet(name: String): Unit = + println(prefix + name + suffix) +} +``` +The return type of the method `greet` is `Unit`, which says there's nothing meaningful to return. It's used similarly to `void` in Java and C. (A difference is that because every Scala expression must have some value, there is actually a singleton value of type Unit, written (). It carries no information.) + +You can make an instance of a class with the `new` keyword. + +```tut +val greeter = new Greeter("Hello, ", "!") +greeter.greet("Scala developer") // Hello, Scala developer! +``` + +We will cover classes in depth [later](classes.html). + +## Case Classes + +Scala has a special type of class called a "case" class. By default, case classes are immutable and compared by value. You can define case classes with the `case class` keywords. + +```tut +case class Point(x: Int, y: Int) +``` + +You can instantiate case classes without `new` keyword. + +```tut +val point = Point(1, 2) +val anotherPoint = Point(1, 2) +val yetAnotherPoint = Point(2, 2) +``` + +And they are compared by value. + +```tut +if (point == anotherPoint) { + println(point + " and " + anotherPoint + " are the same.") +} else { + println(point + " and " + anotherPoint + " are different.") +} +// Point(1,2) and Point(1,2) are the same. + +if (point == yetAnotherPoint) { + println(point + " and " + yetAnotherPoint + " are the same.") +} else { + println(point + " and " + yetAnotherPoint + " are different.") +} +// Point(1,2) and Point(2,2) are different. +``` + +There is a lot more to case classes that we'd like to introduce, and we are convinced you will fall in love with them! We will cover them in depth [later](case-classes.html). + +## Objects + +Objects are single instances of their own definitions. You can think of them as singletons of their own classes. + +You can define objects with the `object` keyword. + +```tut +object IdFactory { + private var counter = 0 + def create(): Int = { + counter += 1 + counter + } +} +``` + +You can access an object by referring to its name. + +```tut +val newId: Int = IdFactory.create() +println(newId) // 1 +val newerId: Int = IdFactory.create() +println(newerId) // 2 +``` + +We will cover objects in depth [later](singleton-objects.html). + +## Traits + +Traits are types containing certain fields and methods. Multiple traits can be combined. + +You can define traits with `trait` keyword. + +```tut +trait Greeter { + def greet(name: String): Unit +} +``` + +Traits can also have default implementations. + +```tut +trait Greeter { + def greet(name: String): Unit = + println("Hello, " + name + "!") +} +``` + +You can extend traits with the `extends` keyword and override an implementation with the `override` keyword. + +```tut +class DefaultGreeter extends Greeter + +class CustomizableGreeter(prefix: String, postfix: String) extends Greeter { + override def greet(name: String): Unit = { + println(prefix + name + postfix) + } +} + +val greeter = new DefaultGreeter() +greeter.greet("Scala developer") // Hello, Scala developer! + +val customGreeter = new CustomizableGreeter("How are you, ", "?") +customGreeter.greet("Scala developer") // How are you, Scala developer? +``` + +Here, `DefaultGreeter` extends only a single trait, but it could extend multiple traits. + +We will cover traits in depth [later](traits.html). + +## Main Method + +The main method is an entry point of a program. The Java Virtual +Machine requires a main method to be named `main` and take one +argument, an array of strings. + +Using an object, you can define a main method as follows: + +```tut +object Main { + def main(args: Array[String]): Unit = + println("Hello, Scala developer!") +} +``` diff --git a/_tour/by-name-parameters.md b/_tour/by-name-parameters.md new file mode 100644 index 0000000000..18b7d2ec1a --- /dev/null +++ b/_tour/by-name-parameters.md @@ -0,0 +1,40 @@ +--- +layout: tour +title: By-name Parameters + +discourse: true + +partof: scala-tour + +num: 31 +next-page: annotations +previous-page: operators +--- + +_By-name parameters_ are only evaluated when used. They are in contrast to _by-value parameters_. To make a parameter called by-name, simply prepend `=>` to its type. +```tut +def calculate(input: => Int) = input * 37 +``` +By-name parameters have the the advantage that they are not evaluated if they aren't used in the function body. On the other hand, by-value parameters have the advantage that they are evaluated only once. + +Here's an example of how we could implement a while loop: + +```tut +def whileLoop(condition: => Boolean)(body: => Unit): Unit = + if (condition) { + body + whileLoop(condition)(body) + } + +var i = 2 + +whileLoop (i > 0) { + println(i) + i -= 1 +} // prints 2 1 +``` +The method `whileLoop` uses multiple parameter lists to take a condition and a body of the loop. If the `condition` is true, the `body` is executed and then a recursive call to whileLoop is made. If the `condition` is false, the body is never evaluated because we prepended `=>` to the type of `body`. + +Now when we pass `i > 0` as our `condition` and `println(i); i-= 1` as the `body`, it behaves like the standard while loop in many languages. + +This ability to delay evaluation of a parameter until it is used can help performance if the parameter is computationally intensive to evaluate or a longer-running block of code such as fetching a URL. diff --git a/_tour/case-classes.md b/_tour/case-classes.md new file mode 100644 index 0000000000..c49f17d928 --- /dev/null +++ b/_tour/case-classes.md @@ -0,0 +1,57 @@ +--- +layout: tour +title: Case Classes + +discourse: true + +partof: scala-tour + +num: 11 +next-page: pattern-matching +previous-page: currying +prerequisite-knowledge: classes, basics, mutability +--- + +Case classes are like regular classes with a few key differences which we will go over. Case classes are good for modeling immutable data. In the next step of the tour, we'll see how they are useful in [pattern matching](pattern-matching.html). + +## Defining a case class +A minimal case class requires the keywords `case class`, an identifier, and a parameter list (which may be empty): +```tut +case class Book(isbn: String) + +val frankenstein = Book("978-0486282114") +``` +Notice how the keyword `new` was not used to instantiate the `Message` case class. This is because case classes have an `apply` method by default which takes care of object construction. + +When you create a case class with parameters, the parameters are public `val`s. +``` +case class Message(sender: String, recipient: String, body: String) +val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?") + +println(message1.sender) // prints guillaume@quebec.ca +message1.sender = "travis@washington.us" // this line does not compile +``` +You can't reassign `message1.sender` because it is a `val` (i.e. immutable). It is possible to use `var`s in case classes but this is discouraged. + +## Comparison +Case classes are compared by structure and not by reference: +``` +case class Message(sender: String, recipient: String, body: String) + +val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") +val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") +val messagesAreTheSame = message2 == message3 // true +``` +Even though `message2` and `message3` refer to different objects, the value of each object is equal. + +## Copying +You can create a deep copy of an instance of a case class simply by using the `copy` method. You can optionally change the constructor arguments. +``` +case class Message(sender: String, recipient: String, body: String) +val message4 = Message("julien@bretagne.fr", "travis@washington.us", "Me zo o komz gant ma amezeg") +val message5 = message4.copy(sender = message4.recipient, recipient = "claire@bourgogne.fr") +message5.sender // travis@washington.us +message5.recipient // claire@bourgogne.fr +message5.body // "Me zo o komz gant ma amezeg" +``` +The recipient of `message4` is used as the sender of `message5` but the `body` of `message4` was copied directly. diff --git a/_tour/classes.md b/_tour/classes.md new file mode 100644 index 0000000000..6f4a842f85 --- /dev/null +++ b/_tour/classes.md @@ -0,0 +1,111 @@ +--- +layout: tour +title: Classes + +discourse: true + +partof: scala-tour + +num: 4 +next-page: traits +previous-page: unified-types +topics: classes +prerequisite-knowledge: no-return-keyword, type-declaration-syntax, string-interpolation, procedures +--- + +Classes in Scala are blueprints for creating objects. They can contain methods, +values, variables, types, objects, traits, and classes which are collectively called _members_. Types, objects, and traits will be covered later in the tour. + +## Defining a class +A minimal class definition is simply the keyword `class` and +an identifier. Class names should be capitalized. +```tut +class User + +val user1 = new User +``` +The keyword `new` is used to create an instance of the class. `User` has a default constructor which takes no arguments because no constructor was defined. However, you'll often want a constructor and class body. Here is an example class definition for a point: + +```tut +class Point(var x: Int, var y: Int) { + + def move(dx: Int, dy: Int): Unit = { + x = x + dx + y = y + dy + } + + override def toString: String = + s"($x, $y)" +} + +val point1 = new Point(2, 3) +point1.x // 2 +println(point1) // prints (x, y) +``` + +This `Point` class has four members: the variables `x` and `y` and the methods `move` and +`toString`. Unlike many other languages, the primary constructor is in the class signature `(var x: Int, var y: Int)`. The `move` method takes two integer arguments and returns the Unit value `()`, which carries no information. This corresponds roughly with `void` in Java-like languages. `toString`, on the other hand, does not take any arguments but returns a `String` value. Since `toString` overrides `toString` from [`AnyRef`](unified-types.html), it is tagged with the `override` keyword. + +## Constructors + +Constructors can have optional parameters by providing a default value like so: + +```tut +class Point(var x: Int = 0, var y: Int = 0) + +val origin = new Point // x and y are both set to 0 +val point1 = new Point(1) +println(point1.x) // prints 1 + +``` + +In this version of the `Point` class, `x` and `y` have the default value `0` so no arguments are required. However, because the constructor reads arguments left to right, if you just wanted to pass in a `y` value, you would need to name the parameter. +``` +class Point(var x: Int = 0, var y: Int = 0) +val point2 = new Point(y=2) +println(point2.y) // prints 2 +``` + +This is also a good practice to enhance clarity. + +## Private Members and Getter/Setter Syntax +Members are public by default. Use the `private` access modifier +to hide them from outside of the class. +```tut +class Point { + private var _x = 0 + private var _y = 0 + private val bound = 100 + + def x = _x + def x_= (newValue: Int): Unit = { + if (newValue < bound) _x = newValue else printWarning + } + + def y = _y + def y_= (newValue: Int): Unit = { + if (newValue < bound) _y = newValue else printWarning + } + + private def printWarning = println("WARNING: Out of bounds") +} + +val point1 = new Point +point1.x = 99 +point1.y = 101 // prints the warning +``` +In this version of the `Point` class, the data is stored in private variables `_x` and `_y`. There are methods `def x` and `def y` for accessing the private data. `def x_=` and `def y_=` are for validating and setting the value of `_x` and `_y`. Notice the special syntax for the setters: the method has `_=` appended to the identifier of the getter and the parameters come after. + +Primary constructor parameters with `val` and `var` are public. However, because `val`s are immutable, you can't write the following. +``` +class Point(val x: Int, val y: Int) +val point = new Point(1, 2) +point.x = 3 // <-- does not compile +``` + +Parameters without `val` or `var` are private values, visible only within the class. +``` +class Point(x: Int, y: Int) +val point = new Point(1, 2) +point.x // <-- does not compile +``` diff --git a/_tour/compound-types.md b/_tour/compound-types.md new file mode 100644 index 0000000000..6e76997e4d --- /dev/null +++ b/_tour/compound-types.md @@ -0,0 +1,52 @@ +--- +layout: tour +title: Compound Types + +discourse: true + +partof: scala-tour + +num: 24 +next-page: self-types +previous-page: abstract-types +--- + +Sometimes it is necessary to express that the type of an object is a subtype of several other types. In Scala this can be expressed with the help of *compound types*, which are intersections of object types. + +Suppose we have two traits `Cloneable` and `Resetable`: + +```tut +trait Cloneable extends java.lang.Cloneable { + override def clone(): Cloneable = { + super.clone().asInstanceOf[Cloneable] + } +} +trait Resetable { + def reset: Unit +} +``` + +Now suppose we want to write a function `cloneAndReset` which takes an object, clones it and resets the original object: + +``` +def cloneAndReset(obj: ?): Cloneable = { + val cloned = obj.clone() + obj.reset + cloned +} +``` + +The question arises what the type of the parameter `obj` is. If it's `Cloneable` then the object can be `clone`d, but not `reset`; if it's `Resetable` we can `reset` it, but there is no `clone` operation. To avoid type casts in such a situation, we can specify the type of `obj` to be both `Cloneable` and `Resetable`. This compound type is written like this in Scala: `Cloneable with Resetable`. + +Here's the updated function: + +``` +def cloneAndReset(obj: Cloneable with Resetable): Cloneable = { + //... +} +``` + +Compound types can consist of several object types and they may have a single refinement which can be used to narrow the signature of existing object members. +The general form is: `A with B with C ... { refinement }` + +An example for the use of refinements is given on the page about [abstract types](abstract-types.html). diff --git a/_tour/currying.md b/_tour/currying.md new file mode 100644 index 0000000000..00aeef1c5a --- /dev/null +++ b/_tour/currying.md @@ -0,0 +1,41 @@ +--- +layout: tour +title: Currying + +discourse: true + +partof: scala-tour + +num: 10 +next-page: case-classes +previous-page: nested-functions +--- + +Methods may define multiple parameter lists. When a method is called with a fewer number of parameter lists, then this will yield a function taking the missing parameter lists as its arguments. + +Here is an example: + +```tut +object CurryTest extends App { + + def filter(xs: List[Int], p: Int => Boolean): List[Int] = + if (xs.isEmpty) xs + else if (p(xs.head)) xs.head :: filter(xs.tail, p) + else filter(xs.tail, p) + + def modN(n: Int)(x: Int) = ((x % n) == 0) + + val nums = List(1, 2, 3, 4, 5, 6, 7, 8) + println(filter(nums, modN(2))) + println(filter(nums, modN(3))) +} +``` + +_Note: method `modN` is partially applied in the two `filter` calls; i.e. only its first argument is actually applied. The term `modN(2)` yields a function of type `Int => Boolean` and is thus a possible candidate for the second argument of function `filter`._ + +Here's the output of the program above: + +``` +List(2,4,6,8) +List(3,6) +``` diff --git a/_tour/default-parameter-values.md b/_tour/default-parameter-values.md new file mode 100644 index 0000000000..4598d8259a --- /dev/null +++ b/_tour/default-parameter-values.md @@ -0,0 +1,47 @@ +--- +layout: tour +title: Default Parameter Values + +discourse: true + +partof: scala-tour + +num: 33 +next-page: named-arguments +previous-page: annotations +prerequisite-knowledge: named-arguments, function syntax +--- + +Scala provides the ability to give parameters default values that can be used to allow a caller to omit those parameters. + +```tut +def log(message: String, level: String = "INFO") = println(s"$level: $message") + +log("System starting") // prints INFO: System starting +log("User not found", "WARNING") // prints WARNING: User not found +``` + +The parameter `level` has a default value so it is optional. On the last line, the argument `"WARNING"` overrides the default argument `"INFO"`. Where you might do overloaded methods in Java, you can use methods with optional parameters to achieve the same effect. However, if the caller omits an argument, any following arguments must be named. + +```tut +class Point(val x: Double = 0, val y: Double = 0) + +val point1 = new Point(y = 1) +``` +Here we have to say `y = 1`. + +Note that default parameters in Scala are not optional when called from Java code: + +```tut +// Point.scala +class Point(val x: Double = 0, val y: Double = 0) +``` + +```java +// Main.java +public class Main { + public static void main(String[] args) { + Point point = new Point(1); // does not compile + } +} +``` diff --git a/_tour/dot-hot-reload.sh b/_tour/dot-hot-reload.sh new file mode 100755 index 0000000000..92f4531240 --- /dev/null +++ b/_tour/dot-hot-reload.sh @@ -0,0 +1 @@ +ls *.dot | entr make $1 # Choose either unified-types or type-casting (see Makefile) diff --git a/_tour/extractor-objects.md b/_tour/extractor-objects.md new file mode 100644 index 0000000000..8c3265f5f7 --- /dev/null +++ b/_tour/extractor-objects.md @@ -0,0 +1,58 @@ +--- +layout: tour +title: Extractor Objects + +discourse: true + +partof: scala-tour + +num: 16 +next-page: for-comprehensions +previous-page: regular-expression-patterns +--- + +An extractor object is an object with an `unapply` method. Whereas the `apply` method is like a constructor which takes arguments and creates an object, the `unapply` takes an object and tries to give back the arguments. This is most often used in pattern matching and partial functions. + +```tut +import scala.util.Random + +object CustomerID { + + def apply(name: String) = s"$name--${Random.nextLong}" + + def unapply(customerID: String): Option[String] = { + val name = customerID.split("--").head + if (name.nonEmpty) Some(name) else None + } +} + +val customer1ID = CustomerID("Sukyoung") // Sukyoung--23098234908 +customer1ID match { + case CustomerID(name) => println(name) // prints Sukyoung + case _ => println("Could not extract a CustomerID") +} +``` + +The `apply` method creates a `CustomerID` string from a `name`. The `unapply` does the inverse to get the `name` back. When we call `CustomerID("Sukyoung")`, this is shorthand syntax for calling `CustomerID.apply("Sukyoung")`. When we call `case CustomerID(name) => customer1ID`, we're calling the unapply method. + +The unapply method can also be used to assign a value. + +```tut +val customer2ID = CustomerID("Nico") +val CustomerID(name) = customer2ID +println(name) // prints Nico +``` + +This is equivalent to `val name = CustomerID.unapply(customer2ID).get`. If there is no match, a `scala.MatchError` is thrown: + +```tut:fail +val CustomerID(name2) = "--asdfasdfasdf" +``` + +The return type of an `unapply` should be chosen as follows: + +* If it is just a test, return a `Boolean`. For instance `case even()` +* If it returns a single sub-value of type T, return an `Option[T]` +* If you want to return several sub-values `T1,...,Tn`, group them in an optional tuple `Option[(T1,...,Tn)]`. + +Sometimes, the number of sub-values is fixed and we would like to return a sequence. For this reason, you can also define patterns through `unapplySeq` which returns `Option[Seq[T]]` This mechanism is used for instance in pattern `case List(x1, ..., xn)`. diff --git a/_tour/for-comprehensions.md b/_tour/for-comprehensions.md new file mode 100644 index 0000000000..54fb57203f --- /dev/null +++ b/_tour/for-comprehensions.md @@ -0,0 +1,51 @@ +--- +layout: tour +title: For Comprehensions + +discourse: true + +partof: scala-tour + +num: 17 +next-page: generic-classes +previous-page: extractor-objects +--- + +Scala offers a lightweight notation for expressing *sequence comprehensions*. Comprehensions have the form `for (enumerators) yield e`, where `enumerators` refers to a semicolon-separated list of enumerators. An *enumerator* is either a generator which introduces new variables, or it is a filter. A comprehension evaluates the body `e` for each binding generated by the enumerators and returns a sequence of these values. + +Here's an example: + +```tut +case class User(val name: String, val age: Int) + +val userBase = List(new User("Travis", 28), + new User("Kelly", 33), + new User("Jennifer", 44), + new User("Dennis", 23)) + +val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30)) + yield user.name // i.e. add this to a list + +twentySomethings.foreach(name => println(name)) // prints Travis Dennis +``` +The `for` loop used with a `yield` statement actually creates a `List`. Because we said `yield user.name`, it's a `List[String]`. `user <- userBase` is our generator and `if (user.age >=20 && user.age < 30)` is a guard that filters out users who are in their 20s. + +Here is a more complicated example using two generators. It computes all pairs of numbers between `0` and `n-1` whose sum is equal to a given value `v`: + +```tut +def foo(n: Int, v: Int) = + for (i <- 0 until n; + j <- i until n if i + j == v) + yield (i, j) + +foo(10, 10) foreach { + case (i, j) => + print(s"($i, $j) ") // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) +} + +``` +Here `n == 10` and `v == 10`. On the first iteration, `i == 0` and `j == 0` so `i + j != v` and therefore nothing is yielded. `j` gets incremented 9 more times before `i` gets incremented to `1`. Without the `if` guard, this would simply print the following: +``` + +(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 1) ... +``` diff --git a/_tour/generic-classes.md b/_tour/generic-classes.md new file mode 100644 index 0000000000..3c3120ad0c --- /dev/null +++ b/_tour/generic-classes.md @@ -0,0 +1,57 @@ +--- +layout: tour +title: Generic Classes + +discourse: true + +partof: scala-tour + +num: 18 +next-page: variances +previous-page: for-comprehensions +assumed-knowledge: classes unified-types +--- +Generic classes are classes which take a type as a parameter. They are particularly useful for collection classes. + +## Defining a generic class +Generic classes take a type as a parameter within square brackets `[]`. One convention is to use the letter `A` as type parameter identifier, though any parameter name may be used. +```tut +class Stack[A] { + private var elements: List[A] = Nil + def push(x: A) { elements = x :: elements } + def peek: A = elements.head + def pop(): A = { + val currentTop = peek + elements = elements.tail + currentTop + } +} +``` +This implementation of a `Stack` class takes any type `A` as a parameter. This means the underlying list, `var elements: List[A] = Nil`, can only store elements of type `A`. The procedure `def push` only accepts objects of type `A` (note: `elements = x :: elements` reassigns `elements` to a new list created by prepending `x` to the current `elements`). + +## Usage + +To use a generic class, put the type in the square brackets in place of `A`. +``` +val stack = new Stack[Int] +stack.push(1) +stack.push(2) +println(stack.pop) // prints 2 +println(stack.pop) // prints 1 +``` +The instance `stack` can only take Ints. However, if the type argument had subtypes, those could be passed in: +``` +class Fruit +class Apple extends Fruit +class Banana extends Fruit + +val stack = new Stack[Fruit] +val apple = new Apple +val banana = new Banana + +stack.push(apple) +stack.push(banana) +``` +Class `Apple` and `Banana` both extend `Fruit` so we can push instances `apple` and `banana` onto the stack of `Fruit`. + +_Note: subtyping of generic types is *invariant*. This means that if we have a stack of characters of type `Stack[Char]` then it cannot be used as an integer stack of type `Stack[Int]`. This would be unsound because it would enable us to enter true integers into the character stack. To conclude, `Stack[A]` is only a subtype of `Stack[B]` if and only if `B = A`. Since this can be quite restrictive, Scala offers a [type parameter annotation mechanism](variances.html) to control the subtyping behavior of generic types._ diff --git a/_tour/higher-order-functions.md b/_tour/higher-order-functions.md new file mode 100644 index 0000000000..f021b94b8a --- /dev/null +++ b/_tour/higher-order-functions.md @@ -0,0 +1,42 @@ +--- +layout: tour +title: Higher-order Functions + +discourse: true + +partof: scala-tour + +num: 8 +next-page: nested-functions +previous-page: mixin-class-composition +--- + +Scala allows the definition of higher-order functions. These are functions that _take other functions as parameters_, or whose _result is a function_. Here is a function `apply` which takes another function `f` and a value `v` and applies function `f` to `v`: + +```tut +def apply(f: Int => String, v: Int) = f(v) +``` + +_Note: methods are automatically coerced to functions if the context requires this._ + +Here is another example: + +```tut +class Decorator(left: String, right: String) { + def layout[A](x: A) = left + x.toString() + right +} + +object FunTest extends App { + def apply(f: Int => String, v: Int) = f(v) + val decorator = new Decorator("[", "]") + println(apply(decorator.layout, 7)) +} +``` + +Execution yields the output: + +``` +[7] +``` + +In this example, the method `decorator.layout` is coerced automatically to a value of type `Int => String` as required by method `apply`. Please note that method `decorator.layout` is a _polymorphic method_ (i.e. it abstracts over some of its signature types) and the Scala compiler has to instantiate its method type first appropriately. diff --git a/_tour/implicit-conversions.md b/_tour/implicit-conversions.md new file mode 100644 index 0000000000..bd38bead3c --- /dev/null +++ b/_tour/implicit-conversions.md @@ -0,0 +1,59 @@ +--- +layout: tour +title: Implicit Conversions + +discourse: true + +partof: scala-tour + +num: 27 +next-page: polymorphic-methods +previous-page: implicit-parameters +--- + +An implicit conversion from type `S` to type `T` is defined by an implicit value which has function type `S => T`, or by an implicit method convertible to a value of that type. + +Implicit conversions are applied in two situations: + +* If an expression `e` is of type `S`, and `S` does not conform to the expression's expected type `T`. +* In a selection `e.m` with `e` of type `S`, if the selector `m` does not denote a member of `S`. + +In the first case, a conversion `c` is searched for which is applicable to `e` and whose result type conforms to `T`. +In the second case, a conversion `c` is searched for which is applicable to `e` and whose result contains a member named `m`. + +The following operation on the two lists xs and ys of type `List[Int]` is legal: + +``` +xs <= ys +``` + +assuming the implicit methods `list2ordered` and `int2ordered` defined below are in scope: + +``` +implicit def list2ordered[A](x: List[A]) + (implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] = + new Ordered[List[A]] { /* .. */ } + +implicit def int2ordered(x: Int): Ordered[Int] = + new Ordered[Int] { /* .. */ } +``` + +The implicitly imported object `scala.Predef` declares several predefined types (e.g. `Pair`) and methods (e.g. `assert`) but also several implicit conversions. + +For example, when calling a Java method that expects a `java.lang.Integer`, you are free to pass it a `scala.Int` instead. That's because Predef includes the following implicit conversions: + +```tut +import scala.language.implicitConversions + +implicit def int2Integer(x: Int) = + java.lang.Integer.valueOf(x) +``` + +Because implicit conversions can have pitfalls if used indiscriminately the compiler warns when compiling the implicit conversion definition. + +To turn off the warnings take either of these actions: + +* Import `scala.language.implicitConversions` into the scope of the implicit conversion definition +* Invoke the compiler with `-language:implicitConversions` + +No warning is emitted when the conversion is applied by the compiler. diff --git a/_tour/implicit-parameters.md b/_tour/implicit-parameters.md new file mode 100644 index 0000000000..76f7317233 --- /dev/null +++ b/_tour/implicit-parameters.md @@ -0,0 +1,58 @@ +--- +layout: tour +title: Implicit Parameters + +discourse: true + +partof: scala-tour + +num: 26 +next-page: implicit-conversions +previous-page: self-types +--- + +A method with _implicit parameters_ can be applied to arguments just like a normal method. In this case the implicit label has no effect. However, if such a method misses arguments for its implicit parameters, such arguments will be automatically provided. + +The actual arguments that are eligible to be passed to an implicit parameter fall into two categories: + +* First, eligible are all identifiers x that can be accessed at the point of the method call without a prefix and that denote an implicit definition or an implicit parameter. +* Second, eligible are also all members of companion modules of the implicit parameter's type that are labeled implicit. + +In the following example we define a method `sum` which computes the sum of a list of elements using the monoid's `add` and `unit` operations. Please note that implicit values can not be top-level, they have to be members of a template. + +```tut +/** This example uses a structure from abstract algebra to show how implicit parameters work. A semigroup is an algebraic structure on a set A with an (associative) operation, called add here, that combines a pair of A's and returns another A. */ +abstract class SemiGroup[A] { + def add(x: A, y: A): A +} +/** A monoid is a semigroup with a distinguished element of A, called unit, that when combined with any other element of A returns that other element again. */ +abstract class Monoid[A] extends SemiGroup[A] { + def unit: A +} +object ImplicitTest extends App { + /** To show how implicit parameters work, we first define monoids for strings and integers. The implicit keyword indicates that the corresponding object can be used implicitly, within this scope, as a parameter of a function marked implicit. */ + implicit object StringMonoid extends Monoid[String] { + def add(x: String, y: String): String = x concat y + def unit: String = "" + } + implicit object IntMonoid extends Monoid[Int] { + def add(x: Int, y: Int): Int = x + y + def unit: Int = 0 + } + /** This method takes a List[A] returns an A which represent the combined value of applying the monoid operation successively across the whole list. Making the parameter m implicit here means we only have to provide the xs parameter at the call site, since if we have a List[A] we know what type A actually is and therefore what type Monoid[A] is needed. We can then implicitly find whichever val or object in the current scope also has that type and use that without needing to specify it explicitly. */ + def sum[A](xs: List[A])(implicit m: Monoid[A]): A = + if (xs.isEmpty) m.unit + else m.add(xs.head, sum(xs.tail)) + + /** Here we call sum twice, with only one parameter each time. Since the second parameter of sum, m, is implicit its value is looked up in the current scope, based on the type of monoid required in each case, meaning both expressions can be fully evaluated. */ + println(sum(List(1, 2, 3))) // uses IntMonoid implicitly + println(sum(List("a", "b", "c"))) // uses StringMonoid implicitly +} +``` + +Here is the output of the Scala program: + +``` +6 +abc +``` diff --git a/_tour/inner-classes.md b/_tour/inner-classes.md new file mode 100644 index 0000000000..f9d6d73986 --- /dev/null +++ b/_tour/inner-classes.md @@ -0,0 +1,81 @@ +--- +layout: tour +title: Inner Classes + +discourse: true + +partof: scala-tour + +num: 22 +next-page: abstract-types +previous-page: lower-type-bounds +--- + +In Scala it is possible to let classes have other classes as members. As opposed to Java-like languages where such inner classes are members of the enclosing class, in Scala such inner classes are bound to the outer object. Suppose we want the compiler to prevent us, at compile time, from mixing up which nodes belong to what graph. Path-dependent types provide a solution. + +To illustrate the difference, we quickly sketch the implementation of a graph datatype: + +```tut +class Graph { + class Node { + var connectedNodes: List[Node] = Nil + def connectTo(node: Node) { + if (connectedNodes.find(node.equals).isEmpty) { + connectedNodes = node :: connectedNodes + } + } + } + var nodes: List[Node] = Nil + def newNode: Node = { + val res = new Node + nodes = res :: nodes + res + } +} +``` +This program represents a graph as a list of nodes (`List[Node]`). Each node has a list of other nodes it's connected to (`connectedNodes`). The `class Node` is a _path-dependent type_ because it is nested in the `class Graph`. Therefore, all nodes in the `connectedNodes` must be created using the `newNode` from the same instance of `Graph`. + +```tut +val graph1: Graph = new Graph +val node1: graph1.Node = graph1.newNode +val node2: graph1.Node = graph1.newNode +val node3: graph1.Node = graph1.newNode +node1.connectTo(node2) +node3.connectTo(node1) +``` +We have explicitly declared the type of `node1`, `node2`, and `node3` as `graph1.Node` for clarity but the compiler could have inferred it. This is because when we call `graph1.newNode` which calls `new Node`, the method is using the instance of `Node` specific to the instance `graph1`. + +If we now have two graphs, the type system of Scala does not allow us to mix nodes defined within one graph with the nodes of another graph, since the nodes of the other graph have a different type. +Here is an illegal program: + +``` +val graph1: Graph = new Graph +val node1: graph1.Node = graph1.newNode +val node2: graph1.Node = graph1.newNode +node1.connectTo(node2) // legal +val graph2: Graph = new Graph +val node3: graph2.Node = graph2.newNode +node1.connectTo(node3) // illegal! +``` +The type `graph1.Node` is distinct from the type `graph2.Node`. In Java, the last line in the previous example program would have been correct. For nodes of both graphs, Java would assign the same type `Graph.Node`; i.e. `Node` is prefixed with class `Graph`. In Scala such a type can be expressed as well, it is written `Graph#Node`. If we want to be able to connect nodes of different graphs, we have to change the definition of our initial graph implementation in the following way: + +```tut +class Graph { + class Node { + var connectedNodes: List[Graph#Node] = Nil + def connectTo(node: Graph#Node) { + if (connectedNodes.find(node.equals).isEmpty) { + connectedNodes = node :: connectedNodes + } + } + } + var nodes: List[Node] = Nil + def newNode: Node = { + val res = new Node + nodes = res :: nodes + res + } +} +``` + +> Note that this program doesn't allow us to attach a node to two different graphs. If we want to remove this restriction as well, we have to change the type of variable nodes to `Graph#Node`. diff --git a/_tour/local-type-inference.md b/_tour/local-type-inference.md new file mode 100644 index 0000000000..4a452a8eac --- /dev/null +++ b/_tour/local-type-inference.md @@ -0,0 +1,62 @@ +--- +layout: tour +title: Local Type Inference + +discourse: true + +partof: scala-tour + +num: 29 +next-page: operators +previous-page: polymorphic-methods +--- +Scala has a built-in type inference mechanism which allows the programmer to omit certain type annotations. It is, for instance, often not necessary in Scala to specify the type of a variable, since the compiler can deduce the type from the initialization expression of the variable. Also return types of methods can often be omitted since they correspond to the type of the body, which gets inferred by the compiler. + +Here is an example: + +```tut +object InferenceTest1 extends App { + val x = 1 + 2 * 3 // the type of x is Int + val y = x.toString() // the type of y is String + def succ(x: Int) = x + 1 // method succ returns Int values +} +``` + +For recursive methods, the compiler is not able to infer a result type. Here is a program which will fail the compiler for this reason: + +```tut:fail +object InferenceTest2 { + def fac(n: Int) = if (n == 0) 1 else n * fac(n - 1) +} +``` + +It is also not compulsory to specify type parameters when [polymorphic methods](polymorphic-methods.html) are called or [generic classes](generic-classes.html) are instantiated. The Scala compiler will infer such missing type parameters from the context and from the types of the actual method/constructor parameters. + +Here is an example which illustrates this: + +``` +case class MyPair[A, B](x: A, y: B); +object InferenceTest3 extends App { + def id[T](x: T) = x + val p = MyPair(1, "scala") // type: MyPair[Int, String] + val q = id(1) // type: Int +} +``` + +The last two lines of this program are equivalent to the following code where all inferred types are made explicit: + +``` +val x: MyPair[Int, String] = MyPair[Int, String](1, "scala") +val y: Int = id[Int](1) +``` + +In some situations it can be quite dangerous to rely on Scala's type inference mechanism as the following program shows: + +```tut:fail +object InferenceTest4 { + var obj = null + obj = new Object() +} +``` + +This program does not compile because the type inferred for variable `obj` is `Null`. Since the only value of that type is `null`, it is impossible to make this variable refer to another value. diff --git a/_tour/lower-type-bounds.md b/_tour/lower-type-bounds.md new file mode 100644 index 0000000000..60e670b48f --- /dev/null +++ b/_tour/lower-type-bounds.md @@ -0,0 +1,67 @@ +--- +layout: tour +title: Lower Type Bounds + +discourse: true + +partof: scala-tour + +num: 21 +next-page: inner-classes +previous-page: upper-type-bounds +prerequisite-knowledge: upper-type-bounds, generics, variance +--- + +While [upper type bounds](upper-type-bounds.html) limit a type to a subtype of another type, *lower type bounds* declare a type to be a supertype of another type. The term `B >: A` expresses that the type parameter `B` or the abstract type `B` refer to a supertype of type `A`. In most cases, `A` will be the type parameter of the class and `B` will be the type parameter of a method. + +Here is an example where this is useful: + +```tut:fail +trait Node[+B] { + def prepend(elem: B): Unit +} + +case class ListNode[+B](h: B, t: Node[B]) extends Node[B] { + def prepend(elem: B) = ListNode[B](elem, this) + def head: B = h + def tail = t +} + +case class Nil[+B]() extends Node[B] { + def prepend(elem: B) = ListNode[B](elem, this) +} +``` +This program implements a singly-linked list. `Nil` represents an empty element (i.e. an empty list). `class ListNode` is a node which contains an element of type `B` (`head`) and a reference to the rest of the list (`tail`). The `class Node` and its subtypes are covariant because we have `+B`. + +However, this program does _not_ compile because the parameter `elem` in `prepend` is of type `B`, which we declared *co*variant. This doesn't work because functions are *contra*variant in their parameter types and *co*variant in their result types. + +To fix this, we need to flip the variance of the type of the parameter `elem` in `prepend`. We do this by introducing a new type parameter `U` that has `B` as a lower type bound. + +```tut +trait Node[+B] { + def prepend[U >: B](elem: U) +} + +case class ListNode[+B](h: B, t: Node[B]) extends Node[B] { + def prepend[U >: B](elem: U) = ListNode[U](elem, this) + def head: B = h + def tail = t +} + +case class Nil[+B]() extends Node[B] { + def prepend[U >: B](elem: U) = ListNode[U](elem, this) +} +``` + +Now we can do the following: +```tut +trait Mammal +case class AfricanSwallow() extends Mammal +case class EuropeanSwallow() extends Mammal + + +val africanSwallowList= ListNode[AfricanSwallow](AfricanSwallow(), Nil()) +val mammalList: Node[Mammal] = africanSwallowList +mammalList.prepend(new EuropeanSwallow) +``` +The `Node[Mammal]` can be assigned the `africanSwallowList` but then accept `EuropeanSwallow`s. diff --git a/_tour/mixin-class-composition.md b/_tour/mixin-class-composition.md new file mode 100644 index 0000000000..c45f3e802b --- /dev/null +++ b/_tour/mixin-class-composition.md @@ -0,0 +1,81 @@ +--- +layout: tour +title: Class Composition with Mixins + +discourse: true + +partof: scala-tour + +num: 6 +next-page: higher-order-functions +previous-page: traits +prerequisite-knowledge: inheritance, traits, abstract-classes, unified-types +--- +Mixins are traits which are used to compose a class. + +```tut +abstract class A { + val message: String +} +class B extends A { + val message = "I'm an instance of class B" +} +trait C extends A { + def loudMessage = message.toUpperCase() +} +class D extends B with C + +val d = new D +d.message // I'm an instance of class B +d.loudMessage // I'M AN INSTANCE OF CLASS B +``` +Class `D` has a superclass `B` and a mixin `C`. Classes can only have one superclass but many mixins (using the keywords `extends` and `with` respectively). The mixins and the superclass may have the same supertype. + +Now let's look at a more interesting example starting with an abstract class: + +```tut +abstract class AbsIterator { + type T + def hasNext: Boolean + def next: T +} +``` +The class has an abstract type `T` and the standard iterator methods. + +Next, we'll implement a concrete class (all abstract members `T`, `hasNext`, and `next` have implementations): + +```tut +class StringIterator(s: String) extends AbsIterator { + type T = Char + private var i = 0 + def hasNext = i < s.length + def next = { + val ch = s charAt i + i += 1 + ch + } +} +``` +`StringIterator` takes a `String` and can be used to iterate over the String (e.g. to see if a String contains a certain character). + +Now let's create a trait which also extends `AbsIterator`. + +```tut +trait RichIterator extends AbsIterator { + def foreach(f: T => Unit): Unit = while (hasNext) f(next) +} +``` +Because `RichIterator` is a trait, it doesn't need to implement the abstract members of AbsIterator. + +We would like to combine the functionality of `StringIterator` and `RichIterator` into a single class. + +```tut +object StringIteratorTest extends App { + class Iter extends StringIterator(args(0)) with RichIterator + val iter = new Iter + iter foreach println +} +``` +The new class `RichStringIter` has `StringIterator` as a superclass and `RichIterator` as a mixin. + +With single inheritance we would not be able to achieve this level of flexibility. diff --git a/_tour/named-arguments.md b/_tour/named-arguments.md new file mode 100644 index 0000000000..de549a8b0e --- /dev/null +++ b/_tour/named-arguments.md @@ -0,0 +1,35 @@ +--- +layout: tour +title: Named Arguments + +discourse: true + +partof: scala-tour + +num: 34 +previous-page: default-parameter-values +prerequisite-knowledge: function-syntax +--- + +When calling methods, you can label the arguments with their parameter names like so: + +```tut + def printName(first: String, last: String): Unit = { + println(first + " " + last) + } + + printName("John", "Smith") // Prints "John Smith" + printName(first = "John", last = "Smith") // Prints "John Smith" + printName(last = "Smith", first = "John") // Prints "John Smith" +``` +Notice how the order of named arguments can be rearranged. However, if some arguments are named and others are not, the unnamed arguments must come first and in the order of their parameters in the method signature. + +``` +def printName(first: String, last: String): Unit = { + println(first + " " + last) +} + +printName(last = "Smith", "john") // Does not compile +``` + +Note that named arguments do not work with calls to Java methods. diff --git a/_tour/nested-functions.md b/_tour/nested-functions.md new file mode 100644 index 0000000000..5d93e4eceb --- /dev/null +++ b/_tour/nested-functions.md @@ -0,0 +1,34 @@ +--- +layout: tour +title: Nested Methods + +discourse: true + +partof: scala-tour + +num: 9 +next-page: currying +previous-page: higher-order-functions +--- + +In Scala it is possible to nest function definitions. The following object provides a `factorial` function for computing the factorial of a given number: + +```tut + def factorial(x: Int): Int = { + def fact(x: Int, accumulator: Int): Int = { + if (x <= 1) accumulator + else fact(x - 1, x * accumulator) + } + fact(x, 1) + } + + println("Factorial of 2: " + factorial(2)) + println("Factorial of 3: " + factorial(3)) +``` + +The output of this program is: + +``` +Factorial of 2: 2 +Factorial of 3: 6 +``` diff --git a/_tour/operators.md b/_tour/operators.md new file mode 100644 index 0000000000..58429795b7 --- /dev/null +++ b/_tour/operators.md @@ -0,0 +1,79 @@ +--- +layout: tour +title: Operators + +discourse: true + +partof: scala-tour + +num: 30 +next-page: by-name-parameters +previous-page: local-type-inference +prerequisite-knowledge: case-classes +--- +In Scala, operators are methods. Any method with a single parameter can be used as an _infix operator_. For example, `+` can be called with dot-notation: +``` +10.+(1) +``` + +However, it's easier to read as an infix operator: +``` +10 + 1 +``` + +## Defining and using operators +You can use any legal identifier as an operator. This includes a name like `add` or a symbol(s) like `+`. +```tut +case class Vec(val x: Double, val y: Double) { + def +(that: Vec) = new Vec(this.x + that.x, this.y + that.y) +} + +val vector1 = Vec(1.0, 1.0) +val vector2 = Vec(2.0, 2.0) + +val vector3 = vector1 + vector2 +vector3.x // 3.0 +vector3.y // 3.0 +``` +The class Vec has a method `+` which we used to add `vector1` and `vector2`. Using parentheses, you can build up complex expressions with readable syntax. Here is the definition of class `MyBool` which includes methods `and` and `or`: + +```tut +case class MyBool(x: Boolean) { + def and(that: MyBool): MyBool = if (x) that else this + def or(that: MyBool): MyBool = if (x) this else that + def negate: MyBool = MyBool(!x) +} +``` + +It is now possible to use `and` and `or` as infix operators: + +```tut +def not(x: MyBool) = x.negate +def xor(x: MyBool, y: MyBool) = (x or y) and not(x and y) +``` + +This helps to make the definition of `xor` more readable. + +## Precedence +When an expression uses multiple operators, the operators are evaluated based on the priority of the first character: +``` +(characters not shown below) +* / % ++ - +: += ! +< > +& +^ +| +(all letters) +``` +This applies to functions you define. For example, the following expression: +``` +a + b ^? c ?^ d less a ==> b | c +``` +Is equivalent to +``` +((a + b) ^? (c ?^ d)) less ((a ==> b) | c) +``` +`?^` has the highest precedence because it starts with the character `?`. `+` has the second highest precedence, followed by `^?`, `==>`, `|`, and `less`. diff --git a/_tour/pattern-matching.md b/_tour/pattern-matching.md new file mode 100644 index 0000000000..23d1a7b4ae --- /dev/null +++ b/_tour/pattern-matching.md @@ -0,0 +1,149 @@ +--- +layout: tour +title: Pattern Matching + +discourse: true + +partof: scala-tour + +num: 12 + +next-page: singleton-objects +previous-page: case-classes +prerequisite-knowledge: case-classes, string-interpolation, subtyping +--- + +Pattern matching is a mechanism for checking a value against a pattern. A successful match can also deconstruct a value into its constituent parts. It is a more powerful version of the `switch` statement in Java and it can likewise be used in place of a series of if/else statements. + +## Syntax +A match expression has a value, the `match` keyword, and at least one `case` clause. +```tut +import scala.util.Random + +val x: Int = Random.nextInt(10) + +x match { + case 0 => "zero" + case 1 => "one" + case 2 => "two" + case _ => "many" +} +``` +The `val x` above is a random integer between 0 and 10. `x` becomes the left operand of the `match` operator and on the right is an expression with four cases. The last case `_` is a "catch all" case for any number greater than 2. Cases are also called _alternatives_. + +Match expressions have a value. +```tut +def matchTest(x: Int): String = x match { + case 1 => "one" + case 2 => "two" + case _ => "many" +} +matchTest(3) // many +matchTest(1) // one +``` +This match expression has a type String because all of the cases return String. Therefore, the function `matchTest` returns a String. + +## Matching on case classes + +Case classes are especially useful for pattern matching. + +```tut +abstract class Notification + +case class Email(sender: String, title: String, body: String) extends Notification + +case class SMS(caller: String, message: String) extends Notification + +case class VoiceRecording(contactName: String, link: String) extends Notification + + +``` +`Notification` is an abstract super class which has three concrete Notification types implemented with case classes `Email`, `SMS`, and `VoiceRecording`. Now we can do pattern matching on these case classes: + +``` +def showNotification(notification: Notification): String = { + notification match { + case Email(email, title, _) => + s"You got an email from $email with title: $title" + case SMS(number, message) => + s"You got an SMS from $number! Message: $message" + case VoiceRecording(name, link) => + s"you received a Voice Recording from $name! Click the link to hear it: $link" + } +} +val someSms = SMS("12345", "Are you there?") +val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123") + +println(showNotification(someSms)) // prints You got an SMS from 12345! Message: Are you there? + +println(showNotification(someVoiceRecording)) // you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123 +``` +The function `showNotification` takes as a parameter the abstract type `Notification` and matches on the type of `Notification` (i.e. it figures out whether it's an `Email`, `SMS`, or `VoiceRecording`). In the `case Email(email, title, _)` the fields `email` and `title` are used in the return value but the `body` field is ignored with `_`. + +## Pattern guards +Pattern guards are simply boolean expressions which are used to make cases more specific. Just add `if ` after the pattern. +``` + +def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { + notification match { + case Email(email, _, _) if importantPeopleInfo.contains(email) => + "You got an email from special someone!" + case SMS(number, _) if importantPeopleInfo.contains(number) => + "You got an SMS from special someone!" + case other => + showNotification(other) // nothing special, delegate to our original showNotification function + } +} + +val importantPeopleInfo = Seq("867-5309", "jenny@gmail.com") + +val someSms = SMS("867-5309", "Are you there?") +val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123") +val importantEmail = Email("jenny@gmail.com", "Drinks tonight?", "I'm free after 5!") +val importantSms = SMS("867-5309", "I'm here! Where are you?") + +println(showImportantNotification(someSms, importantPeopleInfo)) +println(showImportantNotification(someVoiceRecording, importantPeopleInfo)) +println(showImportantNotification(importantEmail, importantPeopleInfo)) +println(showImportantNotification(importantSms, importantPeopleInfo)) +``` + +In the `case Email(email, _, _) if importantPeopleInfo.contains(email)`, the pattern is matched only if the `email` is in the list of important people. + +## Matching on type only +You can match on the type like so: +```tut +abstract class Device +case class Phone(model: String) extends Device{ + def screenOff = "Turning screen off" +} +case class Computer(model: String) extends Device { + def screenSaverOn = "Turning screen saver on..." +} + +def goIdle(device: Device) = device match { + case p: Phone => p.screenOff + case c: Computer => c.screenSaverOn +} +``` +`def goIdle` has a different behavior depending on the type of `Device`. This is useful when the case needs to call a method on the pattern. It is a convention to use the first letter of the type as the case identifier (`p` and `c` in this case). + +## Sealed classes +Traits and classes can be marked `sealed` which means all subtypes must be declared in the same file. The assures that all subtypes are known. + +```tut +sealed abstract class Furniture +case class Couch() extends Furniture +case class Chair() extends Furniture + +def findPlaceToSit(piece: Furniture): String = piece match { + case a: Couch => "Lie on the couch" + case b: Chair => "Sit on the chair" +} +``` +This is useful for pattern matching because we don't need a "catch all" case. + +## Notes + +Scala's pattern matching statement is most useful for matching on algebraic types expressed via [case classes](case-classes.html). +Scala also allows the definition of patterns independently of case classes, using `unapply` methods in [extractor objects](extractor-objects.html). diff --git a/_tour/polymorphic-methods.md b/_tour/polymorphic-methods.md new file mode 100644 index 0000000000..ad2c7533be --- /dev/null +++ b/_tour/polymorphic-methods.md @@ -0,0 +1,33 @@ +--- +layout: tour +title: Polymorphic Methods + +discourse: true + +partof: scala-tour + +num: 28 + +next-page: local-type-inference +previous-page: implicit-conversions +prerequisite-knowledge: unified-types, +--- + +Methods in Scala can be parameterized by type as well as value. The syntax is similar to that of generic classes. Type parameters are declared within a pair of brackets while value parameters are enclosed in a pair of parentheses. + +Here is an example: + +```tut +def listOfDuplicates[A](x: A, length: Int): List[A] = { + if (length < 1) + Nil + else + x :: listOfDuplicates(x, length - 1) +} +println(listOfDuplicates[Int](3, 4)) // List(3, 3, 3, 3) +println(listOfDuplicates("La", 8)) // List(La, La, La, La, La, La, La, La) +``` + +The method `listOfDuplicates` takes a type parameter `A` and values parameters `x` and `n`. In this case, value `x` is of type `A`. If `length < 1` we return an empty list. Otherwise we prepend `x` to the the list of duplicates returned by the recursive call to `listOfDuplicates`. (note: `::` means prepend an element on the left to a sequence on the right). + +When we call `listOfDuplicates` with `[Int]` as the type parameter, the first argument must be an int and the return type will be List[Int]. However, you don't always need to explicitly provide the the type parameter because the compiler can often figure it out based on the type of value argument (`"La"` is a String). In fact, if calling this method from Java it is impossible to provide the type parameter. diff --git a/_tour/regular-expression-patterns.md b/_tour/regular-expression-patterns.md new file mode 100644 index 0000000000..1c3fd8dd12 --- /dev/null +++ b/_tour/regular-expression-patterns.md @@ -0,0 +1,61 @@ +--- +layout: tour +title: Regular Expression Patterns + +discourse: true + +partof: scala-tour + +num: 15 + +next-page: extractor-objects +previous-page: singleton-objects +--- + +Regular expressions are strings which can be used to find patterns (or lack thereof) in data. Any string can be converted to a regular expression using the `.r` method. + +```tut +import scala.util.matching.Regex + +val numberPattern: Regex = "[0-9]".r + +numberPattern.findFirstMatchIn("awesomepassword") match { + case Some(_) => println("Password OK") + case None => println("Password must contain a number") +} +``` + +In the above example, the `numberPattern` is a `Regex` +(regular expression) which we use to make sure a password contains a number. + +You can also search for groups of regular expressions using parentheses. + +```tut +import scala.util.matching.Regex + +val keyValPattern: Regex = "([0-9a-zA-Z-#() ]+): ([0-9a-zA-Z-#() ]+)".r + +val input: String = + """background-color: #A03300; + |background-image: url(img/header100.png); + |background-position: top center; + |background-repeat: repeat-x; + |background-size: 2160px 108px; + |margin: 0; + |height: 108px; + |width: 100%;""".stripMargin + +for (patternMatch <- keyValPattern.findAllMatchIn(input)) + println(s"key: ${patternMatch.group(1)} value: ${patternMatch.group(2)}") +``` +Here we parse out the keys and values of a String. Each match has a group of sub-matches. Here is the output: +``` +key: background-color value: #A03300 +key: background-image value: url(img +key: background-position value: top center +key: background-repeat value: repeat-x +key: background-size value: 2160px 108px +key: margin value: 0 +key: height value: 108px +key: width value: 100 +``` diff --git a/_tour/self-types.md b/_tour/self-types.md new file mode 100644 index 0000000000..ee43cd426b --- /dev/null +++ b/_tour/self-types.md @@ -0,0 +1,37 @@ +--- +layout: tour +title: Self-type + +discourse: true + +partof: scala-tour + +num: 25 +next-page: implicit-parameters +previous-page: compound-types +prerequisite-knowledge: nested-classes, mixin-class-composition +--- +Self-types are a way to declare that a trait must be mixed into another trait, even though it doesn't directly extend it. That makes the members of the dependency available without imports. + +A self-type is a way to narrow the type of `this` or another identifier that aliases `this`. The syntax looks like normal function syntax but means something entirely different. + +To use a self-type in a trait, write an identifier, the type of another trait to mix in, and a `=>` (e.g. `someIdentifier: SomeOtherTrait =>`). +```tut +trait User { + def username: String +} + +trait Tweeter { + this: User => // reassign this + def tweet(tweetText: String) = println(s"$username: $tweetText") +} + +class VerifiedTweeter(val username_ : String) extends Tweeter with User { // We mixin User because Tweeter required it + def username = s"real $username_" +} + +val realBeyoncé = new VerifiedTweeter("Beyoncé") +realBeyoncé.tweet("Just spilled my glass of lemonade") // prints "real Beyoncé: Just spilled my glass of lemonade" +``` + +Because we said `this: User =>` in `trait Tweeter`, now the variable `username` is in scope for the `tweet` method. This also means that since `VerifiedTweeter` extends `Tweeter`, it must also mix-in `User` (using `with User`). diff --git a/_tour/sequence-comprehensions.md b/_tour/sequence-comprehensions.md new file mode 100644 index 0000000000..87160e06bd --- /dev/null +++ b/_tour/sequence-comprehensions.md @@ -0,0 +1,69 @@ +--- +layout: tour +title: Sequence Comprehensions + +disqus: true + +partof: scala-tour + +num: 17 +next-page: generic-classes +previous-page: extractor-objects +--- + +Scala offers a lightweight notation for expressing *sequence comprehensions*. Comprehensions have the form `for (enumerators) yield e`, where `enumerators` refers to a semicolon-separated list of enumerators. An *enumerator* is either a generator which introduces new variables, or it is a filter. A comprehension evaluates the body `e` for each binding generated by the enumerators and returns a sequence of these values. + +Here is an example: + +```tut +object ComprehensionTest1 extends App { + def even(from: Int, to: Int): List[Int] = + for (i <- List.range(from, to) if i % 2 == 0) yield i + Console.println(even(0, 20)) +} +``` + +The for-expression in function introduces a new variable `i` of type `Int` which is subsequently bound to all values of the list `List(from, from + 1, ..., to - 1)`. The guard `if i % 2 == 0` filters out all odd numbers so that the body (which only consists of the expression i) is only evaluated for even numbers. Consequently, the whole for-expression returns a list of even numbers. + +The program yields the following output: + +``` +List(0, 2, 4, 6, 8, 10, 12, 14, 16, 18) +``` + +Here is a more complicated example which computes all pairs of numbers between `0` and `n-1` whose sum is equal to a given value `v`: + +```tut +object ComprehensionTest2 extends App { + def foo(n: Int, v: Int) = + for (i <- 0 until n; + j <- i until n if i + j == v) yield + (i, j); + foo(20, 32) foreach { + case (i, j) => + println(s"($i, $j)") + } +} +``` + +This example shows that comprehensions are not restricted to lists. The previous program uses iterators instead. Every datatype that supports the operations `withFilter`, `map`, and `flatMap` (with the proper types) can be used in sequence comprehensions. + +Here's the output of the program: + +``` +(13, 19) +(14, 18) +(15, 17) +(16, 16) +``` + +There is also a special form of sequence comprehension which returns `Unit`. Here the bindings that are created from the list of generators and filters are used to perform side-effects. The programmer has to omit the keyword `yield` to make use of such a sequence comprehension. +Here's a program which is equivalent to the previous one but uses the special for comprehension returning `Unit`: + +``` +object ComprehensionTest3 extends App { + for (i <- Iterator.range(0, 20); + j <- Iterator.range(i, 20) if i + j == 32) + println(s"($i, $j)") +} +``` diff --git a/_tour/singleton-objects.md b/_tour/singleton-objects.md new file mode 100644 index 0000000000..64b97775eb --- /dev/null +++ b/_tour/singleton-objects.md @@ -0,0 +1,74 @@ +--- +layout: tour +title: Singleton Objects + +discourse: true + +partof: scala-tour + +num: 13 + +next-page: regular-expression-patterns +previous-page: pattern-matching +--- + +Methods and values that aren't associated with individual instances of a [class](classes.html) belong in *singleton objects*, denoted by using the keyword `object` instead of `class`. + +``` +package test + +object Blah { + def sum(l: List[Int]): Int = l.sum +} +``` + +This `sum` method is available globally, and can be referred to, or imported, as `test.Blah.sum`. + +Singleton objects are sort of a shorthand for defining a single-use class, which can't directly be instantiated, and a `val` member at the point of definition of the `object`, with the same name. Indeed, like `val`s, singleton objects can be defined as members of a [trait](traits.html) or class, though this is atypical. + +A singleton object can extend classes and traits. In fact, a [case class](case-classes.html) with no [type parameters](generic-classes.html) will by default create a singleton object of the same name, with a [`Function*`](http://www.scala-lang.org/api/current/scala/Function1.html) trait implemented. + +## Companions ## + +Most singleton objects do not stand alone, but instead are associated with a class of the same name. The “singleton object of the same name” of a case class, mentioned above, is an example of this. When this happens, the singleton object is called the *companion object* of the class, and the class is called the *companion class* of the object. + +[Scaladoc](https://wiki.scala-lang.org/display/SW/Introduction) has special support for jumping between a class and its companion: if the big “C” or “O” circle has its edge folded up at the bottom, you can click the circle to jump to the companion. + +A class and its companion object, if any, must be defined in the same source file. Like this: + +```tut +class IntPair(val x: Int, val y: Int) + +object IntPair { + import math.Ordering + + implicit def ipord: Ordering[IntPair] = + Ordering.by(ip => (ip.x, ip.y)) +} +``` + +It's common to see typeclass instances as [implicit values](implicit-parameters.html), such as `ipord` above, defined in the companion, when following the typeclass pattern. This is because the companion's members are included in the default implicit search for related values. + +## Notes for Java programmers ## + +`static` is not a keyword in Scala. Instead, all members that would be static, including classes, should go in a singleton object. They can be referred to with the same syntax, imported piecemeal or as a group, and so on. + +Frequently, Java programmers define static members, perhaps `private`, as implementation aids for their instance members. These move to the companion, too; a common pattern is to import the companion object's members in the class, like so: + +``` +class X { + import X._ + + def blah = foo +} + +object X { + private def foo = 42 +} +``` + +This illustrates another feature: in the context of `private`, a class and its companion are friends. `object X` can access private members of `class X`, and vice versa. To make a member *really* private to one or the other, use `private[this]`. + +For Java convenience, methods, including `var`s and `val`s, defined directly in a singleton object also have a static method defined in the companion class, called a *static forwarder*. Other members are accessible via the `X$.MODULE$` static field for `object X`. + +If you move everything to a companion object and find that all you have left is a class you don't want to be able to instantiate, simply delete the class. Static forwarders will still be created. diff --git a/_tour/tour-of-scala.md b/_tour/tour-of-scala.md new file mode 100644 index 0000000000..1712f00d45 --- /dev/null +++ b/_tour/tour-of-scala.md @@ -0,0 +1,48 @@ +--- +layout: tour +title: Introduction + +discourse: true + +partof: scala-tour + +num: 1 + +next-page: basics +redirect_from: + - /tutorials/index.html +--- + +Scala is a modern multi-paradigm programming language designed to express common programming patterns in a concise, elegant, and type-safe way. It smoothly integrates features of object-oriented and functional languages. + +## Scala is object-oriented ## +Scala is a pure object-oriented language in the sense that [every value is an object](unified-types.html). Types and behavior of objects are described by [classes](classes.html) and [traits](traits.html). Classes are extended by subclassing and a flexible [mixin-based composition](mixin-class-composition.html) mechanism as a clean replacement for multiple inheritance. + +## Scala is functional ## +Scala is also a functional language in the sense that [every function is a value](unified-types.html). Scala provides a [lightweight syntax](anonymous-function-syntax.html) for defining anonymous functions, it supports [higher-order functions](higher-order-functions.html), it allows functions to be [nested](nested-functions.html), and supports [currying](currying.html). Scala's [case classes](case-classes.html) and its built-in support for [pattern matching](pattern-matching.html) model algebraic types used in many functional programming languages. [Singleton objects](singleton-objects.html) provide a convenient way to group functions that aren't members of a class. + +Furthermore, Scala's notion of pattern matching naturally extends to the [processing of XML data](xml-processing.html) with the help of [right-ignoring sequence patterns](regular-expression-patterns.html), by way of general extension via [extractor objects](extractor-objects.html). In this context, [for comprehensions](for-comprehensions.html) are useful for formulating queries. These features make Scala ideal for developing applications like web services. + +## Scala is statically typed ## +Scala is equipped with an expressive type system that enforces statically that abstractions are used in a safe and coherent manner. In particular, the type system supports: + +* [generic classes](generic-classes.html) +* [variance annotations](variances.html) +* [upper](upper-type-bounds.html) and [lower](lower-type-bounds.html) type bounds, +* [inner classes](inner-classes.html) and [abstract types](abstract-types.html) as object members +* [compound types](compound-types.html) +* [explicitly typed self references](explicitly-typed-self-references.html) +* [implicit parameters](implicit-parameters.html) and [conversions](implicit-conversions.html) +* [polymorphic methods](polymorphic-methods.html) + +A [local type inference mechanism](local-type-inference.html) takes care that the user is not required to annotate the program with redundant type information. In combination, these features provide a powerful basis for the safe reuse of programming abstractions and for the type-safe extension of software. + +## Scala is extensible ## + +In practice, the development of domain-specific applications often requires domain-specific language extensions. Scala provides a unique combination of language mechanisms that make it easy to smoothly add new language constructs in the form of libraries. + +A joint use of both features facilitates the definition of new statements without using meta-programming facilities such as macros. + +Scala is designed to interoperate well with the popular Java 2 Runtime Environment (JRE). In particular, the interaction with the mainstream object-oriented Java programming language is as smooth as possible. Newer Java features like [annotations](annotations.html) and Java generics have direct analogues in Scala. Those Scala features without Java analogues, such as [default](default-parameter-values.html) and [named parameters](named-parameters.html), compile as close to Java as they can reasonably come. Scala has the same compilation model (separate compilation, dynamic class loading) like Java and allows access to thousands of existing high-quality libraries. + +Please continue to the next page to read more. diff --git a/_tour/traits.md b/_tour/traits.md new file mode 100644 index 0000000000..142618b5fe --- /dev/null +++ b/_tour/traits.md @@ -0,0 +1,82 @@ +--- +layout: tour +title: Traits + +discourse: true + +partof: scala-tour + +num: 5 +next-page: mixin-class-composition +previous-page: classes +assumed-knowledge: expressions, classes, generics, objects, companion-objects +--- + +Traits are used to share interfaces and fields between classes. They are similar to Java 8's interfaces. Classes and objects can extend traits but traits cannot be instantiated and therefore have no parameters. + +## Defining a trait +A minimal trait is simply the keyword `trait` and an identifier: + +```tut +trait HairColor +``` + +Traits become especially useful as generic types and with abstract methods. +```tut +trait Iterator[A] { + def hasNext: Boolean + def next(): A +} +``` + +Extending the `trait Iterator[A]` requires a type `A` and implementations of the methods `hasNext` and `next`. + +## Using traits +Use the `extends` keyword to extend a trait. Then implement any abstract members of the trait using the `override` keyword: +```tut +trait Iterator[A] { + def hasNext: Boolean + def next(): A +} + + +class IntIterator(to: Int) extends Iterator[Int] { + private var current = 0 + override def hasNext: Boolean = current < to + override def next(): Int = { + if (hasNext) { + val t = current + current += 1 + t + } else 0 + } +} + + +val iterator = new IntIterator(10) +iterator.next() // prints 0 +iterator.next() // prints 1 +``` +This `IntIterator` class takes a parameter `to` as an upper bound. It `extends Iterator[Int]` which means that the `next` method must return an Int. + +## Subtyping +Subtypes of traits can be used where a the trait is required. +```tut +import scala.collection.mutable.ArrayBuffer + +trait Pet { + val name: String +} + +class Cat(val name: String) extends Pet +class Dog(val name: String) extends Pet + +val dog = new Dog("Harry") +val cat = new Cat("Sally") + +val animals = ArrayBuffer.empty[Pet] +animals.append(dog) +animals.append(cat) +animals.foreach(pet => println(pet.name)) // Prints Harry Sally +``` +The `trait Pet` has an abstract field `name` which gets implemented by Cat and Dog in their constructors. On the last line, we call `pet.name` which must be implemented in any subtype of the trait `Pet`. diff --git a/_tour/type-casting-diagram.dot b/_tour/type-casting-diagram.dot new file mode 100644 index 0000000000..d7a76e0bcc --- /dev/null +++ b/_tour/type-casting-diagram.dot @@ -0,0 +1,14 @@ +digraph UnifiedTypes { + node [fontname = "Courier"]; + rankdir="BT" + + + Byte -> Short; + Short -> Int; + Int -> Long; + Long -> Float; + Float -> Double; + Char -> Int; + + {rank = same; Double; Float; Long; Int; Short; Byte; } +} diff --git a/_tour/type-casting-diagram.svg b/_tour/type-casting-diagram.svg new file mode 100644 index 0000000000..75a294736e --- /dev/null +++ b/_tour/type-casting-diagram.svg @@ -0,0 +1,91 @@ + + + + + + +UnifiedTypes + + + +Byte + +Byte + + + +Short + +Short + + + +Byte->Short + + + + + +Int + +Int + + + +Short->Int + + + + + +Long + +Long + + + +Int->Long + + + + + +Float + +Float + + + +Long->Float + + + + + +Double + +Double + + + +Float->Double + + + + + +Char + +Char + + + +Char->Int + + + + + diff --git a/_tour/unified-types-diagram.dot b/_tour/unified-types-diagram.dot new file mode 100644 index 0000000000..424c80620e --- /dev/null +++ b/_tour/unified-types-diagram.dot @@ -0,0 +1,16 @@ +digraph UnifiedTypes { + node [fontname = "Courier"]; + rankdir="BT" + AnyVal -> Any; + "AnyRef (java.lang.Object)" -> Any; + + Double, Float, Long, Int, Short, Byte, Unit, Boolean, Char -> AnyVal; + List, Option, YourClass -> "AnyRef (java.lang.Object)" + + Null -> {List Option YourClass} + "Nothing" -> {Double, Float, Long, Int, Short, Byte, Char, Unit, Boolean, Null} + + {rank = min; "Nothing"} + {rank = same; Double; Float; Long; Int; Short; Byte; Char; Unit; Boolean; List; Option; YourClass} + {rank = same; "AnyRef (java.lang.Object)"; AnyVal} +} diff --git a/_tour/unified-types-diagram.svg b/_tour/unified-types-diagram.svg new file mode 100644 index 0000000000..2426d76cc0 --- /dev/null +++ b/_tour/unified-types-diagram.svg @@ -0,0 +1,277 @@ + + + + + + +UnifiedTypes + + + +AnyVal + +AnyVal + + + +Any + +Any + + + +AnyVal->Any + + + + + +AnyRef (java.lang.Object) + +AnyRef (java.lang.Object) + + + +AnyRef (java.lang.Object)->Any + + + + + +Double + +Double + + + +Double->AnyVal + + + + + +Float + +Float + + + +Float->AnyVal + + + + + +Long + +Long + + + +Long->AnyVal + + + + + +Int + +Int + + + +Int->AnyVal + + + + + +Short + +Short + + + +Short->AnyVal + + + + + +Byte + +Byte + + + +Byte->AnyVal + + + + + +Unit + +Unit + + + +Unit->AnyVal + + + + + +Boolean + +Boolean + + + +Boolean->AnyVal + + + + + +Char + +Char + + + +Char->AnyVal + + + + + +List + +List + + + +List->AnyRef (java.lang.Object) + + + + + +Option + +Option + + + +Option->AnyRef (java.lang.Object) + + + + + +YourClass + +YourClass + + + +YourClass->AnyRef (java.lang.Object) + + + + + +Null + +Null + + + +Null->List + + + + + +Null->Option + + + + + +Null->YourClass + + + + + +Nothing + +Nothing + + + +Nothing->Double + + + + + +Nothing->Float + + + + + +Nothing->Long + + + + + +Nothing->Int + + + + + +Nothing->Short + + + + + +Nothing->Byte + + + + + +Nothing->Unit + + + + + +Nothing->Boolean + + + + + +Nothing->Char + + + + + +Nothing->Null + + + + + diff --git a/_tour/unified-types.md b/_tour/unified-types.md new file mode 100644 index 0000000000..5e6028ae85 --- /dev/null +++ b/_tour/unified-types.md @@ -0,0 +1,80 @@ +--- +layout: tour +title: Unified Types + +discourse: true + +partof: scala-tour + +num: 3 +next-page: classes +previous-page: basics +prerequisite-knowledge: classes, basics +--- + +In Scala, all values have a type, including numerical values and functions. The diagram below illustrates a subset of the type hierarchy. + +Scala Type Hierarchy + +## Scala Type Hierarchy ## + +[`Any`](http://www.scala-lang.org/api/2.12.1/scala/Any.html) is the supertype of all types, also called the top type. It defines certain universal methods such as `equals`, `hashCode`, and `toString`. `Any` has two direct subclasses: `AnyVal` and `AnyRef`. + +`AnyVal` represents value types. There are nine predefined value types and they are non-nullable: `Double`, `Float`, `Long`, `Int`, `Short`, `Byte`, `Char`, `Unit`, and `Boolean`. `Unit` is a value type which carries no meaningful information. There is exactly one instance of `Unit` which can be declared literally like so: `()`. All functions must return something so sometimes `Unit` is a useful return type. + +`AnyRef` represents reference types. All non-value types are defined as reference types. Anywhere an `AnyRef` Every user-defined type in Scala is a subtype of `AnyRef`. If Scala is used in the context of a Java runtime environment, `AnyRef` corresponds to `java.lang.Object`. + +Here is an example that demonstrates that strings, integers, characters, boolean values, and functions are all objects just like every other object: + +```tut +val list: List[Any] = List( + "a string", + 732, // an integer + 'c', // a character + true, // a boolean value + () => "an anonymous function returning a string" +) + +list.foreach(element => println(element)) +``` + +It defines a variable `list` of type `List[Any]`. The list is initialized with elements of various types, but they all are instance of `scala.Any`, so you can add them to the list. + +Here is the output of the program: + +``` +a string +732 +c +true + +``` + +## Type Casting +Value types can be cast in the following way: +Scala Type Hierarchy + +For example: + +```tut +val x: Long = 987654321 +val y: Float = x // 9.8765434E8 (note that some precision is lost in this case) + +val face: Char = '☺' +val number: Int = face // 9786 +``` + +Casting is unidirectional. This will not compile: + +``` +val x: Long = 987654321 +val y: Float = x // 9.8765434E8 +val z: Long = y // Does not conform +``` + +You can also cast a reference type to a subtype. This will be covered later in the tour. + +## Nothing and Null +`Nothing` is a subtype of all types, also called the bottom type. There is no value that has type `Nothing`. A common use is to signal non-termination such as a thrown exception, program exit, or an infinite loop (i.e., it is the type of an expression which does not evaluate to a value, or a method that does not return normally). + +`Null` is a subtype of all reference types (i.e. any subtype of AnyRef). It has a single value identified by the keyword literal `null`. `Null` is provided mostly for interoperability with other JVM languages and should almost never be used in Scala code. We'll cover alternatives to `null` later in the tour. diff --git a/_tour/upper-type-bounds.md b/_tour/upper-type-bounds.md new file mode 100644 index 0000000000..b434279cd2 --- /dev/null +++ b/_tour/upper-type-bounds.md @@ -0,0 +1,49 @@ +--- +layout: tour +title: Upper Type Bounds + +discourse: true + +partof: scala-tour +categories: tour +num: 20 +next-page: lower-type-bounds +previous-page: variances +--- + +In Scala, [type parameters](generic-classes.html) and [abstract types](abstract-types.html) may be constrained by a type bound. Such type bounds limit the concrete values of the type variables and possibly reveal more information about the members of such types. An _upper type bound_ `T <: A` declares that type variable `T` refers to a subtype of type `A`. +Here is an example that demonstrates upper type bound for a type parameter of class `Cage`: + +```tut +abstract class Animal { + def name: String +} + +abstract class Pet extends Animal {} + +class Cat extends Pet { + override def name: String = "Cat" +} + +class Dog extends Pet { + override def name: String = "Dog" +} + +class Lion extends Animal { + override def name: String = "Lion" +} + +class PetContainer[P <: Pet](p: P) { + def pet: P = p +} + +val dogContainer = new PetContainer[Dog](new Dog) +val catContainer = new PetContainer[Cat](new Cat) +// val lionContainer = new PetContainer[Lion](new Lion) +// ^this would not compile +``` +The `class PetContainer` take a type parameter `P` which must be a subtype of `Pet`. `Dog` and `Cat` are subtypes of `Pet` so we can create a new `PetContainer[Dog]` and `PetContainer[Cat]`. However, if we tried to create a `PetContainer[Lion]`, we would get the following Error: + +`type arguments [Lion] do not conform to class PetContainer's type parameter bounds [P <: Pet]` + +This is because `Lion` is not a subtype of `Pet`. diff --git a/_tour/variances.md b/_tour/variances.md new file mode 100644 index 0000000000..4e54f242e2 --- /dev/null +++ b/_tour/variances.md @@ -0,0 +1,152 @@ +--- +layout: tour +title: Variances + +discourse: true + +partof: scala-tour + +num: 19 +next-page: upper-type-bounds +previous-page: generic-classes +--- + +Variance is the correlation of subtyping relationships of complex types and the subtyping relationships of their component types. Scala supports variance annotations of type parameters of [generic classes](generic-classes.html), to allow them to be covariant, contravariant, or invariant if no annotations are used. The use of variance in the type system allows us to make intuitive connections between complex types, whereas the lack of variance can restrict the reuse of a class abstraction. + +```tut +class Foo[+A] // A covariant class +class Bar[-A] // A contravariant class +class Baz[A] // An invariant class +``` + +### Covariance + +A type parameter `A` of a generic class can be made covariant by using the annotation `+A`. For some `class List[+A]`, making `A` covariant implies that for two types `A` and `B` where `A` is a subtype of `B`, then `List[A]` is a subtype of `List[B]`. This allows us to make very useful and intuitive subtyping relationships using generics. + +Consider this simple class structure: + +```tut +abstract class Animal { + def name: String +} +case class Cat(name: String) extends Animal +case class Dog(name: String) extends Animal +``` + +Both `Cat` and `Dog` are subtypes of `Animal`. The Scala standard library has a generic immutable `sealed abstract class List[+A]` class, where the type parameter `A` is covariant. This means that a `List[Cat]` is a `List[Animal]` and a `List[Dog]` is also a `List[Animal]`. Intuitively, it makes sense that a list of cats and a list of dogs are each lists of animals, and you should be able to substitute either of them for a `List[Animal]`. + +In the following example, the method `printAnimalNames` will accept a list of animals as an argument and print their names each on a new line. If `List[A]` were not covariant, the last two method calls would not compile, which would severely limit the usefulness of the `printAnimalNames` method. + +```tut +object CovarianceTest extends App { + def printAnimalNames(animals: List[Animal]): Unit = { + animals.foreach { animal => + println(animal.name) + } + } + + val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom")) + val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex")) + + printAnimalNames(cats) + // Whiskers + // Tom + + printAnimalNames(dogs) + // Fido + // Rex +} +``` + +### Contravariance + +A type parameter `A` of a generic class can be made contravariant by using the annotation `-A`. This creates a subtyping relationship between the class and its type parameter that is similar, but opposite to what we get with covariance. That is, for some `class Writer[-A]`, making `A` contravariant implies that for two types `A` and `B` where `A` is a subtype of `B`, `Writer[B]` is a subtype of `Writer[A]`. + +Consider the `Cat`, `Dog`, and `Animal` classes defined above for the following example: + +```tut +abstract class Printer[-A] { + def print(value: A): Unit +} +``` + +A `Printer[A]` is a simple class that knows how to print out some type `A`. Let's define some subclasses for specific types: + +```tut +class AnimalPrinter extends Printer[Animal] { + def print(animal: Animal): Unit = + println("The animal's name is: " + animal.name) +} + +class CatPrinter extends Printer[Cat] { + def print(cat: Cat): Unit = + println("The cat's name is: " + cat.name) +} +``` + +If a `Printer[Cat]` knows how to print any `Cat` to the console, and a `Printer[Animal]` knows how to print any `Animal` to the console, it makes sense that a `Printer[Animal]` would also know how to print any `Cat`. The inverse relationship does not apply, because a `Printer[Cat]` does not know how to print any `Animal` to the console. Therefore, we should be able to substitute a `Printer[Animal]` for a `Printer[Cat]`, if we wish, and making `Printer[A]` contravariant allows us to do exactly that. + +```tut +object ContravarianceTest extends App { + val myCat: Cat = Cat("Boots") + + def printMyCat(printer: Printer[Cat]): Unit = { + printer.print(myCat) + } + + val catPrinter: Printer[Cat] = new CatPrinter + val animalPrinter: Printer[Animal] = new AnimalPrinter + + printMyCat(catPrinter) + printMyCat(animalPrinter) +} +``` + +The output of this program will be: + +``` +The cat's name is: Boots +The animal's name is: Boots +``` + +### Invariance + +Generic classes in Scala are invariant by default. This means that they are neither covariant nor contravariant. In the context of the following example, `Container` class is invariant. A `Container[Cat]` is _not_ a `Container[Animal]`, nor is the reverse true. + +```tut +class Container[A](value: A) { + private var _value: A = value + def getValue: A = _value + def setValue(value: A): Unit = { + _value = value + } +} +``` + +It may seem like a `Container[Cat]` should naturally also be a `Container[Animal]`, but allowing a mutable generic class to be covariant would not be safe. In this example, it is very important that `Container` is invariant. Supposing `Container` was actually covariant, something like this could happen: + +``` +val catContainer: Container[Cat] = new Container(Cat("Felix")) +val animalContainer: Container[Animal] = catContainer +animalContainer.setValue(Dog("Spot")) +val cat: Cat = catContainer.getValue // Oops, we'd end up with a Dog assigned to a Cat +``` + +Fortunately, the compiler stops us long before we could get this far. + +### Other Examples + +Another example that can help one understand variance is `trait Function1[-T, R]` from the Scala standard library. `Function1` represents a function with one argument, where the first type parameter `T` represents the argument type, and the second type parameter `R` represents the return type. A `Function1` is contravariant over its argument type, and covariant over its return type. For this example we'll use the literal notation `A => B` to represent a `Function1[A, B]`. + +Assume the similar `Cat`, `Dog`, `Animal` inheritance tree used earlier, plus the following: + +```tut +class SmallAnimal +class Mouse extends SmallAnimal +``` + +Suppose we're working with functions that accept types of animals, and return the types of food they eat. If we would like a `Cat => SmallAnimal` (because cats eat small animals), but are given a `Animal => Mouse` instead, our program will still work. Intuitively an `Animal => Mouse` will still accept a `Cat` as an argument, because a `Cat` is an `Animal`, and it returns a `Mouse`, which is also an `SmallAnimal`. Since we can safely and invisibly substitute the former for the latter, we can say `Animal => Mouse` is a subtype of `Cat => SmallAnimal`. + +### Comparison With Other Languages + +Variance is supported in different ways by some languages that are similar to Scala. For example, variance annotations in Scala closely resemble those in C#, where the annotations are added when a class abstraction is defined (declaration-site variance). In Java, however, variance annotations are given by clients when a class abstraction is used (use-site variance). From 1bb099f7a835f0492e743e3208ce0c0ad665fada Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 09:56:55 +0200 Subject: [PATCH 066/112] Straggler (ba translation) --- _ba/tour/classes.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/_ba/tour/classes.md b/_ba/tour/classes.md index ffe1a8931a..2e7f4f973c 100644 --- a/_ba/tour/classes.md +++ b/_ba/tour/classes.md @@ -4,10 +4,10 @@ title: Klase discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 3 -outof: 33 + language: ba next-page: traits @@ -18,24 +18,24 @@ Klase u Scali su statički šabloni koji mogu biti instancirani u više objekata Slijedi definicija klase `Point`: class Point(xc: Int, yc: Int) { - + var x: Int = xc var y: Int = yc - + def move(dx: Int, dy: Int) { x = x + dx y = y + dy } - + override def toString(): String = "(" + x + ", " + y + ")"; } -Ova klasa definiše dvije varijable: `x` i `y`, i dvije metode: `move` i `toString`. +Ova klasa definiše dvije varijable: `x` i `y`, i dvije metode: `move` i `toString`. Metoda `move` prima dva cjelobrojna argumenta ali ne vraća vrijednost (implicitni povratni tip je `Unit`, koji odgovoara `void`-u u jezicima sličnim Javi). `toString`, za razliku, ne prima nikakve parametre ali vraća `String` vrijednost. Pošto `toString` prebrisava predefinisanu `toString` metodu, mora biti tagovana s `override`. -Klase u Scali se parametrizuju parametrima konstruktora. Kod iznad definiše dva parametra konstruktora, `xc` i `yc`; +Klase u Scali se parametrizuju parametrima konstruktora. Kod iznad definiše dva parametra konstruktora, `xc` i `yc`; oba su vidljiva u cijelom tijelu klase. U našem primjeru korišteni su za inicijalizaciju varijabli `x` i `y`. Klase se inicijalizaciju pomoću `new` primitive, kao u sljedećem primjeru: @@ -49,10 +49,10 @@ Klase se inicijalizaciju pomoću `new` primitive, kao u sljedećem primjeru: } } -Program definiše izvršnu aplikaciju `Classes` u form vrhovnog singlton objekta s `main` metodom. +Program definiše izvršnu aplikaciju `Classes` u form vrhovnog singlton objekta s `main` metodom. Metoda `main` kreira novu instancu klase `Point` i sprema je u vrijednost `pt`. -_Imajte u vidu da se vrijednosti definisane primitivom `val` razlikuju -od varijabli definisanih primitivom `var` (vidi klasu `Point` iznad) +_Imajte u vidu da se vrijednosti definisane primitivom `val` razlikuju +od varijabli definisanih primitivom `var` (vidi klasu `Point` iznad) u tome da ne dozvoljavaju promjenu vrijednosti; tj. vrijednost je konstanta._ Ovo je rezultat programa: From e5b6a7d0ffbb9e905abfe062eef24e986d6e97ad Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 10:13:39 +0200 Subject: [PATCH 067/112] Rest of translations for Scala Tour --- _config.yml | 9 + .../tour/abstract-types.md | 28 +- .../tour/annotations.md | 6 +- .../tour/anonymous-function-syntax.md | 6 +- .../tour/automatic-closures.md | 6 +- .../tour/case-classes.md | 6 +- .../tour/classes.md | 6 +- .../tour/compound-types.md | 6 +- .../tour/currying.md | 6 +- .../tour/default-parameter-values.md | 6 +- .../tour/explicitly-typed-self-references.md | 6 +- .../tour/extractor-objects.md | 6 +- .../tour/generic-classes.md | 6 +- .../tour/higher-order-functions.md | 6 +- .../tour/implicit-conversions.md | 6 +- .../tour/implicit-parameters.md | 6 +- .../tour/inner-classes.md | 6 +- .../tour/local-type-inference.md | 6 +- .../tour/lower-type-bounds.md | 6 +- .../tour/mixin-class-composition.md | 6 +- .../tour/named-parameters.md | 6 +- .../tour/nested-functions.md | 6 +- .../tour/operators.md | 6 +- .../tour/pattern-matching.md | 6 +- .../tour/polymorphic-methods.md | 6 +- .../tour/regular-expression-patterns.md | 6 +- .../tour/sequence-comprehensions.md | 6 +- .../tour/singleton-objects.md | 6 +- .../tour/tour-of-scala.md | 6 +- .../tour/traits.md | 6 +- .../tour/unified-types.md | 6 +- .../tour/upper-type-bounds.md | 6 +- .../tour/variances.md | 6 +- .../tour/abstract-types.md | 20 +- .../tour/annotations.md | 6 +- .../tour/anonymous-function-syntax.md | 6 +- .../tour/automatic-closures.md | 6 +- .../tour/case-classes.md | 6 +- .../tour/classes.md | 6 +- .../tour/compound-types.md | 6 +- .../tour/currying.md | 6 +- .../tour/default-parameter-values.md | 6 +- .../tour/explicitly-typed-self-references.md | 6 +- .../tour/extractor-objects.md | 6 +- .../tour/generic-classes.md | 6 +- .../tour/higher-order-functions.md | 6 +- .../tour/implicit-conversions.md | 6 +- .../tour/implicit-parameters.md | 6 +- .../tour/inner-classes.md | 6 +- .../tour/local-type-inference.md | 6 +- .../tour/lower-type-bounds.md | 6 +- .../tour/mixin-class-composition.md | 6 +- .../tour/named-parameters.md | 6 +- .../tour/nested-functions.md | 6 +- .../tour/operators.md | 6 +- .../tour/pattern-matching.md | 6 +- .../tour/polymorphic-methods.md | 6 +- .../tour/regular-expression-patterns.md | 6 +- .../tour/sequence-comprehensions.md | 6 +- .../tour/singleton-objects.md | 6 +- .../tour/tour-of-scala.md | 6 +- .../tour/traits.md | 6 +- .../tour/unified-types.md | 6 +- .../tour/upper-type-bounds.md | 6 +- .../tour/variances.md | 6 +- .../tour/abstract-types.md | 9 +- .../tour/annotations.md | 6 +- .../tour/anonymous-function-syntax.md | 6 +- .../tour/automatic-closures.md | 6 +- .../tour/case-classes.md | 6 +- .../tour/classes.md | 6 +- .../tour/compound-types.md | 6 +- .../tour/currying.md | 6 +- .../tour/default-parameter-values.md | 6 +- .../tour/explicitly-typed-self-references.md | 6 +- .../tour/extractor-objects.md | 6 +- .../tour/generic-classes.md | 6 +- .../tour/higher-order-functions.md | 6 +- .../tour/implicit-conversions.md | 6 +- .../tour/implicit-parameters.md | 6 +- .../tour/inner-classes.md | 6 +- .../tour/local-type-inference.md | 6 +- .../tour/lower-type-bounds.md | 6 +- .../tour/mixin-class-composition.md | 6 +- .../tour/named-parameters.md | 6 +- .../tour/nested-functions.md | 6 +- .../tour/operators.md | 6 +- .../tour/pattern-matching.md | 6 +- .../tour/polymorphic-methods.md | 6 +- .../tour/regular-expression-patterns.md | 6 +- .../tour/sequence-comprehensions.md | 6 +- .../tour/singleton-objects.md | 6 +- .../tour/tour-of-scala.md | 8 +- .../tour/traits.md | 6 +- .../tour/unified-types.md | 6 +- .../tour/upper-type-bounds.md | 6 +- .../tour/variances.md | 6 +- .../_posts/2017-02-13-polymorphic-methods.md | 37 --- .../tour/_posts/2017-02-13-abstract-types.md | 73 ---- .../tour/_posts/2017-02-13-annotations.md | 126 ------- tutorials/tour/_posts/2017-02-13-basics.md | 311 ------------------ .../_posts/2017-02-13-by-name-parameters.md | 40 --- .../tour/_posts/2017-02-13-case-classes.md | 57 ---- tutorials/tour/_posts/2017-02-13-classes.md | 111 ------- .../tour/_posts/2017-02-13-compound-types.md | 52 --- tutorials/tour/_posts/2017-02-13-currying.md | 41 --- .../2017-02-13-default-parameter-values.md | 47 --- .../_posts/2017-02-13-extractor-objects.md | 58 ---- .../_posts/2017-02-13-for-comprehensions.md | 51 --- .../tour/_posts/2017-02-13-generic-classes.md | 57 ---- .../2017-02-13-higher-order-functions.md | 42 --- .../_posts/2017-02-13-implicit-conversions.md | 59 ---- .../_posts/2017-02-13-implicit-parameters.md | 58 ---- .../tour/_posts/2017-02-13-inner-classes.md | 81 ----- .../_posts/2017-02-13-local-type-inference.md | 63 ---- .../_posts/2017-02-13-lower-type-bounds.md | 67 ---- .../tour/_posts/2017-02-13-named-arguments.md | 35 -- .../_posts/2017-02-13-nested-functions.md | 34 -- tutorials/tour/_posts/2017-02-13-operators.md | 79 ----- .../2017-02-13-regular-expression-patterns.md | 61 ---- .../tour/_posts/2017-02-13-self-types.md | 37 --- .../2017-02-13-sequence-comprehensions.md | 70 ---- .../_posts/2017-02-13-singleton-objects.md | 74 ----- .../tour/_posts/2017-02-13-tour-of-scala.md | 49 --- .../_posts/2017-02-13-upper-type-bounds.md | 49 --- 125 files changed, 317 insertions(+), 2228 deletions(-) rename ko/tutorials/tour/_posts/2017-02-13-abstract-types.md => _ko/tour/abstract-types.md (97%) rename ko/tutorials/tour/_posts/2017-02-13-annotations.md => _ko/tour/annotations.md (99%) rename ko/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md => _ko/tour/anonymous-function-syntax.md (93%) rename ko/tutorials/tour/_posts/2017-02-13-automatic-closures.md => _ko/tour/automatic-closures.md (97%) rename ko/tutorials/tour/_posts/2017-02-13-case-classes.md => _ko/tour/case-classes.md (98%) rename ko/tutorials/tour/_posts/2017-02-13-classes.md => _ko/tour/classes.md (96%) rename ko/tutorials/tour/_posts/2017-02-13-compound-types.md => _ko/tour/compound-types.md (96%) rename ko/tutorials/tour/_posts/2017-02-13-currying.md => _ko/tour/currying.md (94%) rename ko/tutorials/tour/_posts/2017-02-13-default-parameter-values.md => _ko/tour/default-parameter-values.md (97%) rename ko/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md => _ko/tour/explicitly-typed-self-references.md (98%) rename ko/tutorials/tour/_posts/2017-02-13-extractor-objects.md => _ko/tour/extractor-objects.md (96%) rename ko/tutorials/tour/_posts/2017-02-13-generic-classes.md => _ko/tour/generic-classes.md (96%) rename ko/tutorials/tour/_posts/2017-02-13-higher-order-functions.md => _ko/tour/higher-order-functions.md (95%) rename ko/tutorials/tour/_posts/2017-02-13-implicit-conversions.md => _ko/tour/implicit-conversions.md (97%) rename ko/tutorials/tour/_posts/2017-02-13-implicit-parameters.md => _ko/tour/implicit-parameters.md (98%) rename ko/tutorials/tour/_posts/2017-02-13-inner-classes.md => _ko/tour/inner-classes.md (98%) rename ko/tutorials/tour/_posts/2017-02-13-local-type-inference.md => _ko/tour/local-type-inference.md (97%) rename ko/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md => _ko/tour/lower-type-bounds.md (97%) rename ko/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md => _ko/tour/mixin-class-composition.md (97%) rename ko/tutorials/tour/_posts/2017-02-13-named-parameters.md => _ko/tour/named-parameters.md (94%) rename ko/tutorials/tour/_posts/2017-02-13-nested-functions.md => _ko/tour/nested-functions.md (92%) rename ko/tutorials/tour/_posts/2017-02-13-operators.md => _ko/tour/operators.md (95%) rename ko/tutorials/tour/_posts/2017-02-13-pattern-matching.md => _ko/tour/pattern-matching.md (96%) rename ko/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md => _ko/tour/polymorphic-methods.md (95%) rename ko/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md => _ko/tour/regular-expression-patterns.md (96%) rename ko/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md => _ko/tour/sequence-comprehensions.md (97%) rename ko/tutorials/tour/_posts/2017-02-13-singleton-objects.md => _ko/tour/singleton-objects.md (98%) rename ko/tutorials/tour/_posts/2017-02-13-tour-of-scala.md => _ko/tour/tour-of-scala.md (98%) rename ko/tutorials/tour/_posts/2017-02-13-traits.md => _ko/tour/traits.md (96%) rename ko/tutorials/tour/_posts/2017-02-13-unified-types.md => _ko/tour/unified-types.md (97%) rename ko/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md => _ko/tour/upper-type-bounds.md (95%) rename ko/tutorials/tour/_posts/2017-02-13-variances.md => _ko/tour/variances.md (98%) rename pl/tutorials/tour/_posts/2017-02-13-abstract-types.md => _pl/tour/abstract-types.md (97%) rename pl/tutorials/tour/_posts/2017-02-13-annotations.md => _pl/tour/annotations.md (98%) rename pl/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md => _pl/tour/anonymous-function-syntax.md (93%) rename pl/tutorials/tour/_posts/2017-02-13-automatic-closures.md => _pl/tour/automatic-closures.md (96%) rename pl/tutorials/tour/_posts/2017-02-13-case-classes.md => _pl/tour/case-classes.md (98%) rename pl/tutorials/tour/_posts/2017-02-13-classes.md => _pl/tour/classes.md (96%) rename pl/tutorials/tour/_posts/2017-02-13-compound-types.md => _pl/tour/compound-types.md (96%) rename pl/tutorials/tour/_posts/2017-02-13-currying.md => _pl/tour/currying.md (94%) rename pl/tutorials/tour/_posts/2017-02-13-default-parameter-values.md => _pl/tour/default-parameter-values.md (97%) rename pl/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md => _pl/tour/explicitly-typed-self-references.md (98%) rename pl/tutorials/tour/_posts/2017-02-13-extractor-objects.md => _pl/tour/extractor-objects.md (97%) rename pl/tutorials/tour/_posts/2017-02-13-generic-classes.md => _pl/tour/generic-classes.md (95%) rename pl/tutorials/tour/_posts/2017-02-13-higher-order-functions.md => _pl/tour/higher-order-functions.md (95%) rename pl/tutorials/tour/_posts/2017-02-13-implicit-conversions.md => _pl/tour/implicit-conversions.md (96%) rename pl/tutorials/tour/_posts/2017-02-13-implicit-parameters.md => _pl/tour/implicit-parameters.md (98%) rename pl/tutorials/tour/_posts/2017-02-13-inner-classes.md => _pl/tour/inner-classes.md (97%) rename pl/tutorials/tour/_posts/2017-02-13-local-type-inference.md => _pl/tour/local-type-inference.md (97%) rename pl/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md => _pl/tour/lower-type-bounds.md (96%) rename pl/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md => _pl/tour/mixin-class-composition.md (97%) rename pl/tutorials/tour/_posts/2017-02-13-named-parameters.md => _pl/tour/named-parameters.md (93%) rename pl/tutorials/tour/_posts/2017-02-13-nested-functions.md => _pl/tour/nested-functions.md (92%) rename pl/tutorials/tour/_posts/2017-02-13-operators.md => _pl/tour/operators.md (93%) rename pl/tutorials/tour/_posts/2017-02-13-pattern-matching.md => _pl/tour/pattern-matching.md (95%) rename pl/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md => _pl/tour/polymorphic-methods.md (93%) rename pl/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md => _pl/tour/regular-expression-patterns.md (93%) rename pl/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md => _pl/tour/sequence-comprehensions.md (97%) rename pl/tutorials/tour/_posts/2017-02-13-singleton-objects.md => _pl/tour/singleton-objects.md (97%) rename pl/tutorials/tour/_posts/2017-02-13-tour-of-scala.md => _pl/tour/tour-of-scala.md (98%) rename pl/tutorials/tour/_posts/2017-02-13-traits.md => _pl/tour/traits.md (96%) rename pl/tutorials/tour/_posts/2017-02-13-unified-types.md => _pl/tour/unified-types.md (97%) rename pl/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md => _pl/tour/upper-type-bounds.md (95%) rename pl/tutorials/tour/_posts/2017-02-13-variances.md => _pl/tour/variances.md (97%) rename pt-br/tutorials/tour/_posts/2017-02-13-abstract-types.md => _pt-br/tour/abstract-types.md (98%) rename pt-br/tutorials/tour/_posts/2017-02-13-annotations.md => _pt-br/tour/annotations.md (99%) rename pt-br/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md => _pt-br/tour/anonymous-function-syntax.md (93%) rename pt-br/tutorials/tour/_posts/2017-02-13-automatic-closures.md => _pt-br/tour/automatic-closures.md (97%) rename pt-br/tutorials/tour/_posts/2017-02-13-case-classes.md => _pt-br/tour/case-classes.md (98%) rename pt-br/tutorials/tour/_posts/2017-02-13-classes.md => _pt-br/tour/classes.md (96%) rename pt-br/tutorials/tour/_posts/2017-02-13-compound-types.md => _pt-br/tour/compound-types.md (96%) rename pt-br/tutorials/tour/_posts/2017-02-13-currying.md => _pt-br/tour/currying.md (95%) rename pt-br/tutorials/tour/_posts/2017-02-13-default-parameter-values.md => _pt-br/tour/default-parameter-values.md (97%) rename pt-br/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md => _pt-br/tour/explicitly-typed-self-references.md (98%) rename pt-br/tutorials/tour/_posts/2017-02-13-extractor-objects.md => _pt-br/tour/extractor-objects.md (97%) rename pt-br/tutorials/tour/_posts/2017-02-13-generic-classes.md => _pt-br/tour/generic-classes.md (96%) rename pt-br/tutorials/tour/_posts/2017-02-13-higher-order-functions.md => _pt-br/tour/higher-order-functions.md (94%) rename pt-br/tutorials/tour/_posts/2017-02-13-implicit-conversions.md => _pt-br/tour/implicit-conversions.md (96%) rename pt-br/tutorials/tour/_posts/2017-02-13-implicit-parameters.md => _pt-br/tour/implicit-parameters.md (98%) rename pt-br/tutorials/tour/_posts/2017-02-13-inner-classes.md => _pt-br/tour/inner-classes.md (97%) rename pt-br/tutorials/tour/_posts/2017-02-13-local-type-inference.md => _pt-br/tour/local-type-inference.md (97%) rename pt-br/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md => _pt-br/tour/lower-type-bounds.md (96%) rename pt-br/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md => _pt-br/tour/mixin-class-composition.md (97%) rename pt-br/tutorials/tour/_posts/2017-02-13-named-parameters.md => _pt-br/tour/named-parameters.md (94%) rename pt-br/tutorials/tour/_posts/2017-02-13-nested-functions.md => _pt-br/tour/nested-functions.md (92%) rename pt-br/tutorials/tour/_posts/2017-02-13-operators.md => _pt-br/tour/operators.md (93%) rename pt-br/tutorials/tour/_posts/2017-02-13-pattern-matching.md => _pt-br/tour/pattern-matching.md (96%) rename pt-br/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md => _pt-br/tour/polymorphic-methods.md (93%) rename pt-br/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md => _pt-br/tour/regular-expression-patterns.md (96%) rename pt-br/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md => _pt-br/tour/sequence-comprehensions.md (97%) rename pt-br/tutorials/tour/_posts/2017-02-13-singleton-objects.md => _pt-br/tour/singleton-objects.md (98%) rename pt-br/tutorials/tour/_posts/2017-02-13-tour-of-scala.md => _pt-br/tour/tour-of-scala.md (98%) rename pt-br/tutorials/tour/_posts/2017-02-13-traits.md => _pt-br/tour/traits.md (96%) rename pt-br/tutorials/tour/_posts/2017-02-13-unified-types.md => _pt-br/tour/unified-types.md (97%) rename pt-br/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md => _pt-br/tour/upper-type-bounds.md (95%) rename pt-br/tutorials/tour/_posts/2017-02-13-variances.md => _pt-br/tour/variances.md (97%) delete mode 100644 de/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md delete mode 100644 tutorials/tour/_posts/2017-02-13-abstract-types.md delete mode 100644 tutorials/tour/_posts/2017-02-13-annotations.md delete mode 100644 tutorials/tour/_posts/2017-02-13-basics.md delete mode 100644 tutorials/tour/_posts/2017-02-13-by-name-parameters.md delete mode 100644 tutorials/tour/_posts/2017-02-13-case-classes.md delete mode 100644 tutorials/tour/_posts/2017-02-13-classes.md delete mode 100644 tutorials/tour/_posts/2017-02-13-compound-types.md delete mode 100644 tutorials/tour/_posts/2017-02-13-currying.md delete mode 100644 tutorials/tour/_posts/2017-02-13-default-parameter-values.md delete mode 100644 tutorials/tour/_posts/2017-02-13-extractor-objects.md delete mode 100644 tutorials/tour/_posts/2017-02-13-for-comprehensions.md delete mode 100644 tutorials/tour/_posts/2017-02-13-generic-classes.md delete mode 100644 tutorials/tour/_posts/2017-02-13-higher-order-functions.md delete mode 100644 tutorials/tour/_posts/2017-02-13-implicit-conversions.md delete mode 100644 tutorials/tour/_posts/2017-02-13-implicit-parameters.md delete mode 100644 tutorials/tour/_posts/2017-02-13-inner-classes.md delete mode 100644 tutorials/tour/_posts/2017-02-13-local-type-inference.md delete mode 100644 tutorials/tour/_posts/2017-02-13-lower-type-bounds.md delete mode 100644 tutorials/tour/_posts/2017-02-13-named-arguments.md delete mode 100644 tutorials/tour/_posts/2017-02-13-nested-functions.md delete mode 100644 tutorials/tour/_posts/2017-02-13-operators.md delete mode 100644 tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md delete mode 100644 tutorials/tour/_posts/2017-02-13-self-types.md delete mode 100644 tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md delete mode 100644 tutorials/tour/_posts/2017-02-13-singleton-objects.md delete mode 100644 tutorials/tour/_posts/2017-02-13-tour-of-scala.md delete mode 100644 tutorials/tour/_posts/2017-02-13-upper-type-bounds.md diff --git a/_config.yml b/_config.yml index 826a28def4..f597370f83 100644 --- a/_config.yml +++ b/_config.yml @@ -42,6 +42,15 @@ collections: ba: # Bosnian translations output: true permalink: /:collection/:path.html + pl: # Polish translations + output: true + permalink: /:collection/:path.html + pt-br: # Brazilian Portuguese translations + output: true + permalink: /:collection/:path.html + ko: # Korean translations + output: true + permalink: /:collection/:path.html defaults: diff --git a/ko/tutorials/tour/_posts/2017-02-13-abstract-types.md b/_ko/tour/abstract-types.md similarity index 97% rename from ko/tutorials/tour/_posts/2017-02-13-abstract-types.md rename to _ko/tour/abstract-types.md index 287a26ebfc..cd68969e81 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-abstract-types.md +++ b/_ko/tour/abstract-types.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: 추상 타입 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 22 -outof: 35 + language: ko next-page: compound-types @@ -17,28 +17,28 @@ previous-page: inner-classes 스칼라에선 값(생성자 파라미터)과 타입(클래스가 [제네릭](generic-classes.html)일 경우)으로 클래스가 매개변수화된다. 규칙성을 지키기 위해, 값이 객체 멤버가 될 수 있을 뿐만 아니라 값의 타입 역시 객체의 멤버가 된다. 또한 이런 두 형태의 멤버 모두 다 구체화되거나 추상화될 수 있다. 이 예제에선 [클래스](traits.html) `Buffer`의 멤버로써 완전히 확정되지 않은 값과 추상 타입을 정의하고 있다. - + trait Buffer { type T val element: T } - + *추상 타입*은 본성이 완전히 식별되지 않은 타입이다. 위의 예제에선 클래스 `Buffer`의 각 객체가 T라는 타입 멤버를 갖고 있다는 점만 알 수 있으며, 클래스 `Buffer`의 정의는 멤버 타입 `T`에 해당하는 특정 타입이 무엇이지 밝히고 있지 않다. 값 정의와 같이 타입 정의도 서브클래스에서 재정의(override) 할 수 있다. 이것은 타입 경계(추상 타입에 해당하는 예시를 나타내는)를 좀더 엄격하게 함으로써 추상 타입에 대해 좀더 많은 정보를 얻을수 있게 해준다. 다음 프로그램에선 `T` 타입이 새로운 추상 타입 `U`로 표현된 `Seq[U]`의 서브타입이어야 함을 나타내서, 버퍼에 시퀀스 만을 저장하는 클래스 `SeqBuffer`를 만들었다. - + abstract class SeqBuffer extends Buffer { type U type T <: Seq[U] def length = element.length } - -추상 타입 멤버를 포함한 트레잇이나 [클래스](classes.html)는 종종 익명 클래스 인스턴스화와 함께 사용된다. 이를 알아보기 위해 정수의 리스트를 참조하는 시퀀스 버퍼를 다루는 프로그램을 살펴보자. - + +추상 타입 멤버를 포함한 트레잇이나 [클래스](classes.html)는 종종 익명 클래스 인스턴스화와 함께 사용된다. 이를 알아보기 위해 정수의 리스트를 참조하는 시퀀스 버퍼를 다루는 프로그램을 살펴보자. + abstract class IntSeqBuffer extends SeqBuffer { type U = Int } - + object AbstractTypeTest1 extends App { def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer = new IntSeqBuffer { @@ -49,11 +49,11 @@ previous-page: inner-classes println("length = " + buf.length) println("content = " + buf.element) } - + 메소드 `newIntSeqBuf`의 반환 타입은 트레잇 `Buffer`의 특수화를 따르며, 타입 `U`가 `Int`와 같아진다. 메소드 `newIntSeqBuf` 내부의 익명 클래스 인스턴스화에서도 비슷한 타입 별칭이 있다. 여기선 `T` 타입이 `List[Int]`를 가리키는 `IntSeqBuf`의 새로운 인스턴스를 생성한다. 추상 타입 멤버를 클래스의 타입 파라미터로 하거나 클래스의 타입 파라미터로 추상 타입 멤버로를사용할 수 있음에 주목하자. 다음은 타입 파라미터만을 사용한, 앞서 살펴본 코드의 새로운 버전이다. - + abstract class Buffer[+T] { val element: T } @@ -69,7 +69,7 @@ previous-page: inner-classes println("length = " + buf.length) println("content = " + buf.element) } - + 여기선 [가변성 어노테이션](variances.html)을 사용해야만 한다는 점에 유의하자. 이를 사용하지 않으면 메소드 `newIntSeqBuf`에서 반환되는 객체의 특정 시퀀스 구현 타입을 감출 수 없게 된다. 뿐만 아니라 추상 타입을 타입 파라미터로 대체할 수 없는 경우도 있다. 윤창석, 이한욱 옮김, 고광현 수정 diff --git a/ko/tutorials/tour/_posts/2017-02-13-annotations.md b/_ko/tour/annotations.md similarity index 99% rename from ko/tutorials/tour/_posts/2017-02-13-annotations.md rename to _ko/tour/annotations.md index d741a7de38..a846ddf373 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-annotations.md +++ b/_ko/tour/annotations.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 어노테이션 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 31 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md b/_ko/tour/anonymous-function-syntax.md similarity index 93% rename from ko/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md rename to _ko/tour/anonymous-function-syntax.md index 8d36c8fe06..7e6fb5dd42 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md +++ b/_ko/tour/anonymous-function-syntax.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 익명 함수 구문 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 6 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-automatic-closures.md b/_ko/tour/automatic-closures.md similarity index 97% rename from ko/tutorials/tour/_posts/2017-02-13-automatic-closures.md rename to _ko/tour/automatic-closures.md index 8e267b4a7c..17d07625db 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-automatic-closures.md +++ b/_ko/tour/automatic-closures.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 타입 의존 클로저의 자동 구성 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 30 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-case-classes.md b/_ko/tour/case-classes.md similarity index 98% rename from ko/tutorials/tour/_posts/2017-02-13-case-classes.md rename to _ko/tour/case-classes.md index 34c68fe83f..a68a61d7a0 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-case-classes.md +++ b/_ko/tour/case-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 케이스 클래스 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 10 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-classes.md b/_ko/tour/classes.md similarity index 96% rename from ko/tutorials/tour/_posts/2017-02-13-classes.md rename to _ko/tour/classes.md index 78dc003aa3..47a738c11b 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-classes.md +++ b/_ko/tour/classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 클래스 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 3 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-compound-types.md b/_ko/tour/compound-types.md similarity index 96% rename from ko/tutorials/tour/_posts/2017-02-13-compound-types.md rename to _ko/tour/compound-types.md index 3d9836b016..218d27713d 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-compound-types.md +++ b/_ko/tour/compound-types.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 합성 타입 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 23 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-currying.md b/_ko/tour/currying.md similarity index 94% rename from ko/tutorials/tour/_posts/2017-02-13-currying.md rename to _ko/tour/currying.md index ea0fd209a9..03d6e171ec 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-currying.md +++ b/_ko/tour/currying.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 커링 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 9 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-default-parameter-values.md b/_ko/tour/default-parameter-values.md similarity index 97% rename from ko/tutorials/tour/_posts/2017-02-13-default-parameter-values.md rename to _ko/tour/default-parameter-values.md index 5b00d6ee82..7917647f68 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-default-parameter-values.md +++ b/_ko/tour/default-parameter-values.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 기본 파라미터 값 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 32 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md b/_ko/tour/explicitly-typed-self-references.md similarity index 98% rename from ko/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md rename to _ko/tour/explicitly-typed-self-references.md index f89ea99103..1cd164337f 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md +++ b/_ko/tour/explicitly-typed-self-references.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 명시적으로 타입이 지정된 자기 참조 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 24 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-extractor-objects.md b/_ko/tour/extractor-objects.md similarity index 96% rename from ko/tutorials/tour/_posts/2017-02-13-extractor-objects.md rename to _ko/tour/extractor-objects.md index e3349effa8..f72bf933f7 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-extractor-objects.md +++ b/_ko/tour/extractor-objects.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 추출자 오브젝트 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 15 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-generic-classes.md b/_ko/tour/generic-classes.md similarity index 96% rename from ko/tutorials/tour/_posts/2017-02-13-generic-classes.md rename to _ko/tour/generic-classes.md index 1d0418ddd3..66d27163e2 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-generic-classes.md +++ b/_ko/tour/generic-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 제네릭 클래스 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 17 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-higher-order-functions.md b/_ko/tour/higher-order-functions.md similarity index 95% rename from ko/tutorials/tour/_posts/2017-02-13-higher-order-functions.md rename to _ko/tour/higher-order-functions.md index b2f05f2e0a..fc05e4b802 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-higher-order-functions.md +++ b/_ko/tour/higher-order-functions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 고차 함수 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 7 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-implicit-conversions.md b/_ko/tour/implicit-conversions.md similarity index 97% rename from ko/tutorials/tour/_posts/2017-02-13-implicit-conversions.md rename to _ko/tour/implicit-conversions.md index 31f552b985..eb42d4a0fb 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-implicit-conversions.md +++ b/_ko/tour/implicit-conversions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 암시적 변환 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 26 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-implicit-parameters.md b/_ko/tour/implicit-parameters.md similarity index 98% rename from ko/tutorials/tour/_posts/2017-02-13-implicit-parameters.md rename to _ko/tour/implicit-parameters.md index b01d8b9e81..8132d96ad6 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-implicit-parameters.md +++ b/_ko/tour/implicit-parameters.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 암시적 파라미터 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 25 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-inner-classes.md b/_ko/tour/inner-classes.md similarity index 98% rename from ko/tutorials/tour/_posts/2017-02-13-inner-classes.md rename to _ko/tour/inner-classes.md index f763a48573..ac0f2bba2c 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-inner-classes.md +++ b/_ko/tour/inner-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 내부 클래스 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 21 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-local-type-inference.md b/_ko/tour/local-type-inference.md similarity index 97% rename from ko/tutorials/tour/_posts/2017-02-13-local-type-inference.md rename to _ko/tour/local-type-inference.md index 03091f5873..421fb41036 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-local-type-inference.md +++ b/_ko/tour/local-type-inference.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 로컬 타입 추론 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 28 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md b/_ko/tour/lower-type-bounds.md similarity index 97% rename from ko/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md rename to _ko/tour/lower-type-bounds.md index c361b9f7f3..c36d5a43c3 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md +++ b/_ko/tour/lower-type-bounds.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 하위 타입 경계 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 20 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md b/_ko/tour/mixin-class-composition.md similarity index 97% rename from ko/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md rename to _ko/tour/mixin-class-composition.md index 5294e01e81..c290082dca 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md +++ b/_ko/tour/mixin-class-composition.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 믹스인 클래스 컴포지션 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 5 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-named-parameters.md b/_ko/tour/named-parameters.md similarity index 94% rename from ko/tutorials/tour/_posts/2017-02-13-named-parameters.md rename to _ko/tour/named-parameters.md index d6bfbd16e5..7f13650e38 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-named-parameters.md +++ b/_ko/tour/named-parameters.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 이름을 지정한 파라미터 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 33 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-nested-functions.md b/_ko/tour/nested-functions.md similarity index 92% rename from ko/tutorials/tour/_posts/2017-02-13-nested-functions.md rename to _ko/tour/nested-functions.md index 898c7d96fa..d7eed785cf 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-nested-functions.md +++ b/_ko/tour/nested-functions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 중첩 함수 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 8 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-operators.md b/_ko/tour/operators.md similarity index 95% rename from ko/tutorials/tour/_posts/2017-02-13-operators.md rename to _ko/tour/operators.md index 04f89ad267..1eb3c87030 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-operators.md +++ b/_ko/tour/operators.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 연산자 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 29 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-pattern-matching.md b/_ko/tour/pattern-matching.md similarity index 96% rename from ko/tutorials/tour/_posts/2017-02-13-pattern-matching.md rename to _ko/tour/pattern-matching.md index 60ef0254fd..9be1ac47e4 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-pattern-matching.md +++ b/_ko/tour/pattern-matching.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 패턴 매칭 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 11 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md b/_ko/tour/polymorphic-methods.md similarity index 95% rename from ko/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md rename to _ko/tour/polymorphic-methods.md index 76bd362176..4d1cbd177e 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md +++ b/_ko/tour/polymorphic-methods.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 다형성 메소드 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 27 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md b/_ko/tour/regular-expression-patterns.md similarity index 96% rename from ko/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md rename to _ko/tour/regular-expression-patterns.md index ebcdfa2799..4d1a0c00ee 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md +++ b/_ko/tour/regular-expression-patterns.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 정규 표현식 패턴 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 14 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md b/_ko/tour/sequence-comprehensions.md similarity index 97% rename from ko/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md rename to _ko/tour/sequence-comprehensions.md index 9fdc01cad7..3e64c37169 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md +++ b/_ko/tour/sequence-comprehensions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 시퀀스 컴프리헨션 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 16 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-singleton-objects.md b/_ko/tour/singleton-objects.md similarity index 98% rename from ko/tutorials/tour/_posts/2017-02-13-singleton-objects.md rename to _ko/tour/singleton-objects.md index bb5f606e3f..cd1ddf0a57 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-singleton-objects.md +++ b/_ko/tour/singleton-objects.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 싱글톤 객체 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 12 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-tour-of-scala.md b/_ko/tour/tour-of-scala.md similarity index 98% rename from ko/tutorials/tour/_posts/2017-02-13-tour-of-scala.md rename to _ko/tour/tour-of-scala.md index 69ec725cd9..6e2b1cfe56 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-tour-of-scala.md +++ b/_ko/tour/tour-of-scala.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 들어가며 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 1 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-traits.md b/_ko/tour/traits.md similarity index 96% rename from ko/tutorials/tour/_posts/2017-02-13-traits.md rename to _ko/tour/traits.md index 6d4b4d9bda..7972ed6754 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-traits.md +++ b/_ko/tour/traits.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 트레잇 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 4 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-unified-types.md b/_ko/tour/unified-types.md similarity index 97% rename from ko/tutorials/tour/_posts/2017-02-13-unified-types.md rename to _ko/tour/unified-types.md index 66adda2cbb..78d2aa37dc 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-unified-types.md +++ b/_ko/tour/unified-types.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 통합된 타입 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 2 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md b/_ko/tour/upper-type-bounds.md similarity index 95% rename from ko/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md rename to _ko/tour/upper-type-bounds.md index e19fba7313..1060947f1b 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md +++ b/_ko/tour/upper-type-bounds.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 상위 타입 경계 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 19 language: ko diff --git a/ko/tutorials/tour/_posts/2017-02-13-variances.md b/_ko/tour/variances.md similarity index 98% rename from ko/tutorials/tour/_posts/2017-02-13-variances.md rename to _ko/tour/variances.md index 23d85650e0..aa0c5102e2 100644 --- a/ko/tutorials/tour/_posts/2017-02-13-variances.md +++ b/_ko/tour/variances.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: 가변성 discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 18 language: ko diff --git a/pl/tutorials/tour/_posts/2017-02-13-abstract-types.md b/_pl/tour/abstract-types.md similarity index 97% rename from pl/tutorials/tour/_posts/2017-02-13-abstract-types.md rename to _pl/tour/abstract-types.md index a3ec96ee8c..4d152a4940 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-abstract-types.md +++ b/_pl/tour/abstract-types.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Typy abstrakcyjne discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 22 language: pl next-page: compound-types @@ -15,18 +15,18 @@ previous-page: inner-classes W Scali klasy są parametryzowane wartościami (parametry konstruktora) oraz typami (jeżeli klasa jest [generyczna](generic-classes.html)). Aby zachować regularność, zarówno typy jak i wartości są elementami klasy. Analogicznie mogą one być konkretne albo abstrakcyjne. Poniższy przykład definiuje wartość określaną przez abstrakcyjny typ będący elementem [cechy](traits.html) `Buffer`: - + ```tut trait Buffer { type T val element: T } ``` - + *Typy abstrakcyjne* są typami, które nie są jednoznacznie określone. W powyższym przykładzie wiemy tylko, że każdy obiekt klasy `Buffer` posiada typ `T`, ale definicja klasy `Buffer` nie zdradza jakiemu konkretnie typowi on odpowiada. Tak jak definicje wartości, możemy także nadpisać definicje typów w klasach pochodnych. Pozwala to nam na odkrywanie dodatkowych informacji o abstrakcyjnym typie poprzez zawężanie jego ograniczeń (które opisują możliwe warianty konkretnego typu). W poniższym programie definiujemy klasę `SeqBuffer`, która ogranicza możliwe typy `T` do pochodnych sekwencji `Seq[U]` dla nowego typu `U`: - + ```tut abstract class SeqBuffer extends Buffer { type U @@ -34,9 +34,9 @@ abstract class SeqBuffer extends Buffer { def length = element.length } ``` - + Cechy oraz [klasy](classes.html) z abstrakcyjnymi typami są często używane w połączeniu z anonimowymi klasami. Aby to zilustrować, wykorzystamy program, w którym utworzymy bufor sekwencji ograniczony do listy liczb całkowitych: - + ```tut abstract class IntSeqBuffer extends SeqBuffer { type U = Int @@ -53,11 +53,11 @@ object AbstractTypeTest1 extends App { println("content = " + buf.element) } ``` - + Typ zwracany przez metodę `newIntSeqBuf` nawiązuje do specjalizacji cechy `Buffer`, w której typ `U` jest równy `Int`. Podobnie w anonimowej klasie tworzonej w metodzie `newIntSeqBuf` określamy `T` jako `List[Int]`. Warto zwrócić uwagę, że często jest możliwa zamiana abstrakcyjnych typów w parametry typów klas i odwrotnie. Poniższy przykład stosuje wyłącznie parametry typów: - + ```tut abstract class Buffer[+T] { val element: T diff --git a/pl/tutorials/tour/_posts/2017-02-13-annotations.md b/_pl/tour/annotations.md similarity index 98% rename from pl/tutorials/tour/_posts/2017-02-13-annotations.md rename to _pl/tour/annotations.md index cb1fba874b..65921a156f 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-annotations.md +++ b/_pl/tour/annotations.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Adnotacje discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 31 next-page: default-parameter-values previous-page: automatic-closures diff --git a/pl/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md b/_pl/tour/anonymous-function-syntax.md similarity index 93% rename from pl/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md rename to _pl/tour/anonymous-function-syntax.md index b2803236a2..60a428f3b8 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md +++ b/_pl/tour/anonymous-function-syntax.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Funkcje anonimowe discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 6 language: pl next-page: higher-order-functions diff --git a/pl/tutorials/tour/_posts/2017-02-13-automatic-closures.md b/_pl/tour/automatic-closures.md similarity index 96% rename from pl/tutorials/tour/_posts/2017-02-13-automatic-closures.md rename to _pl/tour/automatic-closures.md index ff18a65c86..ca6c14eabf 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-automatic-closures.md +++ b/_pl/tour/automatic-closures.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Automatyczna konstrukcja domknięć discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 30 language: pl next-page: annotations diff --git a/pl/tutorials/tour/_posts/2017-02-13-case-classes.md b/_pl/tour/case-classes.md similarity index 98% rename from pl/tutorials/tour/_posts/2017-02-13-case-classes.md rename to _pl/tour/case-classes.md index 579031473c..831277f07b 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-case-classes.md +++ b/_pl/tour/case-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Klasy przypadków discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 10 language: pl next-page: pattern-matching diff --git a/pl/tutorials/tour/_posts/2017-02-13-classes.md b/_pl/tour/classes.md similarity index 96% rename from pl/tutorials/tour/_posts/2017-02-13-classes.md rename to _pl/tour/classes.md index cfbf9c251e..24a69c466c 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-classes.md +++ b/_pl/tour/classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Klasy discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 3 language: pl next-page: traits diff --git a/pl/tutorials/tour/_posts/2017-02-13-compound-types.md b/_pl/tour/compound-types.md similarity index 96% rename from pl/tutorials/tour/_posts/2017-02-13-compound-types.md rename to _pl/tour/compound-types.md index 382bbf31b1..84ebd0d963 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-compound-types.md +++ b/_pl/tour/compound-types.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Typy złożone discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 23 language: pl next-page: explicitly-typed-self-references diff --git a/pl/tutorials/tour/_posts/2017-02-13-currying.md b/_pl/tour/currying.md similarity index 94% rename from pl/tutorials/tour/_posts/2017-02-13-currying.md rename to _pl/tour/currying.md index d10b9f2740..29735548cc 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-currying.md +++ b/_pl/tour/currying.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Rozwijanie funkcji (Currying) discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 9 language: pl next-page: case-classes diff --git a/pl/tutorials/tour/_posts/2017-02-13-default-parameter-values.md b/_pl/tour/default-parameter-values.md similarity index 97% rename from pl/tutorials/tour/_posts/2017-02-13-default-parameter-values.md rename to _pl/tour/default-parameter-values.md index c143f6306a..5b32053260 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-default-parameter-values.md +++ b/_pl/tour/default-parameter-values.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Domyślne wartości parametrów discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 32 language: pl next-page: named-parameters diff --git a/pl/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md b/_pl/tour/explicitly-typed-self-references.md similarity index 98% rename from pl/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md rename to _pl/tour/explicitly-typed-self-references.md index 77002541f7..2be5e78d67 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md +++ b/_pl/tour/explicitly-typed-self-references.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Jawnie typowane samoreferencje discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 24 language: pl next-page: implicit-parameters diff --git a/pl/tutorials/tour/_posts/2017-02-13-extractor-objects.md b/_pl/tour/extractor-objects.md similarity index 97% rename from pl/tutorials/tour/_posts/2017-02-13-extractor-objects.md rename to _pl/tour/extractor-objects.md index 20ad0639fa..5b3e20f5e6 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-extractor-objects.md +++ b/_pl/tour/extractor-objects.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Obiekty ekstraktorów discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 15 language: pl next-page: sequence-comprehensions diff --git a/pl/tutorials/tour/_posts/2017-02-13-generic-classes.md b/_pl/tour/generic-classes.md similarity index 95% rename from pl/tutorials/tour/_posts/2017-02-13-generic-classes.md rename to _pl/tour/generic-classes.md index 0dd7f34b16..c822d2087d 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-generic-classes.md +++ b/_pl/tour/generic-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Klasy generyczne discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 17 language: pl next-page: variances diff --git a/pl/tutorials/tour/_posts/2017-02-13-higher-order-functions.md b/_pl/tour/higher-order-functions.md similarity index 95% rename from pl/tutorials/tour/_posts/2017-02-13-higher-order-functions.md rename to _pl/tour/higher-order-functions.md index aa23ac2ba8..c93ea72c71 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-higher-order-functions.md +++ b/_pl/tour/higher-order-functions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Funkcje wyższego rzędu discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 7 language: pl next-page: nested-functions diff --git a/pl/tutorials/tour/_posts/2017-02-13-implicit-conversions.md b/_pl/tour/implicit-conversions.md similarity index 96% rename from pl/tutorials/tour/_posts/2017-02-13-implicit-conversions.md rename to _pl/tour/implicit-conversions.md index f1f9c01cbd..aea3a9df82 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-implicit-conversions.md +++ b/_pl/tour/implicit-conversions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Konwersje niejawne discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 26 language: pl next-page: polymorphic-methods diff --git a/pl/tutorials/tour/_posts/2017-02-13-implicit-parameters.md b/_pl/tour/implicit-parameters.md similarity index 98% rename from pl/tutorials/tour/_posts/2017-02-13-implicit-parameters.md rename to _pl/tour/implicit-parameters.md index dca7ff671f..4bac40487a 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-implicit-parameters.md +++ b/_pl/tour/implicit-parameters.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Parametry domniemane discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 25 language: pl next-page: implicit-conversions diff --git a/pl/tutorials/tour/_posts/2017-02-13-inner-classes.md b/_pl/tour/inner-classes.md similarity index 97% rename from pl/tutorials/tour/_posts/2017-02-13-inner-classes.md rename to _pl/tour/inner-classes.md index c423ddf34b..b4a8b66561 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-inner-classes.md +++ b/_pl/tour/inner-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Klasy wewnętrzne discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 21 language: pl next-page: abstract-types diff --git a/pl/tutorials/tour/_posts/2017-02-13-local-type-inference.md b/_pl/tour/local-type-inference.md similarity index 97% rename from pl/tutorials/tour/_posts/2017-02-13-local-type-inference.md rename to _pl/tour/local-type-inference.md index a58db93f0a..c3d057bab9 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-local-type-inference.md +++ b/_pl/tour/local-type-inference.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Lokalna inferencja typów discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 28 language: pl next-page: operators diff --git a/pl/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md b/_pl/tour/lower-type-bounds.md similarity index 96% rename from pl/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md rename to _pl/tour/lower-type-bounds.md index 8765fae391..dd22954df8 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md +++ b/_pl/tour/lower-type-bounds.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Dolne ograniczenia typów discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 20 language: pl next-page: inner-classes diff --git a/pl/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md b/_pl/tour/mixin-class-composition.md similarity index 97% rename from pl/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md rename to _pl/tour/mixin-class-composition.md index 4ede038725..ed358ac8ca 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md +++ b/_pl/tour/mixin-class-composition.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Kompozycja domieszek discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 5 language: pl next-page: anonymous-function-syntax diff --git a/pl/tutorials/tour/_posts/2017-02-13-named-parameters.md b/_pl/tour/named-parameters.md similarity index 93% rename from pl/tutorials/tour/_posts/2017-02-13-named-parameters.md rename to _pl/tour/named-parameters.md index ac391d5b69..29b8bc8c36 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-named-parameters.md +++ b/_pl/tour/named-parameters.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Parametry nazwane discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 33 language: pl previous-page: default-parameter-values diff --git a/pl/tutorials/tour/_posts/2017-02-13-nested-functions.md b/_pl/tour/nested-functions.md similarity index 92% rename from pl/tutorials/tour/_posts/2017-02-13-nested-functions.md rename to _pl/tour/nested-functions.md index a746e4f94f..0f905aff11 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-nested-functions.md +++ b/_pl/tour/nested-functions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Funkcje zagnieżdżone discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 8 language: pl next-page: currying diff --git a/pl/tutorials/tour/_posts/2017-02-13-operators.md b/_pl/tour/operators.md similarity index 93% rename from pl/tutorials/tour/_posts/2017-02-13-operators.md rename to _pl/tour/operators.md index 3160dfc91f..a6a1c9a057 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-operators.md +++ b/_pl/tour/operators.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Operatory discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 29 language: pl next-page: automatic-closures diff --git a/pl/tutorials/tour/_posts/2017-02-13-pattern-matching.md b/_pl/tour/pattern-matching.md similarity index 95% rename from pl/tutorials/tour/_posts/2017-02-13-pattern-matching.md rename to _pl/tour/pattern-matching.md index 73bc18013a..f36648439a 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-pattern-matching.md +++ b/_pl/tour/pattern-matching.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Dopasowanie wzorców (Pattern matching) discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 11 language: pl next-page: singleton-objects diff --git a/pl/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md b/_pl/tour/polymorphic-methods.md similarity index 93% rename from pl/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md rename to _pl/tour/polymorphic-methods.md index 1b9fb81266..c40f48dbac 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md +++ b/_pl/tour/polymorphic-methods.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Metody polimorficzne discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 27 language: pl next-page: local-type-inference diff --git a/pl/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md b/_pl/tour/regular-expression-patterns.md similarity index 93% rename from pl/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md rename to _pl/tour/regular-expression-patterns.md index f47c640906..33c42cbad7 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md +++ b/_pl/tour/regular-expression-patterns.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Wzorce wyrażeń regularnych discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 14 language: pl diff --git a/pl/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md b/_pl/tour/sequence-comprehensions.md similarity index 97% rename from pl/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md rename to _pl/tour/sequence-comprehensions.md index 981dae4046..c477ee25e0 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md +++ b/_pl/tour/sequence-comprehensions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Instrukcje for (For comprehension) discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 16 language: pl next-page: generic-classes diff --git a/pl/tutorials/tour/_posts/2017-02-13-singleton-objects.md b/_pl/tour/singleton-objects.md similarity index 97% rename from pl/tutorials/tour/_posts/2017-02-13-singleton-objects.md rename to _pl/tour/singleton-objects.md index 81406f39b2..653c10ac88 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-singleton-objects.md +++ b/_pl/tour/singleton-objects.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Obiekty singleton discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 12 language: pl diff --git a/pl/tutorials/tour/_posts/2017-02-13-tour-of-scala.md b/_pl/tour/tour-of-scala.md similarity index 98% rename from pl/tutorials/tour/_posts/2017-02-13-tour-of-scala.md rename to _pl/tour/tour-of-scala.md index 71c5bb79a8..936a18277f 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-tour-of-scala.md +++ b/_pl/tour/tour-of-scala.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Wprowadzenie discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 1 outof: 33 language: pl diff --git a/pl/tutorials/tour/_posts/2017-02-13-traits.md b/_pl/tour/traits.md similarity index 96% rename from pl/tutorials/tour/_posts/2017-02-13-traits.md rename to _pl/tour/traits.md index f884e0c905..9d11ab5213 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-traits.md +++ b/_pl/tour/traits.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Cechy discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 4 language: pl next-page: mixin-class-composition diff --git a/pl/tutorials/tour/_posts/2017-02-13-unified-types.md b/_pl/tour/unified-types.md similarity index 97% rename from pl/tutorials/tour/_posts/2017-02-13-unified-types.md rename to _pl/tour/unified-types.md index bdc05dc304..103647d8e7 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-unified-types.md +++ b/_pl/tour/unified-types.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Hierarchia typów discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 2 language: pl next-page: classes diff --git a/pl/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md b/_pl/tour/upper-type-bounds.md similarity index 95% rename from pl/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md rename to _pl/tour/upper-type-bounds.md index 287800cbc6..000cd344c0 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md +++ b/_pl/tour/upper-type-bounds.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Górne ograniczenia typów discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 19 language: pl next-page: lower-type-bounds diff --git a/pl/tutorials/tour/_posts/2017-02-13-variances.md b/_pl/tour/variances.md similarity index 97% rename from pl/tutorials/tour/_posts/2017-02-13-variances.md rename to _pl/tour/variances.md index 16e0fa4ce5..2faedf1cdd 100644 --- a/pl/tutorials/tour/_posts/2017-02-13-variances.md +++ b/_pl/tour/variances.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Wariancje discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 18 language: pl next-page: upper-type-bounds diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-abstract-types.md b/_pt-br/tour/abstract-types.md similarity index 98% rename from pt-br/tutorials/tour/_posts/2017-02-13-abstract-types.md rename to _pt-br/tour/abstract-types.md index 622ef00bf4..5eda5a34b9 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-abstract-types.md +++ b/_pt-br/tour/abstract-types.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Tipos Abstratos discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 22 next-page: compound-types previous-page: inner-classes @@ -15,7 +15,7 @@ language: pt-br Em Scala, as classes são parametrizadas com valores (os parâmetros de construtor) e com tipos (se as [classes genéricas](generic-classes.html)). Por razões de regularidade, só não é possível ter valores como membros de um objeto; tipos juntamente com valores são membros de objetos. Além disso, ambas as formas de membros podem ser concretas e abstratas. Aqui está um exemplo que mostra uma definição de valor diferido e uma definição de tipo abstrato como membros de uma [trait](traits.html) chamada `Buffer`. - + ```tut trait Buffer { type T @@ -77,4 +77,3 @@ object AbstractTypeTest2 extends App { ``` Note que temos que usar [anotação de variância](variances.html) aqui; Caso contrário, não seríamos capazes de ocultar o tipo implementado pela sequência concreta do objeto retornado pelo método `newIntSeqBuf`. Além disso, há casos em que não é possível substituir tipos abstratos com parâmetros de tipo. - diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-annotations.md b/_pt-br/tour/annotations.md similarity index 99% rename from pt-br/tutorials/tour/_posts/2017-02-13-annotations.md rename to _pt-br/tour/annotations.md index 911550dbaa..56032eaecb 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-annotations.md +++ b/_pt-br/tour/annotations.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Anotações discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 31 next-page: default-parameter-values previous-page: automatic-closures diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md b/_pt-br/tour/anonymous-function-syntax.md similarity index 93% rename from pt-br/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md rename to _pt-br/tour/anonymous-function-syntax.md index 819be22bca..94120e366b 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-anonymous-function-syntax.md +++ b/_pt-br/tour/anonymous-function-syntax.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Sintaxe de Função Anônima discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 6 next-page: higher-order-functions previous-page: mixin-class-composition diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-automatic-closures.md b/_pt-br/tour/automatic-closures.md similarity index 97% rename from pt-br/tutorials/tour/_posts/2017-02-13-automatic-closures.md rename to _pt-br/tour/automatic-closures.md index f00633df21..3d919a9ec5 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-automatic-closures.md +++ b/_pt-br/tour/automatic-closures.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Construção Automática de Closures de Tipo-Dependente discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 30 next-page: annotations previous-page: operators diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-case-classes.md b/_pt-br/tour/case-classes.md similarity index 98% rename from pt-br/tutorials/tour/_posts/2017-02-13-case-classes.md rename to _pt-br/tour/case-classes.md index fe0fc88836..e2eb9fa655 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-case-classes.md +++ b/_pt-br/tour/case-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Classes Case discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 10 next-page: pattern-matching previous-page: currying diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-classes.md b/_pt-br/tour/classes.md similarity index 96% rename from pt-br/tutorials/tour/_posts/2017-02-13-classes.md rename to _pt-br/tour/classes.md index eed38e795f..1b9f46df7a 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-classes.md +++ b/_pt-br/tour/classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Classes discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 3 next-page: traits previous-page: unified-types diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-compound-types.md b/_pt-br/tour/compound-types.md similarity index 96% rename from pt-br/tutorials/tour/_posts/2017-02-13-compound-types.md rename to _pt-br/tour/compound-types.md index 8b35ed6c22..0b06a4b1ba 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-compound-types.md +++ b/_pt-br/tour/compound-types.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Tipos Compostos discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 23 next-page: explicitly-typed-self-references previous-page: abstract-types diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-currying.md b/_pt-br/tour/currying.md similarity index 95% rename from pt-br/tutorials/tour/_posts/2017-02-13-currying.md rename to _pt-br/tour/currying.md index 49577b7f30..505c806734 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-currying.md +++ b/_pt-br/tour/currying.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Currying discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 9 next-page: case-classes previous-page: nested-functions diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-default-parameter-values.md b/_pt-br/tour/default-parameter-values.md similarity index 97% rename from pt-br/tutorials/tour/_posts/2017-02-13-default-parameter-values.md rename to _pt-br/tour/default-parameter-values.md index 9dbb276337..dedcc4e702 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-default-parameter-values.md +++ b/_pt-br/tour/default-parameter-values.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Parâmetro com Valor Padrão discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 32 next-page: named-parameters previous-page: annotations diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md b/_pt-br/tour/explicitly-typed-self-references.md similarity index 98% rename from pt-br/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md rename to _pt-br/tour/explicitly-typed-self-references.md index 601f8e94e0..432e8061f9 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-explicitly-typed-self-references.md +++ b/_pt-br/tour/explicitly-typed-self-references.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Auto Referências Explicitamente Tipadas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 24 next-page: implicit-parameters previous-page: compound-types diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-extractor-objects.md b/_pt-br/tour/extractor-objects.md similarity index 97% rename from pt-br/tutorials/tour/_posts/2017-02-13-extractor-objects.md rename to _pt-br/tour/extractor-objects.md index cf0d5da966..d1e71d4330 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-extractor-objects.md +++ b/_pt-br/tour/extractor-objects.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Objetos Extratores discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 15 next-page: sequence-comprehensions previous-page: regular-expression-patterns diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-generic-classes.md b/_pt-br/tour/generic-classes.md similarity index 96% rename from pt-br/tutorials/tour/_posts/2017-02-13-generic-classes.md rename to _pt-br/tour/generic-classes.md index 34d031527f..b9e8fe31bc 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-generic-classes.md +++ b/_pt-br/tour/generic-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Classes Genéricas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 17 next-page: variances previous-page: sequence-comprehensions diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-higher-order-functions.md b/_pt-br/tour/higher-order-functions.md similarity index 94% rename from pt-br/tutorials/tour/_posts/2017-02-13-higher-order-functions.md rename to _pt-br/tour/higher-order-functions.md index 5fa19a8f52..8cb5f3acc6 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-higher-order-functions.md +++ b/_pt-br/tour/higher-order-functions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Funções de ordem superior discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 7 next-page: nested-functions previous-page: anonymous-function-syntax diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-implicit-conversions.md b/_pt-br/tour/implicit-conversions.md similarity index 96% rename from pt-br/tutorials/tour/_posts/2017-02-13-implicit-conversions.md rename to _pt-br/tour/implicit-conversions.md index 4d945a690f..f4686c9001 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-implicit-conversions.md +++ b/_pt-br/tour/implicit-conversions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Conversões Implícitas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 26 next-page: polymorphic-methods previous-page: implicit-parameters diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-implicit-parameters.md b/_pt-br/tour/implicit-parameters.md similarity index 98% rename from pt-br/tutorials/tour/_posts/2017-02-13-implicit-parameters.md rename to _pt-br/tour/implicit-parameters.md index 54871a7c65..60177c17cb 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-implicit-parameters.md +++ b/_pt-br/tour/implicit-parameters.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Parâmetros Implícitos discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 25 next-page: implicit-conversions previous-page: explicitly-typed-self-references diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-inner-classes.md b/_pt-br/tour/inner-classes.md similarity index 97% rename from pt-br/tutorials/tour/_posts/2017-02-13-inner-classes.md rename to _pt-br/tour/inner-classes.md index ee1fe6ca21..266b8faad3 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-inner-classes.md +++ b/_pt-br/tour/inner-classes.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Classes Internas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 21 next-page: abstract-types previous-page: lower-type-bounds diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-local-type-inference.md b/_pt-br/tour/local-type-inference.md similarity index 97% rename from pt-br/tutorials/tour/_posts/2017-02-13-local-type-inference.md rename to _pt-br/tour/local-type-inference.md index efb722291c..a9f5de8f54 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-local-type-inference.md +++ b/_pt-br/tour/local-type-inference.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Inferência de Tipo Local discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 28 next-page: operators previous-page: polymorphic-methods diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md b/_pt-br/tour/lower-type-bounds.md similarity index 96% rename from pt-br/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md rename to _pt-br/tour/lower-type-bounds.md index c0f1130d0a..544609d6d7 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md +++ b/_pt-br/tour/lower-type-bounds.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Limitante Inferior de Tipos discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 20 next-page: inner-classes previous-page: upper-type-bounds diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md b/_pt-br/tour/mixin-class-composition.md similarity index 97% rename from pt-br/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md rename to _pt-br/tour/mixin-class-composition.md index ff7cfe8551..fea1eca336 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md +++ b/_pt-br/tour/mixin-class-composition.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Composição de Classes Mixin discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 5 next-page: anonymous-function-syntax previous-page: traits diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-named-parameters.md b/_pt-br/tour/named-parameters.md similarity index 94% rename from pt-br/tutorials/tour/_posts/2017-02-13-named-parameters.md rename to _pt-br/tour/named-parameters.md index 3c2cabf524..a481e2babe 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-named-parameters.md +++ b/_pt-br/tour/named-parameters.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Parâmetros Nomeados discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 33 previous-page: default-parameter-values language: pt-br diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-nested-functions.md b/_pt-br/tour/nested-functions.md similarity index 92% rename from pt-br/tutorials/tour/_posts/2017-02-13-nested-functions.md rename to _pt-br/tour/nested-functions.md index b43fcbb740..6783c3795e 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-nested-functions.md +++ b/_pt-br/tour/nested-functions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Funções Aninhadas discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 8 next-page: currying previous-page: higher-order-functions diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-operators.md b/_pt-br/tour/operators.md similarity index 93% rename from pt-br/tutorials/tour/_posts/2017-02-13-operators.md rename to _pt-br/tour/operators.md index c3ef4aa345..8e97ec3912 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-operators.md +++ b/_pt-br/tour/operators.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Operadores discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 29 next-page: automatic-closures previous-page: local-type-inference diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-pattern-matching.md b/_pt-br/tour/pattern-matching.md similarity index 96% rename from pt-br/tutorials/tour/_posts/2017-02-13-pattern-matching.md rename to _pt-br/tour/pattern-matching.md index f40ab2eca0..9dcd9e4d3c 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-pattern-matching.md +++ b/_pt-br/tour/pattern-matching.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Correspondência de Padrões discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 11 next-page: singleton-objects diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md b/_pt-br/tour/polymorphic-methods.md similarity index 93% rename from pt-br/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md rename to _pt-br/tour/polymorphic-methods.md index 9957a5c535..6d6c9150b7 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md +++ b/_pt-br/tour/polymorphic-methods.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Métodos Polimórficos discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 27 next-page: local-type-inference diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md b/_pt-br/tour/regular-expression-patterns.md similarity index 96% rename from pt-br/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md rename to _pt-br/tour/regular-expression-patterns.md index a3af0ff94e..2fd77422e6 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md +++ b/_pt-br/tour/regular-expression-patterns.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Padrões de Expressões Regulares discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 14 next-page: extractor-objects diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md b/_pt-br/tour/sequence-comprehensions.md similarity index 97% rename from pt-br/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md rename to _pt-br/tour/sequence-comprehensions.md index 1c05187c9d..6bd993b926 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md +++ b/_pt-br/tour/sequence-comprehensions.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Sequence Comprehensions discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 16 next-page: generic-classes previous-page: extractor-objects diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-singleton-objects.md b/_pt-br/tour/singleton-objects.md similarity index 98% rename from pt-br/tutorials/tour/_posts/2017-02-13-singleton-objects.md rename to _pt-br/tour/singleton-objects.md index 5f186a51bc..808285b761 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-singleton-objects.md +++ b/_pt-br/tour/singleton-objects.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Objetos Singleton discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 12 next-page: regular-expression-patterns diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-tour-of-scala.md b/_pt-br/tour/tour-of-scala.md similarity index 98% rename from pt-br/tutorials/tour/_posts/2017-02-13-tour-of-scala.md rename to _pt-br/tour/tour-of-scala.md index baedf26b5d..8e0bdb9d14 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-tour-of-scala.md +++ b/_pt-br/tour/tour-of-scala.md @@ -1,13 +1,13 @@ --- -layout: inner-page-no-masthead +layout: tour title: Introdução discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 1 -outof: 33 + next-page: unified-types language: pt-br --- diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-traits.md b/_pt-br/tour/traits.md similarity index 96% rename from pt-br/tutorials/tour/_posts/2017-02-13-traits.md rename to _pt-br/tour/traits.md index fd6b805a67..a7a750961f 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-traits.md +++ b/_pt-br/tour/traits.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Traits discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 4 next-page: mixin-class-composition previous-page: classes diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-unified-types.md b/_pt-br/tour/unified-types.md similarity index 97% rename from pt-br/tutorials/tour/_posts/2017-02-13-unified-types.md rename to _pt-br/tour/unified-types.md index 2787025355..d0e240261b 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-unified-types.md +++ b/_pt-br/tour/unified-types.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Tipos Unificados discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 2 next-page: classes previous-page: tour-of-scala diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md b/_pt-br/tour/upper-type-bounds.md similarity index 95% rename from pt-br/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md rename to _pt-br/tour/upper-type-bounds.md index 298ee84fa4..478b729886 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md +++ b/_pt-br/tour/upper-type-bounds.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Limitante Superior de Tipos discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 19 next-page: lower-type-bounds previous-page: variances diff --git a/pt-br/tutorials/tour/_posts/2017-02-13-variances.md b/_pt-br/tour/variances.md similarity index 97% rename from pt-br/tutorials/tour/_posts/2017-02-13-variances.md rename to _pt-br/tour/variances.md index 6e736719d2..7f10c95004 100644 --- a/pt-br/tutorials/tour/_posts/2017-02-13-variances.md +++ b/_pt-br/tour/variances.md @@ -1,11 +1,11 @@ --- -layout: inner-page-no-masthead +layout: tour title: Variâncias discourse: false -tutorial: scala-tour -categories: tour +partof: scala-tour + num: 18 next-page: upper-type-bounds previous-page: generic-classes diff --git a/de/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md b/de/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md deleted file mode 100644 index f42f1dc667..0000000000 --- a/de/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -layout: inner-page-no-masthead -title: Polymorphe Methoden - -discourse: false - -tutorial: scala-tour -categories: tour -num: 21 -language: de ---- - -Methoden in Scala können sowohl in deren Parametern als auch in deren Typen parametrisiert werden. -Wie bei Klassen werden die Parameter von runden Klammern umschlossen, während Typ-Parameter in -eckigen Klammern deklariert werden. Das folgende Beispiel demonstriert dies: - - object PolyTest extends App { - def dup[T](x: T, n: Int): List[T] = - if (n == 0) - Nil - else - x :: dup(x, n - 1) - - println(dup[Int](3, 4)) - println(dup("three", 3)) - } - -Die Methode `dup` des Objektes `PolyTest` ist im Typ `T` sowie den Parametern `x: T` und `n: Int` -parametrisiert. Wenn die Methode `dup` aufgerufen wird, können Typ-Parameter einerseits explizit -angegeben werden, wie in Zeile 8, andererseits kann man sie auslassen, wie in Zeile 9, und von -Scalas Typ-System inferieren lassen. Diese inferierten Typen stammen von den Typen der übergebenen -Argumente, in obigem Beispiel der Wert `"three"` vom Typ `String`. - -Zu bemerken ist, dass der Trait `App` dafür entwickelt worden ist, kurze Testprogramme zu schreiben, -jedoch für wirklich produktiv gehenden Quellcode der Scala Versionen 2.8.x und früher vermieden -werden sollte. An dessen Stelle sollte die Methode `main` verwendet werden. - diff --git a/tutorials/tour/_posts/2017-02-13-abstract-types.md b/tutorials/tour/_posts/2017-02-13-abstract-types.md deleted file mode 100644 index a15645deaf..0000000000 --- a/tutorials/tour/_posts/2017-02-13-abstract-types.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -layout: tour -title: Abstract Types - -discourse: true - -tutorial: scala-tour -categories: tour -num: 23 -next-page: compound-types -previous-page: inner-classes -prerequisite-knowledge: variance, upper-type-bound ---- - -Traits and abstract classes can have an abstract type member. This means that the concrete implementations define the actual type. Here's an example: - -```tut -trait Buffer { - type T - val element: T -} -``` -Here we have defined an abstract `type T`. It is used to describe the type of `element`. We can extend this trait in an abstract class, adding an upper-type-bound to `T` to make it more specific. - -```tut -abstract class SeqBuffer extends Buffer { - type U - type T <: Seq[U] - def length = element.length -} -``` -Notice how we can use yet another abstract `type U` as an upper-type-bound. This `class SeqBuffer` allows us to store only sequences in the buffer by stating that type `T` has to be a subtype of `Seq[U]` for a new abstract type `U`. - -Traits or [classes](classes.html) with abstract type members are often used in combination with anonymous class instantiations. To illustrate this, we now look at a program which deals with a sequence buffer that refers to a list of integers: - -```tut -abstract class IntSeqBuffer extends SeqBuffer { - type U = Int -} - - -def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer = - new IntSeqBuffer { - type T = List[U] - val element = List(elem1, elem2) - } -val buf = newIntSeqBuf(7, 8) -println("length = " + buf.length) -println("content = " + buf.element) -``` -Here the factory `newIntSeqBuf` uses an anonymous class implementation of `IntSeqBuf` (i.e. `new IntSeqBuffer`), setting `type T` to a `List[Int]`. - -It is also possible to turn abstract type members into type parameters of classes and vice versa. Here is a version of the code above which only uses type parameters: - -```tut -abstract class Buffer[+T] { - val element: T -} -abstract class SeqBuffer[U, +T <: Seq[U]] extends Buffer[T] { - def length = element.length -} - -def newIntSeqBuf(e1: Int, e2: Int): SeqBuffer[Int, Seq[Int]] = - new SeqBuffer[Int, List[Int]] { - val element = List(e1, e2) - } - -val buf = newIntSeqBuf(7, 8) -println("length = " + buf.length) -println("content = " + buf.element) -``` - -Note that we have to use [variance annotations](variances.html) here (`+T <: Seq[U]`) in order to hide the concrete sequence implementation type of the object returned from method `newIntSeqBuf`. Furthermore, there are cases where it is not possible to replace abstract types with type parameters. diff --git a/tutorials/tour/_posts/2017-02-13-annotations.md b/tutorials/tour/_posts/2017-02-13-annotations.md deleted file mode 100644 index 4e16adc88b..0000000000 --- a/tutorials/tour/_posts/2017-02-13-annotations.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -layout: tour -title: Annotations - -discourse: true - -tutorial: scala-tour -categories: tour -num: 32 -next-page: default-parameter-values -previous-page: by-name-parameters ---- - -Annotations associate meta-information with definitions. For example, the annotation `@deprecated` before a method causes the compiler to print a warning if the method is used. -``` -object DeprecationDemo extends App { - @deprecated - def hello = "hola" - - hello -} -``` -This will compile but the compiler will print a warning: "there was one deprecation warning". - -An annotation clause applies to the first definition or declaration following it. More than one annotation clause may precede a definition and declaration. The order in which these clauses are given does not matter. - - -## Annotations that ensure correctness of encodings -Certain annotations will actually cause compilation to fail if a condition(s) is not met. For example, the annotation `@tailrec` ensures that a method is [tail-recursive](https://en.wikipedia.org/wiki/Tail_call). Tail-recursion can keep memory requirements constant. Here's how it's used in a method which calculates the factorial: -```tut -import scala.annotation.tailrec - -def factorial(x: Int): Int = { - - @tailrec - def factorialHelper(x: Int, accumulator: Int): Int = { - if (x == 1) accumulator else factorialHelper(x - 1, accumulator * x) - } - factorialHelper(x, 1) -} -``` -The `factorialHelper` method has the `@tailrec` which ensures the method is indeed tail-recursive. If we were to change the implementation of `factorialHelper` to the following, it would fail: -``` -import scala.annotation.tailrec - -def factorial(x: Int): Int = { - @tailrec - def factorialHelper(x: Int): Int = { - if (x == 1) 1 else x * factorialHelper(x - 1) - } - factorialHelper(x) -} -``` -We would get the message "Recursive call not in tail position". - - -## Annotations affecting code generation -Some annotations like `@inline` affect the generated code (i.e. your jar file might have different bytes than if you hadn't used the annotation). Inlining means inserting the code in a method's body at the call site. The resulting bytecode is longer, but hopefully runs faster. Using the annotation `@inline` does not ensure that a method will be inlined, but it will cause the compiler to do it if and only if some heuristics about the size of the generated code are met. - -### Java Annotations ### -When writing Scala code which interoperates with Java, there are a few differences in annotation syntax to note. -**Note:** Make sure you use the `-target:jvm-1.8` option with Java annotations. - -Java has user-defined metadata in the form of [annotations](https://docs.oracle.com/javase/tutorial/java/annotations/). A key feature of annotations is that they rely on specifying name-value pairs to initialize their elements. For instance, if we need an annotation to track the source of some class we might define it as - -``` -@interface Source { - public String URL(); - public String mail(); -} -``` - -And then apply it as follows - -``` -@Source(URL = "http://coders.com/", - mail = "support@coders.com") -public class MyClass extends HisClass ... -``` - -An annotation application in Scala looks like a constructor invocation, for instantiating a Java annotation one has to use named arguments: - -``` -@Source(URL = "http://coders.com/", - mail = "support@coders.com") -class MyScalaClass ... -``` - -This syntax is quite tedious if the annotation contains only one element (without default value) so, by convention, if the name is specified as `value` it can be applied in Java using a constructor-like syntax: - -``` -@interface SourceURL { - public String value(); - public String mail() default ""; -} -``` - -And then apply it as follows - -``` -@SourceURL("http://coders.com/") -public class MyClass extends HisClass ... -``` - -In this case, Scala provides the same possibility - -``` -@SourceURL("http://coders.com/") -class MyScalaClass ... -``` - -The `mail` element was specified with a default value so we need not explicitly provide a value for it. However, if we need to do it we can not mix-and-match the two styles in Java: - -``` -@SourceURL(value = "http://coders.com/", - mail = "support@coders.com") -public class MyClass extends HisClass ... -``` - -Scala provides more flexibility in this respect - -``` -@SourceURL("http://coders.com/", - mail = "support@coders.com") - class MyScalaClass ... -``` diff --git a/tutorials/tour/_posts/2017-02-13-basics.md b/tutorials/tour/_posts/2017-02-13-basics.md deleted file mode 100644 index 6a802ab49a..0000000000 --- a/tutorials/tour/_posts/2017-02-13-basics.md +++ /dev/null @@ -1,311 +0,0 @@ ---- -layout: tour -title: Basics - -discourse: true - -tutorial: scala-tour -categories: tour -num: 2 -next-page: unified-types -previous-page: tour-of-scala ---- - -In this page, we will cover basics of Scala. - -## Trying Scala in the Browser - -You can run Scala in your browser with ScalaFiddle. - -1. Go to [https://scalafiddle.io](https://scalafiddle.io). -2. Paste `println("Hello, world!")` in the left pane. -3. Hit "Run" button. Output appears in the right pane. - -This is an easy, zero-setup way to experiment with pieces of Scala code. - -## Expressions - -Expressions are computable statements. -``` -1 + 1 -``` -You can output results of expressions using `println`. - -```tut -println(1) // 1 -println(1 + 1) // 2 -println("Hello!") // Hello! -println("Hello," + " world!") // Hello, world! -``` - -### Values - -You can name results of expressions with the `val` keyword. - -```tut -val x = 1 + 1 -println(x) // 2 -``` - -Named results, such as `x` here, are called values. Referencing -a value does not re-compute it. - -Values cannot be re-assigned. - -```tut:nofail -val x = 1 + 1 -x = 3 // This does not compile. -``` - -Types of values can be inferred, but you can also explicitly state the type, like this: - -```tut -val x: Int = 1 + 1 -``` - -Notice how the type declaration `Int` comes after the identifier `x`. You also need a `:`. - -### Variables - -Variables are like values, except you can re-assign them. You can define a variable with the `var` keyword. - -```tut -var x = 1 + 1 -x = 3 // This compiles because "x" is declared with the "var" keyword. -println(x * x) // 9 -``` - -As with values, you can explicitly state the type if you want: - -```tut -var x: Int = 1 + 1 -``` - - -## Blocks - -You can combine expressions by surrounding them with `{}`. We call this a block. - -The result of the last expression in the block is the result of the overall block, too. - -```tut -println({ - val x = 1 + 1 - x + 1 -}) // 3 -``` - -## Functions - -Functions are expressions that take parameters. - -You can define an anonymous function (i.e. no name) that returns a given integer plus one: - -```tut -(x: Int) => x + 1 -``` - -On the left of `=>` is a list of parameters. On the right is an expression involving the parameters. - -You can also name functions. - -```tut -val addOne = (x: Int) => x + 1 -println(addOne(1)) // 2 -``` - -Functions may take multiple parameters. - -```tut -val add = (x: Int, y: Int) => x + y -println(add(1, 2)) // 3 -``` - -Or it can take no parameters. - -```tut -val getTheAnswer = () => 42 -println(getTheAnswer()) // 42 -``` - -We will cover functions in depth [later](anonymous-function-syntax.html). - -## Methods - -Methods look and behave very similar to functions, but there are a few key differences between them. - -Methods are defined with the `def` keyword. `def` is followed by a name, parameter lists, a return type, and a body. - -```tut -def add(x: Int, y: Int): Int = x + y -println(add(1, 2)) // 3 -``` - -Notice how the return type is declared _after_ the parameter list and a colon `: Int`. - -Methods can take multiple parameter lists. - -```tut -def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier -println(addThenMultiply(1, 2)(3)) // 9 -``` - -Or no parameter lists at all. - -```tut -def name: String = System.getProperty("name") -println("Hello, " + name + "!") -``` - -There are some other differences, but for now, you can think of them as something similar to functions. - -Methods can have multi-line expressions as well. -```tut -def getSquareString(input: Double): String = { - val square = input * input - square.toString -} -``` -The last expression in the body is the method's return value. (Scala does have a `return` keyword, but it's rarely used.) - -## Classes - -You can define classes with the `class` keyword followed by its name and constructor parameters. - -```tut -class Greeter(prefix: String, suffix: String) { - def greet(name: String): Unit = - println(prefix + name + suffix) -} -``` -The return type of the method `greet` is `Unit`, which says there's nothing meaningful to return. It's used similarly to `void` in Java and C. (A difference is that because every Scala expression must have some value, there is actually a singleton value of type Unit, written (). It carries no information.) - -You can make an instance of a class with the `new` keyword. - -```tut -val greeter = new Greeter("Hello, ", "!") -greeter.greet("Scala developer") // Hello, Scala developer! -``` - -We will cover classes in depth [later](classes.html). - -## Case Classes - -Scala has a special type of class called a "case" class. By default, case classes are immutable and compared by value. You can define case classes with the `case class` keywords. - -```tut -case class Point(x: Int, y: Int) -``` - -You can instantiate case classes without `new` keyword. - -```tut -val point = Point(1, 2) -val anotherPoint = Point(1, 2) -val yetAnotherPoint = Point(2, 2) -``` - -And they are compared by value. - -```tut -if (point == anotherPoint) { - println(point + " and " + anotherPoint + " are the same.") -} else { - println(point + " and " + anotherPoint + " are different.") -} -// Point(1,2) and Point(1,2) are the same. - -if (point == yetAnotherPoint) { - println(point + " and " + yetAnotherPoint + " are the same.") -} else { - println(point + " and " + yetAnotherPoint + " are different.") -} -// Point(1,2) and Point(2,2) are different. -``` - -There is a lot more to case classes that we'd like to introduce, and we are convinced you will fall in love with them! We will cover them in depth [later](case-classes.html). - -## Objects - -Objects are single instances of their own definitions. You can think of them as singletons of their own classes. - -You can define objects with the `object` keyword. - -```tut -object IdFactory { - private var counter = 0 - def create(): Int = { - counter += 1 - counter - } -} -``` - -You can access an object by referring to its name. - -```tut -val newId: Int = IdFactory.create() -println(newId) // 1 -val newerId: Int = IdFactory.create() -println(newerId) // 2 -``` - -We will cover objects in depth [later](singleton-objects.html). - -## Traits - -Traits are types containing certain fields and methods. Multiple traits can be combined. - -You can define traits with `trait` keyword. - -```tut -trait Greeter { - def greet(name: String): Unit -} -``` - -Traits can also have default implementations. - -```tut -trait Greeter { - def greet(name: String): Unit = - println("Hello, " + name + "!") -} -``` - -You can extend traits with the `extends` keyword and override an implementation with the `override` keyword. - -```tut -class DefaultGreeter extends Greeter - -class CustomizableGreeter(prefix: String, postfix: String) extends Greeter { - override def greet(name: String): Unit = { - println(prefix + name + postfix) - } -} - -val greeter = new DefaultGreeter() -greeter.greet("Scala developer") // Hello, Scala developer! - -val customGreeter = new CustomizableGreeter("How are you, ", "?") -customGreeter.greet("Scala developer") // How are you, Scala developer? -``` - -Here, `DefaultGreeter` extends only a single trait, but it could extend multiple traits. - -We will cover traits in depth [later](traits.html). - -## Main Method - -The main method is an entry point of a program. The Java Virtual -Machine requires a main method to be named `main` and take one -argument, an array of strings. - -Using an object, you can define a main method as follows: - -```tut -object Main { - def main(args: Array[String]): Unit = - println("Hello, Scala developer!") -} -``` diff --git a/tutorials/tour/_posts/2017-02-13-by-name-parameters.md b/tutorials/tour/_posts/2017-02-13-by-name-parameters.md deleted file mode 100644 index f9d2178117..0000000000 --- a/tutorials/tour/_posts/2017-02-13-by-name-parameters.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -layout: tour -title: By-name Parameters - -discourse: true - -tutorial: scala-tour -categories: tour -num: 31 -next-page: annotations -previous-page: operators ---- - -_By-name parameters_ are only evaluated when used. They are in contrast to _by-value parameters_. To make a parameter called by-name, simply prepend `=>` to its type. -```tut -def calculate(input: => Int) = input * 37 -``` -By-name parameters have the the advantage that they are not evaluated if they aren't used in the function body. On the other hand, by-value parameters have the advantage that they are evaluated only once. - -Here's an example of how we could implement a while loop: - -```tut -def whileLoop(condition: => Boolean)(body: => Unit): Unit = - if (condition) { - body - whileLoop(condition)(body) - } - -var i = 2 - -whileLoop (i > 0) { - println(i) - i -= 1 -} // prints 2 1 -``` -The method `whileLoop` uses multiple parameter lists to take a condition and a body of the loop. If the `condition` is true, the `body` is executed and then a recursive call to whileLoop is made. If the `condition` is false, the body is never evaluated because we prepended `=>` to the type of `body`. - -Now when we pass `i > 0` as our `condition` and `println(i); i-= 1` as the `body`, it behaves like the standard while loop in many languages. - -This ability to delay evaluation of a parameter until it is used can help performance if the parameter is computationally intensive to evaluate or a longer-running block of code such as fetching a URL. diff --git a/tutorials/tour/_posts/2017-02-13-case-classes.md b/tutorials/tour/_posts/2017-02-13-case-classes.md deleted file mode 100644 index cfd775cdf3..0000000000 --- a/tutorials/tour/_posts/2017-02-13-case-classes.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -layout: tour -title: Case Classes - -discourse: true - -tutorial: scala-tour -categories: tour -num: 11 -next-page: pattern-matching -previous-page: currying -prerequisite-knowledge: classes, basics, mutability ---- - -Case classes are like regular classes with a few key differences which we will go over. Case classes are good for modeling immutable data. In the next step of the tour, we'll see how they are useful in [pattern matching](pattern-matching.html). - -## Defining a case class -A minimal case class requires the keywords `case class`, an identifier, and a parameter list (which may be empty): -```tut -case class Book(isbn: String) - -val frankenstein = Book("978-0486282114") -``` -Notice how the keyword `new` was not used to instantiate the `Message` case class. This is because case classes have an `apply` method by default which takes care of object construction. - -When you create a case class with parameters, the parameters are public `val`s. -``` -case class Message(sender: String, recipient: String, body: String) -val message1 = Message("guillaume@quebec.ca", "jorge@catalonia.es", "Ça va ?") - -println(message1.sender) // prints guillaume@quebec.ca -message1.sender = "travis@washington.us" // this line does not compile -``` -You can't reassign `message1.sender` because it is a `val` (i.e. immutable). It is possible to use `var`s in case classes but this is discouraged. - -## Comparison -Case classes are compared by structure and not by reference: -``` -case class Message(sender: String, recipient: String, body: String) - -val message2 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") -val message3 = Message("jorge@catalonia.es", "guillaume@quebec.ca", "Com va?") -val messagesAreTheSame = message2 == message3 // true -``` -Even though `message2` and `message3` refer to different objects, the value of each object is equal. - -## Copying -You can create a deep copy of an instance of a case class simply by using the `copy` method. You can optionally change the constructor arguments. -``` -case class Message(sender: String, recipient: String, body: String) -val message4 = Message("julien@bretagne.fr", "travis@washington.us", "Me zo o komz gant ma amezeg") -val message5 = message4.copy(sender = message4.recipient, recipient = "claire@bourgogne.fr") -message5.sender // travis@washington.us -message5.recipient // claire@bourgogne.fr -message5.body // "Me zo o komz gant ma amezeg" -``` -The recipient of `message4` is used as the sender of `message5` but the `body` of `message4` was copied directly. diff --git a/tutorials/tour/_posts/2017-02-13-classes.md b/tutorials/tour/_posts/2017-02-13-classes.md deleted file mode 100644 index 1f402c919c..0000000000 --- a/tutorials/tour/_posts/2017-02-13-classes.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -layout: tour -title: Classes - -discourse: true - -tutorial: scala-tour -categories: tour -num: 4 -next-page: traits -previous-page: unified-types -topics: classes -prerequisite-knowledge: no-return-keyword, type-declaration-syntax, string-interpolation, procedures ---- - -Classes in Scala are blueprints for creating objects. They can contain methods, -values, variables, types, objects, traits, and classes which are collectively called _members_. Types, objects, and traits will be covered later in the tour. - -## Defining a class -A minimal class definition is simply the keyword `class` and -an identifier. Class names should be capitalized. -```tut -class User - -val user1 = new User -``` -The keyword `new` is used to create an instance of the class. `User` has a default constructor which takes no arguments because no constructor was defined. However, you'll often want a constructor and class body. Here is an example class definition for a point: - -```tut -class Point(var x: Int, var y: Int) { - - def move(dx: Int, dy: Int): Unit = { - x = x + dx - y = y + dy - } - - override def toString: String = - s"($x, $y)" -} - -val point1 = new Point(2, 3) -point1.x // 2 -println(point1) // prints (x, y) -``` - -This `Point` class has four members: the variables `x` and `y` and the methods `move` and -`toString`. Unlike many other languages, the primary constructor is in the class signature `(var x: Int, var y: Int)`. The `move` method takes two integer arguments and returns the Unit value `()`, which carries no information. This corresponds roughly with `void` in Java-like languages. `toString`, on the other hand, does not take any arguments but returns a `String` value. Since `toString` overrides `toString` from [`AnyRef`](unified-types.html), it is tagged with the `override` keyword. - -## Constructors - -Constructors can have optional parameters by providing a default value like so: - -```tut -class Point(var x: Int = 0, var y: Int = 0) - -val origin = new Point // x and y are both set to 0 -val point1 = new Point(1) -println(point1.x) // prints 1 - -``` - -In this version of the `Point` class, `x` and `y` have the default value `0` so no arguments are required. However, because the constructor reads arguments left to right, if you just wanted to pass in a `y` value, you would need to name the parameter. -``` -class Point(var x: Int = 0, var y: Int = 0) -val point2 = new Point(y=2) -println(point2.y) // prints 2 -``` - -This is also a good practice to enhance clarity. - -## Private Members and Getter/Setter Syntax -Members are public by default. Use the `private` access modifier -to hide them from outside of the class. -```tut -class Point { - private var _x = 0 - private var _y = 0 - private val bound = 100 - - def x = _x - def x_= (newValue: Int): Unit = { - if (newValue < bound) _x = newValue else printWarning - } - - def y = _y - def y_= (newValue: Int): Unit = { - if (newValue < bound) _y = newValue else printWarning - } - - private def printWarning = println("WARNING: Out of bounds") -} - -val point1 = new Point -point1.x = 99 -point1.y = 101 // prints the warning -``` -In this version of the `Point` class, the data is stored in private variables `_x` and `_y`. There are methods `def x` and `def y` for accessing the private data. `def x_=` and `def y_=` are for validating and setting the value of `_x` and `_y`. Notice the special syntax for the setters: the method has `_=` appended to the identifier of the getter and the parameters come after. - -Primary constructor parameters with `val` and `var` are public. However, because `val`s are immutable, you can't write the following. -``` -class Point(val x: Int, val y: Int) -val point = new Point(1, 2) -point.x = 3 // <-- does not compile -``` - -Parameters without `val` or `var` are private values, visible only within the class. -``` -class Point(x: Int, y: Int) -val point = new Point(1, 2) -point.x // <-- does not compile -``` diff --git a/tutorials/tour/_posts/2017-02-13-compound-types.md b/tutorials/tour/_posts/2017-02-13-compound-types.md deleted file mode 100644 index 392bb64c9b..0000000000 --- a/tutorials/tour/_posts/2017-02-13-compound-types.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -layout: tour -title: Compound Types - -discourse: true - -tutorial: scala-tour -categories: tour -num: 24 -next-page: self-types -previous-page: abstract-types ---- - -Sometimes it is necessary to express that the type of an object is a subtype of several other types. In Scala this can be expressed with the help of *compound types*, which are intersections of object types. - -Suppose we have two traits `Cloneable` and `Resetable`: - -```tut -trait Cloneable extends java.lang.Cloneable { - override def clone(): Cloneable = { - super.clone().asInstanceOf[Cloneable] - } -} -trait Resetable { - def reset: Unit -} -``` - -Now suppose we want to write a function `cloneAndReset` which takes an object, clones it and resets the original object: - -``` -def cloneAndReset(obj: ?): Cloneable = { - val cloned = obj.clone() - obj.reset - cloned -} -``` - -The question arises what the type of the parameter `obj` is. If it's `Cloneable` then the object can be `clone`d, but not `reset`; if it's `Resetable` we can `reset` it, but there is no `clone` operation. To avoid type casts in such a situation, we can specify the type of `obj` to be both `Cloneable` and `Resetable`. This compound type is written like this in Scala: `Cloneable with Resetable`. - -Here's the updated function: - -``` -def cloneAndReset(obj: Cloneable with Resetable): Cloneable = { - //... -} -``` - -Compound types can consist of several object types and they may have a single refinement which can be used to narrow the signature of existing object members. -The general form is: `A with B with C ... { refinement }` - -An example for the use of refinements is given on the page about [abstract types](abstract-types.html). diff --git a/tutorials/tour/_posts/2017-02-13-currying.md b/tutorials/tour/_posts/2017-02-13-currying.md deleted file mode 100644 index 72b6447ae0..0000000000 --- a/tutorials/tour/_posts/2017-02-13-currying.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -layout: tour -title: Currying - -discourse: true - -tutorial: scala-tour -categories: tour -num: 10 -next-page: case-classes -previous-page: nested-functions ---- - -Methods may define multiple parameter lists. When a method is called with a fewer number of parameter lists, then this will yield a function taking the missing parameter lists as its arguments. - -Here is an example: - -```tut -object CurryTest extends App { - - def filter(xs: List[Int], p: Int => Boolean): List[Int] = - if (xs.isEmpty) xs - else if (p(xs.head)) xs.head :: filter(xs.tail, p) - else filter(xs.tail, p) - - def modN(n: Int)(x: Int) = ((x % n) == 0) - - val nums = List(1, 2, 3, 4, 5, 6, 7, 8) - println(filter(nums, modN(2))) - println(filter(nums, modN(3))) -} -``` - -_Note: method `modN` is partially applied in the two `filter` calls; i.e. only its first argument is actually applied. The term `modN(2)` yields a function of type `Int => Boolean` and is thus a possible candidate for the second argument of function `filter`._ - -Here's the output of the program above: - -``` -List(2,4,6,8) -List(3,6) -``` diff --git a/tutorials/tour/_posts/2017-02-13-default-parameter-values.md b/tutorials/tour/_posts/2017-02-13-default-parameter-values.md deleted file mode 100644 index d813654594..0000000000 --- a/tutorials/tour/_posts/2017-02-13-default-parameter-values.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: tour -title: Default Parameter Values - -discourse: true - -tutorial: scala-tour -categories: tour -num: 33 -next-page: named-arguments -previous-page: annotations -prerequisite-knowledge: named-arguments, function syntax ---- - -Scala provides the ability to give parameters default values that can be used to allow a caller to omit those parameters. - -```tut -def log(message: String, level: String = "INFO") = println(s"$level: $message") - -log("System starting") // prints INFO: System starting -log("User not found", "WARNING") // prints WARNING: User not found -``` - -The parameter `level` has a default value so it is optional. On the last line, the argument `"WARNING"` overrides the default argument `"INFO"`. Where you might do overloaded methods in Java, you can use methods with optional parameters to achieve the same effect. However, if the caller omits an argument, any following arguments must be named. - -```tut -class Point(val x: Double = 0, val y: Double = 0) - -val point1 = new Point(y = 1) -``` -Here we have to say `y = 1`. - -Note that default parameters in Scala are not optional when called from Java code: - -```tut -// Point.scala -class Point(val x: Double = 0, val y: Double = 0) -``` - -```java -// Main.java -public class Main { - public static void main(String[] args) { - Point point = new Point(1); // does not compile - } -} -``` diff --git a/tutorials/tour/_posts/2017-02-13-extractor-objects.md b/tutorials/tour/_posts/2017-02-13-extractor-objects.md deleted file mode 100644 index f81829c8dc..0000000000 --- a/tutorials/tour/_posts/2017-02-13-extractor-objects.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -layout: tour -title: Extractor Objects - -discourse: true - -tutorial: scala-tour -categories: tour -num: 16 -next-page: for-comprehensions -previous-page: regular-expression-patterns ---- - -An extractor object is an object with an `unapply` method. Whereas the `apply` method is like a constructor which takes arguments and creates an object, the `unapply` takes an object and tries to give back the arguments. This is most often used in pattern matching and partial functions. - -```tut -import scala.util.Random - -object CustomerID { - - def apply(name: String) = s"$name--${Random.nextLong}" - - def unapply(customerID: String): Option[String] = { - val name = customerID.split("--").head - if (name.nonEmpty) Some(name) else None - } -} - -val customer1ID = CustomerID("Sukyoung") // Sukyoung--23098234908 -customer1ID match { - case CustomerID(name) => println(name) // prints Sukyoung - case _ => println("Could not extract a CustomerID") -} -``` - -The `apply` method creates a `CustomerID` string from a `name`. The `unapply` does the inverse to get the `name` back. When we call `CustomerID("Sukyoung")`, this is shorthand syntax for calling `CustomerID.apply("Sukyoung")`. When we call `case CustomerID(name) => customer1ID`, we're calling the unapply method. - -The unapply method can also be used to assign a value. - -```tut -val customer2ID = CustomerID("Nico") -val CustomerID(name) = customer2ID -println(name) // prints Nico -``` - -This is equivalent to `val name = CustomerID.unapply(customer2ID).get`. If there is no match, a `scala.MatchError` is thrown: - -```tut:fail -val CustomerID(name2) = "--asdfasdfasdf" -``` - -The return type of an `unapply` should be chosen as follows: - -* If it is just a test, return a `Boolean`. For instance `case even()` -* If it returns a single sub-value of type T, return an `Option[T]` -* If you want to return several sub-values `T1,...,Tn`, group them in an optional tuple `Option[(T1,...,Tn)]`. - -Sometimes, the number of sub-values is fixed and we would like to return a sequence. For this reason, you can also define patterns through `unapplySeq` which returns `Option[Seq[T]]` This mechanism is used for instance in pattern `case List(x1, ..., xn)`. diff --git a/tutorials/tour/_posts/2017-02-13-for-comprehensions.md b/tutorials/tour/_posts/2017-02-13-for-comprehensions.md deleted file mode 100644 index da011c6a18..0000000000 --- a/tutorials/tour/_posts/2017-02-13-for-comprehensions.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -layout: tour -title: For Comprehensions - -discourse: true - -tutorial: scala-tour -categories: tour -num: 17 -next-page: generic-classes -previous-page: extractor-objects ---- - -Scala offers a lightweight notation for expressing *sequence comprehensions*. Comprehensions have the form `for (enumerators) yield e`, where `enumerators` refers to a semicolon-separated list of enumerators. An *enumerator* is either a generator which introduces new variables, or it is a filter. A comprehension evaluates the body `e` for each binding generated by the enumerators and returns a sequence of these values. - -Here's an example: - -```tut -case class User(val name: String, val age: Int) - -val userBase = List(new User("Travis", 28), - new User("Kelly", 33), - new User("Jennifer", 44), - new User("Dennis", 23)) - -val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30)) - yield user.name // i.e. add this to a list - -twentySomethings.foreach(name => println(name)) // prints Travis Dennis -``` -The `for` loop used with a `yield` statement actually creates a `List`. Because we said `yield user.name`, it's a `List[String]`. `user <- userBase` is our generator and `if (user.age >=20 && user.age < 30)` is a guard that filters out users who are in their 20s. - -Here is a more complicated example using two generators. It computes all pairs of numbers between `0` and `n-1` whose sum is equal to a given value `v`: - -```tut -def foo(n: Int, v: Int) = - for (i <- 0 until n; - j <- i until n if i + j == v) - yield (i, j) - -foo(10, 10) foreach { - case (i, j) => - print(s"($i, $j) ") // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5) -} - -``` -Here `n == 10` and `v == 10`. On the first iteration, `i == 0` and `j == 0` so `i + j != v` and therefore nothing is yielded. `j` gets incremented 9 more times before `i` gets incremented to `1`. Without the `if` guard, this would simply print the following: -``` - -(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 1) ... -``` diff --git a/tutorials/tour/_posts/2017-02-13-generic-classes.md b/tutorials/tour/_posts/2017-02-13-generic-classes.md deleted file mode 100644 index 8f9af790d5..0000000000 --- a/tutorials/tour/_posts/2017-02-13-generic-classes.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -layout: tour -title: Generic Classes - -discourse: true - -tutorial: scala-tour -categories: tour -num: 18 -next-page: variances -previous-page: for-comprehensions -assumed-knowledge: classes unified-types ---- -Generic classes are classes which take a type as a parameter. They are particularly useful for collection classes. - -## Defining a generic class -Generic classes take a type as a parameter within square brackets `[]`. One convention is to use the letter `A` as type parameter identifier, though any parameter name may be used. -```tut -class Stack[A] { - private var elements: List[A] = Nil - def push(x: A) { elements = x :: elements } - def peek: A = elements.head - def pop(): A = { - val currentTop = peek - elements = elements.tail - currentTop - } -} -``` -This implementation of a `Stack` class takes any type `A` as a parameter. This means the underlying list, `var elements: List[A] = Nil`, can only store elements of type `A`. The procedure `def push` only accepts objects of type `A` (note: `elements = x :: elements` reassigns `elements` to a new list created by prepending `x` to the current `elements`). - -## Usage - -To use a generic class, put the type in the square brackets in place of `A`. -``` -val stack = new Stack[Int] -stack.push(1) -stack.push(2) -println(stack.pop) // prints 2 -println(stack.pop) // prints 1 -``` -The instance `stack` can only take Ints. However, if the type argument had subtypes, those could be passed in: -``` -class Fruit -class Apple extends Fruit -class Banana extends Fruit - -val stack = new Stack[Fruit] -val apple = new Apple -val banana = new Banana - -stack.push(apple) -stack.push(banana) -``` -Class `Apple` and `Banana` both extend `Fruit` so we can push instances `apple` and `banana` onto the stack of `Fruit`. - -_Note: subtyping of generic types is *invariant*. This means that if we have a stack of characters of type `Stack[Char]` then it cannot be used as an integer stack of type `Stack[Int]`. This would be unsound because it would enable us to enter true integers into the character stack. To conclude, `Stack[A]` is only a subtype of `Stack[B]` if and only if `B = A`. Since this can be quite restrictive, Scala offers a [type parameter annotation mechanism](variances.html) to control the subtyping behavior of generic types._ diff --git a/tutorials/tour/_posts/2017-02-13-higher-order-functions.md b/tutorials/tour/_posts/2017-02-13-higher-order-functions.md deleted file mode 100644 index 034fe6bbbb..0000000000 --- a/tutorials/tour/_posts/2017-02-13-higher-order-functions.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -layout: tour -title: Higher-order Functions - -discourse: true - -tutorial: scala-tour -categories: tour -num: 8 -next-page: nested-functions -previous-page: mixin-class-composition ---- - -Scala allows the definition of higher-order functions. These are functions that _take other functions as parameters_, or whose _result is a function_. Here is a function `apply` which takes another function `f` and a value `v` and applies function `f` to `v`: - -```tut -def apply(f: Int => String, v: Int) = f(v) -``` - -_Note: methods are automatically coerced to functions if the context requires this._ - -Here is another example: - -```tut -class Decorator(left: String, right: String) { - def layout[A](x: A) = left + x.toString() + right -} - -object FunTest extends App { - def apply(f: Int => String, v: Int) = f(v) - val decorator = new Decorator("[", "]") - println(apply(decorator.layout, 7)) -} -``` - -Execution yields the output: - -``` -[7] -``` - -In this example, the method `decorator.layout` is coerced automatically to a value of type `Int => String` as required by method `apply`. Please note that method `decorator.layout` is a _polymorphic method_ (i.e. it abstracts over some of its signature types) and the Scala compiler has to instantiate its method type first appropriately. diff --git a/tutorials/tour/_posts/2017-02-13-implicit-conversions.md b/tutorials/tour/_posts/2017-02-13-implicit-conversions.md deleted file mode 100644 index e1068f3669..0000000000 --- a/tutorials/tour/_posts/2017-02-13-implicit-conversions.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -layout: tour -title: Implicit Conversions - -discourse: true - -tutorial: scala-tour -categories: tour -num: 27 -next-page: polymorphic-methods -previous-page: implicit-parameters ---- - -An implicit conversion from type `S` to type `T` is defined by an implicit value which has function type `S => T`, or by an implicit method convertible to a value of that type. - -Implicit conversions are applied in two situations: - -* If an expression `e` is of type `S`, and `S` does not conform to the expression's expected type `T`. -* In a selection `e.m` with `e` of type `S`, if the selector `m` does not denote a member of `S`. - -In the first case, a conversion `c` is searched for which is applicable to `e` and whose result type conforms to `T`. -In the second case, a conversion `c` is searched for which is applicable to `e` and whose result contains a member named `m`. - -The following operation on the two lists xs and ys of type `List[Int]` is legal: - -``` -xs <= ys -``` - -assuming the implicit methods `list2ordered` and `int2ordered` defined below are in scope: - -``` -implicit def list2ordered[A](x: List[A]) - (implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] = - new Ordered[List[A]] { /* .. */ } - -implicit def int2ordered(x: Int): Ordered[Int] = - new Ordered[Int] { /* .. */ } -``` - -The implicitly imported object `scala.Predef` declares several predefined types (e.g. `Pair`) and methods (e.g. `assert`) but also several implicit conversions. - -For example, when calling a Java method that expects a `java.lang.Integer`, you are free to pass it a `scala.Int` instead. That's because Predef includes the following implicit conversions: - -```tut -import scala.language.implicitConversions - -implicit def int2Integer(x: Int) = - java.lang.Integer.valueOf(x) -``` - -Because implicit conversions can have pitfalls if used indiscriminately the compiler warns when compiling the implicit conversion definition. - -To turn off the warnings take either of these actions: - -* Import `scala.language.implicitConversions` into the scope of the implicit conversion definition -* Invoke the compiler with `-language:implicitConversions` - -No warning is emitted when the conversion is applied by the compiler. diff --git a/tutorials/tour/_posts/2017-02-13-implicit-parameters.md b/tutorials/tour/_posts/2017-02-13-implicit-parameters.md deleted file mode 100644 index 16658063a4..0000000000 --- a/tutorials/tour/_posts/2017-02-13-implicit-parameters.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -layout: tour -title: Implicit Parameters - -discourse: true - -tutorial: scala-tour -categories: tour -num: 26 -next-page: implicit-conversions -previous-page: self-types ---- - -A method with _implicit parameters_ can be applied to arguments just like a normal method. In this case the implicit label has no effect. However, if such a method misses arguments for its implicit parameters, such arguments will be automatically provided. - -The actual arguments that are eligible to be passed to an implicit parameter fall into two categories: - -* First, eligible are all identifiers x that can be accessed at the point of the method call without a prefix and that denote an implicit definition or an implicit parameter. -* Second, eligible are also all members of companion modules of the implicit parameter's type that are labeled implicit. - -In the following example we define a method `sum` which computes the sum of a list of elements using the monoid's `add` and `unit` operations. Please note that implicit values can not be top-level, they have to be members of a template. - -```tut -/** This example uses a structure from abstract algebra to show how implicit parameters work. A semigroup is an algebraic structure on a set A with an (associative) operation, called add here, that combines a pair of A's and returns another A. */ -abstract class SemiGroup[A] { - def add(x: A, y: A): A -} -/** A monoid is a semigroup with a distinguished element of A, called unit, that when combined with any other element of A returns that other element again. */ -abstract class Monoid[A] extends SemiGroup[A] { - def unit: A -} -object ImplicitTest extends App { - /** To show how implicit parameters work, we first define monoids for strings and integers. The implicit keyword indicates that the corresponding object can be used implicitly, within this scope, as a parameter of a function marked implicit. */ - implicit object StringMonoid extends Monoid[String] { - def add(x: String, y: String): String = x concat y - def unit: String = "" - } - implicit object IntMonoid extends Monoid[Int] { - def add(x: Int, y: Int): Int = x + y - def unit: Int = 0 - } - /** This method takes a List[A] returns an A which represent the combined value of applying the monoid operation successively across the whole list. Making the parameter m implicit here means we only have to provide the xs parameter at the call site, since if we have a List[A] we know what type A actually is and therefore what type Monoid[A] is needed. We can then implicitly find whichever val or object in the current scope also has that type and use that without needing to specify it explicitly. */ - def sum[A](xs: List[A])(implicit m: Monoid[A]): A = - if (xs.isEmpty) m.unit - else m.add(xs.head, sum(xs.tail)) - - /** Here we call sum twice, with only one parameter each time. Since the second parameter of sum, m, is implicit its value is looked up in the current scope, based on the type of monoid required in each case, meaning both expressions can be fully evaluated. */ - println(sum(List(1, 2, 3))) // uses IntMonoid implicitly - println(sum(List("a", "b", "c"))) // uses StringMonoid implicitly -} -``` - -Here is the output of the Scala program: - -``` -6 -abc -``` diff --git a/tutorials/tour/_posts/2017-02-13-inner-classes.md b/tutorials/tour/_posts/2017-02-13-inner-classes.md deleted file mode 100644 index 9bb482c83e..0000000000 --- a/tutorials/tour/_posts/2017-02-13-inner-classes.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -layout: tour -title: Inner Classes - -discourse: true - -tutorial: scala-tour -categories: tour -num: 22 -next-page: abstract-types -previous-page: lower-type-bounds ---- - -In Scala it is possible to let classes have other classes as members. As opposed to Java-like languages where such inner classes are members of the enclosing class, in Scala such inner classes are bound to the outer object. Suppose we want the compiler to prevent us, at compile time, from mixing up which nodes belong to what graph. Path-dependent types provide a solution. - -To illustrate the difference, we quickly sketch the implementation of a graph datatype: - -```tut -class Graph { - class Node { - var connectedNodes: List[Node] = Nil - def connectTo(node: Node) { - if (connectedNodes.find(node.equals).isEmpty) { - connectedNodes = node :: connectedNodes - } - } - } - var nodes: List[Node] = Nil - def newNode: Node = { - val res = new Node - nodes = res :: nodes - res - } -} -``` -This program represents a graph as a list of nodes (`List[Node]`). Each node has a list of other nodes it's connected to (`connectedNodes`). The `class Node` is a _path-dependent type_ because it is nested in the `class Graph`. Therefore, all nodes in the `connectedNodes` must be created using the `newNode` from the same instance of `Graph`. - -```tut -val graph1: Graph = new Graph -val node1: graph1.Node = graph1.newNode -val node2: graph1.Node = graph1.newNode -val node3: graph1.Node = graph1.newNode -node1.connectTo(node2) -node3.connectTo(node1) -``` -We have explicitly declared the type of `node1`, `node2`, and `node3` as `graph1.Node` for clarity but the compiler could have inferred it. This is because when we call `graph1.newNode` which calls `new Node`, the method is using the instance of `Node` specific to the instance `graph1`. - -If we now have two graphs, the type system of Scala does not allow us to mix nodes defined within one graph with the nodes of another graph, since the nodes of the other graph have a different type. -Here is an illegal program: - -``` -val graph1: Graph = new Graph -val node1: graph1.Node = graph1.newNode -val node2: graph1.Node = graph1.newNode -node1.connectTo(node2) // legal -val graph2: Graph = new Graph -val node3: graph2.Node = graph2.newNode -node1.connectTo(node3) // illegal! -``` -The type `graph1.Node` is distinct from the type `graph2.Node`. In Java, the last line in the previous example program would have been correct. For nodes of both graphs, Java would assign the same type `Graph.Node`; i.e. `Node` is prefixed with class `Graph`. In Scala such a type can be expressed as well, it is written `Graph#Node`. If we want to be able to connect nodes of different graphs, we have to change the definition of our initial graph implementation in the following way: - -```tut -class Graph { - class Node { - var connectedNodes: List[Graph#Node] = Nil - def connectTo(node: Graph#Node) { - if (connectedNodes.find(node.equals).isEmpty) { - connectedNodes = node :: connectedNodes - } - } - } - var nodes: List[Node] = Nil - def newNode: Node = { - val res = new Node - nodes = res :: nodes - res - } -} -``` - -> Note that this program doesn't allow us to attach a node to two different graphs. If we want to remove this restriction as well, we have to change the type of variable nodes to `Graph#Node`. diff --git a/tutorials/tour/_posts/2017-02-13-local-type-inference.md b/tutorials/tour/_posts/2017-02-13-local-type-inference.md deleted file mode 100644 index f48c91ceae..0000000000 --- a/tutorials/tour/_posts/2017-02-13-local-type-inference.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -layout: tour -title: Local Type Inference - -discourse: true - -tutorial: scala-tour -categories: tour -num: 29 -next-page: operators -previous-page: polymorphic-methods ---- -Scala has a built-in type inference mechanism which allows the programmer to omit certain type annotations. It is, for instance, often not necessary in Scala to specify the type of a variable, since the compiler can deduce the type from the initialization expression of the variable. Also return types of methods can often be omitted since they correspond to the type of the body, which gets inferred by the compiler. - -Here is an example: - -```tut -object InferenceTest1 extends App { - val x = 1 + 2 * 3 // the type of x is Int - val y = x.toString() // the type of y is String - def succ(x: Int) = x + 1 // method succ returns Int values -} -``` - -For recursive methods, the compiler is not able to infer a result type. Here is a program which will fail the compiler for this reason: - -```tut:fail -object InferenceTest2 { - def fac(n: Int) = if (n == 0) 1 else n * fac(n - 1) -} -``` - -It is also not compulsory to specify type parameters when [polymorphic methods](polymorphic-methods.html) are called or [generic classes](generic-classes.html) are instantiated. The Scala compiler will infer such missing type parameters from the context and from the types of the actual method/constructor parameters. - -Here is an example which illustrates this: - -``` -case class MyPair[A, B](x: A, y: B); -object InferenceTest3 extends App { - def id[T](x: T) = x - val p = MyPair(1, "scala") // type: MyPair[Int, String] - val q = id(1) // type: Int -} -``` - -The last two lines of this program are equivalent to the following code where all inferred types are made explicit: - -``` -val x: MyPair[Int, String] = MyPair[Int, String](1, "scala") -val y: Int = id[Int](1) -``` - -In some situations it can be quite dangerous to rely on Scala's type inference mechanism as the following program shows: - -```tut:fail -object InferenceTest4 { - var obj = null - obj = new Object() -} -``` - -This program does not compile because the type inferred for variable `obj` is `Null`. Since the only value of that type is `null`, it is impossible to make this variable refer to another value. - diff --git a/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md b/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md deleted file mode 100644 index 297f761f1a..0000000000 --- a/tutorials/tour/_posts/2017-02-13-lower-type-bounds.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -layout: tour -title: Lower Type Bounds - -discourse: true - -tutorial: scala-tour -categories: tour -num: 21 -next-page: inner-classes -previous-page: upper-type-bounds -prerequisite-knowledge: upper-type-bounds, generics, variance ---- - -While [upper type bounds](upper-type-bounds.html) limit a type to a subtype of another type, *lower type bounds* declare a type to be a supertype of another type. The term `B >: A` expresses that the type parameter `B` or the abstract type `B` refer to a supertype of type `A`. In most cases, `A` will be the type parameter of the class and `B` will be the type parameter of a method. - -Here is an example where this is useful: - -```tut:fail -trait Node[+B] { - def prepend(elem: B): Unit -} - -case class ListNode[+B](h: B, t: Node[B]) extends Node[B] { - def prepend(elem: B) = ListNode[B](elem, this) - def head: B = h - def tail = t -} - -case class Nil[+B]() extends Node[B] { - def prepend(elem: B) = ListNode[B](elem, this) -} -``` -This program implements a singly-linked list. `Nil` represents an empty element (i.e. an empty list). `class ListNode` is a node which contains an element of type `B` (`head`) and a reference to the rest of the list (`tail`). The `class Node` and its subtypes are covariant because we have `+B`. - -However, this program does _not_ compile because the parameter `elem` in `prepend` is of type `B`, which we declared *co*variant. This doesn't work because functions are *contra*variant in their parameter types and *co*variant in their result types. - -To fix this, we need to flip the variance of the type of the parameter `elem` in `prepend`. We do this by introducing a new type parameter `U` that has `B` as a lower type bound. - -```tut -trait Node[+B] { - def prepend[U >: B](elem: U) -} - -case class ListNode[+B](h: B, t: Node[B]) extends Node[B] { - def prepend[U >: B](elem: U) = ListNode[U](elem, this) - def head: B = h - def tail = t -} - -case class Nil[+B]() extends Node[B] { - def prepend[U >: B](elem: U) = ListNode[U](elem, this) -} -``` - -Now we can do the following: -```tut -trait Mammal -case class AfricanSwallow() extends Mammal -case class EuropeanSwallow() extends Mammal - - -val africanSwallowList= ListNode[AfricanSwallow](AfricanSwallow(), Nil()) -val mammalList: Node[Mammal] = africanSwallowList -mammalList.prepend(new EuropeanSwallow) -``` -The `Node[Mammal]` can be assigned the `africanSwallowList` but then accept `EuropeanSwallow`s. diff --git a/tutorials/tour/_posts/2017-02-13-named-arguments.md b/tutorials/tour/_posts/2017-02-13-named-arguments.md deleted file mode 100644 index 9ce3dd8a75..0000000000 --- a/tutorials/tour/_posts/2017-02-13-named-arguments.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -layout: tour -title: Named Arguments - -discourse: true - -tutorial: scala-tour -categories: tour -num: 34 -previous-page: default-parameter-values -prerequisite-knowledge: function-syntax ---- - -When calling methods, you can label the arguments with their parameter names like so: - -```tut - def printName(first: String, last: String): Unit = { - println(first + " " + last) - } - - printName("John", "Smith") // Prints "John Smith" - printName(first = "John", last = "Smith") // Prints "John Smith" - printName(last = "Smith", first = "John") // Prints "John Smith" -``` -Notice how the order of named arguments can be rearranged. However, if some arguments are named and others are not, the unnamed arguments must come first and in the order of their parameters in the method signature. - -``` -def printName(first: String, last: String): Unit = { - println(first + " " + last) -} - -printName(last = "Smith", "john") // Does not compile -``` - -Note that named arguments do not work with calls to Java methods. diff --git a/tutorials/tour/_posts/2017-02-13-nested-functions.md b/tutorials/tour/_posts/2017-02-13-nested-functions.md deleted file mode 100644 index 8cf2fb4692..0000000000 --- a/tutorials/tour/_posts/2017-02-13-nested-functions.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: tour -title: Nested Methods - -discourse: true - -tutorial: scala-tour -categories: tour -num: 9 -next-page: currying -previous-page: higher-order-functions ---- - -In Scala it is possible to nest function definitions. The following object provides a `factorial` function for computing the factorial of a given number: - -```tut - def factorial(x: Int): Int = { - def fact(x: Int, accumulator: Int): Int = { - if (x <= 1) accumulator - else fact(x - 1, x * accumulator) - } - fact(x, 1) - } - - println("Factorial of 2: " + factorial(2)) - println("Factorial of 3: " + factorial(3)) -``` - -The output of this program is: - -``` -Factorial of 2: 2 -Factorial of 3: 6 -``` diff --git a/tutorials/tour/_posts/2017-02-13-operators.md b/tutorials/tour/_posts/2017-02-13-operators.md deleted file mode 100644 index 42589b0581..0000000000 --- a/tutorials/tour/_posts/2017-02-13-operators.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -layout: tour -title: Operators - -discourse: true - -tutorial: scala-tour -categories: tour -num: 30 -next-page: by-name-parameters -previous-page: local-type-inference -prerequisite-knowledge: case-classes ---- -In Scala, operators are methods. Any method with a single parameter can be used as an _infix operator_. For example, `+` can be called with dot-notation: -``` -10.+(1) -``` - -However, it's easier to read as an infix operator: -``` -10 + 1 -``` - -## Defining and using operators -You can use any legal identifier as an operator. This includes a name like `add` or a symbol(s) like `+`. -```tut -case class Vec(val x: Double, val y: Double) { - def +(that: Vec) = new Vec(this.x + that.x, this.y + that.y) -} - -val vector1 = Vec(1.0, 1.0) -val vector2 = Vec(2.0, 2.0) - -val vector3 = vector1 + vector2 -vector3.x // 3.0 -vector3.y // 3.0 -``` -The class Vec has a method `+` which we used to add `vector1` and `vector2`. Using parentheses, you can build up complex expressions with readable syntax. Here is the definition of class `MyBool` which includes methods `and` and `or`: - -```tut -case class MyBool(x: Boolean) { - def and(that: MyBool): MyBool = if (x) that else this - def or(that: MyBool): MyBool = if (x) this else that - def negate: MyBool = MyBool(!x) -} -``` - -It is now possible to use `and` and `or` as infix operators: - -```tut -def not(x: MyBool) = x.negate -def xor(x: MyBool, y: MyBool) = (x or y) and not(x and y) -``` - -This helps to make the definition of `xor` more readable. - -## Precedence -When an expression uses multiple operators, the operators are evaluated based on the priority of the first character: -``` -(characters not shown below) -* / % -+ - -: -= ! -< > -& -^ -| -(all letters) -``` -This applies to functions you define. For example, the following expression: -``` -a + b ^? c ?^ d less a ==> b | c -``` -Is equivalent to -``` -((a + b) ^? (c ?^ d)) less ((a ==> b) | c) -``` -`?^` has the highest precedence because it starts with the character `?`. `+` has the second highest precedence, followed by `^?`, `==>`, `|`, and `less`. diff --git a/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md b/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md deleted file mode 100644 index bd561c349c..0000000000 --- a/tutorials/tour/_posts/2017-02-13-regular-expression-patterns.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -layout: tour -title: Regular Expression Patterns - -discourse: true - -tutorial: scala-tour -categories: tour -num: 15 - -next-page: extractor-objects -previous-page: singleton-objects ---- - -Regular expressions are strings which can be used to find patterns (or lack thereof) in data. Any string can be converted to a regular expression using the `.r` method. - -```tut -import scala.util.matching.Regex - -val numberPattern: Regex = "[0-9]".r - -numberPattern.findFirstMatchIn("awesomepassword") match { - case Some(_) => println("Password OK") - case None => println("Password must contain a number") -} -``` - -In the above example, the `numberPattern` is a `Regex` -(regular expression) which we use to make sure a password contains a number. - -You can also search for groups of regular expressions using parentheses. - -```tut -import scala.util.matching.Regex - -val keyValPattern: Regex = "([0-9a-zA-Z-#() ]+): ([0-9a-zA-Z-#() ]+)".r - -val input: String = - """background-color: #A03300; - |background-image: url(img/header100.png); - |background-position: top center; - |background-repeat: repeat-x; - |background-size: 2160px 108px; - |margin: 0; - |height: 108px; - |width: 100%;""".stripMargin - -for (patternMatch <- keyValPattern.findAllMatchIn(input)) - println(s"key: ${patternMatch.group(1)} value: ${patternMatch.group(2)}") -``` -Here we parse out the keys and values of a String. Each match has a group of sub-matches. Here is the output: -``` -key: background-color value: #A03300 -key: background-image value: url(img -key: background-position value: top center -key: background-repeat value: repeat-x -key: background-size value: 2160px 108px -key: margin value: 0 -key: height value: 108px -key: width value: 100 -``` diff --git a/tutorials/tour/_posts/2017-02-13-self-types.md b/tutorials/tour/_posts/2017-02-13-self-types.md deleted file mode 100644 index b4f6d35461..0000000000 --- a/tutorials/tour/_posts/2017-02-13-self-types.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -layout: tour -title: Self-type - -discourse: true - -tutorial: scala-tour -categories: tour -num: 25 -next-page: implicit-parameters -previous-page: compound-types -prerequisite-knowledge: nested-classes, mixin-class-composition ---- -Self-types are a way to declare that a trait must be mixed into another trait, even though it doesn't directly extend it. That makes the members of the dependency available without imports. - -A self-type is a way to narrow the type of `this` or another identifier that aliases `this`. The syntax looks like normal function syntax but means something entirely different. - -To use a self-type in a trait, write an identifier, the type of another trait to mix in, and a `=>` (e.g. `someIdentifier: SomeOtherTrait =>`). -```tut -trait User { - def username: String -} - -trait Tweeter { - this: User => // reassign this - def tweet(tweetText: String) = println(s"$username: $tweetText") -} - -class VerifiedTweeter(val username_ : String) extends Tweeter with User { // We mixin User because Tweeter required it - def username = s"real $username_" -} - -val realBeyoncé = new VerifiedTweeter("Beyoncé") -realBeyoncé.tweet("Just spilled my glass of lemonade") // prints "real Beyoncé: Just spilled my glass of lemonade" -``` - -Because we said `this: User =>` in `trait Tweeter`, now the variable `username` is in scope for the `tweet` method. This also means that since `VerifiedTweeter` extends `Tweeter`, it must also mix-in `User` (using `with User`). diff --git a/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md b/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md deleted file mode 100644 index 6d603ad0b8..0000000000 --- a/tutorials/tour/_posts/2017-02-13-sequence-comprehensions.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -layout: tour -title: Sequence Comprehensions - -disqus: true - -tutorial: scala-tour -categories: tour -num: 17 -next-page: generic-classes -previous-page: extractor-objects ---- - -Scala offers a lightweight notation for expressing *sequence comprehensions*. Comprehensions have the form `for (enumerators) yield e`, where `enumerators` refers to a semicolon-separated list of enumerators. An *enumerator* is either a generator which introduces new variables, or it is a filter. A comprehension evaluates the body `e` for each binding generated by the enumerators and returns a sequence of these values. - -Here is an example: - -```tut -object ComprehensionTest1 extends App { - def even(from: Int, to: Int): List[Int] = - for (i <- List.range(from, to) if i % 2 == 0) yield i - Console.println(even(0, 20)) -} -``` - -The for-expression in function introduces a new variable `i` of type `Int` which is subsequently bound to all values of the list `List(from, from + 1, ..., to - 1)`. The guard `if i % 2 == 0` filters out all odd numbers so that the body (which only consists of the expression i) is only evaluated for even numbers. Consequently, the whole for-expression returns a list of even numbers. - -The program yields the following output: - -``` -List(0, 2, 4, 6, 8, 10, 12, 14, 16, 18) -``` - -Here is a more complicated example which computes all pairs of numbers between `0` and `n-1` whose sum is equal to a given value `v`: - -```tut -object ComprehensionTest2 extends App { - def foo(n: Int, v: Int) = - for (i <- 0 until n; - j <- i until n if i + j == v) yield - (i, j); - foo(20, 32) foreach { - case (i, j) => - println(s"($i, $j)") - } -} -``` - -This example shows that comprehensions are not restricted to lists. The previous program uses iterators instead. Every datatype that supports the operations `withFilter`, `map`, and `flatMap` (with the proper types) can be used in sequence comprehensions. - -Here's the output of the program: - -``` -(13, 19) -(14, 18) -(15, 17) -(16, 16) -``` - -There is also a special form of sequence comprehension which returns `Unit`. Here the bindings that are created from the list of generators and filters are used to perform side-effects. The programmer has to omit the keyword `yield` to make use of such a sequence comprehension. -Here's a program which is equivalent to the previous one but uses the special for comprehension returning `Unit`: - -``` -object ComprehensionTest3 extends App { - for (i <- Iterator.range(0, 20); - j <- Iterator.range(i, 20) if i + j == 32) - println(s"($i, $j)") -} -``` - diff --git a/tutorials/tour/_posts/2017-02-13-singleton-objects.md b/tutorials/tour/_posts/2017-02-13-singleton-objects.md deleted file mode 100644 index 4b8569aa19..0000000000 --- a/tutorials/tour/_posts/2017-02-13-singleton-objects.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -layout: tour -title: Singleton Objects - -discourse: true - -tutorial: scala-tour -categories: tour -num: 13 - -next-page: regular-expression-patterns -previous-page: pattern-matching ---- - -Methods and values that aren't associated with individual instances of a [class](classes.html) belong in *singleton objects*, denoted by using the keyword `object` instead of `class`. - -``` -package test - -object Blah { - def sum(l: List[Int]): Int = l.sum -} -``` - -This `sum` method is available globally, and can be referred to, or imported, as `test.Blah.sum`. - -Singleton objects are sort of a shorthand for defining a single-use class, which can't directly be instantiated, and a `val` member at the point of definition of the `object`, with the same name. Indeed, like `val`s, singleton objects can be defined as members of a [trait](traits.html) or class, though this is atypical. - -A singleton object can extend classes and traits. In fact, a [case class](case-classes.html) with no [type parameters](generic-classes.html) will by default create a singleton object of the same name, with a [`Function*`](http://www.scala-lang.org/api/current/scala/Function1.html) trait implemented. - -## Companions ## - -Most singleton objects do not stand alone, but instead are associated with a class of the same name. The “singleton object of the same name” of a case class, mentioned above, is an example of this. When this happens, the singleton object is called the *companion object* of the class, and the class is called the *companion class* of the object. - -[Scaladoc](https://wiki.scala-lang.org/display/SW/Introduction) has special support for jumping between a class and its companion: if the big “C” or “O” circle has its edge folded up at the bottom, you can click the circle to jump to the companion. - -A class and its companion object, if any, must be defined in the same source file. Like this: - -```tut -class IntPair(val x: Int, val y: Int) - -object IntPair { - import math.Ordering - - implicit def ipord: Ordering[IntPair] = - Ordering.by(ip => (ip.x, ip.y)) -} -``` - -It's common to see typeclass instances as [implicit values](implicit-parameters.html), such as `ipord` above, defined in the companion, when following the typeclass pattern. This is because the companion's members are included in the default implicit search for related values. - -## Notes for Java programmers ## - -`static` is not a keyword in Scala. Instead, all members that would be static, including classes, should go in a singleton object. They can be referred to with the same syntax, imported piecemeal or as a group, and so on. - -Frequently, Java programmers define static members, perhaps `private`, as implementation aids for their instance members. These move to the companion, too; a common pattern is to import the companion object's members in the class, like so: - -``` -class X { - import X._ - - def blah = foo -} - -object X { - private def foo = 42 -} -``` - -This illustrates another feature: in the context of `private`, a class and its companion are friends. `object X` can access private members of `class X`, and vice versa. To make a member *really* private to one or the other, use `private[this]`. - -For Java convenience, methods, including `var`s and `val`s, defined directly in a singleton object also have a static method defined in the companion class, called a *static forwarder*. Other members are accessible via the `X$.MODULE$` static field for `object X`. - -If you move everything to a companion object and find that all you have left is a class you don't want to be able to instantiate, simply delete the class. Static forwarders will still be created. diff --git a/tutorials/tour/_posts/2017-02-13-tour-of-scala.md b/tutorials/tour/_posts/2017-02-13-tour-of-scala.md deleted file mode 100644 index 3ce98cbea1..0000000000 --- a/tutorials/tour/_posts/2017-02-13-tour-of-scala.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -layout: tour -title: Introduction - -discourse: true - -tutorial: scala-tour -categories: tour -num: 1 -languages: [ba, es, ko, pt-br, pl] -outof: 34 -next-page: basics -redirect_from: - - /tutorials/index.html ---- - -Scala is a modern multi-paradigm programming language designed to express common programming patterns in a concise, elegant, and type-safe way. It smoothly integrates features of object-oriented and functional languages. - -## Scala is object-oriented ## -Scala is a pure object-oriented language in the sense that [every value is an object](unified-types.html). Types and behavior of objects are described by [classes](classes.html) and [traits](traits.html). Classes are extended by subclassing and a flexible [mixin-based composition](mixin-class-composition.html) mechanism as a clean replacement for multiple inheritance. - -## Scala is functional ## -Scala is also a functional language in the sense that [every function is a value](unified-types.html). Scala provides a [lightweight syntax](anonymous-function-syntax.html) for defining anonymous functions, it supports [higher-order functions](higher-order-functions.html), it allows functions to be [nested](nested-functions.html), and supports [currying](currying.html). Scala's [case classes](case-classes.html) and its built-in support for [pattern matching](pattern-matching.html) model algebraic types used in many functional programming languages. [Singleton objects](singleton-objects.html) provide a convenient way to group functions that aren't members of a class. - -Furthermore, Scala's notion of pattern matching naturally extends to the [processing of XML data](xml-processing.html) with the help of [right-ignoring sequence patterns](regular-expression-patterns.html), by way of general extension via [extractor objects](extractor-objects.html). In this context, [for comprehensions](for-comprehensions.html) are useful for formulating queries. These features make Scala ideal for developing applications like web services. - -## Scala is statically typed ## -Scala is equipped with an expressive type system that enforces statically that abstractions are used in a safe and coherent manner. In particular, the type system supports: - -* [generic classes](generic-classes.html) -* [variance annotations](variances.html) -* [upper](upper-type-bounds.html) and [lower](lower-type-bounds.html) type bounds, -* [inner classes](inner-classes.html) and [abstract types](abstract-types.html) as object members -* [compound types](compound-types.html) -* [explicitly typed self references](explicitly-typed-self-references.html) -* [implicit parameters](implicit-parameters.html) and [conversions](implicit-conversions.html) -* [polymorphic methods](polymorphic-methods.html) - -A [local type inference mechanism](local-type-inference.html) takes care that the user is not required to annotate the program with redundant type information. In combination, these features provide a powerful basis for the safe reuse of programming abstractions and for the type-safe extension of software. - -## Scala is extensible ## - -In practice, the development of domain-specific applications often requires domain-specific language extensions. Scala provides a unique combination of language mechanisms that make it easy to smoothly add new language constructs in the form of libraries. - -A joint use of both features facilitates the definition of new statements without using meta-programming facilities such as macros. - -Scala is designed to interoperate well with the popular Java 2 Runtime Environment (JRE). In particular, the interaction with the mainstream object-oriented Java programming language is as smooth as possible. Newer Java features like [annotations](annotations.html) and Java generics have direct analogues in Scala. Those Scala features without Java analogues, such as [default](default-parameter-values.html) and [named parameters](named-parameters.html), compile as close to Java as they can reasonably come. Scala has the same compilation model (separate compilation, dynamic class loading) like Java and allows access to thousands of existing high-quality libraries. - -Please continue to the next page to read more. diff --git a/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md b/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md deleted file mode 100644 index 800fc000d3..0000000000 --- a/tutorials/tour/_posts/2017-02-13-upper-type-bounds.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -layout: tour -title: Upper Type Bounds - -discourse: true - -tutorial: scala-tour -categories: tour -num: 20 -next-page: lower-type-bounds -previous-page: variances ---- - -In Scala, [type parameters](generic-classes.html) and [abstract types](abstract-types.html) may be constrained by a type bound. Such type bounds limit the concrete values of the type variables and possibly reveal more information about the members of such types. An _upper type bound_ `T <: A` declares that type variable `T` refers to a subtype of type `A`. -Here is an example that demonstrates upper type bound for a type parameter of class `Cage`: - -```tut -abstract class Animal { - def name: String -} - -abstract class Pet extends Animal {} - -class Cat extends Pet { - override def name: String = "Cat" -} - -class Dog extends Pet { - override def name: String = "Dog" -} - -class Lion extends Animal { - override def name: String = "Lion" -} - -class PetContainer[P <: Pet](p: P) { - def pet: P = p -} - -val dogContainer = new PetContainer[Dog](new Dog) -val catContainer = new PetContainer[Cat](new Cat) -// val lionContainer = new PetContainer[Lion](new Lion) -// ^this would not compile -``` -The `class PetContainer` take a type parameter `P` which must be a subtype of `Pet`. `Dog` and `Cat` are subtypes of `Pet` so we can create a new `PetContainer[Dog]` and `PetContainer[Cat]`. However, if we tried to create a `PetContainer[Lion]`, we would get the following Error: - -`type arguments [Lion] do not conform to class PetContainer's type parameter bounds [P <: Pet]` - -This is because `Lion` is not a subtype of `Pet`. From 1412f25c4b08d96e530242a98ecaf45663391884 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 14:18:14 +0200 Subject: [PATCH 068/112] Organizing tutorials/FAQs --- .gitignore | 1 + _config.yml | 3 + _overviews/index.md | 35 - _overviews/tutorials/index.md | 47 - _tutorials/FAQ/breakout.md | 233 ++++ _tutorials/FAQ/chaining-implicits.md | 109 ++ _tutorials/FAQ/collections.md | 376 +++++ _tutorials/FAQ/context-bounds.md | 104 ++ .../FAQ/finding-implicits.md | 15 +- _tutorials/FAQ/finding-symbols.md | 205 +++ _tutorials/FAQ/initialization-order.md | 164 +++ _tutorials/FAQ/stream-view-iterator.md | 45 + _tutorials/FAQ/yield.md | 158 +++ _tutorials/partest-guide.md | 66 + _tutorials/scala-for-csharp-programmers.md | 1236 +++++++++++++++++ _tutorials/scala-for-java-programmers.md | 723 ++++++++++ _tutorials/scala-with-maven.md | 302 ++++ .../2017-02-13-mixin-class-composition.md | 81 -- .../_posts/2017-02-13-pattern-matching.md | 149 -- .../_posts/2017-02-13-polymorphic-methods.md | 34 - tutorials/tour/_posts/2017-02-13-traits.md | 82 -- .../tour/_posts/2017-02-13-unified-types.md | 80 -- tutorials/tour/_posts/2017-02-13-variances.md | 151 -- 23 files changed, 3732 insertions(+), 667 deletions(-) delete mode 100644 _overviews/index.md delete mode 100644 _overviews/tutorials/index.md create mode 100644 _tutorials/FAQ/breakout.md create mode 100644 _tutorials/FAQ/chaining-implicits.md create mode 100644 _tutorials/FAQ/collections.md create mode 100644 _tutorials/FAQ/context-bounds.md rename {tutorials => _tutorials}/FAQ/finding-implicits.md (99%) create mode 100644 _tutorials/FAQ/finding-symbols.md create mode 100644 _tutorials/FAQ/initialization-order.md create mode 100644 _tutorials/FAQ/stream-view-iterator.md create mode 100644 _tutorials/FAQ/yield.md create mode 100644 _tutorials/partest-guide.md create mode 100644 _tutorials/scala-for-csharp-programmers.md create mode 100644 _tutorials/scala-for-java-programmers.md create mode 100644 _tutorials/scala-with-maven.md delete mode 100644 tutorials/tour/_posts/2017-02-13-mixin-class-composition.md delete mode 100644 tutorials/tour/_posts/2017-02-13-pattern-matching.md delete mode 100644 tutorials/tour/_posts/2017-02-13-polymorphic-methods.md delete mode 100644 tutorials/tour/_posts/2017-02-13-traits.md delete mode 100644 tutorials/tour/_posts/2017-02-13-unified-types.md delete mode 100644 tutorials/tour/_posts/2017-02-13-variances.md diff --git a/.gitignore b/.gitignore index c4dfe40928..c73823b558 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ vendor/bundle .idea/ /coursier /tut-tmp/ +.sass-cache/ diff --git a/_config.yml b/_config.yml index f597370f83..dcd1d588a9 100644 --- a/_config.yml +++ b/_config.yml @@ -25,6 +25,9 @@ collections: tour: output: true permalink: /:collection/:path.html + tutorials: + output: true + permalink: /:collection/:path.html books: output: false ja: # Japanese translations diff --git a/_overviews/index.md b/_overviews/index.md deleted file mode 100644 index 335ff94209..0000000000 --- a/_overviews/index.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -layout: inner-page -title: Guides and Overviews -#languages: [ja, zh-cn, es] -permalink: /overviews/:title.html ---- - -{% for category in site.data.overviews %} -

    {{ category.category }}

    - {% if category.description %}

    {{ category.description }}

    {% endif %} -
    - {% for overview in category.overviews %} -
    -
    - {% if overview.icon %}

    {% endif %} -

    {{ overview.title }}

    - {% if overview.label-text %}
    {{ overview.label-text }}
    {% endif %} - {% if overview.by %}
    By {{ overview.by }}
    {% endif %} - {% if overview.description %}

    {{ overview.description }}

    {% endif %} - {% if overview.subdocs %} - Contents - - {% endif %} -
    - -
    - {% endfor %} -
    -{% endfor %} diff --git a/_overviews/tutorials/index.md b/_overviews/tutorials/index.md deleted file mode 100644 index d4ae517d70..0000000000 --- a/_overviews/tutorials/index.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: overview -title: Tutorials ---- - -
    - -
    -

    New to Scala?

    -

    Tutorials geared for people coming...

    - -
    - -
    -

    FAQ

    Frequently Asked Questions (and their answers!)

    -
    - {% for pg in site.pages %} - {% if pg.partof == "FAQ" and pg.outof %} - {% assign totalPagesFAQ = pg.outof %} - {% endif %} - {% endfor %} - - {% if totalPagesFAQ %} -
      - {% for i in (1..totalPagesFAQ) %} - {% for pg in site.pages %} - {% if pg.partof == "FAQ" and pg.num and pg.num == i %} -
    • {{ pg.title }}
    • - {% endif %} - {% endfor %} - {% endfor %} -
    - {% else %} **ERROR**. Couldn't find the total number of pages in this set of tutorial articles. Have you declared the `outof` tag in your YAML front matter? - {% endif %} - -
    - -
    -
    -

    A Tour of Scala

    Bite-size pieces of the essentials...

    -
    - {% include tutorial-tour-list.txt %} -
    diff --git a/_tutorials/FAQ/breakout.md b/_tutorials/FAQ/breakout.md new file mode 100644 index 0000000000..8c11253eb2 --- /dev/null +++ b/_tutorials/FAQ/breakout.md @@ -0,0 +1,233 @@ +--- +layout: overview-large +title: What is breakOut, and how does it work? + +discourse: true + +partof: FAQ +num: 5 +--- +You might have encountered some code like the one below, and wonder what is +`breakOut`, and why is it being passed as parameter? + + import scala.collection.breakOut + val map : Map[Int,String] = List("London", "France").map(x => (x.length, x))(breakOut) + + +The answer is found on the definition of `map`: + + def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That + +Note that it has two parameters. The first is your function and the second is +an implicit. If you do not provide that implicit, Scala will choose the most +_specific_ one available. + +### About breakOut + +So, what's the purpose of `breakOut`? Consider the example given at the +beginning , You take a list of strings, transform each string into a tuple +`(Int, String)`, and then produce a `Map` out of it. The most obvious way to do +that would produce an intermediary `List[(Int, String)]` collection, and then +convert it. + +Given that `map` uses a `Builder` to produce the resulting collection, wouldn't +it be possible to skip the intermediary `List` and collect the results directly +into a `Map`? Evidently, yes, it is. To do so, however, we need to pass a +proper `CanBuildFrom` to `map`, and that is exactly what `breakOut` does. + +Let's look, then, at the definition of `breakOut`: + + def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) = + new CanBuildFrom[From, T, To] { + def apply(from: From) = b.apply() ; def apply() = b.apply() + } + +Note that `breakOut` is parameterized, and that it returns an instance of +`CanBuildFrom`. As it happens, the types `From`, `T` and `To` have already been +inferred, because we know that `map` is expecting `CanBuildFrom[List[String], +(Int, String), Map[Int, String]]`. Therefore: + + From = List[String] + T = (Int, String) + To = Map[Int, String] + +To conclude let's examine the implicit received by `breakOut` itself. It is of +type `CanBuildFrom[Nothing,T,To]`. We already know all these types, so we can +determine that we need an implicit of type +`CanBuildFrom[Nothing,(Int,String),Map[Int,String]]`. But is there such a +definition? + +Let's look at `CanBuildFrom`'s definition: + + trait CanBuildFrom[-From, -Elem, +To] + extends AnyRef + +So `CanBuildFrom` is contra-variant on its first type parameter. Because +`Nothing` is a bottom class (ie, it is a subclass of everything), that means +*any* class can be used in place of `Nothing`. + +Since such a builder exists, Scala can use it to produce the desired output. + +### About Builders + +A lot of methods from Scala's collections library consists of taking the +original collection, processing it somehow (in the case of `map`, transforming +each element), and storing the results in a new collection. + +To maximize code reuse, this storing of results is done through a _builder_ +(`scala.collection.mutable.Builder`), which basically supports two operations: +appending elements, and returning the resulting collection. The type of this +resulting collection will depend on the type of the builder. Thus, a `List` +builder will return a `List`, a `Map` builder will return a `Map`, and so on. +The implementation of the `map` method need not concern itself with the type of +the result: the builder takes care of it. + +On the other hand, that means that `map` needs to receive this builder somehow. +The problem faced when designing Scala 2.8 Collections was how to choose the +best builder possible. For example, if I were to write `Map('a' -> +1).map(_.swap)`, I'd like to get a `Map(1 -> 'a')` back. On the other hand, a +`Map('a' -> 1).map(_._1)` can't return a `Map` (it returns an `Iterable`). + +The magic of producing the best possible `Builder` from the known types of the +expression is performed through this `CanBuildFrom` implicit. + +### About CanBuildFrom + +To better explain what's going on, I'll give an example where the collection +being mapped is a `Map` instead of a `List`. I'll go back to `List` later. For +now, consider these two expressions: + + Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length) + Map(1 -> "one", 2 -> "two") map (_._2) + +The first returns a `Map` and the second returns an `Iterable`. The magic of +returning a fitting collection is the work of `CanBuildFrom`. Let's consider +the definition of `map` again to understand it. + +The method `map` is inherited from `TraversableLike`. It is parameterized on +`B` and `That`, and makes use of the type parameters `A` and `Repr`, which +parameterize the class. Let's see both definitions together: + +The class `TraversableLike` is defined as: + + trait TraversableLike[+A, +Repr] + extends HasNewBuilder[A, Repr] with AnyRef + + def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That + + +To understand where `A` and `Repr` come from, let's consider the definition of +`Map` itself: + + trait Map[A, +B] + extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]] + +Because `TraversableLike` is inherited by all traits which extend `Map`, `A` +and `Repr` could be inherited from any of them. The last one gets the +preference, though. So, following the definition of the immutable `Map` and all +the traits that connect it to `TraversableLike`, we have: + + trait Map[A, +B] + extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]] + + trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] + extends MapLike[A, B, This] + + trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] + extends PartialFunction[A, B] with IterableLike[(A, B), This] with Subtractable[A, This] + + trait IterableLike[+A, +Repr] + extends Equals with TraversableLike[A, Repr] + + trait TraversableLike[+A, +Repr] + extends HasNewBuilder[A, Repr] with AnyRef + +If you pass the type parameters of `Map[Int, String]` all the way down the +chain, we find that the types passed to `TraversableLike`, and, thus, used by +`map`, are: + + A = (Int,String) + Repr = Map[Int, String] + +Going back to the example, the first map is receiving a function of type +`((Int, String)) => (Int, Int)` and the second map is receiving a function of +type `((Int, String)) => Int`. I use the double parenthesis to emphasize it is +a tuple being received, as that's the type of `A` as we saw. + +With that information, let's consider the other types. + + map Function.tupled(_ -> _.length): + B = (Int, Int) + + map (_._2): + B = Int + +We can see that the type returned by the first `map` is `Map[Int,Int]`, and the +second is `Iterable[String]`. Looking at `map`'s definition, it is easy to see +that these are the values of `That`. But where do they come from? + +If we look inside the companion objects of the classes involved, we see some +implicit declarations providing them. On object `Map`: + + implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]] + +And on object `Iterable`, whose class is extended by `Map`: + + implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]] + +These definitions provide factories for parameterized `CanBuildFrom`. + +Scala will choose the most specific implicit available. In the first case, it +was the first `CanBuildFrom`. In the second case, as the first did not match, +it chose the second `CanBuildFrom`. + +### Back to the first example + +Let's see the first example, `List`'s and `map`'s definition (again) to +see how the types are inferred: + + val map : Map[Int,String] = List("London", "France").map(x => (x.length, x))(breakOut) + + sealed abstract class List[+A] + extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]] + + trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]] + extends SeqLike[A, Repr] + + trait SeqLike[+A, +Repr] + extends IterableLike[A, Repr] + + trait IterableLike[+A, +Repr] + extends Equals with TraversableLike[A, Repr] + + trait TraversableLike[+A, +Repr] + extends HasNewBuilder[A, Repr] with AnyRef + + def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That + +The type of `List("London", "France")` is `List[String]`, so the types `A` and +`Repr` defined on `TraversableLike` are: + + A = String + Repr = List[String] + +The type for `(x => (x.length, x))` is `(String) => (Int, String)`, so the type +of `B` is: + + B = (Int, String) + +The last unknown type, `That` is the type of the result of `map`, and we +already have that as well: + + val map : Map[Int,String] = + +So, + + That = Map[Int, String] + +That means `breakOut` must, necessarily, return a type or subtype of +`CanBuildFrom[List[String], (Int, String), Map[Int, String]]`. + +This answer was originally submitted in response to [this question on Stack Overflow][1]. + + [1]: http://stackoverflow.com/q/1715681/53013 diff --git a/_tutorials/FAQ/chaining-implicits.md b/_tutorials/FAQ/chaining-implicits.md new file mode 100644 index 0000000000..a06d40e4f3 --- /dev/null +++ b/_tutorials/FAQ/chaining-implicits.md @@ -0,0 +1,109 @@ +--- +layout: overview-large +title: How can I chain/nest implicit conversions? + +discourse: true + +partof: FAQ +num: 6 +--- + +The enrich-my-library pattern allows one to seemingly add a method to a class by +making available an implicit conversion from that class to one that implements +the method. + +Scala does not allow two such implicit conversions taking place, however, so +one cannot got from `A` to `C` using an implicit `A` to `B` and another +implicit `B` to `C`. Is there a way around this restriction? + +Scala has a restriction on automatic conversions to add a method, which is that +it won't apply more than one conversion in trying to find methods. For example: + + class A(val n: Int) + class B(val m: Int, val n: Int) + class C(val m: Int, val n: Int, val o: Int) { + def total = m + n + o + } + + import scala.language.implicitConversions + + // This demonstrates implicit conversion chaining restrictions + object T1 { // to make it easy to test on REPL + implicit def toA(n: Int): A = new A(n) + implicit def aToB(a: A): B = new B(a.n, a.n) + implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n) + + // won't work + println(5.total) + println(new A(5).total) + + // works + println(new B(5, 5).total) + println(new C(5, 5, 10).total) + } + +However, if an implicit definition requires an implicit parameter itself, Scala +_will_ look for additional implicit values for as long as needed. Continuing from +the last example: + + object T2 { + implicit def toA(n: Int): A = new A(n) + implicit def aToB[A1](a: A1)(implicit f: A1 => A): B = + new B(a.n, a.n) + implicit def bToC[B1](b: B1)(implicit f: B1 => B): C = + new C(b.m, b.n, b.m + b.n) + + // works + println(5.total) + println(new A(5).total) + println(new B(5, 5).total) + println(new C(5, 5, 10).total) + } + +_"Magic!"_, you might say. Not so. Here is how the compiler would translate each +one: + + object T1Translated { + implicit def toA(n: Int): A = new A(n) + implicit def aToB(a: A): B = new B(a.n, a.n) + implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n) + + // Scala won't do this + println(bToC(aToB(toA(5))).total) + println(bToC(aToB(new A(5))).total) + + // Just this + println(bToC(new B(5, 5)).total) + + // No implicits required + println(new C(5, 5, 10).total) + } + + object T2Translated { + implicit def toA(n: Int): A = new A(n) + implicit def aToB[A1](a: A1)(implicit f: A1 => A): B = + new B(a.n, a.n) + implicit def bToC[B1](b: B1)(implicit f: B1 => B): C = + new C(b.m, b.n, b.m + b.n) + + // Scala does this + println(bToC(5)(x => aToB(x)(y => toA(y))).total) + println(bToC(new A(5))(x => aToB(x)(identity)).total) + println(bToC(new B(5, 5))(identity).total) + + // no implicits required + println(new C(5, 5, 10).total) + } + +So, while `bToC` is being used as an implicit conversion, `aToB` and `toA` are +being passed as _implicit parameters_, instead of being chained as implicit +conversions. + +See also: + +* [Context bounds](context-bounds.html) +* [A discussion on types, origin and precedence of implicits](finding-implicits.html) + +This question and answer were originally submitted on [Stack Overflow][1]. + + [1]: http://stackoverflow.com/questions/5332801/how-can-i-chain-implicits-in-scala/5332804 diff --git a/_tutorials/FAQ/collections.md b/_tutorials/FAQ/collections.md new file mode 100644 index 0000000000..e7f9d0f159 --- /dev/null +++ b/_tutorials/FAQ/collections.md @@ -0,0 +1,376 @@ +--- +layout: overview-large +title: How are the collections structured? Which one should I choose? + +discourse: true + +partof: FAQ +num: 8 +--- +## Foreword + +There's a [2.8 collection walk-through][1] by Martin Odersky which should +probably be your first reference. It has been supplemented as well with +[architectural notes][2], which will be of particular interest to those who +want to design their own collections. + +The rest of this answer was written way before any such thing existed (in fact, +before 2.8.0 itself was released). + +You can find a paper about it as [Scala SID #3][3]. Other papers in that area +should be interesting as well to people interested in the differences between +Scala 2.7 and 2.8. + +I'll quote from the paper, selectively, and complement with some thoughts of +mine. There are also some images, generated by Matthias at decodified.com, and +the original SVG files can be found [here][4]. + +## The collection classes/traits themselves + +There are actually three hierarchies of traits for the collections: one for +mutable collections, one for immutable collections, and one which doesn't make +any assumptions about the collections. + +There's also a distinction between parallel, serial and maybe-parallel +collections, which was introduced with Scala 2.9. I'll talk about them in the +next section. The hierarchy described in this section refers _exclusively to +non-parallel collections_. + +The following image shows the non-specific hierarchy introduced with Scala 2.8: +![General collection hierarchy][5] + +All elements shown are traits. In the other two hierarchies there are also +classes directly inheriting the traits as well as classes which can be _viewed +as_ belonging in that hierarchy through implicit conversion to wrapper classes. +The legend for these graphs can be found after them. + +Graph for immutable hierarchy: + + +Graph for mutable hierarchy: + + +Legend: + +![Graph legend][8] + +Here's an abbreviated ASCII depiction of the collection hierarchy, for those who can't see the images. + + Traversable + | + | + Iterable + | + +------------------+--------------------+ + Map Set Seq + | | | + | +----+----+ +-----+------+ + Sorted Map SortedSet BitSet Buffer Vector LinearSeq + + +## Parallel Collections + +When Scala 2.9 introduced parallel collections, one of the design goals was to +make their use as seamless as possible. In the simplest terms, one can replace +a non-parallel (serial) collection with a parallel one, and instantly reap the +benefits. + +However, since all collections until then were serial, many algorithms using +them assumed and depended on the fact that they _were_ serial. Parallel +collections fed to the methods with such assumptions would fail. For this +reason, all the hierarchy described in the previous section _mandates serial +processing_. + +Two new hierarchies were created to support the parallel collections. + +The parallel collections hierarchy has the same names for traits, but preceded +with `Par`: `ParIterable`, `ParSeq`, `ParMap` and `ParSet`. Note that there is +no `ParTraversable`, since any collection supporting parallel access is capable +of supporting the stronger `ParIterable` trait. It doesn't have some of the +more specialized traits present in the serial hierarchy either. This whole +hierarchy is found under the directory `scala.collection.parallel`. + +The classes implementing parallel collections also differ, with `ParHashMap` +and `ParHashSet` for both mutable and immutable parallel collections, plus +`ParRange` and `ParVector` implementing `immutable.ParSeq` and `ParArray` +implementing `mutable.ParSeq`. + +Another hierarchy also exists that mirrors the traits of serial and parallel +collections, but with a prefix `Gen`: `GenTraversable`, `GenIterable`, +`GenSeq`, `GenMap` and `GenSet`. These traits are _parents_ to both parallel +and serial collections. This means that a method taking a `Seq` cannot receive +a parallel collection, but a method taking a `GenSeq` is expected to work with +both serial and parallel collections. + +Given the way these hierarchies were structured, code written for Scala 2.8 was +fully compatible with Scala 2.9, and demanded serial behavior. Without being +rewritten, it cannot take advantage of parallel collections, but the changes +required are very small. + +### Using Parallel Collections + +Any collection can be converted into a parallel one by calling the method `par` +on it. Likewise, any collection can be converted into a serial one by calling +the method `seq` on it. + +If the collection was already of the type requested (parallel or serial), no +conversion will take place. If one calls `seq` on a parallel collection or +`par` on a serial collection, however, a new collection with the requested +characteristic will be generated. + +Do not confuse `seq`, which turns a collection into a non-parallel collection, +with `toSeq`, which returns a `Seq` created from the elements of the +collection. Calling `toSeq` on a parallel collection will return a `ParSeq`, +not a serial collection. + +## The Main Traits + +While there are many implementing classes and subtraits, there are some basic +traits in the hierarchy, each of which providing more methods or more specific +guarantees, but reducing the number of classes that could implement them. + +In the following subsections, I'll give a brief description of the main traits +and the idea behind them. + +### Trait TraversableOnce + +This trait is pretty much like trait `Traversable` described below, but with +the limitation that you can only use it _once_. That is, any methods called on +a `TraversableOnce` _may_ render it unusable. + +This limitation makes it possible for the same methods to be shared between the +collections and `Iterator`. This makes it possible for a method that works with +an `Iterator` but not using `Iterator`-specific methods to actually be able to +work with any collection at all, plus iterators, if rewritten to accept +`TraversableOnce`. + +Because `TraversableOnce` unifies collections and iterators, and iterators are +not considered collections, it does not appear in the previous graphs, which +concern themselves only with collections. + +### Trait Traversable + +At the top of the _collection_ hierarchy is trait `Traversable`. Its only +abstract operation is + + def foreach[U](f: Elem => U) + +The operation is meant to traverse all elements of the collection, and apply +the given operation f to each element. The application is done for its side +effect only; in fact any function result of f is discarded by foreach. + +Traversible objects can be finite or infinite. An example of an infinite +traversable object is the stream of natural numbers `Stream.from(0)`. The +method `hasDefiniteSize` indicates whether a collection is possibly infinite. +If `hasDefiniteSize` returns true, the collection is certainly finite. If it +returns false, the collection has not been not fully elaborated yet, so it +might be infinite or finite. + +This class defines methods which can be efficiently implemented in terms of +`foreach` (over 40 of them). + +### Trait Iterable + +This trait declares an abstract method `iterator` that returns an iterator that +yields all the collection’s elements one by one. The `foreach` method in +`Iterable` is implemented in terms of `iterator`. Subclasses of `Iterable` +often override foreach with a direct implementation for efficiency. + +Class `Iterable` also adds some less-often used methods to `Traversable`, which +can be implemented efficiently only if an `iterator` is available. They are +summarized below. + + xs.iterator An iterator that yields every element in xs, in the same order as foreach traverses elements. + xs takeRight n A collection consisting of the last n elements of xs (or, some arbitrary n elements, if no order is defined). + xs dropRight n The rest of the collection except xs takeRight n. + xs sameElements ys A test whether xs and ys contain the same elements in the same order + +### Seq, Set and Map + +After `Iterable` there come three base traits which inherit from it: `Seq`, +`Set`, and `Map`. All three have an `apply` method and all three implement the +`PartialFunction` trait, but the meaning of `apply` is different in each case. + +I trust the meaning of `Seq`, `Set` and `Map` is intuitive. After them, the +classes break up in specific implementations that offer particular guarantees +with regards to performance, and the methods it makes available as a result of +it. Also available are some traits with further refinements, such as +`LinearSeq`, `IndexedSeq` and `SortedSet`. + +## Complete Overview + +### Base Classes and Traits + +* `TraversableOnce` -- All methods and behavior common to collections and iterators. + + * `Traversable` -- Basic collection class. Can be implemented just with `foreach`. + + * `TraversableProxy` -- Proxy for a `Traversable`. Just point `self` to the real collection. + * `TraversableView` -- A Traversable with some non-strict methods. + * `TraversableForwarder` -- Forwards most methods to `underlying`, except `toString`, `hashCode`, `equals`, `stringPrefix`, `newBuilder`, `view` and all calls creating a new iterable object of the same kind. + * `mutable.Traversable` and `immutable.Traversable` -- same thing as `Traversable`, but restricting the collection type. + * Other special-cases `Iterable` classes, such as `MetaData`, exists. + * `Iterable` -- A collection for which an `Iterator` can be created (through `iterator`). + * `IterableProxy`, `IterableView`, `mutable` and `immutable.Iterable`. + + * `Iterator` -- A trait which is not descendant of `Traversable`. Define `next` and `hasNext`. + * `CountedIterator` -- An `Iterator` defining `count`, which returns the elements seen so far. + * `BufferedIterator` -- Defines `head`, which returns the next element without consuming it. + * Other special-cases `Iterator` classes, such as `Source`, exists. + +### The Sequences + +* `Seq` -- A sequence of elements. One assumes a well-defined size and element repetition. Extends `PartialFunction` as well. + + * `IndexedSeq` -- Sequences that support O(1) element access and O(1) length computation. + * `IndexedSeqView` + * `immutable.PagedSeq` -- An implementation of `IndexedSeq` where the elements are produced on-demand by a function passed through the constructor. + * `immutable.IndexedSeq` + + * `immutable.Range` -- A delimited sequence of integers, closed on the lower end, open on the high end, and with a step. + * `immutable.Range.Inclusive` -- A `Range` closed on the high end as well. + * `immutable.NumericRange` -- A more generic version of `Range` which works with any `Integral`. + * `immutable.NumericRange.Inclusive`, `immutable.NumericRange.Exclusive`. + * `immutable.WrappedString`, `immutable.RichString` -- Wrappers which enables seeing a `String` as a `Seq[Char]`, while still preserving the `String` methods. I'm not sure what the difference between them is. + + * `mutable.IndexedSeq` + * `mutable.GenericArray` -- An `Seq`-based array-like structure. Note that the "class" `Array` is Java's `Array`, which is more of a memory storage method than a class. + * `mutable.ResizableArray` -- Internal class used by classes based on resizable arrays. + * `mutable.PriorityQueue`, `mutable.SynchronizedPriorityQueue` -- Classes implementing prioritized queues -- queues where the elements are dequeued according to an `Ordering` first, and order of queueing last. + * `mutable.PriorityQueueProxy` -- an abstract `Proxy` for a `PriorityQueue`. + + * `LinearSeq` -- A trait for linear sequences, with efficient time for `isEmpty`, `head` and `tail`. + + * `immutable.LinearSeq` + * `immutable.List` -- An immutable, singlely-linked, list implementation. + * `immutable.Stream` -- A lazy-list. Its elements are only computed on-demand, but memoized (kept in memory) afterwards. It can be theoretically infinite. + * `mutable.LinearSeq` + * `mutable.DoublyLinkedList` -- A list with mutable `prev`, `head` (`elem`) and `tail` (`next`). + * `mutable.LinkedList` -- A list with mutable `head` (`elem`) and `tail` (`next`). + * `mutable.MutableList` -- A class used internally to implement classes based on mutable lists. + * `mutable.Queue`, `mutable.QueueProxy` -- A data structure optimized for FIFO (First-In, First-Out) operations. + * `mutable.QueueProxy` -- A `Proxy` for a `mutable.Queue`. + + * `SeqProxy`, `SeqView`, `SeqForwarder` + + * `immutable.Seq` + + * `immutable.Queue` -- A class implementing a FIFO-optimized (First-In, First-Out) data structure. There is no common superclass of both `mutable` and `immutable` queues. + * `immutable.Stack` -- A class implementing a LIFO-optimized (Last-In, First-Out) data structure. There is no common superclass of both `mutable` `immutable` stacks. + * `immutable.Vector` -- ? + * `scala.xml.NodeSeq` -- A specialized XML class which extends `immutable.Seq`. + * `immutable.IndexedSeq` -- As seen above. + * `immutable.LinearSeq` -- As seen above. + + * `mutable.ArrayStack` -- A class implementing a LIFO-optimized data structure using arrays. Supposedly significantly faster than a normal stack. + * `mutable.Stack`, `mutable.SynchronizedStack` -- Classes implementing a LIFO-optimized data structure. + * `mutable.StackProxy` -- A `Proxy` for a `mutable.Stack`.. + * `mutable.Seq` + + * `mutable.Buffer` -- Sequence of elements which can be changed by appending, prepending or inserting new members. + * `mutable.ArrayBuffer` -- An implementation of the `mutable.Buffer` class, with constant amortized time for the append, update and random access operations. It has some specialized subclasses, such as `NodeBuffer`. + * `mutable.BufferProxy`, `mutable.SynchronizedBuffer`. + * `mutable.ListBuffer` -- A buffer backed by a list. It provides constant time append and prepend, with most other operations being linear. + * `mutable.ObservableBuffer` -- A *mixin* trait which, when mixed to a `Buffer`, provides notification events through a `Publisher` interfaces. + * `mutable.IndexedSeq` -- As seen above. + * `mutable.LinearSeq` -- As seen above. + +### The Sets + +* `Set` -- A set is a collection that includes at most one of any object. + + * `BitSet` -- A set of integers stored as a bitset. + * `immutable.BitSet` + * `mutable.BitSet` + + * `SortedSet` -- A set whose elements are ordered. + * `immutable.SortedSet` + * `immutable.TreeSet` -- An implementation of a `SortedSet` based on a tree. + + * `SetProxy` -- A `Proxy` for a `Set`. + + * `immutable.Set` + * `immutable.HashSet` -- An implementation of `Set` based on element hashing. + * `immutable.ListSet` -- An implementation of `Set` based on lists. + * Additional set classes exists to provide optimized implementations for sets from 0 to 4 elements. + * `immutable.SetProxy` -- A `Proxy` for an immutable `Set`. + + * `mutable.Set` + * `mutable.HashSet` -- An implementation of `Set` based on element hashing. + * `mutable.ImmutableSetAdaptor` -- A class implementing a mutable `Set` from an immutable `Set`. + * `LinkedHashSet` -- An implementation of `Set` based on lists. + * `ObservableSet` -- A *mixin* trait which, when mixed with a `Set`, provides notification events through a `Publisher` interface. + * `SetProxy` -- A `Proxy` for a `Set`. + * `SynchronizedSet` -- A *mixin* trait which, when mixed with a `Set`, provides notification events through a `Publisher` interface. + +### The Maps + +* `Map` -- An `Iterable` of `Tuple2`, which also provides methods for retrieving a value (the second element of the tuple) given a key (the first element of the tuple). Extends `PartialFunction` as well. + * `MapProxy` -- A `Proxy` for a `Map`. + * `DefaultMap` -- A trait implementing some of `Map`'s abstract methods. + * `SortedMap` -- A `Map` whose keys are sorted. + * `immutable.SortMap` + * `immutable.TreeMap` -- A class implementing `immutable.SortedMap`. + * `immutable.Map` + * `immutable.MapProxy` + * `immutable.HashMap` -- A class implementing `immutable.Map` through key hashing. + * `immutable.IntMap` -- A class implementing `immutable.Map` specialized for `Int` keys. Uses a tree based on the binary digits of the keys. + * `immutable.ListMap` -- A class implementing `immutable.Map` through lists. + * `immutable.LongMap` -- A class implementing `immutable.Map` specialized for `Long` keys. See `IntMap`. + * There are additional classes optimized for an specific number of elements. + * `mutable.Map` + * `mutable.HashMap` -- A class implementing `mutable.Map` through key hashing. + * `mutable.ImmutableMapAdaptor` -- A class implementing a `mutable.Map` from an existing `immutable.Map`. + * `mutable.LinkedHashMap` -- ? + * `mutable.ListMap` -- A class implementing `mutable.Map` through lists. + * `mutable.MultiMap` -- A class accepting more than one distinct value for each key. + * `mutable.ObservableMap` -- A *mixin* which, when mixed with a `Map`, publishes events to observers through a `Publisher` interface. + * `mutable.OpenHashMap` -- A class based on an open hashing algorithm. + * `mutable.SynchronizedMap` -- A *mixin* which should be mixed with a `Map` to provide a version of it with synchronized methods. + * `mutable.MapProxy`. + +## Bonus Questions + +* Why the Like classes exist (e.g. TraversableLike)? + +This was done to achieve maximum code reuse. The concrete *generic* +implementation for classes with a certain structure (a traversable, a map, etc) +is done in the Like classes. The classes intended for general consumption, +then, override selected methods that can be optmized. + +* What the companion methods are for (e.g. List.companion)? + +The builder for the classes, ie, the object which knows how to create instances +of that class in a way that can be used by methods like `map`, is created by a +method in the companion object. So, in order to build an object of type X, I +need to get that builder from the companion object of X. Unfortunately, there +is no way, in Scala, to get from class X to object X. Because of that, there is +a method defined in each instance of X, `companion`, which returns the +companion object of class X. + +While there might be some use for such method in normal programs, its target is +enabling code reuse in the collection library. + +* How I know what implicit objects are in scope at a given point? + +You aren't supposed to care about that. They are implicit precisely so that you +don't need to figure out how to make it work. + +These implicits exists to enable the methods on the collections to be defined +on parent classes but still return a collection of the same type. For example, +the `map` method is defined on `TraversableLike`, but if you used on a `List` +you'll get a `List` back. + +This answer was originally submitted in response to [this question][9] on Stack +Overflow. + + + [1]: http://docs.scala-lang.org/overviews/collections/introduction.html + [2]: http://docs.scala-lang.org/overviews/core/architecture-of-scala-collections.html + [3]: http://www.scala-lang.org/sid/3 + [4]: https://github.com/sirthias/scala-collections-charts/downloads + [5]: http://i.stack.imgur.com/bSVyA.png + [6]: http://i.stack.imgur.com/2fjoA.png + [7]: http://i.stack.imgur.com/Dsptl.png + [8]: http://i.stack.imgur.com/szWUr.png + [9]: http://stackoverflow.com/q/1722137/53013 diff --git a/_tutorials/FAQ/context-bounds.md b/_tutorials/FAQ/context-bounds.md new file mode 100644 index 0000000000..9ed04ad25d --- /dev/null +++ b/_tutorials/FAQ/context-bounds.md @@ -0,0 +1,104 @@ +--- +layout: overview-large +title: What are Scala context bounds? + +discourse: true + +partof: FAQ +num: 3 +--- + +What is a Context Bound? +------------------------ + +Context bounds were introduced in Scala 2.8.0, and are typically used with the +so-called _type class pattern_, a pattern of code that emulates the +functionality provided by Haskell type classes, though in a more verbose +manner. + +A context bound requires a _parameterized type_, such as `Ordered[A]`, +but unlike `String`. + +A context bound describes an implicit _value_. It is used to declare that for +some type `A`, there is an +implicit value of type `B[A]` available. The syntax goes like this: + + def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A] + +The common example of usage in Scala is this: + + def f[A : ClassTag](n: Int) = new Array[A](n) + +An `Array` initialization on a parameterized type requires a `ClassTag` to +be available, for arcane reasons related to type erasure and the non-erasure +nature of arrays. + +Another very common example in the library is a bit more complex: + + def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b) + +Here, `implicitly` is used to retrive the implicit value we want, one of type +`Ordering[A]`, which class defines the method `compare(a: A, b: A): Int`. + +We'll see another way of doing this below. + +How are Context Bounds implemented? +--------------------------------------------------- + +It shouldn't be surprising that context bounds are +implemented with implicit parameters, given their definition. Actually, the +syntax I showed are syntactic sugars for what really happens. See below how +they de-sugar: + + def g[A : B](a: A) = h(a) + def g[A](a: A)(implicit ev: B[A]) = h(a) + +So, naturally, one can write them in their full syntax, which is specially +useful for context bounds: + + def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b) + +What are Context Bounds used for? +--------------------------------- + +Context bounds are mainly used in what has become known as _typeclass pattern_, +as a reference to Haskell's type classes. Basically, this pattern implements an +alternative to inheritance by making functionality available through a sort of +implicit adapter pattern. + +The classic example is Scala 2.8's `Ordering`. The usage is: + + def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b + +Though you'll usually see that written like this: + + def f[A](a: A, b: A)(implicit ord: Ordering[A]) = { + import ord._ + if (a < b) a else b + } + +Which take advantage of some implicit conversions inside `Ordering` that enable +the traditional operator style. Another example in Scala 2.8 is the `Numeric`: + + def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b) + +A more complex example is the new collection usage of `CanBuildFrom`, but +there's already a very long answer about that, so I'll avoid it here. And, as +mentioned before, there's the `ClassTag` usage, which is required to +initialize new arrays without concrete types. + +Though it has been possible for a long time, the use of context bounds has +really taken off in 2010, and is now found to some degree in most of Scala's +most important libraries and frameworks. The most extreme example of its usage, +though, is the Scalaz library, which brings a lot of the power of Haskell to +Scala. I recommend reading up on typeclass patterns to get more acquainted it +all the ways in which it can be used. + +Related questions of interest: + +* [A discussion on types, origin and precedence of implicits](finding-implicits.html) +* [Chaining implicits](chaining-implicits.html) + +This answer was originally submitted in response to [this question on Stack Overflow][1]. + + [1]: http://stackoverflow.com/q/4465948/53013 diff --git a/tutorials/FAQ/finding-implicits.md b/_tutorials/FAQ/finding-implicits.md similarity index 99% rename from tutorials/FAQ/finding-implicits.md rename to _tutorials/FAQ/finding-implicits.md index 8a788e8c24..f883a7b678 100644 --- a/tutorials/FAQ/finding-implicits.md +++ b/_tutorials/FAQ/finding-implicits.md @@ -1,5 +1,5 @@ --- -layout: overview +layout: overview-large title: Where does Scala look for implicits? discourse: true @@ -14,15 +14,15 @@ For example, where do the values for `integral` below come from? scala> import scala.math._ import scala.math._ - + scala> def foo[T](t: T)(implicit integral: Integral[T]): Unit = { println(integral) } foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit - + scala> foo(0) scala.math.Numeric$IntIsIntegral$@3dbea611 - + scala> foo(0L) scala.math.Numeric$LongIsIntegral$@48c610af @@ -168,7 +168,7 @@ Let's give examples for them. import scala.collection.JavaConversions.mapAsScalaMap def env = System.getenv() // Java map val term = env("TERM") // implicit conversion from Java Map to Scala Map - + ### Wildcard Imports def sum[T : Integral](list: List[T]): T = { @@ -219,7 +219,7 @@ the object `Ordering`, companion to the class `Ordering`, and finds an implicit Note that companion objects of super classes are also looked into. For example: class A(val n: Int) - object A { + object A { implicit def str(a: A) = "A: %d" format a.n } class B(val x: Int, y: Int) extends A(y) @@ -228,7 +228,7 @@ Note that companion objects of super classes are also looked into. For example: This is how Scala found the implicit `Numeric[Int]` and `Numeric[Long]` in the opening example, by the way, as they are found inside `Numeric`, not `Integral`. - + ### Implicit scope of an argument's type If you have a method with an argument type `A`, then the implicit scope of type @@ -325,4 +325,3 @@ This question and answer were originally submitted on [Stack Overflow][3]. [5]: http://scala-lang.org/files/archive/spec/2.11/06-expressions.html [6]: http://scala-lang.org/files/archive/spec/2.11/07-implicits.html [7]: https://github.com/scala/scala.github.com/issues - diff --git a/_tutorials/FAQ/finding-symbols.md b/_tutorials/FAQ/finding-symbols.md new file mode 100644 index 0000000000..a499b7b5ee --- /dev/null +++ b/_tutorials/FAQ/finding-symbols.md @@ -0,0 +1,205 @@ +--- +layout: overview-large +title: How do I find what some symbol means or does? + +discourse: true + +partof: FAQ +num: 1 +outof: 9 +--- +We can divide the operators in Scala, for the purpose of teaching, into four categories: + +* Keywords/reserved symbols +* Normal methods or values +* Methods provided by implicit conversion +* Syntactic sugars/composition + +And let's see some arbitrary examples: + + <- // Keyword + -> // Method provided by implicit conversion + <= // Common method + ++= // Can be a common method or syntactic sugar involving ++ method + :: // Common method or object + _+_ // Not really a single operator; it's parsed as _ + _ + +The exact meaning of most of these methods depends on the class they are defined +on. For example, `<=` on `Int` means _"less than or equal to"_, but it might +mean something else in another class. `::` in an expression is probably the method of the class +`List` but it can also refer to the object of the same name (and in a pattern it +definitely does). + +So, let's discuss these categories. + +Keywords/reserved symbols +------------------------- + +There are a few symbols in Scala that are special and cannot be defined or used as method names. +Two of them are considered proper keywords, while others are just "reserved". They are: + + // Keywords + <- // Used on for-comprehensions, to separate pattern from generator + => // Used for function types, function literals and import renaming + + // Reserved + ( ) // Delimit expressions and parameters + [ ] // Delimit type parameters + { } // Delimit blocks + . // Method call and path separator + // /* */ // Comments + # // Used in type notations + : // Type ascription or context bounds + <: >: // Upper and lower bounds + <% // View bounds (deprecated) + " """ // Strings + ' // Indicate symbols and characters + @ // Annotations and variable binding on pattern matching + ` // Denote constant or enable arbitrary identifiers + , // Parameter separator + ; // Statement separator + _* // vararg expansion + _ // Many different meanings + +These are all _part of the language_, and, as such, can be found in any text +that properly describe the language, such as [Scala Specification][1](PDF) +itself. + +The last one, the underscore, deserve a special description, because it is +widely used, and has different meanings depending on the context. Here's a sample: + + import scala._ // Wild card -- all of Scala is imported + import scala.{ Predef => _, _ } // Exclusion, everything except Predef + def f[M[_]] // Higher kinded type parameter + def f(m: M[_]) // Existential type + _ + _ // Anonymous function placeholder parameter + m _ // Eta expansion of method into method value + m(_) // Partial function application + _ => 5 // Discarded parameter + case _ => // Wild card pattern -- matches anything + f(xs: _*) // Sequence xs is passed as multiple parameters to f(ys: T*) + case Seq(xs @ _*) // Identifier xs is bound to the whole matched sequence + +Common methods +-------------- + +Many symbols are simply methods of a class, a trait, or an object. For instance, if you do + + List(1, 2) ++ List(3, 4) + +You'll find the method `++` right on the Scaladoc for [List][5]. However, +there's one convention that you must be aware when searching for methods. +Methods ending in colon (`:`) bind _to the right_ instead of the left. In other +words, while the above method call is equivalent to: + + List(1, 2).++(List(3, 4)) + +If I had, instead `1 :: List(2, 3)`, that would be equivalent to: + + List(2, 3).::(1) + +So you need to look at the type found _on the right_ when looking for methods +ending in colon. Consider, for instance: + + 1 +: List(2, 3) :+ 4 + +The first method (`+:`) binds to the right, and is found on `List`. The second +method (`:+`) is just a normal method, and binds to the left -- again, on +`List`. + +If the name ends in `=`, look for the method called the same without `=` and +read the last section. + +If you aren't sure what the type of the receiver is, you can look up the symbol +on the Scaladoc [index page for identifiers not starting with letters][2] (for +standard Scala library; of course, third-party libraries can add their own +symbolic methods, for which you should look at the corresponding page of _their_ +Scaladoc). + +Types and objects can also have symbolic names; in particular, it should be mentioned +that for types with two type parameters the name can be written _between_ parameters, +so that e.g. `Int <:< Any` is the same as `<:<[Int, Any]`. + +Methods provided by implicit conversion +--------------------------------------- + +If you did not find the symbol you are looking for in the list of reserved symbols, then +it must be a method, or part of one. But, often, you'll see some symbol and the +documentation for the class will not have that method. When this happens, +either you are looking at a composition of one or more methods with something +else, or the method has been imported into scope, or is available through an +imported implicit conversion. + +These can also be found in Scaladoc's [index][2], as mentioned above. + +All Scala code has three automatic imports: + + // Not necessarily in this order + import java.lang._ + import scala._ + import scala.Predef._ + +The first two only make classes and singleton objects available, none of which +look like operators. [`Predef`][3] is the only interesting one for this post. + +Looking inside `Predef` shows some symbolic names: + + class <:< + class =:= + object =:= + object <%< // removed in Scala 2.10 + def ??? + +There is also `::`, which doesn't appear in the Scaladoc, but is mentioned in the comments. +In addition, `Predef` makes some methods available through _implicit conversions_. Just +look at the methods and classes with `implicit` modifier that receive, as parameter, an +object of type that is receiving the method. For example, consider `"a" -> 1`. We need +to look for an implicit which works on `"a"`, and so it can take `String`, one of its +supertypes (`AnyRef` or `Any`) or a type parameter. In this case, we find +`implicit final class ArrowAssoc[A](private val self: A)` which makes this implicit +avaialable on all types. + +Other implicit conversions may be visible in your scope depending on imports, extended types or +self-type annotations. See [Finding implicits](tutorials/FAQ/finding-implicits.html) for details. + +Syntactic sugars/composition +----------------------------- + +So, here's a few syntactic sugars that may hide a method: + + class Example(arr: Array[Int] = Array.fill(5)(0)) { + def apply(n: Int) = arr(n) + def update(n: Int, v: Int) = arr(n) = v + def a = arr(0); def a_=(v: Int) = arr(0) = v + def b = arr(1); def b_=(v: Int) = arr(1) = v + def c = arr(2); def c_=(v: Int) = arr(2) = v + def d = arr(3); def d_=(v: Int) = arr(3) = v + def e = arr(4); def e_=(v: Int) = arr(4) = v + def +(v: Int) = new Example(arr map (_ + v)) + def unapply(n: Int) = if (arr.indices contains n) Some(arr(n)) else None + } + + val ex = new Example + println(ex(0)) // means ex.apply(0) + ex(0) = 2 // means ex.update(0, 2) + ex.b = 3 // means ex.b_=(3) + val ex(c) = 2 // calls ex.unapply(2) and assigns result to c, if it's Some; throws MatchError if it's None + ex += 1 // means ex = ex + 1; if Example had a += method, it would be used instead + +The last one is interesting, because *any* symbolic method can be combined with `=` in that way. + +And, of course, all of the above can be combined in various combinations, e.g. + + (_+_) // An expression, or parameter, that is an anonymous function with + // two parameters, used exactly where the underscores appear, and + // which calls the "+" method on the first parameter passing the + // second parameter as argument. + +This answer was originally submitted in response to [this question on Stack Overflow][6]. + + [1]: http://scala-lang.org/files/archive/spec/2.11/ + [2]: http://www.scala-lang.org/api/current/index.html#index.index-_ + [3]: http://www.scala-lang.org/api/current/index.html#scala.Predef$ + [4]: http://www.scala-lang.org/api/current/scala/Predef$$ArrowAssoc.html + [5]: http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List + [6]: http://stackoverflow.com/q/7888944/53013 diff --git a/_tutorials/FAQ/initialization-order.md b/_tutorials/FAQ/initialization-order.md new file mode 100644 index 0000000000..d9c3973d7c --- /dev/null +++ b/_tutorials/FAQ/initialization-order.md @@ -0,0 +1,164 @@ +--- +layout: overview-large +title: Why is my abstract or overridden val null? + +discourse: true + +partof: FAQ +num: 9 +--- + +## Example +To understand the problem, let's pick the following concrete example. + + abstract class A { + val x1: String + val x2: String = "mom" + + println("A: " + x1 + ", " + x2) + } + class B extends A { + val x1: String = "hello" + + println("B: " + x1 + ", " + x2) + } + class C extends B { + override val x2: String = "dad" + + println("C: " + x1 + ", " + x2) + } + +Let's observe the initialization order through the Scala REPL: + + scala> new C + A: null, null + B: hello, null + C: hello, dad + +Only when we get to the constructor of `C` are both `x1` and `x2` initialized. Therefore, constructors of `A` and `B` risk running into `NullPointerException`s. + +## Explanation +A 'strict' or 'eager' val is one which is not marked lazy. + +In the absence of "early definitions" (see below), initialization of strict vals is done in the following order. + +1. Superclasses are fully initialized before subclasses. +2. Otherwise, in declaration order. + +Naturally when a val is overridden, it is not initialized more than once. So though x2 in the above example is seemingly defined at every point, this is not the case: an overridden val will appear to be null during the construction of superclasses, as will an abstract val. + +There is a compiler flag which can be useful for identifying this situation: + +**-Xcheckinit**: Add runtime check to field accessors. + +It is inadvisable to use this flag outside of testing. It adds significantly to the code size by putting a wrapper around all potentially uninitialized field accesses: the wrapper will throw an exception rather than allow a null (or 0/false in the case of primitive types) to silently appear. Note also that this adds a *runtime* check: it can only tell you anything about code paths which you exercise with it in place. + +Using it on the opening example: + + % scalac -Xcheckinit a.scala + % scala -e 'new C' + scala.UninitializedFieldError: Uninitialized field: a.scala: 13 + at C.x2(a.scala:13) + at A.(a.scala:5) + at B.(a.scala:7) + at C.(a.scala:12) + +### Solutions ### + +Approaches for avoiding null values include: + +#### Use lazy vals #### + + abstract class A { + val x1: String + lazy val x2: String = "mom" + + println("A: " + x1 + ", " + x2) + } + class B extends A { + lazy val x1: String = "hello" + + println("B: " + x1 + ", " + x2) + } + class C extends B { + override lazy val x2: String = "dad" + + println("C: " + x1 + ", " + x2) + } + // scala> new C + // A: hello, dad + // B: hello, dad + // C: hello, dad + +Usually the best answer. Unfortunately you cannot declare an abstract lazy val. If that is what you're after, your options include: + +1. Declare an abstract strict val, and hope subclasses will implement it as a lazy val or with an early definition. If they do not, it will appear to be uninitialized at some points during construction. +2. Declare an abstract def, and hope subclasses will implement it as a lazy val. If they do not, it will be re-evaluated on every access. +3. Declare a concrete lazy val which throws an exception, and hope subclasses override it. If they do not, it will... throw an exception. + +An exception during initialization of a lazy val will cause the right hand side to be re-evaluated on the next access: see SLS 5.2. + +Note that using multiple lazy vals creates a new risk: cycles among lazy vals can result in a stack overflow on first access. + +#### Use early definitions #### + abstract class A { + val x1: String + val x2: String = "mom" + + println("A: " + x1 + ", " + x2) + } + class B extends { + val x1: String = "hello" + } with A { + println("B: " + x1 + ", " + x2) + } + class C extends { + override val x2: String = "dad" + } with B { + println("C: " + x1 + ", " + x2) + } + // scala> new C + // A: hello, dad + // B: hello, dad + // C: hello, dad + +Early definitions are a bit unwieldy, there are limitations as to what can appear and what can be referenced in an early definitions block, and they don't compose as well as lazy vals: but if a lazy val is undesirable, they present another option. They are specified in SLS 5.1.6. + +#### Use constant value definitions #### + abstract class A { + val x1: String + val x2: String = "mom" + + println("A: " + x1 + ", " + x2) + } + class B extends A { + val x1: String = "hello" + final val x3 = "goodbye" + + println("B: " + x1 + ", " + x2) + } + class C extends B { + override val x2: String = "dad" + + println("C: " + x1 + ", " + x2) + } + abstract class D { + val c: C + val x3 = c.x3 // no exceptions! + println("D: " + c + " but " + x3) + } + class E extends D { + val c = new C + println(s"E: ${c.x1}, ${c.x2}, and $x3...") + } + //scala> new E + //D: null but goodbye + //A: null, null + //B: hello, null + //C: hello, dad + //E: hello, dad, and goodbye... + +Sometimes all you need from an interface is a compile-time constant. + +Constant values are stricter than strict and earlier than early definitions and have even more limitations, +as they must be constants. They are specified in SLS 4.1. diff --git a/_tutorials/FAQ/stream-view-iterator.md b/_tutorials/FAQ/stream-view-iterator.md new file mode 100644 index 0000000000..67defcb41d --- /dev/null +++ b/_tutorials/FAQ/stream-view-iterator.md @@ -0,0 +1,45 @@ +--- +layout: overview-large +title: What is the difference between view, stream and iterator? + +discourse: true + +partof: FAQ +num: 4 +--- +First, they are all _non-strict_. That has a particular mathematical meaning +related to functions, but, basically, means they are computed on-demand instead +of in advance. + +`Stream` is a lazy list indeed. In fact, in Scala, a `Stream` is a `List` whose +`tail` is a `lazy val`. Once computed, a value stays computed and is reused. +Or, as you say, the values are cached. + +An `Iterator` can only be used once because it is a _traversal pointer_ into a +collection, and not a collection in itself. What makes it special in Scala is +the fact that you can apply transformation such as `map` and `filter` and +simply get a new `Iterator` which will only apply these transformations when +you ask for the next element. + +Scala used to provide iterators which could be reset, but that is very hard to +support in a general manner, and they didn't make version 2.8.0. + +Views are meant to be viewed much like a database view. It is a series of +transformation which one applies to a collection to produce a "virtual" +collection. As you said, all transformations are re-applied each time you need +to fetch elements from it. + +Both `Iterator` and views have excellent memory characteristics. `Stream` is +nice, but, in Scala, its main benefit is writing infinite sequences +(particularly sequences recursively defined). One _can_ avoid keeping all of +the `Stream` in memory, though, by making sure you don't keep a reference to +its `head` (for example, by using `def` instead of `val` to define the +`Stream`). + +Because of the penalties incurred by views, one should usually `force` it after +applying the transformations, or keep it as a view if only few elements are +expected to ever be fetched, compared to the total size of the view. + +This answer was originally submitted in response to [this question on Stack Overflow][1]. + + [1]: http://stackoverflow.com/q/5159000/53013 diff --git a/_tutorials/FAQ/yield.md b/_tutorials/FAQ/yield.md new file mode 100644 index 0000000000..ec53a65de8 --- /dev/null +++ b/_tutorials/FAQ/yield.md @@ -0,0 +1,158 @@ +--- +layout: overview-large +title: How does yield work? + +discourse: true + +partof: FAQ +num: 2 +--- +Though there's a `yield` in other languages such as Python and Ruby, Scala's +`yield` does something very different from them. In Scala, `yield` is part +of for comprehensions -- a generalization of Ruby and Python's list-comprehensions. + +Scala's "for comprehensions" are equivalent to Haskell's "do" notation, and it +is nothing more than a syntactic sugar for composition of multiple monadic +operations. As this statement will most likely not help anyone who needs help, +let's try again... + +Translating for-comprehensions +------------------------------ + +Scala's "for comprehensions" are syntactic sugar for composition of multiple +operations with `foreach`, `map`, `flatMap`, `filter` or `withFilter`. +Scala actually translates a for-expression into calls to those methods, +so any class providing them, or a subset of them, can be used with for comprehensions. + +First, let's talk about the translations. There are very simple rules: + +#### Example 1 + + for(x <- c1; y <- c2; z <-c3) {...} + +is translated into + + c1.foreach(x => c2.foreach(y => c3.foreach(z => {...}))) + +#### Example 2 + + for(x <- c1; y <- c2; z <- c3) yield {...} + +is translated into + + c1.flatMap(x => c2.flatMap(y => c3.map(z => {...}))) + +#### Example 3 + + for(x <- c; if cond) yield {...} + +is translated into + + c.withFilter(x => cond).map(x => {...}) + +with a fallback into + + c.filter(x => cond).map(x => {...}) + +if method `withFilter` is not available but `filter` is. +The next chapter has more information on this. + +#### Example 4 + + for(x <- c; y = ...) yield {...} + +is translated into + + c.map(x => (x, ...)).map((x,y) => {...}) + + +When you look at very simple for comprehensions, the map/foreach alternatives +look, indeed, better. Once you start composing them, though, you can easily get +lost in parenthesis and nesting levels. When that happens, for comprehensions +are usually much clearer. + +I'll show one simple example, and intentionally omit any explanation. You can +decide which syntax is easier to understand. + + l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length)) + +or + + for{ + sl <- l + el <- sl + if el > 0 + } yield el.toString.length + + +About withFilter, and strictness +---------------------------------- + +Scala 2.8 introduced a method called `withFilter`, whose main difference is +that, instead of returning a new, filtered, collection, it filters on-demand. +The `filter` method has its behavior defined based on the strictness of the +collection. To understand this better, let's take a look at some Scala 2.7 with +`List` (strict) and `Stream` (non-strict): + + scala> var found = false + found: Boolean = false + + scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) + 1 + 3 + 7 + 9 + + scala> found = false + found: Boolean = false + + scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) + 1 + 3 + +The difference happens because filter is immediately applied with `List`, +returning a list of odds -- since `found` is `false`. Only then `foreach` is +executed, but, by this time, changing `found` is meaningless, as `filter` has +already executed. + +In the case of `Stream`, the condition is not immediatelly applied. Instead, as +each element is requested by `foreach`, `filter` tests the condition, which +enables `foreach` to influence it through `found`. Just to make it clear, here +is the equivalent for-comprehension code: + + for (x <- List.range(1, 10); if x % 2 == 1 && !found) + if (x == 5) found = true else println(x) + + for (x <- Stream.range(1, 10); if x % 2 == 1 && !found) + if (x == 5) found = true else println(x) + +This caused many problems, because people expected the `if` to be considered +on-demand, instead of being applied to the whole collection beforehand. + +Scala 2.8 introduced `withFilter`, which is _always_ non-strict, no matter the +strictness of the collection. The following example shows `List` with both +methods on Scala 2.8: + + scala> var found = false + found: Boolean = false + + scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) + 1 + 3 + 7 + 9 + + scala> found = false + found: Boolean = false + + scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) + 1 + 3 + +This produces the result most people expect, without changing how `filter` +behaves. As a side note, `Range` was changed from non-strict to strict between +Scala 2.7 and Scala 2.8. + +This answer was originally submitted in response to [this question on Stack Overflow][1]. + + [1]: http://stackoverflow.com/questions/1052476/can-someone-explain-scalas-yield/1052510#1052510 diff --git a/_tutorials/partest-guide.md b/_tutorials/partest-guide.md new file mode 100644 index 0000000000..d5be81bfa5 --- /dev/null +++ b/_tutorials/partest-guide.md @@ -0,0 +1,66 @@ +--- +layout: page +title: Running the Test Suite +--- + +Partest is a custom parallel testing tool that we use to run the test suite for the Scala compiler and library. Go the scala project folder from your local checkout and run it via `ant` or standalone as follows. + +## Using ant + +The test suite can be run by using ant from the command line: + + $ ant test.suite + +## Standalone + +There are launch scripts `partest` and `partest.bat` in the `test` folder of the scala project. To have partest run failing tests only and print details about test failures to the console, you can use + + ./test/partest --show-diff --show-log --failed + +You can get a summary of the usage by running partest without arguments. + +* Most commonly you want to invoke partest with an option that tells it which part of the tests to run. For example `--all`, `--pos`, `--neg` or `--run`. +* You can test individual files by specifying individual test files (`.scala` files) as options. Several files can be tested if they are from the same category, e.g., `pos`. +* You can enable output of log and diff using the `-show-log` and `-show-diff` options. +* If you get into real trouble, and want to find out what partest does, you can run it with option `--verbose`. This info is useful as part of bug reports. +* Set custom path from where to load classes: `-classpath ` and `-buildpath `. +* You can use the `SCALAC_OPTS` environment variable to pass command line options to the compiler. +* You can use the `JAVA_OPTS` environment variable to pass command line options to the runner (e.g., for `run/jvm` tests). +* The launch scripts run partest as follows: + + scala -cp scala.tools.partest.nest.NestRunner + + Partest classes from a `quick` build, e.g., can be found in `./build/quick/classes/partest/`. + + Partest will tell you where it loads compiler/library classes from by adding the `partest.debug` property: + + scala -Dpartest.debug=true -cp scala.tools.partest.nest.NestRunner + + + +## ScalaCheck tests + +Tests that depend on [ScalaCheck](https://github.com/rickynils/scalacheck) can be added under folder `./test/files/scalacheck`. A sample test: + + import org.scalacheck._ + import Prop._ + + object Test { + val prop_ConcatLists = property{ (l1: ListInt, l2: ListInt) => + l1.size + l2.size == (l1 ::: l2).size + } + + val tests = List(("prop_ConcatLists", prop_ConcatLists)) + } + +## Troubleshooting + +### Windows + +Some tests might fail because line endings in the `.check` files and the produced results do not match. In that case, set either + + git config core.autocrlf false + +or + + git config core.autocrlf input diff --git a/_tutorials/scala-for-csharp-programmers.md b/_tutorials/scala-for-csharp-programmers.md new file mode 100644 index 0000000000..b61a8b66d6 --- /dev/null +++ b/_tutorials/scala-for-csharp-programmers.md @@ -0,0 +1,1236 @@ +--- +layout: overview +title: A Scala Tutorial for C# Programmers + +discourse: true +--- + +By Ivan Towlson + +## Introduction + +Scala is a hybrid of functional and object-oriented languages. +Its functional aspects make it very expressive when writing algorithmic +code, and play nicely with the brave new world of concurrency; its +object-oriented aspects keep it familiar and convenient when creating +business objects or other stateful models. + +## The same concepts + +Scala shares many features and concepts with C#, as well as introducing +many that are new. In fact, some of the capabilities that people talk +about a lot in Scala introductions or Java-to-Scala guides are very +familiar to C# programmers. For example, as a C# programmer, you don't +need to be gently walked through the idea of function types -- you've +been using delegates and lambdas for years. + +However, some Scala features and behaviours are quite different from C#, +and even those which are common usually have different syntax or work +in slightly different ways. So let's start out by covering some Scala +basics from a C# programmer's point of view. + +### Classes + +The basic concept of classes is the same in Scala as in C#. A class +bundles up a bunch of state (member fields) and hides it behind an +interface (member methods). The syntax for declaring classes is just +like C#: + + class Widget { + } + +### Fields + +The syntax for declaring fields is like this: + + class Widget { + val serialNumber = 123 + private var usageCount = 0 + } + +You can probably figure out what's going on here, but let's just note +a few differences from C#: + +* Fields are public by default, rather than private by default. +* You don't need to put a semicolon after a field declaration. You can + write semicolons if you want (or if your muscle memory makes you), + but if you don't, Scala will figure it out for you. +* Scala automatically figures out the type of the field from its initial + value, just like the C# `var` keyword. (Don't be fooled by the appearance + of the second field though -- the Scala `var` keyword doesn't mean the + same thing as the C# `var` keyword.) + +Now, why is one of these fields introduced as `val` and the other as +`var`? In C#, fields are mutable by default. That is, by default, +any code that can access a field can modify it. You can specify *readonly* +to make a field immutable, so that it can't be changed once the object +has been constructed. + +Scala doesn't have a default setting for mutability. You have to engage +brain, and decide whether each field is mutable or immutable. `val` means +a field is immutable (readonly in C#) and `var` means it's mutable +(a normal field in C#). + +So the class above is equivalent to the C#: + + class Widget { + public readonly int serialNumber = 123; + private int usageCount = 0; + } + +Notice that C# makes you write extra code to make things immutable, +and Scala doesn't. This may seem like a small thing, but it's +going to be a really important theme. + +### Methods + +The syntax for declaring methods is like this: + + class Widget { + def reticulate(winding: Int): String = "some value" + } + +This is a more dramatic departure from C# so let's pick it apart. + +The `def` keyword means we're declaring a method rather than a field. +There isn't a direct equivalent to this in C#, which figures out whether +something is a method or a field from context. As with fields, +methods are public by default. + +The method name is reassuringly language-independent. + +Method arguments go in brackets after the method name, just as in C#. +However, the way Scala specifies the argument types is different from C#. +In C# you would write `int winding`; in Scala you write `winding: Int`. + +This is a general principle: in Scala, when you want to specify the +type of something, you write the type after the something, separated +by a colon. (Whereas in C# you write the type before the something, +separated by a space.) + +You can see the same principle in action for the return type of the +function. `reticulate` returns a `String`. + +Finally, the body of the method has been placed after an equals sign, +rather than inside braces. (Braces are only necessary when the method +body consists of multiple expressions/statements.) What's more, +the body of the method is an expression -- that +is, something with a value -- rather than a set of statements amongst which +is a `return`. I'll come back to this when we look at a more realistic +example, but for the time being, let's focus on the trivial example and +translate it into C# syntax: + + class Widget { + public string Reticulate(int winding) { + return "some value"; + } + } + +### Classes and Structs + +In C#, when you define a type, you can decide whether to make it a +reference type (a class) or a value type (a struct). Scala doesn't +allow you to create custom value types. It has only classes, not +structs. This restriction comes from Scala targeting the Java +Virtual Machine. Unlike .NET, the JVM doesn't really have the concept +of value types. Scala tries to disguise this as best it can, but the +lack of custom value types is one place where the implementation +leaks through. Fortunately, Scala makes it easy to define immutable +reference types, which are nearly as good. + +### Base Types + +Scala's base types are pretty much the same as C#'s, except that they +are named with initial capitals, e.g. `Int` instead of `int`. (In fact +every type in Scala starts with an uppercase letter.) There are +no unsigned variants, and booleans are called `Boolean` instead of `bool`. + +Scala's name for `void` is `Unit`, but unlike `void`, `Unit` is a real type. +We'll see why this is important in a moment. + +### Function Types + +In C#, you can have variables that refer to functions instead of data. +These variables have delegate types, such as *Predicate` or +`Func` or `KeyEventHandler` or `Action`. + +Scala has the same concept, but the function types are built into the +language, rather than being library types. Function types are +spelled `(T1, T2, ...) => TR`. For example, a predicate of integers +would be type `(Int) => Boolean`. If there is only one input type, +the parens can be left out like this: `Int => Boolean`. + +Effectively, Scala gets rid of all those weird custom delegate types +like `Predicate` and `Comparer` and has a single family of +function types like the C# `Func<...>` family. + +What if you want to refer to a method that doesn't have a return value? +In C#, you can't write `Func` because void isn't a valid +type; you have to write `Action` instead. But Scala doesn't have +different families of delegate types, it just has the built-in +function types. Fortunately, in Scala, `Unit` is a real type, so you +can write `(Int) => Unit` for a function which takes an integer +and doesn't return a useful value. This means you can pass `void` methods +interchangeably with non-`void` methods, which is a Good Thing. + +### Implementing Methods + +I showed a method above, but only to illustrate the declaration syntax. +Let's take a look at a slightly more substantial method. + + def power4(x: Int): Int = { + var square = x * x // usually wouldn't write this - see below + square * square + } + +This looks pretty similar to C#, except that: + +* We're allowed to leave out semicolons, as mentioned above. +* We don't need a return statement. The method body consists of + an expression, square * square, with some preliminary bits + to define the components of the expression. + The return value of the method is the value of the expression. + +In order to make this method look like C#, I used the `var` keyword +to declare the local variable `square`. But as with fields, the +Scala `var` keyword doesn't work quite the same as the C# `var` keyword: + +In C#, `var` means "work out the type of the variable from the +right hand side". But Scala does that automatically -- you don't +need to ask for it. In Scala, `var` means "allow me to change this +variable (or field) after initialisation". As with fields, you can +also use `val` to mean 'don't allow me to change this variable +after initialisation.' And in fact since we don't need to change +`square` after initialisation, we'd be better off using val: + + def power4(x: Int): Int = { + val square = x * x // val not var + square * square + } + +Incidentally, if you do ever want to explicitly indicate the type +of a variable, you can do it the same way as with function arguments: + + def power4(x: Int): Int = { + val square : Int = x * x + square * square + } + +Notice that you still need `val` or `var`. Telling Scala the type +of the variable is independent of deciding whether the variable should +be mutable or immutable. + +### Tuples + +Everybody hates out parameters. We all know that code like this just +isn't nice: + + Widget widget; + if (widgetDictionary.TryGetValue(widgetKey, out widget)) + { + widget.ReticulateSplines(); + } + +And once you start composing higher-level functions into the mix, it gets +positively nasty. Suppose I want to make a HTTP request. Well, that's +going to produce two outputs in itself, the success code and the response +data. But now suppose I want to time it. Writing the timing code isn't a +problem, but now I have *three* outputs, and to paraphrase *Was Not Was*, +I feel worse than Jamie Zawinski. + +You can get around this in specific situations by creating custom types +like `DictionaryLookupResult` or `TimedHttpRequestResult`, but eventually +the terrible allure wears off, and you just want it to work. + +Enter tuples. A tuple is just a small number of values -- a single value, +a pair of values, a triplet of values, that sort of thing. You can tell +that tuples were named by mathematicians because the name only makes sense +when you look at the cases you hardly ever use (quadruple, quintuple, +sextuple, etc.). Using tuples, our timed HTTP request might look like this: + + public Tuple Time(Func> action) + { + StartTimer(); + var result = action(); + TimeSpan howLong = StopTimer(); + return Tuple.Create(result.First, result.Second, howLong); + } + + var result = Time(() => MakeRequest(uri)); + var succeeded = result.First; + var response = result.Second; + var howLong = result.Third. + Console.WriteLine("it took " + howLong); + +The reason this keeps getting verbose on us is that C# doesn’t provide any +syntatical support for tuples. To C#, a `Tuple<>` is just another generic +type. To us, the readers, a `Tuple<>` is just another generic type with +particularly unhelpful member names. + +Really, what we're really trying to articulate by returning a `Tuple<>` is, +"this method has several outputs." So what do we want to do with those +outputs? We want to access them, for example to stash them in variables, +without having to construct and pick apart the tuple one value at a time. +That means the language has to provide some kind of syntax-level support +for tuples, instead of treating them like every other class the compiler +doesn’t know about. + +Many functional languages have exactly this kind of syntactical support, +and Scala is no exception. Here’s how the above pseudo-C# looks in Scala: + + def time(action: => (Boolean, Stream)): (Boolean, Stream, TimeSpan) = { + startTimer() + val (succeeded, response) = action + (succeeded, response, stopTimer()) + } + + val (succeeded, response, timeTaken) = time(makeRequest) + Console.WriteLine("it took " + timeTaken) + +Notice the multiple variables on the left hand side of the time call? +Notice the `(Boolean, Stream, TimeSpan)` return type of the time method? +That return value is effectively a tuple type, but instead of having to +capture the returned tuple in a `Tuple<>` variable and extract its various +bits by hand, we are getting the Scala compiler to (in the time function) +compose the tuple implicitly for us, without us having to write the +constructor call, and (in the calling code) unpick the tuple into +meaningful, named variables for us without us having to explicitly copy +the values one by one and by name. + +(By the way, a proper implementation of the `time()` method wouldn’t be +restricted to `(Boolean, Stream)` results: we’d be looking to write a +method that could time anything. I’ve skipped that because it would +distract from the point at hand.) + +How would this play with the dictionary example? + + val (found, widget) = widgetDictionary.getValue(key) + if (found) + widget.reticulateSplines() + +We don’t actually save any lines of code, what with having to now capture +the “found” value into a variable and test it separately; and it’s not as +if the original C# version was horribly unreadable anyway. So maybe this is +a matter of taste, but I find this a lot easier to read and to write: all +the outputs are on the left of the equals sign where they belong, instead +of being spread between the assignment result and the parameter list, and +we don’t have that odd Widget declaration at the top. + +## New and different concepts + +Scala's primary platform is the Java virtual machine, and some of the +interest in Scala comes from Java programmers' interest in features such +as type inference, comprehensions and lambdas, with which C# programmers +are already familiar. So what's left that might be of interest +specifically to C# programmers? + +### Mixins and Traits + +#### Motivation + +Interfaces in C# and Java play a dual role. +First, they are a set of capabilities that an implementer has to, well, +implement. Second, they are a feature set that a client can use. + +These two roles can be conflicting: The first means that interfaces want +to be minimal, so that implementers don't have to implement a whole lot +of superfluous and redundant guff. The second means that interfaces want +to be maximal, so that clients don't have to clog themselves up with +boilerplate utility methods. + +Consider, for example, `IEnumerable` (and its sister interface `IEnumerator`). +This is a very minimal interface: implementers just need to be able to +produce values in sequence. But this minimalism means that clients of +`IEnumerable` need to write the same old boilerplate again and again and +again: foreach loops to filter, foreach loops to call a method on each +element of the sequence, foreach loops to aggregate, foreach loops to +check whether all elements meet a criterion, or to find the first member +that meets a criterion, or... + +This is frustrating because the implementations of "filter," "apply", +"aggregate," and so on are always the same. Of course, we could put +these methods into concrete types (`List` includes several), but then +those concrete types will contain duplicate code, and users who only have +an `IEnumerable` will still miss out. And yet we can't put these methods +into the interface because then every implementer of `IEnumerable` would +have to implement them -- and they'd end up writing the same boilerplate, +just now in all the zillions of `IEnumerable` classes instead of their clients. + +#### The C# and Scala Solutions + +We could resolve this tension if we had a way for interfaces to contain +implementation: for example, if `IEnumerable` required the implementer +to provide the class-specific iteration functionality, but then provided +the standard implementations of "filter," "apply", "aggregate" and so on +automatically: + + public pseudo_interface IEnumerable + { + IEnumerator GetEnumerator(); // must be implemented + IEnumerable Filter(Predicate predicate) // comes for free + { + foreach (object o in this) + if (predicate(o)) + yield return o; + } + } + +C# 3 addresses this using extension methods: the methods mentioned above +are all in fact included as extension methods on `IEnumerable` as +part of LINQ. + +This has some advantages over the approach described above: specifically, +the "standard methods" aren't bound up in the interface, so you can add +your own methods instead of being limited to the ones that the interface +author has included. + +On the other hand, it means that method implementations have to be packaged +in a different class from the interface, which feels less than modular. + +Scala takes a different approach. A Scala trait can contain a mix of +abstract methods without implementation as well as concrete methods with +an implementation. (It can also be a pure interface, with only abstract +members.) + +Here's a Scala trait that represents objects that can be compared +and ordered: + + trait Ord { + def < (that: Any): Boolean + def <=(that: Any): Boolean = (this < that) || (this == that) + def > (that: Any): Boolean = !(this <= that) + def >=(that: Any): Boolean = !(this < that) + } + +Orderable objects can extend `Ord`, but only need to implement the +method `<`. They then get the other operators for free, implemented +automatically by Ord in terms of `<`. + + class Date extends Ord { + def < (that: Any): Boolean = /* implementation */ + } + + // can now write: myDate >= yourDate + +A similar trait, called `Ordered` already exists in Scala, so there is no +need to actually define my trait. + +#### Scala Traits vs. C# Extension Methods + +Okay, so Scala has a different way of packaging standard implementations +from C#'s extension methods. It's different, but why is it interesting? +Well, there are a couple of things that you can do with Scala traits that +don't fall nicely out of the extension methods approach. + +First, you can override the default implementations of trait members, +to take advantage of additional information or capabilities available +in the implementing type. + +Let's look at another `IEnumerable` example, recast as a Scala trait: + + trait Enumerable { + def getEnumerator(): Enumerator + def count: Int = { + // Shockingly bad style; for illustrative purposes only + var c = 0 + val e = getEnumerator() + while (e.moveNext()) c = c + 1 + c + } + } + +This (ignoring style issues for now) is the only fully general +implementation we can provide for count: it will work with any `Enumerable`. +But for collections that know their sizes, such as `arrays` or `List`, +it's gruesomely inefficient. It iterates over the entire collection, +counting elements one by one, when it could just consult the `size` member +and return that. + +Let's fix that: + + class MyList extends Enumerable { + private var _size; + def getEnumerator(): Enumerator = /* ... */ + override def count: Int = _size + } + +The `count` member of the `Enumerable` trait works like a virtual method: +it can be overridden in classes which implement/derive from `Enumerable`. + +Compare this to the `Count()` extension method on `IEnumerable` in LINQ. +This achieves the same effect by trying to cast to `ICollection`, which is +fine as far as it goes but isn't extensible. + +Suppose you create an enumerable class that can count itself quickly but +isn't a collection -- for example a natural numbers range object. +With a Scala trait, the `NumberRange` type could provide an efficient +override of `count`, just like any other virtual method; with C# extension +methods, `Enumerable.Count()` would have to somehow know about the +`NumberRange` type in advance, or fall back on counting elements one by one. + +Second, with Scala you can choose a trait implementation when you +instantiate an object, rather than having it baked in at the class level +once and for all. This is called mixin-composition. + +Suppose you're creating a `MyList` instance, but you want it to puff itself +up to look bigger so as to frighten other `MyList` instances off its territory. +(This example would probably work better with fish, but we're stuck with +`Enumerable`s now. Work with me here.) In C#, you'd need to create a +`PuffedUpMyList` class and override the `Count` property. +In Scala, you can just mix in a `PuffedUp` version of the trait: + + trait PuffedUp extends Enumerable { + override def count: Int = super.count + 100 + } + + val normal = new MyList + Console.WriteLine(normal.count) // meh + val puffedUp = new MyList with PuffedUp + Console.WriteLine(puffedUp.count) // eek! + +As you can imagine this gives us much better granularity and composability +of traits and implementations than we get from the extension methods +approach, or indeed from single implementation inheritance type systems +in general. + +So Scala traits have some distinct advantages over extension methods. +The only downside appears to be the inability for clients to add their +own methods to a trait after the fact. + +Fortunately, you can work around this in Scala using so-called implicit +conversions. They enable Scala programmers to enrich existing types with new +functionality. + +### Singletons + +In C#, if you want to create a singleton object, you have to create a class, +then stop evildoers creating their own instances of that class, then create +and provide an instance of that class yourself. + +While this is hardly a Burma Railway of the programming craft, it does +feel like pushing against the grain of the language. Nor is it great for +maintainers, who have to be able to recognise a singleton by its pattern +(private constructor, public static readonly field, ...), or for clients, +who have to use a slightly clumsy multipart syntax to refer to the +singleton (e.g. `Universe.Instance`). + +What would be easier for all concerned would be if you could just declare +objects *as* singletons. That is, instead of writing class `Universe` and a +`public static readonly` instance of it, you could just write `object Universe`. + +And that's exactly what Scala allows you to do: + + object Universe { + def contains(obj: Any): Boolean = true + } + + val v = Universe.contains(42) + +What's going on behind the scenes here? It pretty much goes without saying +that the Scala compiler is creating a new type for the singleton object. +In fact it creates two types, one for the implementation and one for the +interface. The interface looks like a .NET static class (actually, the +.NET 1.x equivalent, a sealed class with only static members). +Thus, a C# program would call the example above as `Universe.contains(42)`. + +Singleton objects are first-class citizens in Scala, so they can for +example derive from classes. This is a nice way of creating special values +with custom behaviour: you don't need to create a whole new type, you just +define an instance and override methods in it: + + abstract class Cat { + def humiliateSelf() + } + + object Slats extends Cat { + def humiliateSelf() { savage(this.tail) } + } + +Obviously this is a frivolous example, but "special singletons" turn out to +be an important part of the functional idiom, for example for bottoming out +recursion. *Scala by Example (PDF)* describes an implementation of a Set class +which is implemented as a tree-like structure ("left subset - member - right +subset"), and methods such as `contains()` work by recursing down to the +child sets. For this to work requires an `EmptySet` whose implementation +(state) and behaviour are quite different from non-empty sets -- e.g. +`contains()` just returns `false` instead of trying to delegate to +non-existent child sets. Since `EmptySet` is logically unique it is both +simpler and more efficient to represent it as a singleton: i.e. to declare +`object EmptySet` instead of `class EmptySet`. + +In fact the whole thing can become alarmingly deep: *Scala by Example* +also includes a description of `Boolean` as an `abstract class`, and +`True` and `False` as singleton objects which extend `Boolean` and provide +appropriate implementations of the `ifThenElse` method. + +And fans of Giuseppe Peano should definitely check out the hypothetical +implementation of `Int`... + +### Pass by Name + +> You're only on chapter 3 and you're already reduced to writing about +> *calling conventions*? You suck! Do another post about chimney sweeps +> being hunted by jars of marmalade!" + +Silence, cur. Pass by name is not as other calling conventions are. +Pass by name, especially in conjunction with some other rather +theoretical-sounding Scala features, is your gateway to the wonderful +world of language extensibility. + +#### What is Passing By Name? + +First, let's talk about what we mean by *calling convention*. A calling +convention describes how stuff gets passed to a method by its caller. +In the good old days, this used to mean exciting things like which +arguments got passed in registers and who was responsible for resetting +the stack pointer. Sadly, the days of being able to refer to "naked fun +calls" are consigned to history: In modern managed environments, the +runtime takes care of all this guff and the main distinction is pass +data by value or by reference. (The situation on the CLR is slightly +complicated by the need to differentiate passing values by value, values +by reference, references by value and references by reference, but I'm +not going to go into that because (a) it's irrelevant to the subject at +hand and (b) that's +[Jon Skeet](http://www.yoda.arachsys.com/csharp/parameters.html)'s turf +and I don't want him to shank me. Again.) + +In *pass by value*, the called method gets a copy of whatever the caller +passed in. Arguments passed by value therefore work like local variables +that are initialised before the method runs: when you do anything to them, +you're doing it to your own copy. + +In *pass by reference*, the called method gets a reference to the caller's +value. When you do anything to a pass-by-reference argument, you're doing +it to the caller's data. + +In *pass by name*, the called method gets... well, it's a bit messy to +explain what the called method gets. But when the called method does +anything to the argument, the argument gets evaluated and the "anything" +is done to that. Crucially, evaluation happens every time the argument +gets mentioned, and only when the argument gets mentioned. + +#### Not Just Another Calling Convention + +Why does this matter? It matters because there are functions you can't +implement using pass by value or pass by reference, but you can implement +using pass by name. + +Suppose, for example, that C# didn't have the `while` keyword. +You'd probably want to write a method that did the job instead: + + public static void While(bool condition, Action body) + { + if (condition) + { + body(); + While(condition, body); + } + } + +What happens when we call this? + + long x = 0; + While(x < 10, () => x = x + 1); + +C# evaluates the arguments to `While` and invokes the `While` method with +the arguments `true` and `() => x = x + 1`. After watching the CPU sit +on 100% for a while you might check on the value of `x` and find it's +somewhere north of a trillion. *Why?* Because the condition argument was +*passed by value*, so whenever the `While` method tests the value of +condition, it's always `true`. The `While` method doesn't know that +condition originally came from the expression `x < 10`; all `While` knows +is that condition is `true`. + +For the `While` method to work, we need it to re-evaluate `x < 10` every +time it hits the condition argument. While needs not the value of the +argument at the call site, nor a reference to the argument at the call +site, but the actual expression that the caller wants it to use to generate +a value. + +Same goes for short-circuit evaluation. If you want short-circuit +evaluation in C#, your only hope if to get on the blower to Anders +Hejlsberg and persuade him to bake it into the language: + + bool result = (a > 0 && Math.Sqrt(a) < 10); + double result = (a < 0 ? Math.Sqrt(-a) : Math.Sqrt(a)); + +You can't write a function like `&&` or `?:` yourself, because C# will +always try to evaluate all the arguments before calling your function. + +Consider a VB exile who wants to reproduce his favourite keywords in C#: + + bool AndAlso(bool condition1, bool condition2) + { + return condition1 && condition2; + } + + T IIf(bool condition, T ifTrue, T ifFalse) + { + if (condition) + return ifTrue; + else + return ifFalse; + } + +But when C# hits one of these: + + bool result = AndAlso(a > 0, Math.Sqrt(a) < 10); + double result = IIf(a < 0, Math.Sqrt(-a), Math.Sqrt(a)); + +it would try to evaluate all the arguments at the call site, and pass the +results of those evaluations to `AndAlso` or `IIf`. There's no +short-circuiting. So the `AndAlso` call would crash if a were negative, +and the `IIf` call if a were anything other than 0. Again, what you want is +for the `condition1`, `condition2`, `ifTrue` and `ifFalse` arguments to be +evaluated by the callee if it needs them, not for the caller to evaluate +them before making the call. + +And that's what *pass by name* does. A parameter passed by name is not +evaluated when it is passed to a method. It is evaluated -- and +re-evaluated -- when the called method evaluates the parameter; +specifically when the called method requests the value of the parameter by +mentioning its name. This might sound weird and academic, but it's the key +to being able to define your own control constructs. + +#### Using Pass By Name in Scala + +Let's see the custom while implementation again, this time with Scala +*pass by name* parameters: + + def myWhile(condition: => Boolean)(body: => Unit): Unit = + if (condition) { + body + myWhile(condition)(body) + } + +We can call this as follows: + + var i = 0 + myWhile (i < 10) { + println(i) + i += 1 + } + +Unlike the C# attempt, this prints out the numbers from 0 to 9 and then +terminates as you'd wish. + +Pass by name also works for short-circuiting: + + import math._ + + def andAlso(condition1: => Boolean, condition2: => Boolean): Boolean = + condition1 && condition2 + + val d = -1.234 + val result = andAlso(d > 0, sqrt(d) < 10) + +The `andAlso` call returns `false` rather than crashing, because +`sqrt(d) < 10` never gets evaluated. + +What's going on here? What's the weird colon-and-pointy-sticks syntax? +What is actually getting passed to `myWhile` and `andAlso` to make this work? + +The answer is a bit surprising. Nothing is going on here. This is the +normal Scala function parameter syntax. There is no *pass by name* in Scala. + +Here's a bog-standard *pass by value* Scala function declaration: + + def myFunc1(i: Int): Unit = ... + +Takes an integer, returns void: easy enough. Here's another: + + def myFunc2(f: Int => Boolean): Unit = ... + +Even if you've not seen this kind of expression before, it's probably not +too hard to guess what this means. This function takes a *function from +`Int` to `Boolean`* as its argument. In C# terms, +`void MyFunc2(Func f)`. We could call this as follows: + + myFunc2 { (i: Int) => i > 0 } + +So now you can guess what this means: + + def myFunc3(f: => Boolean) : Unit = ... + +Well, if `myFunc2` took an *Int-to-Boolean* function, `myFunc3` must be +taking a "blank-to-Boolean" function -- a function that takes no arguments +and returns a `Boolean`. In short, a conditional expression. So we can +call `myFunc3` as follows: + + val j = 123 + myFunc3 { j > 0 } + +The squirly brackets are what we'd expect from an anonymous function, and +because the function has no arguments Scala doesn't make us write +`{ () => j > 0 }`, even though that's what it means really. The anonymous +function has no arguments because `j` is a captured local variable, not an +argument to the function. But there's more. Scala also lets us call +`myFunc3` like this: + + val j = 123 + myFunc3(j > 0) + +This is normal function call syntax, but the Scala compiler realises that +`myFunc3` expects a nullary function (a function with no arguments) rather +than a `Boolean`, and therefore treats `myFunc3(j > 0)` as shorthand for +`myFunc3(() => j > 0)`. This is the same kind of logic that the C# compiler +uses when it decides whether to compile a lambda expression to a delegate +or an expression tree. + +You can probably figure out where it goes from here: + + def myFunc4(f1: => Boolean)(f2: => Unit): Unit = ... + +This takes two functions: a conditional expression, and a function that +takes no arguments and returns no value (in .NET terms, an `Action`). +Using our powers of anticipation, we can imagine how this might be called +using some unholy combination of the two syntaxes we saw for calling +`myFunc3`: + + val j = 123; + myFunc4(j > 0) { println(j); j -= 1; } + +We can mix and match the `()` and `{}` bracketing at whim, except that we +have to use `{}` bracketing if we want to batch up multiple expressions. +For example, you could legally equally well write the following: + + myFunc4 { j > 0 } { println(j); j -= 1; } + myFunc4 { println(j); j > 0 } (j -= 1) + myFunc4 { println(j); j > 0 } { j -= 1 } + +And we'll bow to the inevitable by supplying a body for this function: + + def myFunc5(f1: => Boolean)(f2: => Unit): Unit = + if (f1()) { + f2() + myFunc5(f1)(f2) + } + +Written like this, it's clear that `f1` is getting evaluated each time we +execute the if statement, but is getting passed (as a function) when +`myFunc5` recurses. But Scala allows us to leave the parentheses off +function calls with no arguments, so we can write the above as: + + def myFunc5(f1: => Boolean)(f2: => Unit): Unit = + if (f1) { + f2 + myFunc5(f1)(f2) + } + +Again, type inference allows Scala to distinguish the *evaluation of +`f1`* in the if statement from the *passing of `f1`* in the `myFunc5` +recursion. + +And with a bit of renaming, that's `myWhile`. There's no separate +*pass by name* convention: just the usual closure behaviour of capturing +local variables in an anonymous method or lambda, a bit of syntactic sugar +for nullary functions (functions with no arguments), just like C#'s +syntactic sugar for property getters, and the Scala compiler's ability to +recognise when a closure is required instead of a value. + +In fact, armed with this understanding of the Scala "syntax," we can +easily map it back to C#: + + void While(Func condition, Action body) + { + if (condition()) + { + body(); + While(condition, body); + } + } + + int i = 0; + While(() => i < 10, () => + { + Console.WriteLine(i); + ++i; + }); + +The implementation of the `While` method in C# is, to my eyes, a bit +clearer than the Scala version. However, the syntax for *calling* the +`While` method in C# is clearly way more complicated and less natural than +the syntax for calling `myWhile` in Scala. Calling `myWhile` in Scala was +like using a native language construct. Calling While in C# required a +great deal of clutter at the call site to prevent C# from trying to treat +`i < 10` as a once-and-for-all value, and to express the body at all. + +So that's so-called "pass by name" demystified: The Scala Web site, with +crushing mundanity, demotes it to "automatic type-dependent closure +construction," which is indeed exactly how it works. As we've seen, +however, this technical-sounding feature is actually essential to +creating nice syntax for your own control constructs. We'll shortly see +how this works together with other Scala features to give you even more +flexibility in defining your construct's syntax. + +### Implicits + +Scala implicits offer some features which will be familiar to the C# +programmer, but are much more general in nature and go far beyond what can +be done in C#. + +#### Enriching types in C# and Scala + +Scala, like C#, is statically typed: a class’ methods are compiled into the +class definition and are not open for renegotiation. You cannot, as you +might in Ruby or Python, just go ahead and declare additional methods on an +existing class. + +This is of course very inconvenient. You end up declaring a load of +`FooHelper` or `FooUtils` classes full of static methods, and having to +write verbose calling code such as `if (EnumerableUtils.IsEmpty(sequence))` +rather than the rather more readable `if (sequence.IsEmpty())`. + +C# 3 tries to address this problem by introducing extension methods. +Extension methods are static methods in a `FooHelper` or `FooUtils` kind +of class, except you’re allowed to write them using member syntax. +By defining `IsEmpty` as an extension method on `IEnumerable`, you can +write `if (sequence.IsEmpty())` after all. + +Scala disapproves of static classes and global methods, so it plumps for +an alternative approach. You’ll still write a `FooHelper` or `FooUtils` +kind of class, but instead of taking the `Foo` to be Helped or Utilised as +a method parameter, your class will wrap `Foo` and enrich it with +additional methods. Let’s see this in action as we try to add a method to +the `Double` type: + + class RicherDouble(d : Double) { + def toThe(exp: Double): Double = System.Math.Pow(d, exp) + } + +(We call the class `RicherDouble` because Scala already has a `RichDouble` +class defined which provides further methods to `Double`.) + +Notice that `toThe` is an instance method, and that `RicherDouble` takes a +`Double` as a constructor parameter. This seems pretty grim, because we’d +normally have to access the function like this: + + val result = new DoubleExtensions(2.0).toThe(7.0) + +Hardly readable. To make it look nice, Scala requires us to define an +*implicit conversion* from `Double` to `RicherDouble`: + + object Implicits { + implicit def richerDouble(d: Double) = new RicherDouble(d) + } + +and to bring that implicit conversion into scope: + + import Implicits._ + +Now we can write this: + + val twoToTheSeven = 2.0.toThe(7.0) + +and all will be well. The `Double` type has apparently been successfully +enriched with the `toThe` method. + +This is, of course, just as much an illusion as the C# equivalent. +C# extension methods don’t add methods to a type, and nor do Scala +implicit conversions. What has happened here is that the Scala compiler +has looked around for implicit methods that are applicable to the type of +`2.0` (namely `Double`), and return a type that has a `toThe` method. +Our `Implicits.richerDouble` method fits the bill, so the Scala compiler +silently inserts a call to that method. At runtime, therefore, Scala calls +`Implicits.richerDouble(2.0)` and calls the `toThe` of the resulting +`RicherDouble`. + +If setting this up seems a bit verbose, well, maybe. C# extension methods +are designed to be easily – one might even say implicitly – brought into +scope. That’s very important for operators like the LINQ standard query +operators, but it can result in unwanted extension methods being dragged +into scope and causing havoc. Scala requires the caller to be a bit more +explicit about implicits, which results in a slightly higher setup cost but +gives the caller finer control over which implicit methods are considered. + +But as it happens you can avoid the need for separate definitions of +`Implicits` and `RicherDouble`, and get back to a more concise +representation by using an anonymous class. (As you’d expect, Scala +anonymous classes are fully capable, like Java ones, rather than the +neutered C# version.) Here’s how it looks: + + object Implicits { + implicit def doubleToThe(d1 : Double) = new { + def toThe(d2 : Double) : Double = Math.Pow(d1, d2) + } + } + +Well, big deal. Scala can enrich existing types with new methods just like +C#, but using a different syntax. In related news, Lisp uses a different +kind of bracket: film at eleven. Why should we be interested in Scala +implicits if they’re just another take on extension methods? + +#### Implicit Parameters + +What we saw above was an implicit method – a method which, like a C# +implicit conversion operator, the compiler is allowed to insert a call to +without the programmer writing that call. Scala also has the idea of +implicit parameters – that is, parameters which the compiler is allowed to +insert a value for without the programmer specifying that value. + +That’s just optional parameters with default values, right? Like C++ and +Visual Basic have had since “visual” meant ASCII art on a teletype, and +like C# is about to get? Well, no. + +C++, Visual Basic and C# optional parameters have fixed defaults specified +by the called function. For example, if you have a method like this: + + public void Fie(int a, int b = 123) { … } + +and you call `Fie(456)`, it’s always going to be equivalent to calling +`Fie(456, 123)`. + +A Scala implicit parameter, on the other hand, gets its value from the +calling context. That allows programmer calling the method to control the +implicit parameter value, creating an extensibility point that optional +parameters don’t provide. + +This probably all sounds a bit weird, so let’s look at an example. Consider +the following `Concatenate` method: + + public T Concatenate(IEnumerable sequence, T seed, Func concatenator); + +We pass this guy a sequence, a start value and a function that combines two +values into one, and it returns the result of calling that function across +the sequence. For example, you could pass a sequence of strings, a start +value of `String.Empty`, and `(s1, s2) => s1 + s2`, and it would return you +all the strings concatenated together: + + IEnumerable sequence = new string[] { “mog”, “bites”, “man” }; + string result = Concatenate(sequence, String.Empty, (s1, s2) => s1 + s2); + // result is “mogbitesman” + +But this is a unpleasantly verbose. We’re having to pass in `String.Empty` +and `(s1, s2) => s1 + s2` every time we want to concatenate a sequence of +strings. Not only is this tedious, it also creates the opportunity for +error when the boss decides to “help” and passes the literal +`"String.Empty"` as the seed value instead. (“Oh, and I upgraded all the +semi-colons to colons while I was in there. No, don’t thank me!”) We’d +like to just tell the Concatenate function, “Look, this is how you +concatenate strings,” once and for all. + +Let’s start out by redefining the `Concatenate` method in Scala. +I’m going to factor out the seed and the concatenator method into a trait +because we’ll typically be defining them together. + + trait Concatenator[T] { + def startValue: T + def concat(x: T, y: T): T + } + + object implicitParameters { + def concatenate[T](xs: List[T])(c: Concatenator[T]): T = + if (xs.isEmpty) c.startValue + else c.concat(xs.head, concatenate(xs.tail)(c)) + } + +We can call this as follows: + + object stringConcatenator extends Concatenator[String] { + def startValue: String = "" + def concat(x: String, y: String) = x.concat(y) + } + + object implicitParameters { + def main(args: Array[String]) = { + val result = concatenate(List("mog", "bites", "man"))(stringConcatenator) + println(result) + } + } + +So far, this looks like the C# version except for the factoring out of the +`Concatenator` trait. We’re still having to pass in the +`stringConcatenator` at the point of the call. Let’s fix that: + + def concatenate[T](xs: List[T])(implicit c: Concatenator[T]): T = + if (xs.isEmpty) c.startValue + else c.concat(xs.head, concatenate(xs.tail)) + +We’ve changed two things here. First, we’ve declared c to be an *implicit +parameter*, meaning the caller can leave it out. Second, we’ve left +it out ourselves, in the recursive call to `concatenate(xs.tail)`. + +Well, okay, it’s nice that `concatenate` now doesn’t have to pass the +`Concatenator` explicitly to the recursive call, but we’re still having to +pass in the `stringConcatenator` object to get things started. If only +there were some way to make the `stringConcatenator` object itself implicit… + + object Implicits { + implicit object stringConcatenator extends Concatenator[String] { + def startValue: String = "" + def concat(x: String, y: String) = x.concat(y) + } + } + +Again, we’ve done two things here. First, we’ve declared the +`stringConcatenator` object implicit. Consequently, we’ve had to move it +out of the top level, because Scala doesn’t allow implicits at the top +level (because they’d pollute the global namespace, being in scope even +without an explicit import statement). + +Now we can call `concatenate` like this: + + import Implicits._ + + object implicitParameters { + def main(args: Array[String]) = { + val result = concatenate(List("mog", "bites", "man")) + println(result) + } + } + +And we’ll still get “mogbitesman” as the output. + +Let’s review what’s going on here. The implicit parameter of concatenate +has been set to our `stringConcatenator`, a default value that the +`concatenate` method knew nothing about when it was compiled. This is +somewhere north of what classical optional parameters are capable of, +and we’re not finished yet. Let’s build a `listConcatenator`. + + object Implicits { + class ListConcatenator[T] extends Concatenator[List[T]] { + def startValue: List[T] = Nil + def concat(x: List[T], y: List[T]) = x ::: y + } + implicit object stringListConcatenator extends ListConcatenator[String] { } + } + +This is a bit vexing. `List` in Scala is a generic type, and has a generic +concatenation method called `:::`. But we can’t create a generic object, +because an object is an instance. And implicit parameters have to be objects. +So the best we can do is build a generic `ListConcatenator` class, and then +create trivial implicit objects for each generic parameter type we might +need. + +However, let’s not worry about the implementation, and see how this is used +at the calling end: + + val result = concatenate(List( + List("mog", "bites", "man"), + List("on", "beard") + )) + +This displays `List(mog, bites, man, on, beard)`; that is, it concatenates +the two `List[String]`s into one. Once again, we have not had to pass +`stringListConcatenator` explicitly: the Scala compiler has gone and found +it for us. We can use the exact same calling code to concatenate lists and +strings. + +#### Why Should I Care? + +Isn’t this pointless? At the call site, I have access to +`stringConcatenator` and `listStringConcatenator`. I can easily pass them +in rather than relying on spooky compiler magic to do it for me. +Aren’t implicit parameters just job security for compiler writers? + +Yes, implicit parameters are technically unnecessary. But if we’re going +to play that game, C# is technically unnecessary. You could write all that +code in IL. Extension methods are unnecessary because you could write the +static method out longhand. Optional parameters are unnecessary because +you could read the documentation and pass them in explicitly. +Post-It notes are unnecessary because you could fire up Outlook and create +a Note instead. + +Implicit parameters are about convenience and expressiveness. Implicit +parameters give you a way of describing how a function should handle +different situations, without needing to bake those situations into the +function logic or to specify them every time you call the function. +You don’t want to have to tell the `concatenate` function whether to use +the `List` or `String` concatenator every time you call it: the compiler +knows what you’re concatenating; specifying how to concatenate it just +gives you a chance to get it wrong! + +Consequently, implicit parameters – like implicit conversions – contribute +to Scala’s ability to support internal DSLs. By setting up appropriate +implicits, you can write code that reads much more naturally than if you +had to pepper it with function objects or callbacks. + +#### Conclusion + +Scala’s `implicit` keyword goes beyond C#’s equivalent. As in C#, it is +used for implicit conversions; unlike C#, this is the idiomatic way to add +operations to an existing type, removing the need for the separate +extension method syntax. Implicit parameters have no equivalent in C#. +They are like being able to add default values to a method: just as a C# +using statement bring implicit methods into scope, a Scala import statement +can bring default values into scope. If implicit conversions are a way of +extending classes, then implicit parameters are a way of extending methods, +creating simple, reliable shorthands for complex generic methods, and +making up another piece of the Scala DSL jigsaw. + +#### Method Call Syntax + +C#, like most object-oriented programming languages, is pretty strict about +how you call methods: you use the dot notation, unless the method is a +special ‘operator’ method such as `operator+`, `operator==` or a conversion +operator. The special operator methods are predefined by the compiler: you +can write your own implementation, but you can’t create your own operator +names. You can teach the `+` operator how to handle your custom type, but +you can’t add an exponentiation operator: + + int a = b ** c; + +C# has three problems with this: first, it doesn’t like the method name +`**`; second, it doesn’t like that there’s no `.` before the name; and +third, it doesn’t like that there’s no brackets around the method argument. + +To get around the objection to the name, let’s compromise and call it +`ToThe` for now. So what C# insists on seeing is `a.ToThe(b)`. + +Scala, like many functional languages, isn’t so strict. Scala allows you +to use any method with a single argument in an infix position. Before we +can see this in the exponentiation example, we will enrich the `Double` +type with the `toThe` method as we learned earlier: + + import Implicits._ + import math._ + + class RicherDouble(d: Double) { + def toThe(exp: Double): Double = pow(d, exp) + } + + object Implicits { + implicit def richerDouble(d: Double) = new RicherDouble(d) + } + +Recall that this is just the Scala idiom for extension methods – it’s the +equivalent of writing +`public static ToThe(this double first, double second) { … }` in C#. +(If we were wanting to use infix notation with our own class, we wouldn’t +need all this malarkey.) So now we can write: + + val raised = 2.0.toThe(7.0) + +Okay, so what do we need to do to get this to work in infix position? +Nothing, it turns out. + + val raised = 2.0 toThe 8.0 // it just works + +This still doesn’t look much like a built-in operator, but it turns out +Scala is less fussy than C# about method names too. + + class DoubleExtensions(d : Double) { + def **(exp: Double): Double = Pow(d, exp) + } + + val raised = 2.0 ** 9.0 // it still just works + +Much nicer. + +This sorta-kinda works for postfix operators too: + + class RicherString(s: String) { + def twice: String = s + s + } + + val drivel = "bibble" twice + +Calling methods in infix and postfix nodadion is obviously fairly simple +syntactic sugar over normal dot notation. But this seemingly minor feature +is very important in constructing DSLs, allowing Scala to do in internal +DSLs what many languages can do only using external tools. For example, +where most languages do parsing via an external file format and a tool to +translate that file format into native code (a la `lex` and `yacc`), +Scala’s parser library makes extensive use of infix and postfix methods to +provide a “traditional” syntax for describing a parser, but manages it +entirely within the Scala language. diff --git a/_tutorials/scala-for-java-programmers.md b/_tutorials/scala-for-java-programmers.md new file mode 100644 index 0000000000..b8618b431c --- /dev/null +++ b/_tutorials/scala-for-java-programmers.md @@ -0,0 +1,723 @@ +--- +layout: overview +title: A Scala Tutorial for Java Programmers +overview: scala-for-java-programmers + +discourse: true +multilingual-overview: true +languages: [es, ko, de, it, zh-tw] +--- + +By Michel Schinz and Philipp Haller + +## Introduction + +This document gives a quick introduction to the Scala language and +compiler. It is intended for people who already have some programming +experience and want an overview of what they can do with Scala. A +basic knowledge of object-oriented programming, especially in Java, is +assumed. + +## A First Example + +As a first example, we will use the standard *Hello world* program. It +is not very fascinating but makes it easy to demonstrate the use of +the Scala tools without knowing too much about the language. Here is +how it looks: + + object HelloWorld { + def main(args: Array[String]) { + println("Hello, world!") + } + } + +The structure of this program should be familiar to Java programmers: +it consists of one method called `main` which takes the command +line arguments, an array of strings, as parameter; the body of this +method consists of a single call to the predefined method `println` +with the friendly greeting as argument. The `main` method does not +return a value (it is a procedure method). Therefore, it is not necessary +to declare a return type. + +What is less familiar to Java programmers is the `object` +declaration containing the `main` method. Such a declaration +introduces what is commonly known as a *singleton object*, that +is a class with a single instance. The declaration above thus declares +both a class called `HelloWorld` and an instance of that class, +also called `HelloWorld`. This instance is created on demand, +the first time it is used. + +The astute reader might have noticed that the `main` method is +not declared as `static` here. This is because static members +(methods or fields) do not exist in Scala. Rather than defining static +members, the Scala programmer declares these members in singleton +objects. + +### Compiling the example + +To compile the example, we use `scalac`, the Scala compiler. `scalac` +works like most compilers: it takes a source file as argument, maybe +some options, and produces one or several object files. The object +files it produces are standard Java class files. + +If we save the above program in a file called +`HelloWorld.scala`, we can compile it by issuing the following +command (the greater-than sign `>` represents the shell prompt +and should not be typed): + + > scalac HelloWorld.scala + +This will generate a few class files in the current directory. One of +them will be called `HelloWorld.class`, and contains a class +which can be directly executed using the `scala` command, as the +following section shows. + +### Running the example + +Once compiled, a Scala program can be run using the `scala` command. +Its usage is very similar to the `java` command used to run Java +programs, and accepts the same options. The above example can be +executed using the following command, which produces the expected +output: + + > scala -classpath . HelloWorld + + Hello, world! + +## Interaction with Java + +One of Scala's strengths is that it makes it very easy to interact +with Java code. All classes from the `java.lang` package are +imported by default, while others need to be imported explicitly. + +Let's look at an example that demonstrates this. We want to obtain +and format the current date according to the conventions used in a +specific country, say France. (Other regions such as the +French-speaking part of Switzerland use the same conventions.) + +Java's class libraries define powerful utility classes, such as +`Date` and `DateFormat`. Since Scala interoperates +seemlessly with Java, there is no need to implement equivalent +classes in the Scala class library--we can simply import the classes +of the corresponding Java packages: + + import java.util.{Date, Locale} + import java.text.DateFormat + import java.text.DateFormat._ + + object FrenchDate { + def main(args: Array[String]) { + val now = new Date + val df = getDateInstance(LONG, Locale.FRANCE) + println(df format now) + } + } + +Scala's import statement looks very similar to Java's equivalent, +however, it is more powerful. Multiple classes can be imported from +the same package by enclosing them in curly braces as on the first +line. Another difference is that when importing all the names of a +package or class, one uses the underscore character (`_`) instead +of the asterisk (`*`). That's because the asterisk is a valid +Scala identifier (e.g. method name), as we will see later. + +The import statement on the third line therefore imports all members +of the `DateFormat` class. This makes the static method +`getDateInstance` and the static field `LONG` directly +visible. + +Inside the `main` method we first create an instance of Java's +`Date` class which by default contains the current date. Next, we +define a date format using the static `getDateInstance` method +that we imported previously. Finally, we print the current date +formatted according to the localized `DateFormat` instance. This +last line shows an interesting property of Scala's syntax. Methods +taking one argument can be used with an infix syntax. That is, the +expression + + df format now + +is just another, slightly less verbose way of writing the expression + + df.format(now) + +This might seem like a minor syntactic detail, but it has important +consequences, one of which will be explored in the next section. + +To conclude this section about integration with Java, it should be +noted that it is also possible to inherit from Java classes and +implement Java interfaces directly in Scala. + +## Everything is an Object + +Scala is a pure object-oriented language in the sense that +*everything* is an object, including numbers or functions. It +differs from Java in that respect, since Java distinguishes +primitive types (such as `boolean` and `int`) from reference +types, and does not enable one to manipulate functions as values. + +### Numbers are objects + +Since numbers are objects, they also have methods. And in fact, an +arithmetic expression like the following: + + 1 + 2 * 3 / x + +consists exclusively of method calls, because it is equivalent to the +following expression, as we saw in the previous section: + + (1).+(((2).*(3))./(x)) + +This also means that `+`, `*`, etc. are valid identifiers +in Scala. + +The parentheses around the numbers in the second version are necessary +because Scala's lexer uses a longest match rule for tokens. +Therefore, it would break the following expression: + + 1.+(2) + +into the tokens `1.`, `+`, and `2`. The reason that +this tokenization is chosen is because `1.` is a longer valid +match than `1`. The token `1.` is interpreted as the +literal `1.0`, making it a `Double` rather than an +`Int`. Writing the expression as: + + (1).+(2) + +prevents `1` from being interpreted as a `Double`. + +### Functions are objects + +Perhaps more surprising for the Java programmer, functions are also +objects in Scala. It is therefore possible to pass functions as +arguments, to store them in variables, and to return them from other +functions. This ability to manipulate functions as values is one of +the cornerstone of a very interesting programming paradigm called +*functional programming*. + +As a very simple example of why it can be useful to use functions as +values, let's consider a timer function whose aim is to perform some +action every second. How do we pass it the action to perform? Quite +logically, as a function. This very simple kind of function passing +should be familiar to many programmers: it is often used in +user-interface code, to register call-back functions which get called +when some event occurs. + +In the following program, the timer function is called +`oncePerSecond`, and it gets a call-back function as argument. +The type of this function is written `() => Unit` and is the type +of all functions which take no arguments and return nothing (the type +`Unit` is similar to `void` in C/C++). The main function of +this program simply calls this timer function with a call-back which +prints a sentence on the terminal. In other words, this program +endlessly prints the sentence "time flies like an arrow" every +second. + + object Timer { + def oncePerSecond(callback: () => Unit) { + while (true) { callback(); Thread sleep 1000 } + } + def timeFlies() { + println("time flies like an arrow...") + } + def main(args: Array[String]) { + oncePerSecond(timeFlies) + } + } + +Note that in order to print the string, we used the predefined method +`println` instead of using the one from `System.out`. + +#### Anonymous functions + +While this program is easy to understand, it can be refined a bit. +First of all, notice that the function `timeFlies` is only +defined in order to be passed later to the `oncePerSecond` +function. Having to name that function, which is only used once, might +seem unnecessary, and it would in fact be nice to be able to construct +this function just as it is passed to `oncePerSecond`. This is +possible in Scala using *anonymous functions*, which are exactly +that: functions without a name. The revised version of our timer +program using an anonymous function instead of *timeFlies* looks +like that: + + object TimerAnonymous { + def oncePerSecond(callback: () => Unit) { + while (true) { callback(); Thread sleep 1000 } + } + def main(args: Array[String]) { + oncePerSecond(() => + println("time flies like an arrow...")) + } + } + +The presence of an anonymous function in this example is revealed by +the right arrow `=>` which separates the function's argument +list from its body. In this example, the argument list is empty, as +witnessed by the empty pair of parenthesis on the left of the arrow. +The body of the function is the same as the one of `timeFlies` +above. + +## Classes + +As we have seen above, Scala is an object-oriented language, and as +such it has a concept of class. (For the sake of completeness, + it should be noted that some object-oriented languages do not have + the concept of class, but Scala is not one of them.) +Classes in Scala are declared using a syntax which is close to +Java's syntax. One important difference is that classes in Scala can +have parameters. This is illustrated in the following definition of +complex numbers. + + class Complex(real: Double, imaginary: Double) { + def re() = real + def im() = imaginary + } + +This `Complex` class takes two arguments, which are the real and +imaginary part of the complex. These arguments must be passed when +creating an instance of class `Complex`, as follows: `new + Complex(1.5, 2.3)`. The class contains two methods, called `re` +and `im`, which give access to these two parts. + +It should be noted that the return type of these two methods is not +given explicitly. It will be inferred automatically by the compiler, +which looks at the right-hand side of these methods and deduces that +both return a value of type `Double`. + +The compiler is not always able to infer types like it does here, and +there is unfortunately no simple rule to know exactly when it will be, +and when not. In practice, this is usually not a problem since the +compiler complains when it is not able to infer a type which was not +given explicitly. As a simple rule, beginner Scala programmers should +try to omit type declarations which seem to be easy to deduce from the +context, and see if the compiler agrees. After some time, the +programmer should get a good feeling about when to omit types, and +when to specify them explicitly. + +### Methods without arguments + +A small problem of the methods `re` and `im` is that, in +order to call them, one has to put an empty pair of parenthesis after +their name, as the following example shows: + + object ComplexNumbers { + def main(args: Array[String]) { + val c = new Complex(1.2, 3.4) + println("imaginary part: " + c.im()) + } + } + +It would be nicer to be able to access the real and imaginary parts +like if they were fields, without putting the empty pair of +parenthesis. This is perfectly doable in Scala, simply by defining +them as methods *without arguments*. Such methods differ from +methods with zero arguments in that they don't have parenthesis after +their name, neither in their definition nor in their use. Our +`Complex` class can be rewritten as follows: + + class Complex(real: Double, imaginary: Double) { + def re = real + def im = imaginary + } + + +### Inheritance and overriding + +All classes in Scala inherit from a super-class. When no super-class +is specified, as in the `Complex` example of previous section, +`scala.AnyRef` is implicitly used. + +It is possible to override methods inherited from a super-class in +Scala. It is however mandatory to explicitly specify that a method +overrides another one using the `override` modifier, in order to +avoid accidental overriding. As an example, our `Complex` class +can be augmented with a redefinition of the `toString` method +inherited from `Object`. + + class Complex(real: Double, imaginary: Double) { + def re = real + def im = imaginary + override def toString() = + "" + re + (if (im < 0) "" else "+") + im + "i" + } + + +## Case Classes and Pattern Matching + +A kind of data structure that often appears in programs is the tree. +For example, interpreters and compilers usually represent programs +internally as trees; XML documents are trees; and several kinds of +containers are based on trees, like red-black trees. + +We will now examine how such trees are represented and manipulated in +Scala through a small calculator program. The aim of this program is +to manipulate very simple arithmetic expressions composed of sums, +integer constants and variables. Two examples of such expressions are +`1+2` and `(x+x)+(7+y)`. + +We first have to decide on a representation for such expressions. The +most natural one is the tree, where nodes are operations (here, the +addition) and leaves are values (here constants or variables). + +In Java, such a tree would be represented using an abstract +super-class for the trees, and one concrete sub-class per node or +leaf. In a functional programming language, one would use an algebraic +data-type for the same purpose. Scala provides the concept of +*case classes* which is somewhat in between the two. Here is how +they can be used to define the type of the trees for our example: + + abstract class Tree + case class Sum(l: Tree, r: Tree) extends Tree + case class Var(n: String) extends Tree + case class Const(v: Int) extends Tree + +The fact that classes `Sum`, `Var` and `Const` are +declared as case classes means that they differ from standard classes +in several respects: + +- the `new` keyword is not mandatory to create instances of + these classes (i.e., one can write `Const(5)` instead of + `new Const(5)`), +- getter functions are automatically defined for the constructor + parameters (i.e., it is possible to get the value of the `v` + constructor parameter of some instance `c` of class + `Const` just by writing `c.v`), +- default definitions for methods `equals` and + `hashCode` are provided, which work on the *structure* of + the instances and not on their identity, +- a default definition for method `toString` is provided, and + prints the value in a "source form" (e.g., the tree for expression + `x+1` prints as `Sum(Var(x),Const(1))`), +- instances of these classes can be decomposed through + *pattern matching* as we will see below. + +Now that we have defined the data-type to represent our arithmetic +expressions, we can start defining operations to manipulate them. We +will start with a function to evaluate an expression in some +*environment*. The aim of the environment is to give values to +variables. For example, the expression `x+1` evaluated in an +environment which associates the value `5` to variable `x`, written +`{ x -> 5 }`, gives `6` as result. + +We therefore have to find a way to represent environments. We could of +course use some associative data-structure like a hash table, but we +can also directly use functions! An environment is really nothing more +than a function which associates a value to a (variable) name. The +environment `{ x -> 5 }` given above can simply be written as +follows in Scala: + + { case "x" => 5 } + +This notation defines a function which, when given the string +`"x"` as argument, returns the integer `5`, and fails with an +exception otherwise. + +Before writing the evaluation function, let us give a name to the type +of the environments. We could of course always use the type +`String => Int` for environments, but it simplifies the program +if we introduce a name for this type, and makes future changes easier. +This is accomplished in Scala with the following notation: + + type Environment = String => Int + +From then on, the type `Environment` can be used as an alias of +the type of functions from `String` to `Int`. + +We can now give the definition of the evaluation function. +Conceptually, it is very simple: the value of a sum of two expressions +is simply the sum of the value of these expressions; the value of a +variable is obtained directly from the environment; and the value of a +constant is the constant itself. Expressing this in Scala is not more +difficult: + + def eval(t: Tree, env: Environment): Int = t match { + case Sum(l, r) => eval(l, env) + eval(r, env) + case Var(n) => env(n) + case Const(v) => v + } + +This evaluation function works by performing *pattern matching* +on the tree `t`. Intuitively, the meaning of the above definition +should be clear: + +1. it first checks if the tree `t` is a `Sum`, and if it + is, it binds the left sub-tree to a new variable called `l` and + the right sub-tree to a variable called `r`, and then proceeds + with the evaluation of the expression following the arrow; this + expression can (and does) make use of the variables bound by the + pattern appearing on the left of the arrow, i.e., `l` and + `r`, +2. if the first check does not succeed, that is, if the tree is not + a `Sum`, it goes on and checks if `t` is a `Var`; if + it is, it binds the name contained in the `Var` node to a + variable `n` and proceeds with the right-hand expression, +3. if the second check also fails, that is if `t` is neither a + `Sum` nor a `Var`, it checks if it is a `Const`, and + if it is, it binds the value contained in the `Const` node to a + variable `v` and proceeds with the right-hand side, +4. finally, if all checks fail, an exception is raised to signal + the failure of the pattern matching expression; this could happen + here only if more sub-classes of `Tree` were declared. + +We see that the basic idea of pattern matching is to attempt to match +a value to a series of patterns, and as soon as a pattern matches, +extract and name various parts of the value, to finally evaluate some +code which typically makes use of these named parts. + +A seasoned object-oriented programmer might wonder why we did not +define `eval` as a *method* of class `Tree` and its +subclasses. We could have done it actually, since Scala allows method +definitions in case classes just like in normal classes. Deciding +whether to use pattern matching or methods is therefore a matter of +taste, but it also has important implications on extensibility: + +- when using methods, it is easy to add a new kind of node as this + can be done just by defining a sub-class of `Tree` for it; on + the other hand, adding a new operation to manipulate the tree is + tedious, as it requires modifications to all sub-classes of + `Tree`, +- when using pattern matching, the situation is reversed: adding a + new kind of node requires the modification of all functions which do + pattern matching on the tree, to take the new node into account; on + the other hand, adding a new operation is easy, by just defining it + as an independent function. + +To explore pattern matching further, let us define another operation +on arithmetic expressions: symbolic derivation. The reader might +remember the following rules regarding this operation: + +1. the derivative of a sum is the sum of the derivatives, +2. the derivative of some variable `v` is one if `v` is the + variable relative to which the derivation takes place, and zero + otherwise, +3. the derivative of a constant is zero. + +These rules can be translated almost literally into Scala code, to +obtain the following definition: + + def derive(t: Tree, v: String): Tree = t match { + case Sum(l, r) => Sum(derive(l, v), derive(r, v)) + case Var(n) if (v == n) => Const(1) + case _ => Const(0) + } + +This function introduces two new concepts related to pattern matching. +First of all, the `case` expression for variables has a +*guard*, an expression following the `if` keyword. This +guard prevents pattern matching from succeeding unless its expression +is true. Here it is used to make sure that we return the constant `1` +only if the name of the variable being derived is the same as the +derivation variable `v`. The second new feature of pattern +matching used here is the *wildcard*, written `_`, which is +a pattern matching any value, without giving it a name. + +We did not explore the whole power of pattern matching yet, but we +will stop here in order to keep this document short. We still want to +see how the two functions above perform on a real example. For that +purpose, let's write a simple `main` function which performs +several operations on the expression `(x+x)+(7+y)`: it first computes +its value in the environment `{ x -> 5, y -> 7 }`, then +computes its derivative relative to `x` and then `y`. + + def main(args: Array[String]) { + val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) + val env: Environment = { case "x" => 5 case "y" => 7 } + println("Expression: " + exp) + println("Evaluation with x=5, y=7: " + eval(exp, env)) + println("Derivative relative to x:\n " + derive(exp, "x")) + println("Derivative relative to y:\n " + derive(exp, "y")) + } + +Executing this program, we get the expected output: + + Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) + Evaluation with x=5, y=7: 24 + Derivative relative to x: + Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0))) + Derivative relative to y: + Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1))) + +By examining the output, we see that the result of the derivative +should be simplified before being presented to the user. Defining a +basic simplification function using pattern matching is an interesting +(but surprisingly tricky) problem, left as an exercise for the reader. + +## Traits + +Apart from inheriting code from a super-class, a Scala class can also +import code from one or several *traits*. + +Maybe the easiest way for a Java programmer to understand what traits +are is to view them as interfaces which can also contain code. In +Scala, when a class inherits from a trait, it implements that trait's +interface, and inherits all the code contained in the trait. + +To see the usefulness of traits, let's look at a classical example: +ordered objects. It is often useful to be able to compare objects of a +given class among themselves, for example to sort them. In Java, +objects which are comparable implement the `Comparable` +interface. In Scala, we can do a bit better than in Java by defining +our equivalent of `Comparable` as a trait, which we will call +`Ord`. + +When comparing objects, six different predicates can be useful: +smaller, smaller or equal, equal, not equal, greater or equal, and +greater. However, defining all of them is fastidious, especially since +four out of these six can be expressed using the remaining two. That +is, given the equal and smaller predicates (for example), one can +express the other ones. In Scala, all these observations can be +nicely captured by the following trait declaration: + + trait Ord { + def < (that: Any): Boolean + def <=(that: Any): Boolean = (this < that) || (this == that) + def > (that: Any): Boolean = !(this <= that) + def >=(that: Any): Boolean = !(this < that) + } + +This definition both creates a new type called `Ord`, which +plays the same role as Java's `Comparable` interface, and +default implementations of three predicates in terms of a fourth, +abstract one. The predicates for equality and inequality do not appear +here since they are by default present in all objects. + +The type `Any` which is used above is the type which is a +super-type of all other types in Scala. It can be seen as a more +general version of Java's `Object` type, since it is also a +super-type of basic types like `Int`, `Float`, etc. + +To make objects of a class comparable, it is therefore sufficient to +define the predicates which test equality and inferiority, and mix in +the `Ord` class above. As an example, let's define a +`Date` class representing dates in the Gregorian calendar. Such +dates are composed of a day, a month and a year, which we will all +represent as integers. We therefore start the definition of the +`Date` class as follows: + + class Date(y: Int, m: Int, d: Int) extends Ord { + def year = y + def month = m + def day = d + override def toString(): String = year + "-" + month + "-" + day + +The important part here is the `extends Ord` declaration which +follows the class name and parameters. It declares that the +`Date` class inherits from the `Ord` trait. + +Then, we redefine the `equals` method, inherited from +`Object`, so that it correctly compares dates by comparing their +individual fields. The default implementation of `equals` is not +usable, because as in Java it compares objects physically. We arrive +at the following definition: + + override def equals(that: Any): Boolean = + that.isInstanceOf[Date] && { + val o = that.asInstanceOf[Date] + o.day == day && o.month == month && o.year == year + } + +This method makes use of the predefined methods `isInstanceOf` +and `asInstanceOf`. The first one, `isInstanceOf`, +corresponds to Java's `instanceof` operator, and returns true +if and only if the object on which it is applied is an instance of the +given type. The second one, `asInstanceOf`, corresponds to +Java's cast operator: if the object is an instance of the given type, +it is viewed as such, otherwise a `ClassCastException` is +thrown. + +Finally, the last method to define is the predicate which tests for +inferiority, as follows. It makes use of another predefined method, +`error`, which throws an exception with the given error message. + + def <(that: Any): Boolean = { + if (!that.isInstanceOf[Date]) + error("cannot compare " + that + " and a Date") + + val o = that.asInstanceOf[Date] + (year < o.year) || + (year == o.year && (month < o.month || + (month == o.month && day < o.day))) + } + +This completes the definition of the `Date` class. Instances of +this class can be seen either as dates or as comparable objects. +Moreover, they all define the six comparison predicates mentioned +above: `equals` and `<` because they appear directly in +the definition of the `Date` class, and the others because they +are inherited from the `Ord` trait. + +Traits are useful in other situations than the one shown here, of +course, but discussing their applications in length is outside the +scope of this document. + +## Genericity + +The last characteristic of Scala we will explore in this tutorial is +genericity. Java programmers should be well aware of the problems +posed by the lack of genericity in their language, a shortcoming which +is addressed in Java 1.5. + +Genericity is the ability to write code parametrized by types. For +example, a programmer writing a library for linked lists faces the +problem of deciding which type to give to the elements of the list. +Since this list is meant to be used in many different contexts, it is +not possible to decide that the type of the elements has to be, say, +`Int`. This would be completely arbitrary and overly +restrictive. + +Java programmers resort to using `Object`, which is the +super-type of all objects. This solution is however far from being +ideal, since it doesn't work for basic types (`int`, +`long`, `float`, etc.) and it implies that a lot of +dynamic type casts have to be inserted by the programmer. + +Scala makes it possible to define generic classes (and methods) to +solve this problem. Let us examine this with an example of the +simplest container class possible: a reference, which can either be +empty or point to an object of some type. + + class Reference[T] { + private var contents: T = _ + def set(value: T) { contents = value } + def get: T = contents + } + +The class `Reference` is parametrized by a type, called `T`, +which is the type of its element. This type is used in the body of the +class as the type of the `contents` variable, the argument of +the `set` method, and the return type of the `get` method. + +The above code sample introduces variables in Scala, which should not +require further explanations. It is however interesting to see that +the initial value given to that variable is `_`, which represents +a default value. This default value is 0 for numeric types, +`false` for the `Boolean` type, `()` for the `Unit` +type and `null` for all object types. + +To use this `Reference` class, one needs to specify which type to use +for the type parameter `T`, that is the type of the element +contained by the cell. For example, to create and use a cell holding +an integer, one could write the following: + + object IntegerReference { + def main(args: Array[String]) { + val cell = new Reference[Int] + cell.set(13) + println("Reference contains the half of " + (cell.get * 2)) + } + } + +As can be seen in that example, it is not necessary to cast the value +returned by the `get` method before using it as an integer. It +is also not possible to store anything but an integer in that +particular cell, since it was declared as holding an integer. + +## Conclusion + +This document gave a quick overview of the Scala language and +presented some basic examples. The interested reader can go on, for example, by +reading the document *Scala By Example*, which +contains much more advanced examples, and consult the *Scala + Language Specification* when needed. diff --git a/_tutorials/scala-with-maven.md b/_tutorials/scala-with-maven.md new file mode 100644 index 0000000000..2b22614c52 --- /dev/null +++ b/_tutorials/scala-with-maven.md @@ -0,0 +1,302 @@ +--- +layout: overview +title: Scala with Maven + +discourse: true +--- + +By Adrian Null + +## Introduction +[Maven][1] is a build/project management tool. It favours "convention over configuration"; it can greatly simplify builds for "standard" projects and a Maven user can usually understand the structure of another Maven project just by looking at its `pom.xml` (Project Object Model). Maven is a plugin-based architecture, making it easy to add new libraries and modules to existing projects. For example, adding a new dependency usually involves only 5 extra lines in the `pom.xml`. These "artifacts" are downloaded from repositories such as [The Central Repository][2]. + +You can also check out the official [example project][36] which uses the same Scala plugin we will show here. + +## Jumping Ahead +If you're familiar with Maven, you can go ahead with the [Scala Maven Plugin][5]. + +## The Scala Maven Plugin +We'll be using the Scala Maven Plugin ([GitHub repo][5], [website][22]) (formerly known as the maven-scala-plugin; renamed to honour the new naming policy where only Maven core plugins are prefixed with "maven"), by far the dominant plugin for Scala projects. *Note: the plugin includes Scala from the Central Repository so there's no need to install it yourself if you're compiling with Maven.* + + +## Getting Maven + +### Linux (Debian) +On Debian and Debian-derivatives, Maven is usually available via `apt-get`. Just do `(sudo) apt-get install maven` and you're good to go. + +### OSX +OSX prior to 10.9 (Mavericks) comes with Maven 3 built in. +If you don't have it, you can get it with the package managers [MacPorts][4], [Homebrew][6], or [Fink][7]. +The Scala Maven Plugin requires Maven 3.0+ + +### Manually (Red Hat Linux, OSX, Windows) +You can download Maven from its [Apache homepage][3]. After extracting it (`tar -zxvf apache-maven-X.X.X-bin.tar.gz`, or use something like [7-zip][8]) to your directory of choice (on Linux and OSX, Unix-like systems, [I like to put them in `/opt/`][9]. On Windows I would probably put this in `C:/`), you need to add Maven to your environment Path variable: + +- Linux/OSX (option 1): Create a symlink to `/usr/bin`, which is already on your Path + - `ln -s /usr/bin/mvn /opt/apache-maven-X.X.X/bin/mvn` +- Linux/OSX (option 2): Add the Maven `bin` folder directly to your path, using your [shell configuration][10] file (e.g. `~/.bash_profile`) + - Add `export PATH=$PATH:/opt/apache-maven-X.X.X/bin` to `.bash_profile` (or whatever profile for the shell you use) + - Example: `echo "export PATH=$PATH:/opt/apache-maven-X.X.X/bin" >> ~/.bash_profile` +- Linux/OSX (option 3): Make a `mvn` shell script in an existing path location + - Example: you have `$HOME/bin` in your path + - Put the folder you extracted in `$HOME/bin` (`mv apache-maven-X.X.X "$HOME/bin/"`) + - Create a file `mvn` in `$HOME/bin` + - Add `"$HOME/bin/apache-maven-X.X.X/bin/mvn" $@` to it, and `chmod u+x mvn` to make it executable + - This is probably the least intrusive way; `$HOME/bin` is usually added to the user's path by default, and if not, it's a useful thing to do/have anyways. The shell script simply invokes the Maven location (which is at some other location) and passes on the arguments +- Windows + - Hit Start. Right click on "My Computer" and go to "Properties" + - This should bring you to "Control Panel -> System and Security -> System", giving an overview of your computer + - On the left sidebar there should be four options; click on "Advanced system settings" (fourth one) + - Under the "Advanced" tab, hit "Environment Variables..." in the bottom right + - Note: I recommend creating/editing your User variables (top box). You can do the same with System variables though (bottom box) + - Create a new variable called "MAVEN3_HOME". Point this to your Maven folder (e.g. `C:\apache-maven-X.X.X`). Use backslashes to be safe, and do not include a trailing slash + - Create a new variable called "MAVEN3_BIN" with this value: `%MAVEN3_HOME%\bin` + - Edit your Path variable: being careful not to change anything else, append `;%MAVEN3_BIN%` to it + - You'll need to restart cmd to see these changes + +## Creating Your First Project +The easiest way to create new projects is using an ["archetype"][11]. An archetype is a general skeleton structure, or template for a project. Think back to "convention over configuration"; in our case, the Scala Maven Plugin provides an archetype for scala projects. + +You run the archetype plugin like this: + + mvn archetype:generate + +If this is your first time, you'll notice that Maven is downloading many jar files. Maven resolves dependencies and downloads them as needed (and only once). Right now, Maven is downloading its core plugins. + +Afterwards, it should give you a list of archetypes (in the hundreds). +The Scala Maven Plugin was 339 on my list: "net.alchim31.maven:scala-archetype-simple (The maven-scala-plugin is used for compiling/testing/running/documenting scala code in maven.)". +You can type "scala" (or something else) to filter the results. +As of 2015 January 27, there you can choose version 3.1.4 or 3.1.5 of this plugin; you should choose the latest + + Choose net.alchim31.maven:scala-archetype-simple version: + 1: 1.4 + 2: 1.5 + +Next, Maven will ask you for a groupId, artifactId, and package. You can read the [guide to naming conventions][12], but in short: + +- groupId: inverted domain name (e.g. com.my-organization) +- artifactId: project name (e.g. playground-project) +- version: anything you want, but I recommend you read and follow the guidelines for [Semantic Versioning][13] (e.g. 0.0.1) +- package: the default is the groupId, but you can change this (e.g. com.my-organization) + +The groupId and artifactId together should serve as a globally unique identifier for your project + +When it's done, you should see a new folder named with the artifactId. `cd` into it and run: + + mvn package + +You'll see Maven downloading dependencies including the Scala library (as mentioned above), [JUnit][14], [ScalaTest][15], and [Specs2][16] (the latter three are test frameworks; the archetype includes an example "Hello world" program, and tests with each of the frameworks). + +### Explaining this Archetype +In your project root, you'll see a `pom.xml`, `src` folder, and `target` folder (target folder only appears after building). *Note: this archetype also includes a `.gitignore`* + +Inside the `src` folder you'll see `main` and `test`; `main` includes your application code, and `test` includes your test suites. Inside each of those you'll find a `scala` folder, followed by your package structure (actually, `test/scala` includes a sample package, but you should replace this with your own package and tests). If you want to mix Scala and Java source code, simply add a `java` folder inside `main` or `test`. + +`target` includes generated/built files, such as `.class` and `.jar` files. You can read about `pom.xml` at the [Maven page][18]. + +Example structure: + +- pom.xml +- src + - main + - scala + - com/my-package/... + *.scala + - java + - com/my-package/... + *.java + - test + - scala + - com/my-package/... + *.scala + - java + - com/my-package/... + *.java +- target + ... + +Again, you can read more about the Scala Maven Plugin at its [website][22]. + +### Creating a Jar +By default the jar created by the Scala Maven Plugin doesn't include a `Main-Class` attribute in the manifest. I had to add the [Maven Assembly Plugin][19] to my `pom.xml` in order to specify custom attributes in the manifest. You can check the latest version of this plugin at the [project summary][20] or at [The Central Repository][21] + + + X.X.X + ... + + ... + + + + ... + + + + ... + + + + ... + + ... + + org.apache.maven.plugins + maven-assembly-plugin + 2.4 + + + jar-with-dependencies + + + + com.your-package.MainClass + + + + + + package + + single + + + + + + + + +After adding this, `mvn package` will also create `[artifactId]-[version]-jar-with-dependencies.jar` under `target`. *Note: this will also copy the Scala library into your Jar. This is normal. Be careful that your dependencies use the same version of Scala, or you will quickly end up with a massive Jar.* + +### Useful commands +- `mvn dependency:copy-dependencies`: copy all libraries and dependencies to the `target/dependency` folder +- `mvn clean` +- `mvn package`: compile, run tests, and create jar + +### Integration with Eclipse ([Scala IDE][24]) +There are instructions at the [Scala Maven Plugin FAQs][23], but I thought I'd expand a bit. The [maven-eclipse-plugin][33] is a core plugin (all plugins prefixed with "maven" are, and are available by default) to generate Eclipse configuration files. However, this plugin does not know about our new Scala source files. We'll be using the [build-helper-maven-plugin][34] to add new source folders: + + ... + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + src/main/scala + + + + + add-test-source + generate-sources + + add-test-source + + + + src/test/scala + + + + + + ... + +After adding that to your `pom.xml`: + +1. Run `mvn eclipse:eclipse` - this generates the Eclipse project files (which are already ignored by our archetype's `.gitignore`) +2. Run `mvn -Declipse.workspace="path/to/your/eclipse/workspace" eclipse:configure-workspace` - this adds an `M2_REPO` classpath variable to Eclipse +3. Run `mvn package` to ensure you have all the dependencies in your local Maven repo + +Unfortunately, the integration isn't perfect. Firstly, open up the generated `.classpath` file (it will be hidden by default as it's a dotfile, but it should be in your project root directory; where you ran `mvn eclipse:eclipse`). You should see something like this near the top. + + + + +Change the `*.java` to `*.scala` (or duplicate the lines and change them to `*.scala` if you also have Java sources). + +Secondly, open the `.project` eclipse file (again, in the same folder). Change `` and `` to look like this. Now Eclipse knows to use the Scala editor and it won't think that everything is a syntax error. + + + + org.scala-ide.sdt.core.scalabuilder + + + + org.scala-ide.sdt.core.scalanature + org.eclipse.jdt.core.javanature + + +Finally, in Eclipse, under "File" choose "Import..." and find your project. + +#### Using [m2eclipse-scala][35] for Eclipse integration +m2eclipse-scala is a work in progress, and their website/repository may have updated information. +It aims to ease integration between m2eclipse and Scala IDE for Eclipse. + +Under "Help -> Install New Software", enter "http://alchim31.free.fr/m2e-scala/update-site" and hit enter. +You should see "Maven Integration for Eclipse -> Maven Integration for Scala IDE". + +Afer it installs, go to "New -> Project -> Other" and select "Maven Project". +Search fo "scala-archetype" choose the one with the group "net.alchim31.maven". +The wizard will more or less guide you through what was done with `mvn archetype:generate` above, and you should end up with a new Scala project! + +To import an existing project, simply go to "File -> Import -> Existing Maven Project" and find the directory containing your project. + +## Adding Dependencies +The first thing I do is look for "Maven" in the project page. For example, Google's [Guava] page includes [Maven Central links][28]. As you can see in the previous link, The Central Repository conveniently includes the snippet you have to add to your `pom.xml` on the left sidebar. + +If you can't find Maven information at the project page, try a Google search for "[project name] maven". Sometimes, you still won't find anything. For [scopt][29] (Scala command line option parser), I couldn't find the latest version from Google. However, [manually searching The Central Repository did][30] + +Afterwards, running + + mvn package + +Will download any new dependencies before packaging + +## Other Useful Reading +I'm not going to explain all of Maven in this tutorial (though I hope to add more in the future, because I do feel that the resources are a bit scattered), so here are some useful articles: +- [Maven Lifecycle][17] (explanation of goals like clean, package, install) + +[1]: http://maven.apache.org +[2]: http://maven.org +[3]: http://maven.apache.org/download.html +[4]: http://macports.org +[5]: https://github.com/davidB/scala-maven-plugin +[6]: http://brew.sh +[7]: http://fink.sf.net +[8]: http://7-zip.org +[9]: http://unix.stackexchange.com/questions/63531/ +[10]: http://wikipedia.org/wiki/Unix_shell#Configuration_files_for_shells +[11]: http://maven.apache.org/archetype/maven-archetype-plugin/ +[12]: http://maven.apache.org/guides/mini/guide-naming-conventions.html +[13]: http://semver.org +[14]: http://junit.org +[15]: http://scalatest.org +[16]: http://specs2.org +[17]: http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html +[18]: http://maven.apache.org/pom.html +[19]: http://maven.apache.org/plugins/maven-assembly-plugin/ +[20]: http://maven.apache.org/plugins/maven-assembly-plugin/project-summary.html +[21]: http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22maven-assembly-plugin%22 +[22]: http://davidb.github.io/scala-maven-plugin +[23]: http://davidb.github.io/scala-maven-plugin/faq.html +[24]: http://scala-ide.org +[25]: http://scala-lang.org/api/current/index.html#scala.App +[26]: http://docs.scala-lang.org/tutorials/tour/polymorphic-methods.html +[27]: https://code.google.com/p/guava-libraries/ +[28]: http://search.maven.org/#artifactdetails%7Ccom.google.guava%7Cguava%7C14.0.1%7Cbundle +[29]: https://github.com/scopt/scopt +[30]: http://search.maven.org/#search%7Cga%7C1%7Cscopt +[31]: http://mvnrepository.com +[32]: http://docs.scala-lang.org/contribute.html +[33]: http://maven.apache.org/plugins/maven-eclipse-plugin/ +[34]: http://mojo.codehaus.org/build-helper-maven-plugin/ +[35]: https://github.com/sonatype/m2eclipse-scala +[36]: https://github.com/scala/scala-module-dependency-sample diff --git a/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md b/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md deleted file mode 100644 index ea93b700c3..0000000000 --- a/tutorials/tour/_posts/2017-02-13-mixin-class-composition.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -layout: tour -title: Class Composition with Mixins - -discourse: true - -tutorial: scala-tour -categories: tour -num: 6 -next-page: higher-order-functions -previous-page: traits -prerequisite-knowledge: inheritance, traits, abstract-classes, unified-types ---- -Mixins are traits which are used to compose a class. - -```tut -abstract class A { - val message: String -} -class B extends A { - val message = "I'm an instance of class B" -} -trait C extends A { - def loudMessage = message.toUpperCase() -} -class D extends B with C - -val d = new D -d.message // I'm an instance of class B -d.loudMessage // I'M AN INSTANCE OF CLASS B -``` -Class `D` has a superclass `B` and a mixin `C`. Classes can only have one superclass but many mixins (using the keywords `extends` and `with` respectively). The mixins and the superclass may have the same supertype. - -Now let's look at a more interesting example starting with an abstract class: - -```tut -abstract class AbsIterator { - type T - def hasNext: Boolean - def next: T -} -``` -The class has an abstract type `T` and the standard iterator methods. - -Next, we'll implement a concrete class (all abstract members `T`, `hasNext`, and `next` have implementations): - -```tut -class StringIterator(s: String) extends AbsIterator { - type T = Char - private var i = 0 - def hasNext = i < s.length - def next = { - val ch = s charAt i - i += 1 - ch - } -} -``` -`StringIterator` takes a `String` and can be used to iterate over the String (e.g. to see if a String contains a certain character). - -Now let's create a trait which also extends `AbsIterator`. - -```tut -trait RichIterator extends AbsIterator { - def foreach(f: T => Unit): Unit = while (hasNext) f(next) -} -``` -Because `RichIterator` is a trait, it doesn't need to implement the abstract members of AbsIterator. - -We would like to combine the functionality of `StringIterator` and `RichIterator` into a single class. - -```tut -object StringIteratorTest extends App { - class RichStringIter extends StringIterator(args(0)) with RichIterator - val iter = new RichStringIter - iter foreach println -} -``` -The new class `RichStringIter` has `StringIterator` as a superclass and `RichIterator` as a mixin. - -With single inheritance we would not be able to achieve this level of flexibility. diff --git a/tutorials/tour/_posts/2017-02-13-pattern-matching.md b/tutorials/tour/_posts/2017-02-13-pattern-matching.md deleted file mode 100644 index 33e1b4a1f9..0000000000 --- a/tutorials/tour/_posts/2017-02-13-pattern-matching.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -layout: tour -title: Pattern Matching - -discourse: true - -tutorial: scala-tour -categories: tour -num: 12 - -next-page: singleton-objects -previous-page: case-classes -prerequisite-knowledge: case-classes, string-interpolation, subtyping ---- - -Pattern matching is a mechanism for checking a value against a pattern. A successful match can also deconstruct a value into its constituent parts. It is a more powerful version of the `switch` statement in Java and it can likewise be used in place of a series of if/else statements. - -## Syntax -A match expression has a value, the `match` keyword, and at least one `case` clause. -```tut -import scala.util.Random - -val x: Int = Random.nextInt(10) - -x match { - case 0 => "zero" - case 1 => "one" - case 2 => "two" - case _ => "many" -} -``` -The `val x` above is a random integer between 0 and 10. `x` becomes the left operand of the `match` operator and on the right is an expression with four cases. The last case `_` is a "catch all" case for any number greater than 2. Cases are also called _alternatives_. - -Match expressions have a value. -```tut -def matchTest(x: Int): String = x match { - case 1 => "one" - case 2 => "two" - case _ => "many" -} -matchTest(3) // many -matchTest(1) // one -``` -This match expression has a type String because all of the cases return String. Therefore, the function `matchTest` returns a String. - -## Matching on case classes - -Case classes are especially useful for pattern matching. - -```tut -abstract class Notification - -case class Email(sender: String, title: String, body: String) extends Notification - -case class SMS(caller: String, message: String) extends Notification - -case class VoiceRecording(contactName: String, link: String) extends Notification - - -``` -`Notification` is an abstract super class which has three concrete Notification types implemented with case classes `Email`, `SMS`, and `VoiceRecording`. Now we can do pattern matching on these case classes: - -``` -def showNotification(notification: Notification): String = { - notification match { - case Email(email, title, _) => - s"You got an email from $email with title: $title" - case SMS(number, message) => - s"You got an SMS from $number! Message: $message" - case VoiceRecording(name, link) => - s"you received a Voice Recording from $name! Click the link to hear it: $link" - } -} -val someSms = SMS("12345", "Are you there?") -val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123") - -println(showNotification(someSms)) // prints You got an SMS from 12345! Message: Are you there? - -println(showNotification(someVoiceRecording)) // you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123 -``` -The function `showNotification` takes as a parameter the abstract type `Notification` and matches on the type of `Notification` (i.e. it figures out whether it's an `Email`, `SMS`, or `VoiceRecording`). In the `case Email(email, title, _)` the fields `email` and `title` are used in the return value but the `body` field is ignored with `_`. - -## Pattern guards -Pattern guards are simply boolean expressions which are used to make cases more specific. Just add `if ` after the pattern. -``` - -def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = { - notification match { - case Email(email, _, _) if importantPeopleInfo.contains(email) => - "You got an email from special someone!" - case SMS(number, _) if importantPeopleInfo.contains(number) => - "You got an SMS from special someone!" - case other => - showNotification(other) // nothing special, delegate to our original showNotification function - } -} - -val importantPeopleInfo = Seq("867-5309", "jenny@gmail.com") - -val someSms = SMS("867-5309", "Are you there?") -val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123") -val importantEmail = Email("jenny@gmail.com", "Drinks tonight?", "I'm free after 5!") -val importantSms = SMS("867-5309", "I'm here! Where are you?") - -println(showImportantNotification(someSms, importantPeopleInfo)) -println(showImportantNotification(someVoiceRecording, importantPeopleInfo)) -println(showImportantNotification(importantEmail, importantPeopleInfo)) -println(showImportantNotification(importantSms, importantPeopleInfo)) -``` - -In the `case Email(email, _, _) if importantPeopleInfo.contains(email)`, the pattern is matched only if the `email` is in the list of important people. - -## Matching on type only -You can match on the type like so: -```tut -abstract class Device -case class Phone(model: String) extends Device { - def screenOff = "Turning screen off" -} -case class Computer(model: String) extends Device { - def screenSaverOn = "Turning screen saver on..." -} - -def goIdle(device: Device) = device match { - case p: Phone => p.screenOff - case c: Computer => c.screenSaverOn -} -``` -`def goIdle` has a different behavior depending on the type of `Device`. This is useful when the case needs to call a method on the pattern. It is a convention to use the first letter of the type as the case identifier (`p` and `c` in this case). - -## Sealed classes -Traits and classes can be marked `sealed` which means all subtypes must be declared in the same file. The assures that all subtypes are known. - -```tut -sealed abstract class Furniture -case class Couch() extends Furniture -case class Chair() extends Furniture - -def findPlaceToSit(piece: Furniture): String = piece match { - case a: Couch => "Lie on the couch" - case b: Chair => "Sit on the chair" -} -``` -This is useful for pattern matching because we don't need a "catch all" case. - -## Notes - -Scala's pattern matching statement is most useful for matching on algebraic types expressed via [case classes](case-classes.html). -Scala also allows the definition of patterns independently of case classes, using `unapply` methods in [extractor objects](extractor-objects.html). diff --git a/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md b/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md deleted file mode 100644 index 7d21fe2943..0000000000 --- a/tutorials/tour/_posts/2017-02-13-polymorphic-methods.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: tour -title: Polymorphic Methods - -discourse: true - -tutorial: scala-tour -categories: tour -section: -num: 28 - -next-page: local-type-inference -previous-page: implicit-conversions -prerequisite-knowledge: unified-types, ---- - -Methods in Scala can be parameterized by type as well as value. The syntax is similar to that of generic classes. Type parameters are declared within a pair of brackets while value parameters are enclosed in a pair of parentheses. - -Here is an example: - -```tut -def listOfDuplicates[A](x: A, length: Int): List[A] = { - if (length < 1) - Nil - else - x :: listOfDuplicates(x, length - 1) -} -println(listOfDuplicates[Int](3, 4)) // List(3, 3, 3, 3) -println(listOfDuplicates("La", 8)) // List(La, La, La, La, La, La, La, La) -``` - -The method `listOfDuplicates` takes a type parameter `A` and values parameters `x` and `length`. In this case, value `x` is of type `A`. If `length < 1` we return an empty list. Otherwise we prepend `x` to the the list of duplicates returned by the recursive call to `listOfDuplicates`. (note: `::` means prepend an element on the left to a sequence on the right). - -When we call `listOfDuplicates` with `[Int]` as the type parameter, the first argument must be an int and the return type will be List[Int]. However, you don't always need to explicitly provide the the type parameter because the compiler can often figure it out based on the type of value argument (`"La"` is a String). In fact, if calling this method from Java it is impossible to provide the type parameter. diff --git a/tutorials/tour/_posts/2017-02-13-traits.md b/tutorials/tour/_posts/2017-02-13-traits.md deleted file mode 100644 index 8c1c1f8608..0000000000 --- a/tutorials/tour/_posts/2017-02-13-traits.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -layout: tour -title: Traits - -discourse: true - -tutorial: scala-tour -categories: tour -num: 5 -next-page: mixin-class-composition -previous-page: classes -assumed-knowledge: expressions, classes, generics, objects, companion-objects ---- - -Traits are used to share interfaces and fields between classes. They are similar to Java 8's interfaces. Classes and objects can extend traits but traits cannot be instantiated and therefore have no parameters. - -## Defining a trait -A minimal trait is simply the keyword `trait` and an identifier: - -```tut -trait HairColor -``` - -Traits become especially useful as generic types and with abstract methods. -```tut -trait Iterator[A] { - def hasNext: Boolean - def next(): A -} -``` - -Extending the `trait Iterator[A]` requires a type `A` and implementations of the methods `hasNext` and `next`. - -## Using traits -Use the `extends` keyword to extend a trait. Then implement any abstract members of the trait using the `override` keyword: -```tut -trait Iterator[A] { - def hasNext: Boolean - def next(): A -} - - -class IntIterator(to: Int) extends Iterator[Int] { - private var current = 0 - override def hasNext: Boolean = current < to - override def next(): Int = { - if (hasNext) { - val t = current - current += 1 - t - } else 0 - } -} - - -val iterator = new IntIterator(10) -iterator.next() // prints 0 -iterator.next() // prints 1 -``` -This `IntIterator` class takes a parameter `to` as an upper bound. It `extends Iterator[Int]` which means that the `next` method must return an Int. - -## Subtyping -Where a given trait is required, a subtype of the trait can be used instead. -```tut -import scala.collection.mutable.ArrayBuffer - -trait Pet { - val name: String -} - -class Cat(val name: String) extends Pet -class Dog(val name: String) extends Pet - -val dog = new Dog("Harry") -val cat = new Cat("Sally") - -val animals = ArrayBuffer.empty[Pet] -animals.append(dog) -animals.append(cat) -animals.foreach(pet => println(pet.name)) // Prints Harry Sally -``` -The `trait Pet` has an abstract field `name` which gets implemented by Cat and Dog in their constructors. On the last line, we call `pet.name` which must be implemented in any subtype of the trait `Pet`. diff --git a/tutorials/tour/_posts/2017-02-13-unified-types.md b/tutorials/tour/_posts/2017-02-13-unified-types.md deleted file mode 100644 index 92026a8494..0000000000 --- a/tutorials/tour/_posts/2017-02-13-unified-types.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -layout: tour -title: Unified Types - -discourse: true - -tutorial: scala-tour -categories: tour -num: 3 -next-page: classes -previous-page: basics -prerequisite-knowledge: classes, basics ---- - -In Scala, all values have a type, including numerical values and functions. The diagram below illustrates a subset of the type hierarchy. - -Scala Type Hierarchy - -## Scala Type Hierarchy ## - -[`Any`](http://www.scala-lang.org/api/2.12.1/scala/Any.html) is the supertype of all types, also called the top type. It defines certain universal methods such as `equals`, `hashCode`, and `toString`. `Any` has two direct subclasses: `AnyVal` and `AnyRef`. - -`AnyVal` represents value types. There are nine predefined value types and they are non-nullable: `Double`, `Float`, `Long`, `Int`, `Short`, `Byte`, `Char`, `Unit`, and `Boolean`. `Unit` is a value type which carries no meaningful information. There is exactly one instance of `Unit` which can be declared literally like so: `()`. All functions must return something so sometimes `Unit` is a useful return type. - -`AnyRef` represents reference types. All non-value types are defined as reference types. Every user-defined type in Scala is a subtype of `AnyRef`. If Scala is used in the context of a Java runtime environment, `AnyRef` corresponds to `java.lang.Object`. - -Here is an example that demonstrates that strings, integers, characters, boolean values, and functions are all objects just like every other object: - -```tut -val list: List[Any] = List( - "a string", - 732, // an integer - 'c', // a character - true, // a boolean value - () => "an anonymous function returning a string" -) - -list.foreach(element => println(element)) -``` - -It defines a variable `list` of type `List[Any]`. The list is initialized with elements of various types, but they all are instance of `scala.Any`, so you can add them to the list. - -Here is the output of the program: - -``` -a string -732 -c -true - -``` - -## Type Casting -Value types can be cast in the following way: -Scala Type Hierarchy - -For example: - -```tut -val x: Long = 987654321 -val y: Float = x // 9.8765434E8 (note that some precision is lost in this case) - -val face: Char = '☺' -val number: Int = face // 9786 -``` - -Casting is unidirectional. This will not compile: - -``` -val x: Long = 987654321 -val y: Float = x // 9.8765434E8 -val z: Long = y // Does not conform -``` - -You can also cast a reference type to a subtype. This will be covered later in the tour. - -## Nothing and Null -`Nothing` is a subtype of all types, also called the bottom type. There is no value that has type `Nothing`. A common use is to signal non-termination such as a thrown exception, program exit, or an infinite loop (i.e., it is the type of an expression which does not evaluate to a value, or a method that does not return normally). - -`Null` is a subtype of all reference types (i.e. any subtype of AnyRef). It has a single value identified by the keyword literal `null`. `Null` is provided mostly for interoperability with other JVM languages and should almost never be used in Scala code. We'll cover alternatives to `null` later in the tour. diff --git a/tutorials/tour/_posts/2017-02-13-variances.md b/tutorials/tour/_posts/2017-02-13-variances.md deleted file mode 100644 index b92cfba195..0000000000 --- a/tutorials/tour/_posts/2017-02-13-variances.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -layout: tour -title: Variances - -discourse: true - -tutorial: scala-tour -num: 19 -next-page: upper-type-bounds -previous-page: generic-classes ---- - -Variance is the correlation of subtyping relationships of complex types and the subtyping relationships of their component types. Scala supports variance annotations of type parameters of [generic classes](generic-classes.html), to allow them to be covariant, contravariant, or invariant if no annotations are used. The use of variance in the type system allows us to make intuitive connections between complex types, whereas the lack of variance can restrict the reuse of a class abstraction. - -```tut -class Foo[+A] // A covariant class -class Bar[-A] // A contravariant class -class Baz[A] // An invariant class -``` - -### Covariance - -A type parameter `A` of a generic class can be made covariant by using the annotation `+A`. For some `class List[+A]`, making `A` covariant implies that for two types `A` and `B` where `A` is a subtype of `B`, then `List[A]` is a subtype of `List[B]`. This allows us to make very useful and intuitive subtyping relationships using generics. - -Consider this simple class structure: - -```tut -abstract class Animal { - def name: String -} -case class Cat(name: String) extends Animal -case class Dog(name: String) extends Animal -``` - -Both `Cat` and `Dog` are subtypes of `Animal`. The Scala standard library has a generic immutable `sealed abstract class List[+A]` class, where the type parameter `A` is covariant. This means that a `List[Cat]` is a `List[Animal]` and a `List[Dog]` is also a `List[Animal]`. Intuitively, it makes sense that a list of cats and a list of dogs are each lists of animals, and you should be able to substitute either of them for a `List[Animal]`. - -In the following example, the method `printAnimalNames` will accept a list of animals as an argument and print their names each on a new line. If `List[A]` were not covariant, the last two method calls would not compile, which would severely limit the usefulness of the `printAnimalNames` method. - -```tut -object CovarianceTest extends App { - def printAnimalNames(animals: List[Animal]): Unit = { - animals.foreach { animal => - println(animal.name) - } - } - - val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom")) - val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex")) - - printAnimalNames(cats) - // Whiskers - // Tom - - printAnimalNames(dogs) - // Fido - // Rex -} -``` - -### Contravariance - -A type parameter `A` of a generic class can be made contravariant by using the annotation `-A`. This creates a subtyping relationship between the class and its type parameter that is similar, but opposite to what we get with covariance. That is, for some `class Writer[-A]`, making `A` contravariant implies that for two types `A` and `B` where `A` is a subtype of `B`, `Writer[B]` is a subtype of `Writer[A]`. - -Consider the `Cat`, `Dog`, and `Animal` classes defined above for the following example: - -```tut -abstract class Printer[-A] { - def print(value: A): Unit -} -``` - -A `Printer[A]` is a simple class that knows how to print out some type `A`. Let's define some subclasses for specific types: - -```tut -class AnimalPrinter extends Printer[Animal] { - def print(animal: Animal): Unit = - println("The animal's name is: " + animal.name) -} - -class CatPrinter extends Printer[Cat] { - def print(cat: Cat): Unit = - println("The cat's name is: " + cat.name) -} -``` - -If a `Printer[Cat]` knows how to print any `Cat` to the console, and a `Printer[Animal]` knows how to print any `Animal` to the console, it makes sense that a `Printer[Animal]` would also know how to print any `Cat`. The inverse relationship does not apply, because a `Printer[Cat]` does not know how to print any `Animal` to the console. Therefore, we should be able to substitute a `Printer[Animal]` for a `Printer[Cat]`, if we wish, and making `Printer[A]` contravariant allows us to do exactly that. - -```tut -object ContravarianceTest extends App { - val myCat: Cat = Cat("Boots") - - def printMyCat(printer: Printer[Cat]): Unit = { - printer.print(myCat) - } - - val catPrinter: Printer[Cat] = new CatPrinter - val animalPrinter: Printer[Animal] = new AnimalPrinter - - printMyCat(catPrinter) - printMyCat(animalPrinter) -} -``` - -The output of this program will be: - -``` -The cat's name is: Boots -The animal's name is: Boots -``` - -### Invariance - -Generic classes in Scala are invariant by default. This means that they are neither covariant nor contravariant. In the context of the following example, `Container` class is invariant. A `Container[Cat]` is _not_ a `Container[Animal]`, nor is the reverse true. - -```tut -class Container[A](value: A) { - private var _value: A = value - def getValue: A = _value - def setValue(value: A): Unit = { - _value = value - } -} -``` - -It may seem like a `Container[Cat]` should naturally also be a `Container[Animal]`, but allowing a mutable generic class to be covariant would not be safe. In this example, it is very important that `Container` is invariant. Supposing `Container` was actually covariant, something like this could happen: - -``` -val catContainer: Container[Cat] = new Container(Cat("Felix")) -val animalContainer: Container[Animal] = catContainer -animalContainer.setValue(Dog("Spot")) -val cat: Cat = catContainer.getValue // Oops, we'd end up with a Dog assigned to a Cat -``` - -Fortunately, the compiler stops us long before we could get this far. - -### Other Examples - -Another example that can help one understand variance is `trait Function1[-T, +R]` from the Scala standard library. `Function1` represents a function with one argument, where the first type parameter `T` represents the argument type, and the second type parameter `R` represents the return type. A `Function1` is contravariant over its argument type, and covariant over its return type. For this example we'll use the literal notation `A => B` to represent a `Function1[A, B]`. - -Assume the similar `Cat`, `Dog`, `Animal` inheritance tree used earlier, plus the following: - -```tut -class SmallAnimal -class Mouse extends SmallAnimal -``` - -Suppose we're working with functions that accept types of animals, and return the types of food they eat. If we would like a `Cat => SmallAnimal` (because cats eat small animals), but are given a `Animal => Mouse` instead, our program will still work. Intuitively an `Animal => Mouse` will still accept a `Cat` as an argument, because a `Cat` is an `Animal`, and it returns a `Mouse`, which is also an `SmallAnimal`. Since we can safely and invisibly substitute the former for the latter, we can say `Animal => Mouse` is a subtype of `Cat => SmallAnimal`. - -### Comparison With Other Languages - -Variance is supported in different ways by some languages that are similar to Scala. For example, variance annotations in Scala closely resemble those in C#, where the annotations are added when a class abstraction is defined (declaration-site variance). In Java, however, variance annotations are given by clients when a class abstraction is used (use-site variance). From 98d24ae714c06ed883a346fd8f5e75d0ac8b0bf7 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 14:19:16 +0200 Subject: [PATCH 069/112] Keep tut AND syntax highlighting both w/ Jekyll plugin Requires me to disable the github-pages plugin because it disables all other plugins from running. --- Gemfile | 1 - _plugins/tut_replace.rb | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 _plugins/tut_replace.rb diff --git a/Gemfile b/Gemfile index bcd3be7d03..61e9b59ad0 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,2 @@ source 'https://rubygems.org' -gem 'github-pages', group: :jekyll_plugins gem 'jekyll-redirect-from' diff --git a/_plugins/tut_replace.rb b/_plugins/tut_replace.rb new file mode 100644 index 0000000000..bb932932a2 --- /dev/null +++ b/_plugins/tut_replace.rb @@ -0,0 +1,18 @@ +module Jekyll + class TutConverter < Converter + safe false + priority :high + + def matches(ext) + ext =~ /^\.(md|markdown)$/i + end + + def output_ext(ext) + ".html" + end + + def convert(content) + content.gsub("```tut\n", "```scala\n") + end + end +end From 1ec1fc848fb77d509f574da6c7beb160cde52932 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 15:03:02 +0200 Subject: [PATCH 070/112] Sidebar bugfix and work on tutorials --- _includes/sidebar-toc-multipage-overview.html | 4 +- _layouts/multipage-overview.html | 2 +- {_tutorials => _overviews}/FAQ/breakout.md | 9 +- .../FAQ/chaining-implicits.md | 5 +- _overviews/{tutorials => }/FAQ/collections.md | 6 +- .../FAQ/context-bounds.md | 5 +- .../{tutorials => }/FAQ/finding-implicits.md | 18 +- .../FAQ/finding-symbols.md | 7 +- .../FAQ/initialization-order.md | 5 +- .../FAQ/stream-view-iterator.md | 6 +- {_tutorials => _overviews}/FAQ/yield.md | 3 +- _overviews/tutorials/FAQ/breakout.md | 234 ---- .../tutorials/FAQ/chaining-implicits.md | 109 -- _overviews/tutorials/FAQ/context-bounds.md | 105 -- _overviews/tutorials/FAQ/finding-symbols.md | 205 --- .../tutorials/FAQ/initialization-order.md | 164 --- _overviews/tutorials/FAQ/yield.md | 158 --- _tutorials/FAQ/chaining-implicits.md | 109 -- _tutorials/FAQ/collections.md | 376 ----- _tutorials/FAQ/finding-implicits.md | 327 ----- _tutorials/FAQ/initialization-order.md | 164 --- _tutorials/FAQ/stream-view-iterator.md | 45 - _tutorials/partest-guide.md | 66 - _tutorials/scala-for-csharp-programmers.md | 1236 ---------------- _tutorials/scala-for-java-programmers.md | 723 ---------- _tutorials/scala-with-maven.md | 302 ---- tutorials/FAQ/breakout.md | 234 ---- tutorials/FAQ/collections.md | 377 ----- tutorials/FAQ/context-bounds.md | 105 -- tutorials/FAQ/finding-symbols.md | 205 --- tutorials/FAQ/stream-view-iterator.md | 46 - tutorials/FAQ/yield.md | 158 --- ...scala-for-csharp-programmers.disabled.html | 1237 ----------------- tutorials/scala-for-java-programmers.md | 723 ---------- tutorials/scala-with-maven.md | 302 ---- 35 files changed, 46 insertions(+), 7734 deletions(-) rename {_tutorials => _overviews}/FAQ/breakout.md (98%) rename {tutorials => _overviews}/FAQ/chaining-implicits.md (97%) rename _overviews/{tutorials => }/FAQ/collections.md (99%) rename {_tutorials => _overviews}/FAQ/context-bounds.md (97%) rename _overviews/{tutorials => }/FAQ/finding-implicits.md (99%) rename {_tutorials => _overviews}/FAQ/finding-symbols.md (99%) rename {tutorials => _overviews}/FAQ/initialization-order.md (98%) rename _overviews/{tutorials => }/FAQ/stream-view-iterator.md (95%) rename {_tutorials => _overviews}/FAQ/yield.md (98%) delete mode 100644 _overviews/tutorials/FAQ/breakout.md delete mode 100644 _overviews/tutorials/FAQ/chaining-implicits.md delete mode 100644 _overviews/tutorials/FAQ/context-bounds.md delete mode 100644 _overviews/tutorials/FAQ/finding-symbols.md delete mode 100644 _overviews/tutorials/FAQ/initialization-order.md delete mode 100644 _overviews/tutorials/FAQ/yield.md delete mode 100644 _tutorials/FAQ/chaining-implicits.md delete mode 100644 _tutorials/FAQ/collections.md delete mode 100644 _tutorials/FAQ/finding-implicits.md delete mode 100644 _tutorials/FAQ/initialization-order.md delete mode 100644 _tutorials/FAQ/stream-view-iterator.md delete mode 100644 _tutorials/partest-guide.md delete mode 100644 _tutorials/scala-for-csharp-programmers.md delete mode 100644 _tutorials/scala-for-java-programmers.md delete mode 100644 _tutorials/scala-with-maven.md delete mode 100644 tutorials/FAQ/breakout.md delete mode 100644 tutorials/FAQ/collections.md delete mode 100644 tutorials/FAQ/context-bounds.md delete mode 100644 tutorials/FAQ/finding-symbols.md delete mode 100644 tutorials/FAQ/stream-view-iterator.md delete mode 100644 tutorials/FAQ/yield.md delete mode 100644 tutorials/scala-for-csharp-programmers.disabled.html delete mode 100644 tutorials/scala-for-java-programmers.md delete mode 100644 tutorials/scala-with-maven.md diff --git a/_includes/sidebar-toc-multipage-overview.html b/_includes/sidebar-toc-multipage-overview.html index a0cde76863..cfd6ee71a6 100644 --- a/_includes/sidebar-toc-multipage-overview.html +++ b/_includes/sidebar-toc-multipage-overview.html @@ -7,7 +7,7 @@
    Contents
    {% assign sorted = site.overviews | sort: 'num' %} {% for pg in sorted %} {% if pg.num and (page.partof == pg.partof) %} - {% if page.language %} + {% if page.language %} {% assign prefix = page.language | prepend: '/' %} {% assign localizedId = pg.id | prepend: prefix %} {% for lpg in site.[page.language] %} @@ -15,7 +15,7 @@
    Contents
  • {{ lpg.title }}
  • {% endif %} {% endfor %} - {% else %} + {% else %}
  • {{ pg.title }}
  • {% endif %} {% if pg.num == page.num %}
    {% endif %} diff --git a/_layouts/multipage-overview.html b/_layouts/multipage-overview.html index 8ba00e63a8..cf7c0b5a32 100644 --- a/_layouts/multipage-overview.html +++ b/_layouts/multipage-overview.html @@ -13,6 +13,6 @@ - {% include sidebar-toc-tour-overview.html %} + {% include sidebar-toc-multipage-overview.html %} diff --git a/_tutorials/FAQ/breakout.md b/_overviews/FAQ/breakout.md similarity index 98% rename from _tutorials/FAQ/breakout.md rename to _overviews/FAQ/breakout.md index 8c11253eb2..0a39214d55 100644 --- a/_tutorials/FAQ/breakout.md +++ b/_overviews/FAQ/breakout.md @@ -1,11 +1,14 @@ --- -layout: overview-large +layout: multipage-overview title: What is breakOut, and how does it work? discourse: true +overview-name: FAQ partof: FAQ + num: 5 +permalink: /tutorials/FAQ/:title.html --- You might have encountered some code like the one below, and wonder what is `breakOut`, and why is it being passed as parameter? @@ -169,11 +172,11 @@ that these are the values of `That`. But where do they come from? If we look inside the companion objects of the classes involved, we see some implicit declarations providing them. On object `Map`: - implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]] + implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]] And on object `Iterable`, whose class is extended by `Map`: - implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]] + implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]] These definitions provide factories for parameterized `CanBuildFrom`. diff --git a/tutorials/FAQ/chaining-implicits.md b/_overviews/FAQ/chaining-implicits.md similarity index 97% rename from tutorials/FAQ/chaining-implicits.md rename to _overviews/FAQ/chaining-implicits.md index 71424b80d6..261d8b7bb7 100644 --- a/tutorials/FAQ/chaining-implicits.md +++ b/_overviews/FAQ/chaining-implicits.md @@ -1,11 +1,14 @@ --- -layout: overview +layout: multipage-overview title: How can I chain/nest implicit conversions? discourse: true +overview-name: FAQ partof: FAQ + num: 6 +permalink: /tutorials/FAQ/:title.html --- The enrich-my-library pattern allows one to seemingly add a method to a class by diff --git a/_overviews/tutorials/FAQ/collections.md b/_overviews/FAQ/collections.md similarity index 99% rename from _overviews/tutorials/FAQ/collections.md rename to _overviews/FAQ/collections.md index 8108915363..97ad5520a1 100644 --- a/_overviews/tutorials/FAQ/collections.md +++ b/_overviews/FAQ/collections.md @@ -1,11 +1,14 @@ --- -layout: overview +layout: multipage-overview title: How are the collections structured? Which one should I choose? discourse: true +overview-name: FAQ partof: FAQ + num: 8 +permalink: /tutorials/FAQ/:title.html --- ## Foreword @@ -374,4 +377,3 @@ Overflow. [7]: http://i.stack.imgur.com/Dsptl.png [8]: http://i.stack.imgur.com/szWUr.png [9]: http://stackoverflow.com/q/1722137/53013 - diff --git a/_tutorials/FAQ/context-bounds.md b/_overviews/FAQ/context-bounds.md similarity index 97% rename from _tutorials/FAQ/context-bounds.md rename to _overviews/FAQ/context-bounds.md index 9ed04ad25d..fa0beaaaa6 100644 --- a/_tutorials/FAQ/context-bounds.md +++ b/_overviews/FAQ/context-bounds.md @@ -1,11 +1,14 @@ --- -layout: overview-large +layout: multipage-overview title: What are Scala context bounds? discourse: true +overview-name: FAQ partof: FAQ + num: 3 +permalink: /tutorials/FAQ/:title.html --- What is a Context Bound? diff --git a/_overviews/tutorials/FAQ/finding-implicits.md b/_overviews/FAQ/finding-implicits.md similarity index 99% rename from _overviews/tutorials/FAQ/finding-implicits.md rename to _overviews/FAQ/finding-implicits.md index 5784fc583a..5dd36ec510 100644 --- a/_overviews/tutorials/FAQ/finding-implicits.md +++ b/_overviews/FAQ/finding-implicits.md @@ -1,11 +1,14 @@ --- -layout: overview +layout: multipage-overview title: Where does Scala look for implicits? discourse: true +overview-name: FAQ partof: FAQ + num: 7 +permalink: /tutorials/FAQ/:title.html --- Newcomers to Scala often ask: Where does the compiler look for implicits? @@ -14,15 +17,15 @@ For example, where do the values for `integral` below come from? scala> import scala.math._ import scala.math._ - + scala> def foo[T](t: T)(implicit integral: Integral[T]): Unit = { println(integral) } foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit - + scala> foo(0) scala.math.Numeric$IntIsIntegral$@3dbea611 - + scala> foo(0L) scala.math.Numeric$LongIsIntegral$@48c610af @@ -168,7 +171,7 @@ Let's give examples for them. import scala.collection.JavaConversions.mapAsScalaMap def env = System.getenv() // Java map val term = env("TERM") // implicit conversion from Java Map to Scala Map - + ### Wildcard Imports def sum[T : Integral](list: List[T]): T = { @@ -219,7 +222,7 @@ the object `Ordering`, companion to the class `Ordering`, and finds an implicit Note that companion objects of super classes are also looked into. For example: class A(val n: Int) - object A { + object A { implicit def str(a: A) = "A: %d" format a.n } class B(val x: Int, y: Int) extends A(y) @@ -228,7 +231,7 @@ Note that companion objects of super classes are also looked into. For example: This is how Scala found the implicit `Numeric[Int]` and `Numeric[Long]` in the opening example, by the way, as they are found inside `Numeric`, not `Integral`. - + ### Implicit scope of an argument's type If you have a method with an argument type `A`, then the implicit scope of type @@ -325,4 +328,3 @@ This question and answer were originally submitted on [Stack Overflow][3]. [5]: http://scala-lang.org/files/archive/spec/2.11/06-expressions.html [6]: http://scala-lang.org/files/archive/spec/2.11/07-implicits.html [7]: https://github.com/scala/scala.github.com/issues - diff --git a/_tutorials/FAQ/finding-symbols.md b/_overviews/FAQ/finding-symbols.md similarity index 99% rename from _tutorials/FAQ/finding-symbols.md rename to _overviews/FAQ/finding-symbols.md index a499b7b5ee..948dc99db6 100644 --- a/_tutorials/FAQ/finding-symbols.md +++ b/_overviews/FAQ/finding-symbols.md @@ -1,12 +1,15 @@ --- -layout: overview-large +layout: multipage-overview title: How do I find what some symbol means or does? discourse: true +overview-name: FAQ partof: FAQ + num: 1 -outof: 9 + +permalink: /tutorials/FAQ/:title.html --- We can divide the operators in Scala, for the purpose of teaching, into four categories: diff --git a/tutorials/FAQ/initialization-order.md b/_overviews/FAQ/initialization-order.md similarity index 98% rename from tutorials/FAQ/initialization-order.md rename to _overviews/FAQ/initialization-order.md index 57374e4352..2c6f0b678f 100644 --- a/tutorials/FAQ/initialization-order.md +++ b/_overviews/FAQ/initialization-order.md @@ -1,11 +1,14 @@ --- -layout: overview +layout: multipage-overview title: Why is my abstract or overridden val null? discourse: true +overview-name: FAQ partof: FAQ + num: 9 +permalink: /tutorials/FAQ/:title.html --- ## Example diff --git a/_overviews/tutorials/FAQ/stream-view-iterator.md b/_overviews/FAQ/stream-view-iterator.md similarity index 95% rename from _overviews/tutorials/FAQ/stream-view-iterator.md rename to _overviews/FAQ/stream-view-iterator.md index 6c9413474b..cc6215233a 100644 --- a/_overviews/tutorials/FAQ/stream-view-iterator.md +++ b/_overviews/FAQ/stream-view-iterator.md @@ -1,11 +1,14 @@ --- -layout: overview +layout: multipage-overview title: What is the difference between view, stream and iterator? discourse: true +overview-name: FAQ partof: FAQ + num: 4 +permalink: /tutorials/FAQ/:title.html --- First, they are all _non-strict_. That has a particular mathematical meaning related to functions, but, basically, means they are computed on-demand instead @@ -43,4 +46,3 @@ expected to ever be fetched, compared to the total size of the view. This answer was originally submitted in response to [this question on Stack Overflow][1]. [1]: http://stackoverflow.com/q/5159000/53013 - diff --git a/_tutorials/FAQ/yield.md b/_overviews/FAQ/yield.md similarity index 98% rename from _tutorials/FAQ/yield.md rename to _overviews/FAQ/yield.md index ec53a65de8..44a839882f 100644 --- a/_tutorials/FAQ/yield.md +++ b/_overviews/FAQ/yield.md @@ -1,11 +1,12 @@ --- -layout: overview-large +layout: multipage-overview title: How does yield work? discourse: true partof: FAQ num: 2 +permalink: /tutorials/FAQ/:title.html --- Though there's a `yield` in other languages such as Python and Ruby, Scala's `yield` does something very different from them. In Scala, `yield` is part diff --git a/_overviews/tutorials/FAQ/breakout.md b/_overviews/tutorials/FAQ/breakout.md deleted file mode 100644 index bd672df922..0000000000 --- a/_overviews/tutorials/FAQ/breakout.md +++ /dev/null @@ -1,234 +0,0 @@ ---- -layout: overview -title: What is breakOut, and how does it work? - -discourse: true - -partof: FAQ -num: 5 ---- -You might have encountered some code like the one below, and wonder what is -`breakOut`, and why is it being passed as parameter? - - import scala.collection.breakOut - val map : Map[Int,String] = List("London", "France").map(x => (x.length, x))(breakOut) - - -The answer is found on the definition of `map`: - - def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That - -Note that it has two parameters. The first is your function and the second is -an implicit. If you do not provide that implicit, Scala will choose the most -_specific_ one available. - -### About breakOut - -So, what's the purpose of `breakOut`? Consider the example given at the -beginning , You take a list of strings, transform each string into a tuple -`(Int, String)`, and then produce a `Map` out of it. The most obvious way to do -that would produce an intermediary `List[(Int, String)]` collection, and then -convert it. - -Given that `map` uses a `Builder` to produce the resulting collection, wouldn't -it be possible to skip the intermediary `List` and collect the results directly -into a `Map`? Evidently, yes, it is. To do so, however, we need to pass a -proper `CanBuildFrom` to `map`, and that is exactly what `breakOut` does. - -Let's look, then, at the definition of `breakOut`: - - def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) = - new CanBuildFrom[From, T, To] { - def apply(from: From) = b.apply() ; def apply() = b.apply() - } - -Note that `breakOut` is parameterized, and that it returns an instance of -`CanBuildFrom`. As it happens, the types `From`, `T` and `To` have already been -inferred, because we know that `map` is expecting `CanBuildFrom[List[String], -(Int, String), Map[Int, String]]`. Therefore: - - From = List[String] - T = (Int, String) - To = Map[Int, String] - -To conclude let's examine the implicit received by `breakOut` itself. It is of -type `CanBuildFrom[Nothing,T,To]`. We already know all these types, so we can -determine that we need an implicit of type -`CanBuildFrom[Nothing,(Int,String),Map[Int,String]]`. But is there such a -definition? - -Let's look at `CanBuildFrom`'s definition: - - trait CanBuildFrom[-From, -Elem, +To] - extends AnyRef - -So `CanBuildFrom` is contra-variant on its first type parameter. Because -`Nothing` is a bottom class (ie, it is a subclass of everything), that means -*any* class can be used in place of `Nothing`. - -Since such a builder exists, Scala can use it to produce the desired output. - -### About Builders - -A lot of methods from Scala's collections library consists of taking the -original collection, processing it somehow (in the case of `map`, transforming -each element), and storing the results in a new collection. - -To maximize code reuse, this storing of results is done through a _builder_ -(`scala.collection.mutable.Builder`), which basically supports two operations: -appending elements, and returning the resulting collection. The type of this -resulting collection will depend on the type of the builder. Thus, a `List` -builder will return a `List`, a `Map` builder will return a `Map`, and so on. -The implementation of the `map` method need not concern itself with the type of -the result: the builder takes care of it. - -On the other hand, that means that `map` needs to receive this builder somehow. -The problem faced when designing Scala 2.8 Collections was how to choose the -best builder possible. For example, if I were to write `Map('a' -> -1).map(_.swap)`, I'd like to get a `Map(1 -> 'a')` back. On the other hand, a -`Map('a' -> 1).map(_._1)` can't return a `Map` (it returns an `Iterable`). - -The magic of producing the best possible `Builder` from the known types of the -expression is performed through this `CanBuildFrom` implicit. - -### About CanBuildFrom - -To better explain what's going on, I'll give an example where the collection -being mapped is a `Map` instead of a `List`. I'll go back to `List` later. For -now, consider these two expressions: - - Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length) - Map(1 -> "one", 2 -> "two") map (_._2) - -The first returns a `Map` and the second returns an `Iterable`. The magic of -returning a fitting collection is the work of `CanBuildFrom`. Let's consider -the definition of `map` again to understand it. - -The method `map` is inherited from `TraversableLike`. It is parameterized on -`B` and `That`, and makes use of the type parameters `A` and `Repr`, which -parameterize the class. Let's see both definitions together: - -The class `TraversableLike` is defined as: - - trait TraversableLike[+A, +Repr] - extends HasNewBuilder[A, Repr] with AnyRef - - def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That - - -To understand where `A` and `Repr` come from, let's consider the definition of -`Map` itself: - - trait Map[A, +B] - extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]] - -Because `TraversableLike` is inherited by all traits which extend `Map`, `A` -and `Repr` could be inherited from any of them. The last one gets the -preference, though. So, following the definition of the immutable `Map` and all -the traits that connect it to `TraversableLike`, we have: - - trait Map[A, +B] - extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]] - - trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] - extends MapLike[A, B, This] - - trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] - extends PartialFunction[A, B] with IterableLike[(A, B), This] with Subtractable[A, This] - - trait IterableLike[+A, +Repr] - extends Equals with TraversableLike[A, Repr] - - trait TraversableLike[+A, +Repr] - extends HasNewBuilder[A, Repr] with AnyRef - -If you pass the type parameters of `Map[Int, String]` all the way down the -chain, we find that the types passed to `TraversableLike`, and, thus, used by -`map`, are: - - A = (Int,String) - Repr = Map[Int, String] - -Going back to the example, the first map is receiving a function of type -`((Int, String)) => (Int, Int)` and the second map is receiving a function of -type `((Int, String)) => Int`. I use the double parenthesis to emphasize it is -a tuple being received, as that's the type of `A` as we saw. - -With that information, let's consider the other types. - - map Function.tupled(_ -> _.length): - B = (Int, Int) - - map (_._2): - B = Int - -We can see that the type returned by the first `map` is `Map[Int,Int]`, and the -second is `Iterable[String]`. Looking at `map`'s definition, it is easy to see -that these are the values of `That`. But where do they come from? - -If we look inside the companion objects of the classes involved, we see some -implicit declarations providing them. On object `Map`: - - implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]] - -And on object `Iterable`, whose class is extended by `Map`: - - implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]] - -These definitions provide factories for parameterized `CanBuildFrom`. - -Scala will choose the most specific implicit available. In the first case, it -was the first `CanBuildFrom`. In the second case, as the first did not match, -it chose the second `CanBuildFrom`. - -### Back to the first example - -Let's see the first example, `List`'s and `map`'s definition (again) to -see how the types are inferred: - - val map : Map[Int,String] = List("London", "France").map(x => (x.length, x))(breakOut) - - sealed abstract class List[+A] - extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]] - - trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]] - extends SeqLike[A, Repr] - - trait SeqLike[+A, +Repr] - extends IterableLike[A, Repr] - - trait IterableLike[+A, +Repr] - extends Equals with TraversableLike[A, Repr] - - trait TraversableLike[+A, +Repr] - extends HasNewBuilder[A, Repr] with AnyRef - - def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That - -The type of `List("London", "France")` is `List[String]`, so the types `A` and -`Repr` defined on `TraversableLike` are: - - A = String - Repr = List[String] - -The type for `(x => (x.length, x))` is `(String) => (Int, String)`, so the type -of `B` is: - - B = (Int, String) - -The last unknown type, `That` is the type of the result of `map`, and we -already have that as well: - - val map : Map[Int,String] = - -So, - - That = Map[Int, String] - -That means `breakOut` must, necessarily, return a type or subtype of -`CanBuildFrom[List[String], (Int, String), Map[Int, String]]`. - -This answer was originally submitted in response to [this question on Stack Overflow][1]. - - [1]: http://stackoverflow.com/q/1715681/53013 - diff --git a/_overviews/tutorials/FAQ/chaining-implicits.md b/_overviews/tutorials/FAQ/chaining-implicits.md deleted file mode 100644 index 71424b80d6..0000000000 --- a/_overviews/tutorials/FAQ/chaining-implicits.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -layout: overview -title: How can I chain/nest implicit conversions? - -discourse: true - -partof: FAQ -num: 6 ---- - -The enrich-my-library pattern allows one to seemingly add a method to a class by -making available an implicit conversion from that class to one that implements -the method. - -Scala does not allow two such implicit conversions taking place, however, so -one cannot got from `A` to `C` using an implicit `A` to `B` and another -implicit `B` to `C`. Is there a way around this restriction? - -Scala has a restriction on automatic conversions to add a method, which is that -it won't apply more than one conversion in trying to find methods. For example: - - class A(val n: Int) - class B(val m: Int, val n: Int) - class C(val m: Int, val n: Int, val o: Int) { - def total = m + n + o - } - - import scala.language.implicitConversions - - // This demonstrates implicit conversion chaining restrictions - object T1 { // to make it easy to test on REPL - implicit def toA(n: Int): A = new A(n) - implicit def aToB(a: A): B = new B(a.n, a.n) - implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n) - - // won't work - println(5.total) - println(new A(5).total) - - // works - println(new B(5, 5).total) - println(new C(5, 5, 10).total) - } - -However, if an implicit definition requires an implicit parameter itself, Scala -_will_ look for additional implicit values for as long as needed. Continuing from -the last example: - - object T2 { - implicit def toA(n: Int): A = new A(n) - implicit def aToB[A1](a: A1)(implicit f: A1 => A): B = - new B(a.n, a.n) - implicit def bToC[B1](b: B1)(implicit f: B1 => B): C = - new C(b.m, b.n, b.m + b.n) - - // works - println(5.total) - println(new A(5).total) - println(new B(5, 5).total) - println(new C(5, 5, 10).total) - } - -_"Magic!"_, you might say. Not so. Here is how the compiler would translate each -one: - - object T1Translated { - implicit def toA(n: Int): A = new A(n) - implicit def aToB(a: A): B = new B(a.n, a.n) - implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n) - - // Scala won't do this - println(bToC(aToB(toA(5))).total) - println(bToC(aToB(new A(5))).total) - - // Just this - println(bToC(new B(5, 5)).total) - - // No implicits required - println(new C(5, 5, 10).total) - } - - object T2Translated { - implicit def toA(n: Int): A = new A(n) - implicit def aToB[A1](a: A1)(implicit f: A1 => A): B = - new B(a.n, a.n) - implicit def bToC[B1](b: B1)(implicit f: B1 => B): C = - new C(b.m, b.n, b.m + b.n) - - // Scala does this - println(bToC(5)(x => aToB(x)(y => toA(y))).total) - println(bToC(new A(5))(x => aToB(x)(identity)).total) - println(bToC(new B(5, 5))(identity).total) - - // no implicits required - println(new C(5, 5, 10).total) - } - -So, while `bToC` is being used as an implicit conversion, `aToB` and `toA` are -being passed as _implicit parameters_, instead of being chained as implicit -conversions. - -See also: - -* [Context bounds](context-bounds.html) -* [A discussion on types, origin and precedence of implicits](finding-implicits.html) - -This question and answer were originally submitted on [Stack Overflow][1]. - - [1]: http://stackoverflow.com/questions/5332801/how-can-i-chain-implicits-in-scala/5332804 diff --git a/_overviews/tutorials/FAQ/context-bounds.md b/_overviews/tutorials/FAQ/context-bounds.md deleted file mode 100644 index 45bad40b92..0000000000 --- a/_overviews/tutorials/FAQ/context-bounds.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -layout: overview -title: What are Scala context bounds? - -discourse: true - -partof: FAQ -num: 3 ---- - -What is a Context Bound? ------------------------- - -Context bounds were introduced in Scala 2.8.0, and are typically used with the -so-called _type class pattern_, a pattern of code that emulates the -functionality provided by Haskell type classes, though in a more verbose -manner. - -A context bound requires a _parameterized type_, such as `Ordered[A]`, -but unlike `String`. - -A context bound describes an implicit _value_. It is used to declare that for -some type `A`, there is an -implicit value of type `B[A]` available. The syntax goes like this: - - def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A] - -The common example of usage in Scala is this: - - def f[A : ClassTag](n: Int) = new Array[A](n) - -An `Array` initialization on a parameterized type requires a `ClassTag` to -be available, for arcane reasons related to type erasure and the non-erasure -nature of arrays. - -Another very common example in the library is a bit more complex: - - def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b) - -Here, `implicitly` is used to retrive the implicit value we want, one of type -`Ordering[A]`, which class defines the method `compare(a: A, b: A): Int`. - -We'll see another way of doing this below. - -How are Context Bounds implemented? ---------------------------------------------------- - -It shouldn't be surprising that context bounds are -implemented with implicit parameters, given their definition. Actually, the -syntax I showed are syntactic sugars for what really happens. See below how -they de-sugar: - - def g[A : B](a: A) = h(a) - def g[A](a: A)(implicit ev: B[A]) = h(a) - -So, naturally, one can write them in their full syntax, which is specially -useful for context bounds: - - def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b) - -What are Context Bounds used for? ---------------------------------- - -Context bounds are mainly used in what has become known as _typeclass pattern_, -as a reference to Haskell's type classes. Basically, this pattern implements an -alternative to inheritance by making functionality available through a sort of -implicit adapter pattern. - -The classic example is Scala 2.8's `Ordering`. The usage is: - - def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b - -Though you'll usually see that written like this: - - def f[A](a: A, b: A)(implicit ord: Ordering[A]) = { - import ord._ - if (a < b) a else b - } - -Which take advantage of some implicit conversions inside `Ordering` that enable -the traditional operator style. Another example in Scala 2.8 is the `Numeric`: - - def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b) - -A more complex example is the new collection usage of `CanBuildFrom`, but -there's already a very long answer about that, so I'll avoid it here. And, as -mentioned before, there's the `ClassTag` usage, which is required to -initialize new arrays without concrete types. - -Though it has been possible for a long time, the use of context bounds has -really taken off in 2010, and is now found to some degree in most of Scala's -most important libraries and frameworks. The most extreme example of its usage, -though, is the Scalaz library, which brings a lot of the power of Haskell to -Scala. I recommend reading up on typeclass patterns to get more acquainted it -all the ways in which it can be used. - -Related questions of interest: - -* [A discussion on types, origin and precedence of implicits](finding-implicits.html) -* [Chaining implicits](chaining-implicits.html) - -This answer was originally submitted in response to [this question on Stack Overflow][1]. - - [1]: http://stackoverflow.com/q/4465948/53013 - diff --git a/_overviews/tutorials/FAQ/finding-symbols.md b/_overviews/tutorials/FAQ/finding-symbols.md deleted file mode 100644 index ea980e95bd..0000000000 --- a/_overviews/tutorials/FAQ/finding-symbols.md +++ /dev/null @@ -1,205 +0,0 @@ ---- -layout: overview -title: How do I find what some symbol means or does? - -discourse: true - -partof: FAQ -num: 1 -outof: 9 ---- -We can divide the operators in Scala, for the purpose of teaching, into four categories: - -* Keywords/reserved symbols -* Normal methods or values -* Methods provided by implicit conversion -* Syntactic sugars/composition - -And let's see some arbitrary examples: - - <- // Keyword - -> // Method provided by implicit conversion - <= // Common method - ++= // Can be a common method or syntactic sugar involving ++ method - :: // Common method or object - _+_ // Not really a single operator; it's parsed as _ + _ - -The exact meaning of most of these methods depends on the class they are defined -on. For example, `<=` on `Int` means _"less than or equal to"_, but it might -mean something else in another class. `::` in an expression is probably the method of the class -`List` but it can also refer to the object of the same name (and in a pattern it -definitely does). - -So, let's discuss these categories. - -Keywords/reserved symbols -------------------------- - -There are a few symbols in Scala that are special and cannot be defined or used as method names. -Two of them are considered proper keywords, while others are just "reserved". They are: - - // Keywords - <- // Used on for-comprehensions, to separate pattern from generator - => // Used for function types, function literals and import renaming - - // Reserved - ( ) // Delimit expressions and parameters - [ ] // Delimit type parameters - { } // Delimit blocks - . // Method call and path separator - // /* */ // Comments - # // Used in type notations - : // Type ascription or context bounds - <: >: // Upper and lower bounds - <% // View bounds (deprecated) - " """ // Strings - ' // Indicate symbols and characters - @ // Annotations and variable binding on pattern matching - ` // Denote constant or enable arbitrary identifiers - , // Parameter separator - ; // Statement separator - _* // vararg expansion - _ // Many different meanings - -These are all _part of the language_, and, as such, can be found in any text -that properly describe the language, such as [Scala Specification][1](PDF) -itself. - -The last one, the underscore, deserve a special description, because it is -widely used, and has different meanings depending on the context. Here's a sample: - - import scala._ // Wild card -- all of Scala is imported - import scala.{ Predef => _, _ } // Exclusion, everything except Predef - def f[M[_]] // Higher kinded type parameter - def f(m: M[_]) // Existential type - _ + _ // Anonymous function placeholder parameter - m _ // Eta expansion of method into method value - m(_) // Partial function application - _ => 5 // Discarded parameter - case _ => // Wild card pattern -- matches anything - f(xs: _*) // Sequence xs is passed as multiple parameters to f(ys: T*) - case Seq(xs @ _*) // Identifier xs is bound to the whole matched sequence - -Common methods --------------- - -Many symbols are simply methods of a class, a trait, or an object. For instance, if you do - - List(1, 2) ++ List(3, 4) - -You'll find the method `++` right on the Scaladoc for [List][5]. However, -there's one convention that you must be aware when searching for methods. -Methods ending in colon (`:`) bind _to the right_ instead of the left. In other -words, while the above method call is equivalent to: - - List(1, 2).++(List(3, 4)) - -If I had, instead `1 :: List(2, 3)`, that would be equivalent to: - - List(2, 3).::(1) - -So you need to look at the type found _on the right_ when looking for methods -ending in colon. Consider, for instance: - - 1 +: List(2, 3) :+ 4 - -The first method (`+:`) binds to the right, and is found on `List`. The second -method (`:+`) is just a normal method, and binds to the left -- again, on -`List`. - -If the name ends in `=`, look for the method called the same without `=` and -read the last section. - -If you aren't sure what the type of the receiver is, you can look up the symbol -on the Scaladoc [index page for identifiers not starting with letters][2] (for -standard Scala library; of course, third-party libraries can add their own -symbolic methods, for which you should look at the corresponding page of _their_ -Scaladoc). - -Types and objects can also have symbolic names; in particular, it should be mentioned -that for types with two type parameters the name can be written _between_ parameters, -so that e.g. `Int <:< Any` is the same as `<:<[Int, Any]`. - -Methods provided by implicit conversion ---------------------------------------- - -If you did not find the symbol you are looking for in the list of reserved symbols, then -it must be a method, or part of one. But, often, you'll see some symbol and the -documentation for the class will not have that method. When this happens, -either you are looking at a composition of one or more methods with something -else, or the method has been imported into scope, or is available through an -imported implicit conversion. - -These can also be found in Scaladoc's [index][2], as mentioned above. - -All Scala code has three automatic imports: - - // Not necessarily in this order - import java.lang._ - import scala._ - import scala.Predef._ - -The first two only make classes and singleton objects available, none of which -look like operators. [`Predef`][3] is the only interesting one for this post. - -Looking inside `Predef` shows some symbolic names: - - class <:< - class =:= - object =:= - object <%< // removed in Scala 2.10 - def ??? - -There is also `::`, which doesn't appear in the Scaladoc, but is mentioned in the comments. -In addition, `Predef` makes some methods available through _implicit conversions_. Just -look at the methods and classes with `implicit` modifier that receive, as parameter, an -object of type that is receiving the method. For example, consider `"a" -> 1`. We need -to look for an implicit which works on `"a"`, and so it can take `String`, one of its -supertypes (`AnyRef` or `Any`) or a type parameter. In this case, we find -`implicit final class ArrowAssoc[A](private val self: A)` which makes this implicit -avaialable on all types. - -Other implicit conversions may be visible in your scope depending on imports, extended types or -self-type annotations. See [Finding implicits](tutorials/FAQ/finding-implicits.html) for details. - -Syntactic sugars/composition ------------------------------ - -So, here's a few syntactic sugars that may hide a method: - - class Example(arr: Array[Int] = Array.fill(5)(0)) { - def apply(n: Int) = arr(n) - def update(n: Int, v: Int) = arr(n) = v - def a = arr(0); def a_=(v: Int) = arr(0) = v - def b = arr(1); def b_=(v: Int) = arr(1) = v - def c = arr(2); def c_=(v: Int) = arr(2) = v - def d = arr(3); def d_=(v: Int) = arr(3) = v - def e = arr(4); def e_=(v: Int) = arr(4) = v - def +(v: Int) = new Example(arr map (_ + v)) - def unapply(n: Int) = if (arr.indices contains n) Some(arr(n)) else None - } - - val ex = new Example - println(ex(0)) // means ex.apply(0) - ex(0) = 2 // means ex.update(0, 2) - ex.b = 3 // means ex.b_=(3) - val ex(c) = 2 // calls ex.unapply(2) and assigns result to c, if it's Some; throws MatchError if it's None - ex += 1 // means ex = ex + 1; if Example had a += method, it would be used instead - -The last one is interesting, because *any* symbolic method can be combined with `=` in that way. - -And, of course, all of the above can be combined in various combinations, e.g. - - (_+_) // An expression, or parameter, that is an anonymous function with - // two parameters, used exactly where the underscores appear, and - // which calls the "+" method on the first parameter passing the - // second parameter as argument. - -This answer was originally submitted in response to [this question on Stack Overflow][6]. - - [1]: http://scala-lang.org/files/archive/spec/2.11/ - [2]: http://www.scala-lang.org/api/current/index.html#index.index-_ - [3]: http://www.scala-lang.org/api/current/index.html#scala.Predef$ - [4]: http://www.scala-lang.org/api/current/scala/Predef$$ArrowAssoc.html - [5]: http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List - [6]: http://stackoverflow.com/q/7888944/53013 diff --git a/_overviews/tutorials/FAQ/initialization-order.md b/_overviews/tutorials/FAQ/initialization-order.md deleted file mode 100644 index 57374e4352..0000000000 --- a/_overviews/tutorials/FAQ/initialization-order.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -layout: overview -title: Why is my abstract or overridden val null? - -discourse: true - -partof: FAQ -num: 9 ---- - -## Example -To understand the problem, let's pick the following concrete example. - - abstract class A { - val x1: String - val x2: String = "mom" - - println("A: " + x1 + ", " + x2) - } - class B extends A { - val x1: String = "hello" - - println("B: " + x1 + ", " + x2) - } - class C extends B { - override val x2: String = "dad" - - println("C: " + x1 + ", " + x2) - } - -Let's observe the initialization order through the Scala REPL: - - scala> new C - A: null, null - B: hello, null - C: hello, dad - -Only when we get to the constructor of `C` are both `x1` and `x2` initialized. Therefore, constructors of `A` and `B` risk running into `NullPointerException`s. - -## Explanation -A 'strict' or 'eager' val is one which is not marked lazy. - -In the absence of "early definitions" (see below), initialization of strict vals is done in the following order. - -1. Superclasses are fully initialized before subclasses. -2. Otherwise, in declaration order. - -Naturally when a val is overridden, it is not initialized more than once. So though x2 in the above example is seemingly defined at every point, this is not the case: an overridden val will appear to be null during the construction of superclasses, as will an abstract val. - -There is a compiler flag which can be useful for identifying this situation: - -**-Xcheckinit**: Add runtime check to field accessors. - -It is inadvisable to use this flag outside of testing. It adds significantly to the code size by putting a wrapper around all potentially uninitialized field accesses: the wrapper will throw an exception rather than allow a null (or 0/false in the case of primitive types) to silently appear. Note also that this adds a *runtime* check: it can only tell you anything about code paths which you exercise with it in place. - -Using it on the opening example: - - % scalac -Xcheckinit a.scala - % scala -e 'new C' - scala.UninitializedFieldError: Uninitialized field: a.scala: 13 - at C.x2(a.scala:13) - at A.(a.scala:5) - at B.(a.scala:7) - at C.(a.scala:12) - -### Solutions ### - -Approaches for avoiding null values include: - -#### Use lazy vals #### - - abstract class A { - val x1: String - lazy val x2: String = "mom" - - println("A: " + x1 + ", " + x2) - } - class B extends A { - lazy val x1: String = "hello" - - println("B: " + x1 + ", " + x2) - } - class C extends B { - override lazy val x2: String = "dad" - - println("C: " + x1 + ", " + x2) - } - // scala> new C - // A: hello, dad - // B: hello, dad - // C: hello, dad - -Usually the best answer. Unfortunately you cannot declare an abstract lazy val. If that is what you're after, your options include: - -1. Declare an abstract strict val, and hope subclasses will implement it as a lazy val or with an early definition. If they do not, it will appear to be uninitialized at some points during construction. -2. Declare an abstract def, and hope subclasses will implement it as a lazy val. If they do not, it will be re-evaluated on every access. -3. Declare a concrete lazy val which throws an exception, and hope subclasses override it. If they do not, it will... throw an exception. - -An exception during initialization of a lazy val will cause the right hand side to be re-evaluated on the next access: see SLS 5.2. - -Note that using multiple lazy vals creates a new risk: cycles among lazy vals can result in a stack overflow on first access. - -#### Use early definitions #### - abstract class A { - val x1: String - val x2: String = "mom" - - println("A: " + x1 + ", " + x2) - } - class B extends { - val x1: String = "hello" - } with A { - println("B: " + x1 + ", " + x2) - } - class C extends { - override val x2: String = "dad" - } with B { - println("C: " + x1 + ", " + x2) - } - // scala> new C - // A: hello, dad - // B: hello, dad - // C: hello, dad - -Early definitions are a bit unwieldy, there are limitations as to what can appear and what can be referenced in an early definitions block, and they don't compose as well as lazy vals: but if a lazy val is undesirable, they present another option. They are specified in SLS 5.1.6. - -#### Use constant value definitions #### - abstract class A { - val x1: String - val x2: String = "mom" - - println("A: " + x1 + ", " + x2) - } - class B extends A { - val x1: String = "hello" - final val x3 = "goodbye" - - println("B: " + x1 + ", " + x2) - } - class C extends B { - override val x2: String = "dad" - - println("C: " + x1 + ", " + x2) - } - abstract class D { - val c: C - val x3 = c.x3 // no exceptions! - println("D: " + c + " but " + x3) - } - class E extends D { - val c = new C - println(s"E: ${c.x1}, ${c.x2}, and $x3...") - } - //scala> new E - //D: null but goodbye - //A: null, null - //B: hello, null - //C: hello, dad - //E: hello, dad, and goodbye... - -Sometimes all you need from an interface is a compile-time constant. - -Constant values are stricter than strict and earlier than early definitions and have even more limitations, -as they must be constants. They are specified in SLS 4.1. diff --git a/_overviews/tutorials/FAQ/yield.md b/_overviews/tutorials/FAQ/yield.md deleted file mode 100644 index cb579ec155..0000000000 --- a/_overviews/tutorials/FAQ/yield.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -layout: overview -title: How does yield work? - -discourse: true - -partof: FAQ -num: 2 ---- -Though there's a `yield` in other languages such as Python and Ruby, Scala's -`yield` does something very different from them. In Scala, `yield` is part -of for comprehensions -- a generalization of Ruby and Python's list-comprehensions. - -Scala's "for comprehensions" are equivalent to Haskell's "do" notation, and it -is nothing more than a syntactic sugar for composition of multiple monadic -operations. As this statement will most likely not help anyone who needs help, -let's try again... - -Translating for-comprehensions ------------------------------- - -Scala's "for comprehensions" are syntactic sugar for composition of multiple -operations with `foreach`, `map`, `flatMap`, `filter` or `withFilter`. -Scala actually translates a for-expression into calls to those methods, -so any class providing them, or a subset of them, can be used with for comprehensions. - -First, let's talk about the translations. There are very simple rules: - -#### Example 1 - - for(x <- c1; y <- c2; z <-c3) {...} - -is translated into - - c1.foreach(x => c2.foreach(y => c3.foreach(z => {...}))) - -#### Example 2 - - for(x <- c1; y <- c2; z <- c3) yield {...} - -is translated into - - c1.flatMap(x => c2.flatMap(y => c3.map(z => {...}))) - -#### Example 3 - - for(x <- c; if cond) yield {...} - -is translated into - - c.withFilter(x => cond).map(x => {...}) - -with a fallback into - - c.filter(x => cond).map(x => {...}) - -if method `withFilter` is not available but `filter` is. -The next chapter has more information on this. - -#### Example 4 - - for(x <- c; y = ...) yield {...} - -is translated into - - c.map(x => (x, ...)).map((x,y) => {...}) - - -When you look at very simple for comprehensions, the map/foreach alternatives -look, indeed, better. Once you start composing them, though, you can easily get -lost in parenthesis and nesting levels. When that happens, for comprehensions -are usually much clearer. - -I'll show one simple example, and intentionally omit any explanation. You can -decide which syntax is easier to understand. - - l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length)) - -or - - for{ - sl <- l - el <- sl - if el > 0 - } yield el.toString.length - - -About withFilter, and strictness ----------------------------------- - -Scala 2.8 introduced a method called `withFilter`, whose main difference is -that, instead of returning a new, filtered, collection, it filters on-demand. -The `filter` method has its behavior defined based on the strictness of the -collection. To understand this better, let's take a look at some Scala 2.7 with -`List` (strict) and `Stream` (non-strict): - - scala> var found = false - found: Boolean = false - - scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) - 1 - 3 - 7 - 9 - - scala> found = false - found: Boolean = false - - scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) - 1 - 3 - -The difference happens because filter is immediately applied with `List`, -returning a list of odds -- since `found` is `false`. Only then `foreach` is -executed, but, by this time, changing `found` is meaningless, as `filter` has -already executed. - -In the case of `Stream`, the condition is not immediatelly applied. Instead, as -each element is requested by `foreach`, `filter` tests the condition, which -enables `foreach` to influence it through `found`. Just to make it clear, here -is the equivalent for-comprehension code: - - for (x <- List.range(1, 10); if x % 2 == 1 && !found) - if (x == 5) found = true else println(x) - - for (x <- Stream.range(1, 10); if x % 2 == 1 && !found) - if (x == 5) found = true else println(x) - -This caused many problems, because people expected the `if` to be considered -on-demand, instead of being applied to the whole collection beforehand. - -Scala 2.8 introduced `withFilter`, which is _always_ non-strict, no matter the -strictness of the collection. The following example shows `List` with both -methods on Scala 2.8: - - scala> var found = false - found: Boolean = false - - scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) - 1 - 3 - 7 - 9 - - scala> found = false - found: Boolean = false - - scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) - 1 - 3 - -This produces the result most people expect, without changing how `filter` -behaves. As a side note, `Range` was changed from non-strict to strict between -Scala 2.7 and Scala 2.8. - -This answer was originally submitted in response to [this question on Stack Overflow][1]. - - [1]: http://stackoverflow.com/questions/1052476/can-someone-explain-scalas-yield/1052510#1052510 diff --git a/_tutorials/FAQ/chaining-implicits.md b/_tutorials/FAQ/chaining-implicits.md deleted file mode 100644 index a06d40e4f3..0000000000 --- a/_tutorials/FAQ/chaining-implicits.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -layout: overview-large -title: How can I chain/nest implicit conversions? - -discourse: true - -partof: FAQ -num: 6 ---- - -The enrich-my-library pattern allows one to seemingly add a method to a class by -making available an implicit conversion from that class to one that implements -the method. - -Scala does not allow two such implicit conversions taking place, however, so -one cannot got from `A` to `C` using an implicit `A` to `B` and another -implicit `B` to `C`. Is there a way around this restriction? - -Scala has a restriction on automatic conversions to add a method, which is that -it won't apply more than one conversion in trying to find methods. For example: - - class A(val n: Int) - class B(val m: Int, val n: Int) - class C(val m: Int, val n: Int, val o: Int) { - def total = m + n + o - } - - import scala.language.implicitConversions - - // This demonstrates implicit conversion chaining restrictions - object T1 { // to make it easy to test on REPL - implicit def toA(n: Int): A = new A(n) - implicit def aToB(a: A): B = new B(a.n, a.n) - implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n) - - // won't work - println(5.total) - println(new A(5).total) - - // works - println(new B(5, 5).total) - println(new C(5, 5, 10).total) - } - -However, if an implicit definition requires an implicit parameter itself, Scala -_will_ look for additional implicit values for as long as needed. Continuing from -the last example: - - object T2 { - implicit def toA(n: Int): A = new A(n) - implicit def aToB[A1](a: A1)(implicit f: A1 => A): B = - new B(a.n, a.n) - implicit def bToC[B1](b: B1)(implicit f: B1 => B): C = - new C(b.m, b.n, b.m + b.n) - - // works - println(5.total) - println(new A(5).total) - println(new B(5, 5).total) - println(new C(5, 5, 10).total) - } - -_"Magic!"_, you might say. Not so. Here is how the compiler would translate each -one: - - object T1Translated { - implicit def toA(n: Int): A = new A(n) - implicit def aToB(a: A): B = new B(a.n, a.n) - implicit def bToC(b: B): C = new C(b.m, b.n, b.m + b.n) - - // Scala won't do this - println(bToC(aToB(toA(5))).total) - println(bToC(aToB(new A(5))).total) - - // Just this - println(bToC(new B(5, 5)).total) - - // No implicits required - println(new C(5, 5, 10).total) - } - - object T2Translated { - implicit def toA(n: Int): A = new A(n) - implicit def aToB[A1](a: A1)(implicit f: A1 => A): B = - new B(a.n, a.n) - implicit def bToC[B1](b: B1)(implicit f: B1 => B): C = - new C(b.m, b.n, b.m + b.n) - - // Scala does this - println(bToC(5)(x => aToB(x)(y => toA(y))).total) - println(bToC(new A(5))(x => aToB(x)(identity)).total) - println(bToC(new B(5, 5))(identity).total) - - // no implicits required - println(new C(5, 5, 10).total) - } - -So, while `bToC` is being used as an implicit conversion, `aToB` and `toA` are -being passed as _implicit parameters_, instead of being chained as implicit -conversions. - -See also: - -* [Context bounds](context-bounds.html) -* [A discussion on types, origin and precedence of implicits](finding-implicits.html) - -This question and answer were originally submitted on [Stack Overflow][1]. - - [1]: http://stackoverflow.com/questions/5332801/how-can-i-chain-implicits-in-scala/5332804 diff --git a/_tutorials/FAQ/collections.md b/_tutorials/FAQ/collections.md deleted file mode 100644 index e7f9d0f159..0000000000 --- a/_tutorials/FAQ/collections.md +++ /dev/null @@ -1,376 +0,0 @@ ---- -layout: overview-large -title: How are the collections structured? Which one should I choose? - -discourse: true - -partof: FAQ -num: 8 ---- -## Foreword - -There's a [2.8 collection walk-through][1] by Martin Odersky which should -probably be your first reference. It has been supplemented as well with -[architectural notes][2], which will be of particular interest to those who -want to design their own collections. - -The rest of this answer was written way before any such thing existed (in fact, -before 2.8.0 itself was released). - -You can find a paper about it as [Scala SID #3][3]. Other papers in that area -should be interesting as well to people interested in the differences between -Scala 2.7 and 2.8. - -I'll quote from the paper, selectively, and complement with some thoughts of -mine. There are also some images, generated by Matthias at decodified.com, and -the original SVG files can be found [here][4]. - -## The collection classes/traits themselves - -There are actually three hierarchies of traits for the collections: one for -mutable collections, one for immutable collections, and one which doesn't make -any assumptions about the collections. - -There's also a distinction between parallel, serial and maybe-parallel -collections, which was introduced with Scala 2.9. I'll talk about them in the -next section. The hierarchy described in this section refers _exclusively to -non-parallel collections_. - -The following image shows the non-specific hierarchy introduced with Scala 2.8: -![General collection hierarchy][5] - -All elements shown are traits. In the other two hierarchies there are also -classes directly inheriting the traits as well as classes which can be _viewed -as_ belonging in that hierarchy through implicit conversion to wrapper classes. -The legend for these graphs can be found after them. - -Graph for immutable hierarchy: - - -Graph for mutable hierarchy: - - -Legend: - -![Graph legend][8] - -Here's an abbreviated ASCII depiction of the collection hierarchy, for those who can't see the images. - - Traversable - | - | - Iterable - | - +------------------+--------------------+ - Map Set Seq - | | | - | +----+----+ +-----+------+ - Sorted Map SortedSet BitSet Buffer Vector LinearSeq - - -## Parallel Collections - -When Scala 2.9 introduced parallel collections, one of the design goals was to -make their use as seamless as possible. In the simplest terms, one can replace -a non-parallel (serial) collection with a parallel one, and instantly reap the -benefits. - -However, since all collections until then were serial, many algorithms using -them assumed and depended on the fact that they _were_ serial. Parallel -collections fed to the methods with such assumptions would fail. For this -reason, all the hierarchy described in the previous section _mandates serial -processing_. - -Two new hierarchies were created to support the parallel collections. - -The parallel collections hierarchy has the same names for traits, but preceded -with `Par`: `ParIterable`, `ParSeq`, `ParMap` and `ParSet`. Note that there is -no `ParTraversable`, since any collection supporting parallel access is capable -of supporting the stronger `ParIterable` trait. It doesn't have some of the -more specialized traits present in the serial hierarchy either. This whole -hierarchy is found under the directory `scala.collection.parallel`. - -The classes implementing parallel collections also differ, with `ParHashMap` -and `ParHashSet` for both mutable and immutable parallel collections, plus -`ParRange` and `ParVector` implementing `immutable.ParSeq` and `ParArray` -implementing `mutable.ParSeq`. - -Another hierarchy also exists that mirrors the traits of serial and parallel -collections, but with a prefix `Gen`: `GenTraversable`, `GenIterable`, -`GenSeq`, `GenMap` and `GenSet`. These traits are _parents_ to both parallel -and serial collections. This means that a method taking a `Seq` cannot receive -a parallel collection, but a method taking a `GenSeq` is expected to work with -both serial and parallel collections. - -Given the way these hierarchies were structured, code written for Scala 2.8 was -fully compatible with Scala 2.9, and demanded serial behavior. Without being -rewritten, it cannot take advantage of parallel collections, but the changes -required are very small. - -### Using Parallel Collections - -Any collection can be converted into a parallel one by calling the method `par` -on it. Likewise, any collection can be converted into a serial one by calling -the method `seq` on it. - -If the collection was already of the type requested (parallel or serial), no -conversion will take place. If one calls `seq` on a parallel collection or -`par` on a serial collection, however, a new collection with the requested -characteristic will be generated. - -Do not confuse `seq`, which turns a collection into a non-parallel collection, -with `toSeq`, which returns a `Seq` created from the elements of the -collection. Calling `toSeq` on a parallel collection will return a `ParSeq`, -not a serial collection. - -## The Main Traits - -While there are many implementing classes and subtraits, there are some basic -traits in the hierarchy, each of which providing more methods or more specific -guarantees, but reducing the number of classes that could implement them. - -In the following subsections, I'll give a brief description of the main traits -and the idea behind them. - -### Trait TraversableOnce - -This trait is pretty much like trait `Traversable` described below, but with -the limitation that you can only use it _once_. That is, any methods called on -a `TraversableOnce` _may_ render it unusable. - -This limitation makes it possible for the same methods to be shared between the -collections and `Iterator`. This makes it possible for a method that works with -an `Iterator` but not using `Iterator`-specific methods to actually be able to -work with any collection at all, plus iterators, if rewritten to accept -`TraversableOnce`. - -Because `TraversableOnce` unifies collections and iterators, and iterators are -not considered collections, it does not appear in the previous graphs, which -concern themselves only with collections. - -### Trait Traversable - -At the top of the _collection_ hierarchy is trait `Traversable`. Its only -abstract operation is - - def foreach[U](f: Elem => U) - -The operation is meant to traverse all elements of the collection, and apply -the given operation f to each element. The application is done for its side -effect only; in fact any function result of f is discarded by foreach. - -Traversible objects can be finite or infinite. An example of an infinite -traversable object is the stream of natural numbers `Stream.from(0)`. The -method `hasDefiniteSize` indicates whether a collection is possibly infinite. -If `hasDefiniteSize` returns true, the collection is certainly finite. If it -returns false, the collection has not been not fully elaborated yet, so it -might be infinite or finite. - -This class defines methods which can be efficiently implemented in terms of -`foreach` (over 40 of them). - -### Trait Iterable - -This trait declares an abstract method `iterator` that returns an iterator that -yields all the collection’s elements one by one. The `foreach` method in -`Iterable` is implemented in terms of `iterator`. Subclasses of `Iterable` -often override foreach with a direct implementation for efficiency. - -Class `Iterable` also adds some less-often used methods to `Traversable`, which -can be implemented efficiently only if an `iterator` is available. They are -summarized below. - - xs.iterator An iterator that yields every element in xs, in the same order as foreach traverses elements. - xs takeRight n A collection consisting of the last n elements of xs (or, some arbitrary n elements, if no order is defined). - xs dropRight n The rest of the collection except xs takeRight n. - xs sameElements ys A test whether xs and ys contain the same elements in the same order - -### Seq, Set and Map - -After `Iterable` there come three base traits which inherit from it: `Seq`, -`Set`, and `Map`. All three have an `apply` method and all three implement the -`PartialFunction` trait, but the meaning of `apply` is different in each case. - -I trust the meaning of `Seq`, `Set` and `Map` is intuitive. After them, the -classes break up in specific implementations that offer particular guarantees -with regards to performance, and the methods it makes available as a result of -it. Also available are some traits with further refinements, such as -`LinearSeq`, `IndexedSeq` and `SortedSet`. - -## Complete Overview - -### Base Classes and Traits - -* `TraversableOnce` -- All methods and behavior common to collections and iterators. - - * `Traversable` -- Basic collection class. Can be implemented just with `foreach`. - - * `TraversableProxy` -- Proxy for a `Traversable`. Just point `self` to the real collection. - * `TraversableView` -- A Traversable with some non-strict methods. - * `TraversableForwarder` -- Forwards most methods to `underlying`, except `toString`, `hashCode`, `equals`, `stringPrefix`, `newBuilder`, `view` and all calls creating a new iterable object of the same kind. - * `mutable.Traversable` and `immutable.Traversable` -- same thing as `Traversable`, but restricting the collection type. - * Other special-cases `Iterable` classes, such as `MetaData`, exists. - * `Iterable` -- A collection for which an `Iterator` can be created (through `iterator`). - * `IterableProxy`, `IterableView`, `mutable` and `immutable.Iterable`. - - * `Iterator` -- A trait which is not descendant of `Traversable`. Define `next` and `hasNext`. - * `CountedIterator` -- An `Iterator` defining `count`, which returns the elements seen so far. - * `BufferedIterator` -- Defines `head`, which returns the next element without consuming it. - * Other special-cases `Iterator` classes, such as `Source`, exists. - -### The Sequences - -* `Seq` -- A sequence of elements. One assumes a well-defined size and element repetition. Extends `PartialFunction` as well. - - * `IndexedSeq` -- Sequences that support O(1) element access and O(1) length computation. - * `IndexedSeqView` - * `immutable.PagedSeq` -- An implementation of `IndexedSeq` where the elements are produced on-demand by a function passed through the constructor. - * `immutable.IndexedSeq` - - * `immutable.Range` -- A delimited sequence of integers, closed on the lower end, open on the high end, and with a step. - * `immutable.Range.Inclusive` -- A `Range` closed on the high end as well. - * `immutable.NumericRange` -- A more generic version of `Range` which works with any `Integral`. - * `immutable.NumericRange.Inclusive`, `immutable.NumericRange.Exclusive`. - * `immutable.WrappedString`, `immutable.RichString` -- Wrappers which enables seeing a `String` as a `Seq[Char]`, while still preserving the `String` methods. I'm not sure what the difference between them is. - - * `mutable.IndexedSeq` - * `mutable.GenericArray` -- An `Seq`-based array-like structure. Note that the "class" `Array` is Java's `Array`, which is more of a memory storage method than a class. - * `mutable.ResizableArray` -- Internal class used by classes based on resizable arrays. - * `mutable.PriorityQueue`, `mutable.SynchronizedPriorityQueue` -- Classes implementing prioritized queues -- queues where the elements are dequeued according to an `Ordering` first, and order of queueing last. - * `mutable.PriorityQueueProxy` -- an abstract `Proxy` for a `PriorityQueue`. - - * `LinearSeq` -- A trait for linear sequences, with efficient time for `isEmpty`, `head` and `tail`. - - * `immutable.LinearSeq` - * `immutable.List` -- An immutable, singlely-linked, list implementation. - * `immutable.Stream` -- A lazy-list. Its elements are only computed on-demand, but memoized (kept in memory) afterwards. It can be theoretically infinite. - * `mutable.LinearSeq` - * `mutable.DoublyLinkedList` -- A list with mutable `prev`, `head` (`elem`) and `tail` (`next`). - * `mutable.LinkedList` -- A list with mutable `head` (`elem`) and `tail` (`next`). - * `mutable.MutableList` -- A class used internally to implement classes based on mutable lists. - * `mutable.Queue`, `mutable.QueueProxy` -- A data structure optimized for FIFO (First-In, First-Out) operations. - * `mutable.QueueProxy` -- A `Proxy` for a `mutable.Queue`. - - * `SeqProxy`, `SeqView`, `SeqForwarder` - - * `immutable.Seq` - - * `immutable.Queue` -- A class implementing a FIFO-optimized (First-In, First-Out) data structure. There is no common superclass of both `mutable` and `immutable` queues. - * `immutable.Stack` -- A class implementing a LIFO-optimized (Last-In, First-Out) data structure. There is no common superclass of both `mutable` `immutable` stacks. - * `immutable.Vector` -- ? - * `scala.xml.NodeSeq` -- A specialized XML class which extends `immutable.Seq`. - * `immutable.IndexedSeq` -- As seen above. - * `immutable.LinearSeq` -- As seen above. - - * `mutable.ArrayStack` -- A class implementing a LIFO-optimized data structure using arrays. Supposedly significantly faster than a normal stack. - * `mutable.Stack`, `mutable.SynchronizedStack` -- Classes implementing a LIFO-optimized data structure. - * `mutable.StackProxy` -- A `Proxy` for a `mutable.Stack`.. - * `mutable.Seq` - - * `mutable.Buffer` -- Sequence of elements which can be changed by appending, prepending or inserting new members. - * `mutable.ArrayBuffer` -- An implementation of the `mutable.Buffer` class, with constant amortized time for the append, update and random access operations. It has some specialized subclasses, such as `NodeBuffer`. - * `mutable.BufferProxy`, `mutable.SynchronizedBuffer`. - * `mutable.ListBuffer` -- A buffer backed by a list. It provides constant time append and prepend, with most other operations being linear. - * `mutable.ObservableBuffer` -- A *mixin* trait which, when mixed to a `Buffer`, provides notification events through a `Publisher` interfaces. - * `mutable.IndexedSeq` -- As seen above. - * `mutable.LinearSeq` -- As seen above. - -### The Sets - -* `Set` -- A set is a collection that includes at most one of any object. - - * `BitSet` -- A set of integers stored as a bitset. - * `immutable.BitSet` - * `mutable.BitSet` - - * `SortedSet` -- A set whose elements are ordered. - * `immutable.SortedSet` - * `immutable.TreeSet` -- An implementation of a `SortedSet` based on a tree. - - * `SetProxy` -- A `Proxy` for a `Set`. - - * `immutable.Set` - * `immutable.HashSet` -- An implementation of `Set` based on element hashing. - * `immutable.ListSet` -- An implementation of `Set` based on lists. - * Additional set classes exists to provide optimized implementations for sets from 0 to 4 elements. - * `immutable.SetProxy` -- A `Proxy` for an immutable `Set`. - - * `mutable.Set` - * `mutable.HashSet` -- An implementation of `Set` based on element hashing. - * `mutable.ImmutableSetAdaptor` -- A class implementing a mutable `Set` from an immutable `Set`. - * `LinkedHashSet` -- An implementation of `Set` based on lists. - * `ObservableSet` -- A *mixin* trait which, when mixed with a `Set`, provides notification events through a `Publisher` interface. - * `SetProxy` -- A `Proxy` for a `Set`. - * `SynchronizedSet` -- A *mixin* trait which, when mixed with a `Set`, provides notification events through a `Publisher` interface. - -### The Maps - -* `Map` -- An `Iterable` of `Tuple2`, which also provides methods for retrieving a value (the second element of the tuple) given a key (the first element of the tuple). Extends `PartialFunction` as well. - * `MapProxy` -- A `Proxy` for a `Map`. - * `DefaultMap` -- A trait implementing some of `Map`'s abstract methods. - * `SortedMap` -- A `Map` whose keys are sorted. - * `immutable.SortMap` - * `immutable.TreeMap` -- A class implementing `immutable.SortedMap`. - * `immutable.Map` - * `immutable.MapProxy` - * `immutable.HashMap` -- A class implementing `immutable.Map` through key hashing. - * `immutable.IntMap` -- A class implementing `immutable.Map` specialized for `Int` keys. Uses a tree based on the binary digits of the keys. - * `immutable.ListMap` -- A class implementing `immutable.Map` through lists. - * `immutable.LongMap` -- A class implementing `immutable.Map` specialized for `Long` keys. See `IntMap`. - * There are additional classes optimized for an specific number of elements. - * `mutable.Map` - * `mutable.HashMap` -- A class implementing `mutable.Map` through key hashing. - * `mutable.ImmutableMapAdaptor` -- A class implementing a `mutable.Map` from an existing `immutable.Map`. - * `mutable.LinkedHashMap` -- ? - * `mutable.ListMap` -- A class implementing `mutable.Map` through lists. - * `mutable.MultiMap` -- A class accepting more than one distinct value for each key. - * `mutable.ObservableMap` -- A *mixin* which, when mixed with a `Map`, publishes events to observers through a `Publisher` interface. - * `mutable.OpenHashMap` -- A class based on an open hashing algorithm. - * `mutable.SynchronizedMap` -- A *mixin* which should be mixed with a `Map` to provide a version of it with synchronized methods. - * `mutable.MapProxy`. - -## Bonus Questions - -* Why the Like classes exist (e.g. TraversableLike)? - -This was done to achieve maximum code reuse. The concrete *generic* -implementation for classes with a certain structure (a traversable, a map, etc) -is done in the Like classes. The classes intended for general consumption, -then, override selected methods that can be optmized. - -* What the companion methods are for (e.g. List.companion)? - -The builder for the classes, ie, the object which knows how to create instances -of that class in a way that can be used by methods like `map`, is created by a -method in the companion object. So, in order to build an object of type X, I -need to get that builder from the companion object of X. Unfortunately, there -is no way, in Scala, to get from class X to object X. Because of that, there is -a method defined in each instance of X, `companion`, which returns the -companion object of class X. - -While there might be some use for such method in normal programs, its target is -enabling code reuse in the collection library. - -* How I know what implicit objects are in scope at a given point? - -You aren't supposed to care about that. They are implicit precisely so that you -don't need to figure out how to make it work. - -These implicits exists to enable the methods on the collections to be defined -on parent classes but still return a collection of the same type. For example, -the `map` method is defined on `TraversableLike`, but if you used on a `List` -you'll get a `List` back. - -This answer was originally submitted in response to [this question][9] on Stack -Overflow. - - - [1]: http://docs.scala-lang.org/overviews/collections/introduction.html - [2]: http://docs.scala-lang.org/overviews/core/architecture-of-scala-collections.html - [3]: http://www.scala-lang.org/sid/3 - [4]: https://github.com/sirthias/scala-collections-charts/downloads - [5]: http://i.stack.imgur.com/bSVyA.png - [6]: http://i.stack.imgur.com/2fjoA.png - [7]: http://i.stack.imgur.com/Dsptl.png - [8]: http://i.stack.imgur.com/szWUr.png - [9]: http://stackoverflow.com/q/1722137/53013 diff --git a/_tutorials/FAQ/finding-implicits.md b/_tutorials/FAQ/finding-implicits.md deleted file mode 100644 index f883a7b678..0000000000 --- a/_tutorials/FAQ/finding-implicits.md +++ /dev/null @@ -1,327 +0,0 @@ ---- -layout: overview-large -title: Where does Scala look for implicits? - -discourse: true - -partof: FAQ -num: 7 ---- - -Newcomers to Scala often ask: Where does the compiler look for implicits? - -For example, where do the values for `integral` below come from? - - scala> import scala.math._ - import scala.math._ - - scala> def foo[T](t: T)(implicit integral: Integral[T]): Unit = { - println(integral) - } - foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit - - scala> foo(0) - scala.math.Numeric$IntIsIntegral$@3dbea611 - - scala> foo(0L) - scala.math.Numeric$LongIsIntegral$@48c610af - -The natural continuation of this line of inquiry leads to a second question: How -does the compiler choose which implicit to use, in certain situations of apparent -ambiguity (but that compile anyway)? - -For instance, `scala.Predef` defines two conversions from `String`: one to -`WrappedString` and another to `StringOps`. Both classes, however, share a lot -of methods, so why doesn't Scala complain about ambiguity when, say, calling -`map`? - -**Note:** this question was inspired by [this other question on Stack -Overflow][4], but states the problem in more general terms. The example was -copied from there, because it is referred to in the answer. - -## Types of Implicits - -Implicits in Scala refers to either a value that can be passed "automatically", -so to speak, or a conversion from one type to another that is made -automatically. - -### Implicit Conversion - -Speaking very briefly about the latter type, if one calls a method `m` on an -object `o` of a class `C`, and that class does not support method `m`, then -Scala will look for an implicit conversion from `C` to something that _does_ -support `m`. A simple example would be the method `map` on `String`: - - "abc".map(_.toInt) - -`String` does not support the method `map`, but `StringOps` does, and there's -an implicit conversion from `String` to `StringOps` available (see `implicit -def augmentString` on `Predef`). - -### Implicit Parameters - -The other kind of implicit is the implicit _parameter_. These are passed to -method calls like any other parameter, but the compiler tries to fill them in -automatically. If it can't, it will complain. One _can_ pass these parameters -explicitly, which is how one uses `breakOut`, for example (see question about -`breakOut`, on a day you are feeling up for a challenge). - -In this case, one has to declare the need for an implicit, such as the `foo` -method declaration: - - def foo[T](t: T)(implicit integral: Integral[T]): Unit = { - println(integral) - } - -### Implicit conversions as implicit parameters - -There's one situation where an implicit is both an implicit conversion and an -implicit parameter. For example: - - def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value) - - getIndex("abc", 'a') - -The method `getIndex` can receive any object, as long as there is an implicit -conversion available from its class to `Seq[T]`. Because of that, a `String` can be -passed to `getIndex`, and it will work. - -Behind the scenes, the compiler changes `seq.indexOf(value)` to -`conv(seq).indexOf(value)`. - -### Context Bounds - -Another common pattern in implicit parameters is the _type class pattern_. This -pattern enables the provision of common interfaces to classes which did not -declare them. It can both serve as a bridge pattern -- gaining separation of -concerns -- and as an adapter pattern. - -The `Integral` class mentioned above is a classic example of type class pattern. -Another example on Scala's standard library is `Ordering`. Scalaz is a library -that makes heavy use of this pattern. - -This is an example of its use: - - def sum[T](list: List[T])(implicit integral: Integral[T]): T = { - import integral._ // get the implicits in question into scope - list.foldLeft(integral.zero)(_ + _) - } - -There is also a syntactic sugar for it, called a _context bound_, which is made -less useful by the need to refer to the implicit. A straight conversion of that -method looks like this: - - def sum[T : Integral](list: List[T]): T = { - val integral = implicitly[Integral[T]] - import integral._ // get the implicits in question into scope - list.foldLeft(integral.zero)(_ + _) - } - -Context bounds are more useful when you just need to _pass_ them to other -methods that use them. For example, the method `sorted` on `Seq` needs an -implicit `Ordering`. To create a method `reverseSort`, one could write: - - def reverseSort[T : Ordering](seq: Seq[T]) = seq.reverse.sorted - -Because `Ordering[T]` was implicitly passed to `reverseSort`, it can then pass -it implicitly to `sorted`. - -## Where do Implicits Come From? - -When the compiler sees the need for an implicit, either because you are calling -a method which does not exist on the object's class, or because you are calling -a method that requires an implicit parameter, it will search for an implicit -that will fit the need. - -This search obeys certain rules that define which implicits are visible and -which are not. The following table showing where the compiler will search for -implicits was taken from an excellent [presentation][1] about implicits by Josh -Suereth, which is heartily recommend to anyone wanting to improve their Scala -knowledge. It has been complemented since then with feedback and updates. - -The implicits available under number 1 below have precedence over the ones under -number 2. Other than that, if there are several eligible arguments which match -the implicit parameter’s type, a most specific one will be chosen using the rules -of static overloading resolution (see [Scala Specification][5] §6.26.4). - -1. First look in current scope - * Implicits defined in current scope - * Explicit imports - * wildcard imports - * Same scope in other files -2. Now look at associated types in - * Companion objects of a type - * Implicit scope of an argument's type **(2.9.1)** - * Implicit scope of type arguments **(2.8.0)** - * Outer objects for nested types - -Let's give examples for them. - -### Implicits Defined in Current Scope - - implicit val n: Int = 5 - def add(x: Int)(implicit y: Int) = x + y - add(5) // takes n from the current scope, res: Int = 10 - -### Explicit Imports - - import scala.collection.JavaConversions.mapAsScalaMap - def env = System.getenv() // Java map - val term = env("TERM") // implicit conversion from Java Map to Scala Map - -### Wildcard Imports - - def sum[T : Integral](list: List[T]): T = { - val integral = implicitly[Integral[T]] - import integral._ // get the implicits in question into scope - list.foldLeft(integral.zero)(_ + _) - } - -### Same Scope in Other Files - -**Edit**: It seems this does not have a different precedence. If you have some -example that demonstrates a precedence distinction, please make a comment. -Otherwise, don't rely on this one. - -This is like the first example, but assuming the implicit definition is in a -different file than its usage. See also how [package objects][2] might be used -in to bring in implicits. - -### Companion Objects of a Type - -There are two object companions of note here. First, the object companion of -the "source" type is looked into. For instance, inside the object `Option` -there is an implicit conversion to `Iterable`, so one can call `Iterable` -methods on `Option`, or pass `Option` to something expecting an `Iterable`. For -example: - - for { - x <- List(1, 2, 3) - y <- Some('x') - } yield (x, y) - -That expression is translated by the compiler into - - List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y))) - -However, `List.flatMap` expects a `TraversableOnce`, which `Option` is not. The -compiler then looks inside `Option`'s object companion and finds the conversion -to `Iterable`, which is a `TraversableOnce`, making this expression correct. - -Second, the companion object of the expected type: - - List(1, 2, 3).sorted - -The method `sorted` takes an implicit `Ordering`. In this case, it looks inside -the object `Ordering`, companion to the class `Ordering`, and finds an implicit -`Ordering[Int]` there. - -Note that companion objects of super classes are also looked into. For example: - - class A(val n: Int) - object A { - implicit def str(a: A) = "A: %d" format a.n - } - class B(val x: Int, y: Int) extends A(y) - val b = new B(5, 2) - val s: String = b // s == "A: 2" - -This is how Scala found the implicit `Numeric[Int]` and `Numeric[Long]` in the -opening example, by the way, as they are found inside `Numeric`, not `Integral`. - -### Implicit scope of an argument's type - -If you have a method with an argument type `A`, then the implicit scope of type -`A` will also be considered. Here "implicit scope" means all these rules -will be applied recursively -- for example, the companion object of `A` will be -searched for implicits, as per the rule above. - -Note that this does not mean the implicit scope of `A` will be searched for -conversions of that parameter alone, but of the whole expression. For example: - - class A(val n: Int) { - def +(other: A) = new A(n + other.n) - } - object A { - implicit def fromInt(n: Int) = new A(n) - } - - // This becomes possible: - 1 + new A(1) - // because it is converted into this: - A.fromInt(1) + new A(1) - -**This available only since Scala 2.9.1.** - -### Implicit scope of type arguments - -This is required to make the type class pattern really work. Consider -`Ordering`, for instance... it comes with some implicits in its companion -object, but you can't add stuff to it. So how can you make an `Ordering` for -your own class that is automatically found? - -Let's start with the implementation: - - class A(val n: Int) - object A { - implicit val ord = new Ordering[A] { - def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n) - } - } - -So, consider what happens when you call - - List(new A(5), new A(2)).sorted - -As we saw, the method `sorted` expects an `Ordering[A]` (actually, it expects -an `Ordering[B]`, where `B >: A`). There isn't any such thing inside -`Ordering`, and there is no "source" type on which to look. Obviously, it is -finding it inside `A`, which is a _type argument_ of `Ordering`. - -This is also how various collection methods expecting `CanBuildFrom` work: the -implicits are found inside companion objects to the type parameters of -`CanBuildFrom`. - -**Note**: `Ordering` is defined as `trait Ordering[T]`, where `T` is a type -parameter. The implicit looked for above is `Ordering[A]`, where -`A` is an actual type, not type parameter: it is a _type argument_ to -`Ordering`. See section 7.2 of the [Scala Specification][6]. - -**This available only since Scala 2.8.0.** - -### Outer Objects for Nested Types - -The principle is simple: - - class A(val n: Int) { - class B(val m: Int) { require(m < n) } - } - object A { - implicit def bToString(b: A#B) = "B: %d" format b.m - } - val a = new A(5) - val b = new a.B(3) - val s: String = b // s == "B: 3" - -A real world example of this would be welcome. Please share your example! - -### Call To Action - -Avoid taking this question as being the final arbiter of what is happening. -If you do notice it has become out-of-date, do [open a ticket about it][7], or, if -you know how to correct it, please fix it. - -Related questions of interest: - -* [Context bounds](context-bounds.html) -* [Chaining implicits](chaining-implicits.html) - -This question and answer were originally submitted on [Stack Overflow][3]. - - [1]: http://jsuereth.com/scala/2011/02/18/2011-implicits-without-tax.html - [2]: https://issues.scala-lang.org/browse/SI-4427 - [3]: http://stackoverflow.com/q/5598085/53013 - [4]: http://stackoverflow.com/questions/5512397/passing-scala-math-integral-as-implicit-parameter - [5]: http://scala-lang.org/files/archive/spec/2.11/06-expressions.html - [6]: http://scala-lang.org/files/archive/spec/2.11/07-implicits.html - [7]: https://github.com/scala/scala.github.com/issues diff --git a/_tutorials/FAQ/initialization-order.md b/_tutorials/FAQ/initialization-order.md deleted file mode 100644 index d9c3973d7c..0000000000 --- a/_tutorials/FAQ/initialization-order.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -layout: overview-large -title: Why is my abstract or overridden val null? - -discourse: true - -partof: FAQ -num: 9 ---- - -## Example -To understand the problem, let's pick the following concrete example. - - abstract class A { - val x1: String - val x2: String = "mom" - - println("A: " + x1 + ", " + x2) - } - class B extends A { - val x1: String = "hello" - - println("B: " + x1 + ", " + x2) - } - class C extends B { - override val x2: String = "dad" - - println("C: " + x1 + ", " + x2) - } - -Let's observe the initialization order through the Scala REPL: - - scala> new C - A: null, null - B: hello, null - C: hello, dad - -Only when we get to the constructor of `C` are both `x1` and `x2` initialized. Therefore, constructors of `A` and `B` risk running into `NullPointerException`s. - -## Explanation -A 'strict' or 'eager' val is one which is not marked lazy. - -In the absence of "early definitions" (see below), initialization of strict vals is done in the following order. - -1. Superclasses are fully initialized before subclasses. -2. Otherwise, in declaration order. - -Naturally when a val is overridden, it is not initialized more than once. So though x2 in the above example is seemingly defined at every point, this is not the case: an overridden val will appear to be null during the construction of superclasses, as will an abstract val. - -There is a compiler flag which can be useful for identifying this situation: - -**-Xcheckinit**: Add runtime check to field accessors. - -It is inadvisable to use this flag outside of testing. It adds significantly to the code size by putting a wrapper around all potentially uninitialized field accesses: the wrapper will throw an exception rather than allow a null (or 0/false in the case of primitive types) to silently appear. Note also that this adds a *runtime* check: it can only tell you anything about code paths which you exercise with it in place. - -Using it on the opening example: - - % scalac -Xcheckinit a.scala - % scala -e 'new C' - scala.UninitializedFieldError: Uninitialized field: a.scala: 13 - at C.x2(a.scala:13) - at A.(a.scala:5) - at B.(a.scala:7) - at C.(a.scala:12) - -### Solutions ### - -Approaches for avoiding null values include: - -#### Use lazy vals #### - - abstract class A { - val x1: String - lazy val x2: String = "mom" - - println("A: " + x1 + ", " + x2) - } - class B extends A { - lazy val x1: String = "hello" - - println("B: " + x1 + ", " + x2) - } - class C extends B { - override lazy val x2: String = "dad" - - println("C: " + x1 + ", " + x2) - } - // scala> new C - // A: hello, dad - // B: hello, dad - // C: hello, dad - -Usually the best answer. Unfortunately you cannot declare an abstract lazy val. If that is what you're after, your options include: - -1. Declare an abstract strict val, and hope subclasses will implement it as a lazy val or with an early definition. If they do not, it will appear to be uninitialized at some points during construction. -2. Declare an abstract def, and hope subclasses will implement it as a lazy val. If they do not, it will be re-evaluated on every access. -3. Declare a concrete lazy val which throws an exception, and hope subclasses override it. If they do not, it will... throw an exception. - -An exception during initialization of a lazy val will cause the right hand side to be re-evaluated on the next access: see SLS 5.2. - -Note that using multiple lazy vals creates a new risk: cycles among lazy vals can result in a stack overflow on first access. - -#### Use early definitions #### - abstract class A { - val x1: String - val x2: String = "mom" - - println("A: " + x1 + ", " + x2) - } - class B extends { - val x1: String = "hello" - } with A { - println("B: " + x1 + ", " + x2) - } - class C extends { - override val x2: String = "dad" - } with B { - println("C: " + x1 + ", " + x2) - } - // scala> new C - // A: hello, dad - // B: hello, dad - // C: hello, dad - -Early definitions are a bit unwieldy, there are limitations as to what can appear and what can be referenced in an early definitions block, and they don't compose as well as lazy vals: but if a lazy val is undesirable, they present another option. They are specified in SLS 5.1.6. - -#### Use constant value definitions #### - abstract class A { - val x1: String - val x2: String = "mom" - - println("A: " + x1 + ", " + x2) - } - class B extends A { - val x1: String = "hello" - final val x3 = "goodbye" - - println("B: " + x1 + ", " + x2) - } - class C extends B { - override val x2: String = "dad" - - println("C: " + x1 + ", " + x2) - } - abstract class D { - val c: C - val x3 = c.x3 // no exceptions! - println("D: " + c + " but " + x3) - } - class E extends D { - val c = new C - println(s"E: ${c.x1}, ${c.x2}, and $x3...") - } - //scala> new E - //D: null but goodbye - //A: null, null - //B: hello, null - //C: hello, dad - //E: hello, dad, and goodbye... - -Sometimes all you need from an interface is a compile-time constant. - -Constant values are stricter than strict and earlier than early definitions and have even more limitations, -as they must be constants. They are specified in SLS 4.1. diff --git a/_tutorials/FAQ/stream-view-iterator.md b/_tutorials/FAQ/stream-view-iterator.md deleted file mode 100644 index 67defcb41d..0000000000 --- a/_tutorials/FAQ/stream-view-iterator.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -layout: overview-large -title: What is the difference between view, stream and iterator? - -discourse: true - -partof: FAQ -num: 4 ---- -First, they are all _non-strict_. That has a particular mathematical meaning -related to functions, but, basically, means they are computed on-demand instead -of in advance. - -`Stream` is a lazy list indeed. In fact, in Scala, a `Stream` is a `List` whose -`tail` is a `lazy val`. Once computed, a value stays computed and is reused. -Or, as you say, the values are cached. - -An `Iterator` can only be used once because it is a _traversal pointer_ into a -collection, and not a collection in itself. What makes it special in Scala is -the fact that you can apply transformation such as `map` and `filter` and -simply get a new `Iterator` which will only apply these transformations when -you ask for the next element. - -Scala used to provide iterators which could be reset, but that is very hard to -support in a general manner, and they didn't make version 2.8.0. - -Views are meant to be viewed much like a database view. It is a series of -transformation which one applies to a collection to produce a "virtual" -collection. As you said, all transformations are re-applied each time you need -to fetch elements from it. - -Both `Iterator` and views have excellent memory characteristics. `Stream` is -nice, but, in Scala, its main benefit is writing infinite sequences -(particularly sequences recursively defined). One _can_ avoid keeping all of -the `Stream` in memory, though, by making sure you don't keep a reference to -its `head` (for example, by using `def` instead of `val` to define the -`Stream`). - -Because of the penalties incurred by views, one should usually `force` it after -applying the transformations, or keep it as a view if only few elements are -expected to ever be fetched, compared to the total size of the view. - -This answer was originally submitted in response to [this question on Stack Overflow][1]. - - [1]: http://stackoverflow.com/q/5159000/53013 diff --git a/_tutorials/partest-guide.md b/_tutorials/partest-guide.md deleted file mode 100644 index d5be81bfa5..0000000000 --- a/_tutorials/partest-guide.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -layout: page -title: Running the Test Suite ---- - -Partest is a custom parallel testing tool that we use to run the test suite for the Scala compiler and library. Go the scala project folder from your local checkout and run it via `ant` or standalone as follows. - -## Using ant - -The test suite can be run by using ant from the command line: - - $ ant test.suite - -## Standalone - -There are launch scripts `partest` and `partest.bat` in the `test` folder of the scala project. To have partest run failing tests only and print details about test failures to the console, you can use - - ./test/partest --show-diff --show-log --failed - -You can get a summary of the usage by running partest without arguments. - -* Most commonly you want to invoke partest with an option that tells it which part of the tests to run. For example `--all`, `--pos`, `--neg` or `--run`. -* You can test individual files by specifying individual test files (`.scala` files) as options. Several files can be tested if they are from the same category, e.g., `pos`. -* You can enable output of log and diff using the `-show-log` and `-show-diff` options. -* If you get into real trouble, and want to find out what partest does, you can run it with option `--verbose`. This info is useful as part of bug reports. -* Set custom path from where to load classes: `-classpath ` and `-buildpath `. -* You can use the `SCALAC_OPTS` environment variable to pass command line options to the compiler. -* You can use the `JAVA_OPTS` environment variable to pass command line options to the runner (e.g., for `run/jvm` tests). -* The launch scripts run partest as follows: - - scala -cp scala.tools.partest.nest.NestRunner - - Partest classes from a `quick` build, e.g., can be found in `./build/quick/classes/partest/`. - - Partest will tell you where it loads compiler/library classes from by adding the `partest.debug` property: - - scala -Dpartest.debug=true -cp scala.tools.partest.nest.NestRunner - - - -## ScalaCheck tests - -Tests that depend on [ScalaCheck](https://github.com/rickynils/scalacheck) can be added under folder `./test/files/scalacheck`. A sample test: - - import org.scalacheck._ - import Prop._ - - object Test { - val prop_ConcatLists = property{ (l1: ListInt, l2: ListInt) => - l1.size + l2.size == (l1 ::: l2).size - } - - val tests = List(("prop_ConcatLists", prop_ConcatLists)) - } - -## Troubleshooting - -### Windows - -Some tests might fail because line endings in the `.check` files and the produced results do not match. In that case, set either - - git config core.autocrlf false - -or - - git config core.autocrlf input diff --git a/_tutorials/scala-for-csharp-programmers.md b/_tutorials/scala-for-csharp-programmers.md deleted file mode 100644 index b61a8b66d6..0000000000 --- a/_tutorials/scala-for-csharp-programmers.md +++ /dev/null @@ -1,1236 +0,0 @@ ---- -layout: overview -title: A Scala Tutorial for C# Programmers - -discourse: true ---- - -By Ivan Towlson - -## Introduction - -Scala is a hybrid of functional and object-oriented languages. -Its functional aspects make it very expressive when writing algorithmic -code, and play nicely with the brave new world of concurrency; its -object-oriented aspects keep it familiar and convenient when creating -business objects or other stateful models. - -## The same concepts - -Scala shares many features and concepts with C#, as well as introducing -many that are new. In fact, some of the capabilities that people talk -about a lot in Scala introductions or Java-to-Scala guides are very -familiar to C# programmers. For example, as a C# programmer, you don't -need to be gently walked through the idea of function types -- you've -been using delegates and lambdas for years. - -However, some Scala features and behaviours are quite different from C#, -and even those which are common usually have different syntax or work -in slightly different ways. So let's start out by covering some Scala -basics from a C# programmer's point of view. - -### Classes - -The basic concept of classes is the same in Scala as in C#. A class -bundles up a bunch of state (member fields) and hides it behind an -interface (member methods). The syntax for declaring classes is just -like C#: - - class Widget { - } - -### Fields - -The syntax for declaring fields is like this: - - class Widget { - val serialNumber = 123 - private var usageCount = 0 - } - -You can probably figure out what's going on here, but let's just note -a few differences from C#: - -* Fields are public by default, rather than private by default. -* You don't need to put a semicolon after a field declaration. You can - write semicolons if you want (or if your muscle memory makes you), - but if you don't, Scala will figure it out for you. -* Scala automatically figures out the type of the field from its initial - value, just like the C# `var` keyword. (Don't be fooled by the appearance - of the second field though -- the Scala `var` keyword doesn't mean the - same thing as the C# `var` keyword.) - -Now, why is one of these fields introduced as `val` and the other as -`var`? In C#, fields are mutable by default. That is, by default, -any code that can access a field can modify it. You can specify *readonly* -to make a field immutable, so that it can't be changed once the object -has been constructed. - -Scala doesn't have a default setting for mutability. You have to engage -brain, and decide whether each field is mutable or immutable. `val` means -a field is immutable (readonly in C#) and `var` means it's mutable -(a normal field in C#). - -So the class above is equivalent to the C#: - - class Widget { - public readonly int serialNumber = 123; - private int usageCount = 0; - } - -Notice that C# makes you write extra code to make things immutable, -and Scala doesn't. This may seem like a small thing, but it's -going to be a really important theme. - -### Methods - -The syntax for declaring methods is like this: - - class Widget { - def reticulate(winding: Int): String = "some value" - } - -This is a more dramatic departure from C# so let's pick it apart. - -The `def` keyword means we're declaring a method rather than a field. -There isn't a direct equivalent to this in C#, which figures out whether -something is a method or a field from context. As with fields, -methods are public by default. - -The method name is reassuringly language-independent. - -Method arguments go in brackets after the method name, just as in C#. -However, the way Scala specifies the argument types is different from C#. -In C# you would write `int winding`; in Scala you write `winding: Int`. - -This is a general principle: in Scala, when you want to specify the -type of something, you write the type after the something, separated -by a colon. (Whereas in C# you write the type before the something, -separated by a space.) - -You can see the same principle in action for the return type of the -function. `reticulate` returns a `String`. - -Finally, the body of the method has been placed after an equals sign, -rather than inside braces. (Braces are only necessary when the method -body consists of multiple expressions/statements.) What's more, -the body of the method is an expression -- that -is, something with a value -- rather than a set of statements amongst which -is a `return`. I'll come back to this when we look at a more realistic -example, but for the time being, let's focus on the trivial example and -translate it into C# syntax: - - class Widget { - public string Reticulate(int winding) { - return "some value"; - } - } - -### Classes and Structs - -In C#, when you define a type, you can decide whether to make it a -reference type (a class) or a value type (a struct). Scala doesn't -allow you to create custom value types. It has only classes, not -structs. This restriction comes from Scala targeting the Java -Virtual Machine. Unlike .NET, the JVM doesn't really have the concept -of value types. Scala tries to disguise this as best it can, but the -lack of custom value types is one place where the implementation -leaks through. Fortunately, Scala makes it easy to define immutable -reference types, which are nearly as good. - -### Base Types - -Scala's base types are pretty much the same as C#'s, except that they -are named with initial capitals, e.g. `Int` instead of `int`. (In fact -every type in Scala starts with an uppercase letter.) There are -no unsigned variants, and booleans are called `Boolean` instead of `bool`. - -Scala's name for `void` is `Unit`, but unlike `void`, `Unit` is a real type. -We'll see why this is important in a moment. - -### Function Types - -In C#, you can have variables that refer to functions instead of data. -These variables have delegate types, such as *Predicate` or -`Func` or `KeyEventHandler` or `Action`. - -Scala has the same concept, but the function types are built into the -language, rather than being library types. Function types are -spelled `(T1, T2, ...) => TR`. For example, a predicate of integers -would be type `(Int) => Boolean`. If there is only one input type, -the parens can be left out like this: `Int => Boolean`. - -Effectively, Scala gets rid of all those weird custom delegate types -like `Predicate` and `Comparer` and has a single family of -function types like the C# `Func<...>` family. - -What if you want to refer to a method that doesn't have a return value? -In C#, you can't write `Func` because void isn't a valid -type; you have to write `Action` instead. But Scala doesn't have -different families of delegate types, it just has the built-in -function types. Fortunately, in Scala, `Unit` is a real type, so you -can write `(Int) => Unit` for a function which takes an integer -and doesn't return a useful value. This means you can pass `void` methods -interchangeably with non-`void` methods, which is a Good Thing. - -### Implementing Methods - -I showed a method above, but only to illustrate the declaration syntax. -Let's take a look at a slightly more substantial method. - - def power4(x: Int): Int = { - var square = x * x // usually wouldn't write this - see below - square * square - } - -This looks pretty similar to C#, except that: - -* We're allowed to leave out semicolons, as mentioned above. -* We don't need a return statement. The method body consists of - an expression, square * square, with some preliminary bits - to define the components of the expression. - The return value of the method is the value of the expression. - -In order to make this method look like C#, I used the `var` keyword -to declare the local variable `square`. But as with fields, the -Scala `var` keyword doesn't work quite the same as the C# `var` keyword: - -In C#, `var` means "work out the type of the variable from the -right hand side". But Scala does that automatically -- you don't -need to ask for it. In Scala, `var` means "allow me to change this -variable (or field) after initialisation". As with fields, you can -also use `val` to mean 'don't allow me to change this variable -after initialisation.' And in fact since we don't need to change -`square` after initialisation, we'd be better off using val: - - def power4(x: Int): Int = { - val square = x * x // val not var - square * square - } - -Incidentally, if you do ever want to explicitly indicate the type -of a variable, you can do it the same way as with function arguments: - - def power4(x: Int): Int = { - val square : Int = x * x - square * square - } - -Notice that you still need `val` or `var`. Telling Scala the type -of the variable is independent of deciding whether the variable should -be mutable or immutable. - -### Tuples - -Everybody hates out parameters. We all know that code like this just -isn't nice: - - Widget widget; - if (widgetDictionary.TryGetValue(widgetKey, out widget)) - { - widget.ReticulateSplines(); - } - -And once you start composing higher-level functions into the mix, it gets -positively nasty. Suppose I want to make a HTTP request. Well, that's -going to produce two outputs in itself, the success code and the response -data. But now suppose I want to time it. Writing the timing code isn't a -problem, but now I have *three* outputs, and to paraphrase *Was Not Was*, -I feel worse than Jamie Zawinski. - -You can get around this in specific situations by creating custom types -like `DictionaryLookupResult` or `TimedHttpRequestResult`, but eventually -the terrible allure wears off, and you just want it to work. - -Enter tuples. A tuple is just a small number of values -- a single value, -a pair of values, a triplet of values, that sort of thing. You can tell -that tuples were named by mathematicians because the name only makes sense -when you look at the cases you hardly ever use (quadruple, quintuple, -sextuple, etc.). Using tuples, our timed HTTP request might look like this: - - public Tuple Time(Func> action) - { - StartTimer(); - var result = action(); - TimeSpan howLong = StopTimer(); - return Tuple.Create(result.First, result.Second, howLong); - } - - var result = Time(() => MakeRequest(uri)); - var succeeded = result.First; - var response = result.Second; - var howLong = result.Third. - Console.WriteLine("it took " + howLong); - -The reason this keeps getting verbose on us is that C# doesn’t provide any -syntatical support for tuples. To C#, a `Tuple<>` is just another generic -type. To us, the readers, a `Tuple<>` is just another generic type with -particularly unhelpful member names. - -Really, what we're really trying to articulate by returning a `Tuple<>` is, -"this method has several outputs." So what do we want to do with those -outputs? We want to access them, for example to stash them in variables, -without having to construct and pick apart the tuple one value at a time. -That means the language has to provide some kind of syntax-level support -for tuples, instead of treating them like every other class the compiler -doesn’t know about. - -Many functional languages have exactly this kind of syntactical support, -and Scala is no exception. Here’s how the above pseudo-C# looks in Scala: - - def time(action: => (Boolean, Stream)): (Boolean, Stream, TimeSpan) = { - startTimer() - val (succeeded, response) = action - (succeeded, response, stopTimer()) - } - - val (succeeded, response, timeTaken) = time(makeRequest) - Console.WriteLine("it took " + timeTaken) - -Notice the multiple variables on the left hand side of the time call? -Notice the `(Boolean, Stream, TimeSpan)` return type of the time method? -That return value is effectively a tuple type, but instead of having to -capture the returned tuple in a `Tuple<>` variable and extract its various -bits by hand, we are getting the Scala compiler to (in the time function) -compose the tuple implicitly for us, without us having to write the -constructor call, and (in the calling code) unpick the tuple into -meaningful, named variables for us without us having to explicitly copy -the values one by one and by name. - -(By the way, a proper implementation of the `time()` method wouldn’t be -restricted to `(Boolean, Stream)` results: we’d be looking to write a -method that could time anything. I’ve skipped that because it would -distract from the point at hand.) - -How would this play with the dictionary example? - - val (found, widget) = widgetDictionary.getValue(key) - if (found) - widget.reticulateSplines() - -We don’t actually save any lines of code, what with having to now capture -the “found” value into a variable and test it separately; and it’s not as -if the original C# version was horribly unreadable anyway. So maybe this is -a matter of taste, but I find this a lot easier to read and to write: all -the outputs are on the left of the equals sign where they belong, instead -of being spread between the assignment result and the parameter list, and -we don’t have that odd Widget declaration at the top. - -## New and different concepts - -Scala's primary platform is the Java virtual machine, and some of the -interest in Scala comes from Java programmers' interest in features such -as type inference, comprehensions and lambdas, with which C# programmers -are already familiar. So what's left that might be of interest -specifically to C# programmers? - -### Mixins and Traits - -#### Motivation - -Interfaces in C# and Java play a dual role. -First, they are a set of capabilities that an implementer has to, well, -implement. Second, they are a feature set that a client can use. - -These two roles can be conflicting: The first means that interfaces want -to be minimal, so that implementers don't have to implement a whole lot -of superfluous and redundant guff. The second means that interfaces want -to be maximal, so that clients don't have to clog themselves up with -boilerplate utility methods. - -Consider, for example, `IEnumerable` (and its sister interface `IEnumerator`). -This is a very minimal interface: implementers just need to be able to -produce values in sequence. But this minimalism means that clients of -`IEnumerable` need to write the same old boilerplate again and again and -again: foreach loops to filter, foreach loops to call a method on each -element of the sequence, foreach loops to aggregate, foreach loops to -check whether all elements meet a criterion, or to find the first member -that meets a criterion, or... - -This is frustrating because the implementations of "filter," "apply", -"aggregate," and so on are always the same. Of course, we could put -these methods into concrete types (`List` includes several), but then -those concrete types will contain duplicate code, and users who only have -an `IEnumerable` will still miss out. And yet we can't put these methods -into the interface because then every implementer of `IEnumerable` would -have to implement them -- and they'd end up writing the same boilerplate, -just now in all the zillions of `IEnumerable` classes instead of their clients. - -#### The C# and Scala Solutions - -We could resolve this tension if we had a way for interfaces to contain -implementation: for example, if `IEnumerable` required the implementer -to provide the class-specific iteration functionality, but then provided -the standard implementations of "filter," "apply", "aggregate" and so on -automatically: - - public pseudo_interface IEnumerable - { - IEnumerator GetEnumerator(); // must be implemented - IEnumerable Filter(Predicate predicate) // comes for free - { - foreach (object o in this) - if (predicate(o)) - yield return o; - } - } - -C# 3 addresses this using extension methods: the methods mentioned above -are all in fact included as extension methods on `IEnumerable` as -part of LINQ. - -This has some advantages over the approach described above: specifically, -the "standard methods" aren't bound up in the interface, so you can add -your own methods instead of being limited to the ones that the interface -author has included. - -On the other hand, it means that method implementations have to be packaged -in a different class from the interface, which feels less than modular. - -Scala takes a different approach. A Scala trait can contain a mix of -abstract methods without implementation as well as concrete methods with -an implementation. (It can also be a pure interface, with only abstract -members.) - -Here's a Scala trait that represents objects that can be compared -and ordered: - - trait Ord { - def < (that: Any): Boolean - def <=(that: Any): Boolean = (this < that) || (this == that) - def > (that: Any): Boolean = !(this <= that) - def >=(that: Any): Boolean = !(this < that) - } - -Orderable objects can extend `Ord`, but only need to implement the -method `<`. They then get the other operators for free, implemented -automatically by Ord in terms of `<`. - - class Date extends Ord { - def < (that: Any): Boolean = /* implementation */ - } - - // can now write: myDate >= yourDate - -A similar trait, called `Ordered` already exists in Scala, so there is no -need to actually define my trait. - -#### Scala Traits vs. C# Extension Methods - -Okay, so Scala has a different way of packaging standard implementations -from C#'s extension methods. It's different, but why is it interesting? -Well, there are a couple of things that you can do with Scala traits that -don't fall nicely out of the extension methods approach. - -First, you can override the default implementations of trait members, -to take advantage of additional information or capabilities available -in the implementing type. - -Let's look at another `IEnumerable` example, recast as a Scala trait: - - trait Enumerable { - def getEnumerator(): Enumerator - def count: Int = { - // Shockingly bad style; for illustrative purposes only - var c = 0 - val e = getEnumerator() - while (e.moveNext()) c = c + 1 - c - } - } - -This (ignoring style issues for now) is the only fully general -implementation we can provide for count: it will work with any `Enumerable`. -But for collections that know their sizes, such as `arrays` or `List`, -it's gruesomely inefficient. It iterates over the entire collection, -counting elements one by one, when it could just consult the `size` member -and return that. - -Let's fix that: - - class MyList extends Enumerable { - private var _size; - def getEnumerator(): Enumerator = /* ... */ - override def count: Int = _size - } - -The `count` member of the `Enumerable` trait works like a virtual method: -it can be overridden in classes which implement/derive from `Enumerable`. - -Compare this to the `Count()` extension method on `IEnumerable` in LINQ. -This achieves the same effect by trying to cast to `ICollection`, which is -fine as far as it goes but isn't extensible. - -Suppose you create an enumerable class that can count itself quickly but -isn't a collection -- for example a natural numbers range object. -With a Scala trait, the `NumberRange` type could provide an efficient -override of `count`, just like any other virtual method; with C# extension -methods, `Enumerable.Count()` would have to somehow know about the -`NumberRange` type in advance, or fall back on counting elements one by one. - -Second, with Scala you can choose a trait implementation when you -instantiate an object, rather than having it baked in at the class level -once and for all. This is called mixin-composition. - -Suppose you're creating a `MyList` instance, but you want it to puff itself -up to look bigger so as to frighten other `MyList` instances off its territory. -(This example would probably work better with fish, but we're stuck with -`Enumerable`s now. Work with me here.) In C#, you'd need to create a -`PuffedUpMyList` class and override the `Count` property. -In Scala, you can just mix in a `PuffedUp` version of the trait: - - trait PuffedUp extends Enumerable { - override def count: Int = super.count + 100 - } - - val normal = new MyList - Console.WriteLine(normal.count) // meh - val puffedUp = new MyList with PuffedUp - Console.WriteLine(puffedUp.count) // eek! - -As you can imagine this gives us much better granularity and composability -of traits and implementations than we get from the extension methods -approach, or indeed from single implementation inheritance type systems -in general. - -So Scala traits have some distinct advantages over extension methods. -The only downside appears to be the inability for clients to add their -own methods to a trait after the fact. - -Fortunately, you can work around this in Scala using so-called implicit -conversions. They enable Scala programmers to enrich existing types with new -functionality. - -### Singletons - -In C#, if you want to create a singleton object, you have to create a class, -then stop evildoers creating their own instances of that class, then create -and provide an instance of that class yourself. - -While this is hardly a Burma Railway of the programming craft, it does -feel like pushing against the grain of the language. Nor is it great for -maintainers, who have to be able to recognise a singleton by its pattern -(private constructor, public static readonly field, ...), or for clients, -who have to use a slightly clumsy multipart syntax to refer to the -singleton (e.g. `Universe.Instance`). - -What would be easier for all concerned would be if you could just declare -objects *as* singletons. That is, instead of writing class `Universe` and a -`public static readonly` instance of it, you could just write `object Universe`. - -And that's exactly what Scala allows you to do: - - object Universe { - def contains(obj: Any): Boolean = true - } - - val v = Universe.contains(42) - -What's going on behind the scenes here? It pretty much goes without saying -that the Scala compiler is creating a new type for the singleton object. -In fact it creates two types, one for the implementation and one for the -interface. The interface looks like a .NET static class (actually, the -.NET 1.x equivalent, a sealed class with only static members). -Thus, a C# program would call the example above as `Universe.contains(42)`. - -Singleton objects are first-class citizens in Scala, so they can for -example derive from classes. This is a nice way of creating special values -with custom behaviour: you don't need to create a whole new type, you just -define an instance and override methods in it: - - abstract class Cat { - def humiliateSelf() - } - - object Slats extends Cat { - def humiliateSelf() { savage(this.tail) } - } - -Obviously this is a frivolous example, but "special singletons" turn out to -be an important part of the functional idiom, for example for bottoming out -recursion. *Scala by Example (PDF)* describes an implementation of a Set class -which is implemented as a tree-like structure ("left subset - member - right -subset"), and methods such as `contains()` work by recursing down to the -child sets. For this to work requires an `EmptySet` whose implementation -(state) and behaviour are quite different from non-empty sets -- e.g. -`contains()` just returns `false` instead of trying to delegate to -non-existent child sets. Since `EmptySet` is logically unique it is both -simpler and more efficient to represent it as a singleton: i.e. to declare -`object EmptySet` instead of `class EmptySet`. - -In fact the whole thing can become alarmingly deep: *Scala by Example* -also includes a description of `Boolean` as an `abstract class`, and -`True` and `False` as singleton objects which extend `Boolean` and provide -appropriate implementations of the `ifThenElse` method. - -And fans of Giuseppe Peano should definitely check out the hypothetical -implementation of `Int`... - -### Pass by Name - -> You're only on chapter 3 and you're already reduced to writing about -> *calling conventions*? You suck! Do another post about chimney sweeps -> being hunted by jars of marmalade!" - -Silence, cur. Pass by name is not as other calling conventions are. -Pass by name, especially in conjunction with some other rather -theoretical-sounding Scala features, is your gateway to the wonderful -world of language extensibility. - -#### What is Passing By Name? - -First, let's talk about what we mean by *calling convention*. A calling -convention describes how stuff gets passed to a method by its caller. -In the good old days, this used to mean exciting things like which -arguments got passed in registers and who was responsible for resetting -the stack pointer. Sadly, the days of being able to refer to "naked fun -calls" are consigned to history: In modern managed environments, the -runtime takes care of all this guff and the main distinction is pass -data by value or by reference. (The situation on the CLR is slightly -complicated by the need to differentiate passing values by value, values -by reference, references by value and references by reference, but I'm -not going to go into that because (a) it's irrelevant to the subject at -hand and (b) that's -[Jon Skeet](http://www.yoda.arachsys.com/csharp/parameters.html)'s turf -and I don't want him to shank me. Again.) - -In *pass by value*, the called method gets a copy of whatever the caller -passed in. Arguments passed by value therefore work like local variables -that are initialised before the method runs: when you do anything to them, -you're doing it to your own copy. - -In *pass by reference*, the called method gets a reference to the caller's -value. When you do anything to a pass-by-reference argument, you're doing -it to the caller's data. - -In *pass by name*, the called method gets... well, it's a bit messy to -explain what the called method gets. But when the called method does -anything to the argument, the argument gets evaluated and the "anything" -is done to that. Crucially, evaluation happens every time the argument -gets mentioned, and only when the argument gets mentioned. - -#### Not Just Another Calling Convention - -Why does this matter? It matters because there are functions you can't -implement using pass by value or pass by reference, but you can implement -using pass by name. - -Suppose, for example, that C# didn't have the `while` keyword. -You'd probably want to write a method that did the job instead: - - public static void While(bool condition, Action body) - { - if (condition) - { - body(); - While(condition, body); - } - } - -What happens when we call this? - - long x = 0; - While(x < 10, () => x = x + 1); - -C# evaluates the arguments to `While` and invokes the `While` method with -the arguments `true` and `() => x = x + 1`. After watching the CPU sit -on 100% for a while you might check on the value of `x` and find it's -somewhere north of a trillion. *Why?* Because the condition argument was -*passed by value*, so whenever the `While` method tests the value of -condition, it's always `true`. The `While` method doesn't know that -condition originally came from the expression `x < 10`; all `While` knows -is that condition is `true`. - -For the `While` method to work, we need it to re-evaluate `x < 10` every -time it hits the condition argument. While needs not the value of the -argument at the call site, nor a reference to the argument at the call -site, but the actual expression that the caller wants it to use to generate -a value. - -Same goes for short-circuit evaluation. If you want short-circuit -evaluation in C#, your only hope if to get on the blower to Anders -Hejlsberg and persuade him to bake it into the language: - - bool result = (a > 0 && Math.Sqrt(a) < 10); - double result = (a < 0 ? Math.Sqrt(-a) : Math.Sqrt(a)); - -You can't write a function like `&&` or `?:` yourself, because C# will -always try to evaluate all the arguments before calling your function. - -Consider a VB exile who wants to reproduce his favourite keywords in C#: - - bool AndAlso(bool condition1, bool condition2) - { - return condition1 && condition2; - } - - T IIf(bool condition, T ifTrue, T ifFalse) - { - if (condition) - return ifTrue; - else - return ifFalse; - } - -But when C# hits one of these: - - bool result = AndAlso(a > 0, Math.Sqrt(a) < 10); - double result = IIf(a < 0, Math.Sqrt(-a), Math.Sqrt(a)); - -it would try to evaluate all the arguments at the call site, and pass the -results of those evaluations to `AndAlso` or `IIf`. There's no -short-circuiting. So the `AndAlso` call would crash if a were negative, -and the `IIf` call if a were anything other than 0. Again, what you want is -for the `condition1`, `condition2`, `ifTrue` and `ifFalse` arguments to be -evaluated by the callee if it needs them, not for the caller to evaluate -them before making the call. - -And that's what *pass by name* does. A parameter passed by name is not -evaluated when it is passed to a method. It is evaluated -- and -re-evaluated -- when the called method evaluates the parameter; -specifically when the called method requests the value of the parameter by -mentioning its name. This might sound weird and academic, but it's the key -to being able to define your own control constructs. - -#### Using Pass By Name in Scala - -Let's see the custom while implementation again, this time with Scala -*pass by name* parameters: - - def myWhile(condition: => Boolean)(body: => Unit): Unit = - if (condition) { - body - myWhile(condition)(body) - } - -We can call this as follows: - - var i = 0 - myWhile (i < 10) { - println(i) - i += 1 - } - -Unlike the C# attempt, this prints out the numbers from 0 to 9 and then -terminates as you'd wish. - -Pass by name also works for short-circuiting: - - import math._ - - def andAlso(condition1: => Boolean, condition2: => Boolean): Boolean = - condition1 && condition2 - - val d = -1.234 - val result = andAlso(d > 0, sqrt(d) < 10) - -The `andAlso` call returns `false` rather than crashing, because -`sqrt(d) < 10` never gets evaluated. - -What's going on here? What's the weird colon-and-pointy-sticks syntax? -What is actually getting passed to `myWhile` and `andAlso` to make this work? - -The answer is a bit surprising. Nothing is going on here. This is the -normal Scala function parameter syntax. There is no *pass by name* in Scala. - -Here's a bog-standard *pass by value* Scala function declaration: - - def myFunc1(i: Int): Unit = ... - -Takes an integer, returns void: easy enough. Here's another: - - def myFunc2(f: Int => Boolean): Unit = ... - -Even if you've not seen this kind of expression before, it's probably not -too hard to guess what this means. This function takes a *function from -`Int` to `Boolean`* as its argument. In C# terms, -`void MyFunc2(Func f)`. We could call this as follows: - - myFunc2 { (i: Int) => i > 0 } - -So now you can guess what this means: - - def myFunc3(f: => Boolean) : Unit = ... - -Well, if `myFunc2` took an *Int-to-Boolean* function, `myFunc3` must be -taking a "blank-to-Boolean" function -- a function that takes no arguments -and returns a `Boolean`. In short, a conditional expression. So we can -call `myFunc3` as follows: - - val j = 123 - myFunc3 { j > 0 } - -The squirly brackets are what we'd expect from an anonymous function, and -because the function has no arguments Scala doesn't make us write -`{ () => j > 0 }`, even though that's what it means really. The anonymous -function has no arguments because `j` is a captured local variable, not an -argument to the function. But there's more. Scala also lets us call -`myFunc3` like this: - - val j = 123 - myFunc3(j > 0) - -This is normal function call syntax, but the Scala compiler realises that -`myFunc3` expects a nullary function (a function with no arguments) rather -than a `Boolean`, and therefore treats `myFunc3(j > 0)` as shorthand for -`myFunc3(() => j > 0)`. This is the same kind of logic that the C# compiler -uses when it decides whether to compile a lambda expression to a delegate -or an expression tree. - -You can probably figure out where it goes from here: - - def myFunc4(f1: => Boolean)(f2: => Unit): Unit = ... - -This takes two functions: a conditional expression, and a function that -takes no arguments and returns no value (in .NET terms, an `Action`). -Using our powers of anticipation, we can imagine how this might be called -using some unholy combination of the two syntaxes we saw for calling -`myFunc3`: - - val j = 123; - myFunc4(j > 0) { println(j); j -= 1; } - -We can mix and match the `()` and `{}` bracketing at whim, except that we -have to use `{}` bracketing if we want to batch up multiple expressions. -For example, you could legally equally well write the following: - - myFunc4 { j > 0 } { println(j); j -= 1; } - myFunc4 { println(j); j > 0 } (j -= 1) - myFunc4 { println(j); j > 0 } { j -= 1 } - -And we'll bow to the inevitable by supplying a body for this function: - - def myFunc5(f1: => Boolean)(f2: => Unit): Unit = - if (f1()) { - f2() - myFunc5(f1)(f2) - } - -Written like this, it's clear that `f1` is getting evaluated each time we -execute the if statement, but is getting passed (as a function) when -`myFunc5` recurses. But Scala allows us to leave the parentheses off -function calls with no arguments, so we can write the above as: - - def myFunc5(f1: => Boolean)(f2: => Unit): Unit = - if (f1) { - f2 - myFunc5(f1)(f2) - } - -Again, type inference allows Scala to distinguish the *evaluation of -`f1`* in the if statement from the *passing of `f1`* in the `myFunc5` -recursion. - -And with a bit of renaming, that's `myWhile`. There's no separate -*pass by name* convention: just the usual closure behaviour of capturing -local variables in an anonymous method or lambda, a bit of syntactic sugar -for nullary functions (functions with no arguments), just like C#'s -syntactic sugar for property getters, and the Scala compiler's ability to -recognise when a closure is required instead of a value. - -In fact, armed with this understanding of the Scala "syntax," we can -easily map it back to C#: - - void While(Func condition, Action body) - { - if (condition()) - { - body(); - While(condition, body); - } - } - - int i = 0; - While(() => i < 10, () => - { - Console.WriteLine(i); - ++i; - }); - -The implementation of the `While` method in C# is, to my eyes, a bit -clearer than the Scala version. However, the syntax for *calling* the -`While` method in C# is clearly way more complicated and less natural than -the syntax for calling `myWhile` in Scala. Calling `myWhile` in Scala was -like using a native language construct. Calling While in C# required a -great deal of clutter at the call site to prevent C# from trying to treat -`i < 10` as a once-and-for-all value, and to express the body at all. - -So that's so-called "pass by name" demystified: The Scala Web site, with -crushing mundanity, demotes it to "automatic type-dependent closure -construction," which is indeed exactly how it works. As we've seen, -however, this technical-sounding feature is actually essential to -creating nice syntax for your own control constructs. We'll shortly see -how this works together with other Scala features to give you even more -flexibility in defining your construct's syntax. - -### Implicits - -Scala implicits offer some features which will be familiar to the C# -programmer, but are much more general in nature and go far beyond what can -be done in C#. - -#### Enriching types in C# and Scala - -Scala, like C#, is statically typed: a class’ methods are compiled into the -class definition and are not open for renegotiation. You cannot, as you -might in Ruby or Python, just go ahead and declare additional methods on an -existing class. - -This is of course very inconvenient. You end up declaring a load of -`FooHelper` or `FooUtils` classes full of static methods, and having to -write verbose calling code such as `if (EnumerableUtils.IsEmpty(sequence))` -rather than the rather more readable `if (sequence.IsEmpty())`. - -C# 3 tries to address this problem by introducing extension methods. -Extension methods are static methods in a `FooHelper` or `FooUtils` kind -of class, except you’re allowed to write them using member syntax. -By defining `IsEmpty` as an extension method on `IEnumerable`, you can -write `if (sequence.IsEmpty())` after all. - -Scala disapproves of static classes and global methods, so it plumps for -an alternative approach. You’ll still write a `FooHelper` or `FooUtils` -kind of class, but instead of taking the `Foo` to be Helped or Utilised as -a method parameter, your class will wrap `Foo` and enrich it with -additional methods. Let’s see this in action as we try to add a method to -the `Double` type: - - class RicherDouble(d : Double) { - def toThe(exp: Double): Double = System.Math.Pow(d, exp) - } - -(We call the class `RicherDouble` because Scala already has a `RichDouble` -class defined which provides further methods to `Double`.) - -Notice that `toThe` is an instance method, and that `RicherDouble` takes a -`Double` as a constructor parameter. This seems pretty grim, because we’d -normally have to access the function like this: - - val result = new DoubleExtensions(2.0).toThe(7.0) - -Hardly readable. To make it look nice, Scala requires us to define an -*implicit conversion* from `Double` to `RicherDouble`: - - object Implicits { - implicit def richerDouble(d: Double) = new RicherDouble(d) - } - -and to bring that implicit conversion into scope: - - import Implicits._ - -Now we can write this: - - val twoToTheSeven = 2.0.toThe(7.0) - -and all will be well. The `Double` type has apparently been successfully -enriched with the `toThe` method. - -This is, of course, just as much an illusion as the C# equivalent. -C# extension methods don’t add methods to a type, and nor do Scala -implicit conversions. What has happened here is that the Scala compiler -has looked around for implicit methods that are applicable to the type of -`2.0` (namely `Double`), and return a type that has a `toThe` method. -Our `Implicits.richerDouble` method fits the bill, so the Scala compiler -silently inserts a call to that method. At runtime, therefore, Scala calls -`Implicits.richerDouble(2.0)` and calls the `toThe` of the resulting -`RicherDouble`. - -If setting this up seems a bit verbose, well, maybe. C# extension methods -are designed to be easily – one might even say implicitly – brought into -scope. That’s very important for operators like the LINQ standard query -operators, but it can result in unwanted extension methods being dragged -into scope and causing havoc. Scala requires the caller to be a bit more -explicit about implicits, which results in a slightly higher setup cost but -gives the caller finer control over which implicit methods are considered. - -But as it happens you can avoid the need for separate definitions of -`Implicits` and `RicherDouble`, and get back to a more concise -representation by using an anonymous class. (As you’d expect, Scala -anonymous classes are fully capable, like Java ones, rather than the -neutered C# version.) Here’s how it looks: - - object Implicits { - implicit def doubleToThe(d1 : Double) = new { - def toThe(d2 : Double) : Double = Math.Pow(d1, d2) - } - } - -Well, big deal. Scala can enrich existing types with new methods just like -C#, but using a different syntax. In related news, Lisp uses a different -kind of bracket: film at eleven. Why should we be interested in Scala -implicits if they’re just another take on extension methods? - -#### Implicit Parameters - -What we saw above was an implicit method – a method which, like a C# -implicit conversion operator, the compiler is allowed to insert a call to -without the programmer writing that call. Scala also has the idea of -implicit parameters – that is, parameters which the compiler is allowed to -insert a value for without the programmer specifying that value. - -That’s just optional parameters with default values, right? Like C++ and -Visual Basic have had since “visual” meant ASCII art on a teletype, and -like C# is about to get? Well, no. - -C++, Visual Basic and C# optional parameters have fixed defaults specified -by the called function. For example, if you have a method like this: - - public void Fie(int a, int b = 123) { … } - -and you call `Fie(456)`, it’s always going to be equivalent to calling -`Fie(456, 123)`. - -A Scala implicit parameter, on the other hand, gets its value from the -calling context. That allows programmer calling the method to control the -implicit parameter value, creating an extensibility point that optional -parameters don’t provide. - -This probably all sounds a bit weird, so let’s look at an example. Consider -the following `Concatenate` method: - - public T Concatenate(IEnumerable sequence, T seed, Func concatenator); - -We pass this guy a sequence, a start value and a function that combines two -values into one, and it returns the result of calling that function across -the sequence. For example, you could pass a sequence of strings, a start -value of `String.Empty`, and `(s1, s2) => s1 + s2`, and it would return you -all the strings concatenated together: - - IEnumerable sequence = new string[] { “mog”, “bites”, “man” }; - string result = Concatenate(sequence, String.Empty, (s1, s2) => s1 + s2); - // result is “mogbitesman” - -But this is a unpleasantly verbose. We’re having to pass in `String.Empty` -and `(s1, s2) => s1 + s2` every time we want to concatenate a sequence of -strings. Not only is this tedious, it also creates the opportunity for -error when the boss decides to “help” and passes the literal -`"String.Empty"` as the seed value instead. (“Oh, and I upgraded all the -semi-colons to colons while I was in there. No, don’t thank me!”) We’d -like to just tell the Concatenate function, “Look, this is how you -concatenate strings,” once and for all. - -Let’s start out by redefining the `Concatenate` method in Scala. -I’m going to factor out the seed and the concatenator method into a trait -because we’ll typically be defining them together. - - trait Concatenator[T] { - def startValue: T - def concat(x: T, y: T): T - } - - object implicitParameters { - def concatenate[T](xs: List[T])(c: Concatenator[T]): T = - if (xs.isEmpty) c.startValue - else c.concat(xs.head, concatenate(xs.tail)(c)) - } - -We can call this as follows: - - object stringConcatenator extends Concatenator[String] { - def startValue: String = "" - def concat(x: String, y: String) = x.concat(y) - } - - object implicitParameters { - def main(args: Array[String]) = { - val result = concatenate(List("mog", "bites", "man"))(stringConcatenator) - println(result) - } - } - -So far, this looks like the C# version except for the factoring out of the -`Concatenator` trait. We’re still having to pass in the -`stringConcatenator` at the point of the call. Let’s fix that: - - def concatenate[T](xs: List[T])(implicit c: Concatenator[T]): T = - if (xs.isEmpty) c.startValue - else c.concat(xs.head, concatenate(xs.tail)) - -We’ve changed two things here. First, we’ve declared c to be an *implicit -parameter*, meaning the caller can leave it out. Second, we’ve left -it out ourselves, in the recursive call to `concatenate(xs.tail)`. - -Well, okay, it’s nice that `concatenate` now doesn’t have to pass the -`Concatenator` explicitly to the recursive call, but we’re still having to -pass in the `stringConcatenator` object to get things started. If only -there were some way to make the `stringConcatenator` object itself implicit… - - object Implicits { - implicit object stringConcatenator extends Concatenator[String] { - def startValue: String = "" - def concat(x: String, y: String) = x.concat(y) - } - } - -Again, we’ve done two things here. First, we’ve declared the -`stringConcatenator` object implicit. Consequently, we’ve had to move it -out of the top level, because Scala doesn’t allow implicits at the top -level (because they’d pollute the global namespace, being in scope even -without an explicit import statement). - -Now we can call `concatenate` like this: - - import Implicits._ - - object implicitParameters { - def main(args: Array[String]) = { - val result = concatenate(List("mog", "bites", "man")) - println(result) - } - } - -And we’ll still get “mogbitesman” as the output. - -Let’s review what’s going on here. The implicit parameter of concatenate -has been set to our `stringConcatenator`, a default value that the -`concatenate` method knew nothing about when it was compiled. This is -somewhere north of what classical optional parameters are capable of, -and we’re not finished yet. Let’s build a `listConcatenator`. - - object Implicits { - class ListConcatenator[T] extends Concatenator[List[T]] { - def startValue: List[T] = Nil - def concat(x: List[T], y: List[T]) = x ::: y - } - implicit object stringListConcatenator extends ListConcatenator[String] { } - } - -This is a bit vexing. `List` in Scala is a generic type, and has a generic -concatenation method called `:::`. But we can’t create a generic object, -because an object is an instance. And implicit parameters have to be objects. -So the best we can do is build a generic `ListConcatenator` class, and then -create trivial implicit objects for each generic parameter type we might -need. - -However, let’s not worry about the implementation, and see how this is used -at the calling end: - - val result = concatenate(List( - List("mog", "bites", "man"), - List("on", "beard") - )) - -This displays `List(mog, bites, man, on, beard)`; that is, it concatenates -the two `List[String]`s into one. Once again, we have not had to pass -`stringListConcatenator` explicitly: the Scala compiler has gone and found -it for us. We can use the exact same calling code to concatenate lists and -strings. - -#### Why Should I Care? - -Isn’t this pointless? At the call site, I have access to -`stringConcatenator` and `listStringConcatenator`. I can easily pass them -in rather than relying on spooky compiler magic to do it for me. -Aren’t implicit parameters just job security for compiler writers? - -Yes, implicit parameters are technically unnecessary. But if we’re going -to play that game, C# is technically unnecessary. You could write all that -code in IL. Extension methods are unnecessary because you could write the -static method out longhand. Optional parameters are unnecessary because -you could read the documentation and pass them in explicitly. -Post-It notes are unnecessary because you could fire up Outlook and create -a Note instead. - -Implicit parameters are about convenience and expressiveness. Implicit -parameters give you a way of describing how a function should handle -different situations, without needing to bake those situations into the -function logic or to specify them every time you call the function. -You don’t want to have to tell the `concatenate` function whether to use -the `List` or `String` concatenator every time you call it: the compiler -knows what you’re concatenating; specifying how to concatenate it just -gives you a chance to get it wrong! - -Consequently, implicit parameters – like implicit conversions – contribute -to Scala’s ability to support internal DSLs. By setting up appropriate -implicits, you can write code that reads much more naturally than if you -had to pepper it with function objects or callbacks. - -#### Conclusion - -Scala’s `implicit` keyword goes beyond C#’s equivalent. As in C#, it is -used for implicit conversions; unlike C#, this is the idiomatic way to add -operations to an existing type, removing the need for the separate -extension method syntax. Implicit parameters have no equivalent in C#. -They are like being able to add default values to a method: just as a C# -using statement bring implicit methods into scope, a Scala import statement -can bring default values into scope. If implicit conversions are a way of -extending classes, then implicit parameters are a way of extending methods, -creating simple, reliable shorthands for complex generic methods, and -making up another piece of the Scala DSL jigsaw. - -#### Method Call Syntax - -C#, like most object-oriented programming languages, is pretty strict about -how you call methods: you use the dot notation, unless the method is a -special ‘operator’ method such as `operator+`, `operator==` or a conversion -operator. The special operator methods are predefined by the compiler: you -can write your own implementation, but you can’t create your own operator -names. You can teach the `+` operator how to handle your custom type, but -you can’t add an exponentiation operator: - - int a = b ** c; - -C# has three problems with this: first, it doesn’t like the method name -`**`; second, it doesn’t like that there’s no `.` before the name; and -third, it doesn’t like that there’s no brackets around the method argument. - -To get around the objection to the name, let’s compromise and call it -`ToThe` for now. So what C# insists on seeing is `a.ToThe(b)`. - -Scala, like many functional languages, isn’t so strict. Scala allows you -to use any method with a single argument in an infix position. Before we -can see this in the exponentiation example, we will enrich the `Double` -type with the `toThe` method as we learned earlier: - - import Implicits._ - import math._ - - class RicherDouble(d: Double) { - def toThe(exp: Double): Double = pow(d, exp) - } - - object Implicits { - implicit def richerDouble(d: Double) = new RicherDouble(d) - } - -Recall that this is just the Scala idiom for extension methods – it’s the -equivalent of writing -`public static ToThe(this double first, double second) { … }` in C#. -(If we were wanting to use infix notation with our own class, we wouldn’t -need all this malarkey.) So now we can write: - - val raised = 2.0.toThe(7.0) - -Okay, so what do we need to do to get this to work in infix position? -Nothing, it turns out. - - val raised = 2.0 toThe 8.0 // it just works - -This still doesn’t look much like a built-in operator, but it turns out -Scala is less fussy than C# about method names too. - - class DoubleExtensions(d : Double) { - def **(exp: Double): Double = Pow(d, exp) - } - - val raised = 2.0 ** 9.0 // it still just works - -Much nicer. - -This sorta-kinda works for postfix operators too: - - class RicherString(s: String) { - def twice: String = s + s - } - - val drivel = "bibble" twice - -Calling methods in infix and postfix nodadion is obviously fairly simple -syntactic sugar over normal dot notation. But this seemingly minor feature -is very important in constructing DSLs, allowing Scala to do in internal -DSLs what many languages can do only using external tools. For example, -where most languages do parsing via an external file format and a tool to -translate that file format into native code (a la `lex` and `yacc`), -Scala’s parser library makes extensive use of infix and postfix methods to -provide a “traditional” syntax for describing a parser, but manages it -entirely within the Scala language. diff --git a/_tutorials/scala-for-java-programmers.md b/_tutorials/scala-for-java-programmers.md deleted file mode 100644 index b8618b431c..0000000000 --- a/_tutorials/scala-for-java-programmers.md +++ /dev/null @@ -1,723 +0,0 @@ ---- -layout: overview -title: A Scala Tutorial for Java Programmers -overview: scala-for-java-programmers - -discourse: true -multilingual-overview: true -languages: [es, ko, de, it, zh-tw] ---- - -By Michel Schinz and Philipp Haller - -## Introduction - -This document gives a quick introduction to the Scala language and -compiler. It is intended for people who already have some programming -experience and want an overview of what they can do with Scala. A -basic knowledge of object-oriented programming, especially in Java, is -assumed. - -## A First Example - -As a first example, we will use the standard *Hello world* program. It -is not very fascinating but makes it easy to demonstrate the use of -the Scala tools without knowing too much about the language. Here is -how it looks: - - object HelloWorld { - def main(args: Array[String]) { - println("Hello, world!") - } - } - -The structure of this program should be familiar to Java programmers: -it consists of one method called `main` which takes the command -line arguments, an array of strings, as parameter; the body of this -method consists of a single call to the predefined method `println` -with the friendly greeting as argument. The `main` method does not -return a value (it is a procedure method). Therefore, it is not necessary -to declare a return type. - -What is less familiar to Java programmers is the `object` -declaration containing the `main` method. Such a declaration -introduces what is commonly known as a *singleton object*, that -is a class with a single instance. The declaration above thus declares -both a class called `HelloWorld` and an instance of that class, -also called `HelloWorld`. This instance is created on demand, -the first time it is used. - -The astute reader might have noticed that the `main` method is -not declared as `static` here. This is because static members -(methods or fields) do not exist in Scala. Rather than defining static -members, the Scala programmer declares these members in singleton -objects. - -### Compiling the example - -To compile the example, we use `scalac`, the Scala compiler. `scalac` -works like most compilers: it takes a source file as argument, maybe -some options, and produces one or several object files. The object -files it produces are standard Java class files. - -If we save the above program in a file called -`HelloWorld.scala`, we can compile it by issuing the following -command (the greater-than sign `>` represents the shell prompt -and should not be typed): - - > scalac HelloWorld.scala - -This will generate a few class files in the current directory. One of -them will be called `HelloWorld.class`, and contains a class -which can be directly executed using the `scala` command, as the -following section shows. - -### Running the example - -Once compiled, a Scala program can be run using the `scala` command. -Its usage is very similar to the `java` command used to run Java -programs, and accepts the same options. The above example can be -executed using the following command, which produces the expected -output: - - > scala -classpath . HelloWorld - - Hello, world! - -## Interaction with Java - -One of Scala's strengths is that it makes it very easy to interact -with Java code. All classes from the `java.lang` package are -imported by default, while others need to be imported explicitly. - -Let's look at an example that demonstrates this. We want to obtain -and format the current date according to the conventions used in a -specific country, say France. (Other regions such as the -French-speaking part of Switzerland use the same conventions.) - -Java's class libraries define powerful utility classes, such as -`Date` and `DateFormat`. Since Scala interoperates -seemlessly with Java, there is no need to implement equivalent -classes in the Scala class library--we can simply import the classes -of the corresponding Java packages: - - import java.util.{Date, Locale} - import java.text.DateFormat - import java.text.DateFormat._ - - object FrenchDate { - def main(args: Array[String]) { - val now = new Date - val df = getDateInstance(LONG, Locale.FRANCE) - println(df format now) - } - } - -Scala's import statement looks very similar to Java's equivalent, -however, it is more powerful. Multiple classes can be imported from -the same package by enclosing them in curly braces as on the first -line. Another difference is that when importing all the names of a -package or class, one uses the underscore character (`_`) instead -of the asterisk (`*`). That's because the asterisk is a valid -Scala identifier (e.g. method name), as we will see later. - -The import statement on the third line therefore imports all members -of the `DateFormat` class. This makes the static method -`getDateInstance` and the static field `LONG` directly -visible. - -Inside the `main` method we first create an instance of Java's -`Date` class which by default contains the current date. Next, we -define a date format using the static `getDateInstance` method -that we imported previously. Finally, we print the current date -formatted according to the localized `DateFormat` instance. This -last line shows an interesting property of Scala's syntax. Methods -taking one argument can be used with an infix syntax. That is, the -expression - - df format now - -is just another, slightly less verbose way of writing the expression - - df.format(now) - -This might seem like a minor syntactic detail, but it has important -consequences, one of which will be explored in the next section. - -To conclude this section about integration with Java, it should be -noted that it is also possible to inherit from Java classes and -implement Java interfaces directly in Scala. - -## Everything is an Object - -Scala is a pure object-oriented language in the sense that -*everything* is an object, including numbers or functions. It -differs from Java in that respect, since Java distinguishes -primitive types (such as `boolean` and `int`) from reference -types, and does not enable one to manipulate functions as values. - -### Numbers are objects - -Since numbers are objects, they also have methods. And in fact, an -arithmetic expression like the following: - - 1 + 2 * 3 / x - -consists exclusively of method calls, because it is equivalent to the -following expression, as we saw in the previous section: - - (1).+(((2).*(3))./(x)) - -This also means that `+`, `*`, etc. are valid identifiers -in Scala. - -The parentheses around the numbers in the second version are necessary -because Scala's lexer uses a longest match rule for tokens. -Therefore, it would break the following expression: - - 1.+(2) - -into the tokens `1.`, `+`, and `2`. The reason that -this tokenization is chosen is because `1.` is a longer valid -match than `1`. The token `1.` is interpreted as the -literal `1.0`, making it a `Double` rather than an -`Int`. Writing the expression as: - - (1).+(2) - -prevents `1` from being interpreted as a `Double`. - -### Functions are objects - -Perhaps more surprising for the Java programmer, functions are also -objects in Scala. It is therefore possible to pass functions as -arguments, to store them in variables, and to return them from other -functions. This ability to manipulate functions as values is one of -the cornerstone of a very interesting programming paradigm called -*functional programming*. - -As a very simple example of why it can be useful to use functions as -values, let's consider a timer function whose aim is to perform some -action every second. How do we pass it the action to perform? Quite -logically, as a function. This very simple kind of function passing -should be familiar to many programmers: it is often used in -user-interface code, to register call-back functions which get called -when some event occurs. - -In the following program, the timer function is called -`oncePerSecond`, and it gets a call-back function as argument. -The type of this function is written `() => Unit` and is the type -of all functions which take no arguments and return nothing (the type -`Unit` is similar to `void` in C/C++). The main function of -this program simply calls this timer function with a call-back which -prints a sentence on the terminal. In other words, this program -endlessly prints the sentence "time flies like an arrow" every -second. - - object Timer { - def oncePerSecond(callback: () => Unit) { - while (true) { callback(); Thread sleep 1000 } - } - def timeFlies() { - println("time flies like an arrow...") - } - def main(args: Array[String]) { - oncePerSecond(timeFlies) - } - } - -Note that in order to print the string, we used the predefined method -`println` instead of using the one from `System.out`. - -#### Anonymous functions - -While this program is easy to understand, it can be refined a bit. -First of all, notice that the function `timeFlies` is only -defined in order to be passed later to the `oncePerSecond` -function. Having to name that function, which is only used once, might -seem unnecessary, and it would in fact be nice to be able to construct -this function just as it is passed to `oncePerSecond`. This is -possible in Scala using *anonymous functions*, which are exactly -that: functions without a name. The revised version of our timer -program using an anonymous function instead of *timeFlies* looks -like that: - - object TimerAnonymous { - def oncePerSecond(callback: () => Unit) { - while (true) { callback(); Thread sleep 1000 } - } - def main(args: Array[String]) { - oncePerSecond(() => - println("time flies like an arrow...")) - } - } - -The presence of an anonymous function in this example is revealed by -the right arrow `=>` which separates the function's argument -list from its body. In this example, the argument list is empty, as -witnessed by the empty pair of parenthesis on the left of the arrow. -The body of the function is the same as the one of `timeFlies` -above. - -## Classes - -As we have seen above, Scala is an object-oriented language, and as -such it has a concept of class. (For the sake of completeness, - it should be noted that some object-oriented languages do not have - the concept of class, but Scala is not one of them.) -Classes in Scala are declared using a syntax which is close to -Java's syntax. One important difference is that classes in Scala can -have parameters. This is illustrated in the following definition of -complex numbers. - - class Complex(real: Double, imaginary: Double) { - def re() = real - def im() = imaginary - } - -This `Complex` class takes two arguments, which are the real and -imaginary part of the complex. These arguments must be passed when -creating an instance of class `Complex`, as follows: `new - Complex(1.5, 2.3)`. The class contains two methods, called `re` -and `im`, which give access to these two parts. - -It should be noted that the return type of these two methods is not -given explicitly. It will be inferred automatically by the compiler, -which looks at the right-hand side of these methods and deduces that -both return a value of type `Double`. - -The compiler is not always able to infer types like it does here, and -there is unfortunately no simple rule to know exactly when it will be, -and when not. In practice, this is usually not a problem since the -compiler complains when it is not able to infer a type which was not -given explicitly. As a simple rule, beginner Scala programmers should -try to omit type declarations which seem to be easy to deduce from the -context, and see if the compiler agrees. After some time, the -programmer should get a good feeling about when to omit types, and -when to specify them explicitly. - -### Methods without arguments - -A small problem of the methods `re` and `im` is that, in -order to call them, one has to put an empty pair of parenthesis after -their name, as the following example shows: - - object ComplexNumbers { - def main(args: Array[String]) { - val c = new Complex(1.2, 3.4) - println("imaginary part: " + c.im()) - } - } - -It would be nicer to be able to access the real and imaginary parts -like if they were fields, without putting the empty pair of -parenthesis. This is perfectly doable in Scala, simply by defining -them as methods *without arguments*. Such methods differ from -methods with zero arguments in that they don't have parenthesis after -their name, neither in their definition nor in their use. Our -`Complex` class can be rewritten as follows: - - class Complex(real: Double, imaginary: Double) { - def re = real - def im = imaginary - } - - -### Inheritance and overriding - -All classes in Scala inherit from a super-class. When no super-class -is specified, as in the `Complex` example of previous section, -`scala.AnyRef` is implicitly used. - -It is possible to override methods inherited from a super-class in -Scala. It is however mandatory to explicitly specify that a method -overrides another one using the `override` modifier, in order to -avoid accidental overriding. As an example, our `Complex` class -can be augmented with a redefinition of the `toString` method -inherited from `Object`. - - class Complex(real: Double, imaginary: Double) { - def re = real - def im = imaginary - override def toString() = - "" + re + (if (im < 0) "" else "+") + im + "i" - } - - -## Case Classes and Pattern Matching - -A kind of data structure that often appears in programs is the tree. -For example, interpreters and compilers usually represent programs -internally as trees; XML documents are trees; and several kinds of -containers are based on trees, like red-black trees. - -We will now examine how such trees are represented and manipulated in -Scala through a small calculator program. The aim of this program is -to manipulate very simple arithmetic expressions composed of sums, -integer constants and variables. Two examples of such expressions are -`1+2` and `(x+x)+(7+y)`. - -We first have to decide on a representation for such expressions. The -most natural one is the tree, where nodes are operations (here, the -addition) and leaves are values (here constants or variables). - -In Java, such a tree would be represented using an abstract -super-class for the trees, and one concrete sub-class per node or -leaf. In a functional programming language, one would use an algebraic -data-type for the same purpose. Scala provides the concept of -*case classes* which is somewhat in between the two. Here is how -they can be used to define the type of the trees for our example: - - abstract class Tree - case class Sum(l: Tree, r: Tree) extends Tree - case class Var(n: String) extends Tree - case class Const(v: Int) extends Tree - -The fact that classes `Sum`, `Var` and `Const` are -declared as case classes means that they differ from standard classes -in several respects: - -- the `new` keyword is not mandatory to create instances of - these classes (i.e., one can write `Const(5)` instead of - `new Const(5)`), -- getter functions are automatically defined for the constructor - parameters (i.e., it is possible to get the value of the `v` - constructor parameter of some instance `c` of class - `Const` just by writing `c.v`), -- default definitions for methods `equals` and - `hashCode` are provided, which work on the *structure* of - the instances and not on their identity, -- a default definition for method `toString` is provided, and - prints the value in a "source form" (e.g., the tree for expression - `x+1` prints as `Sum(Var(x),Const(1))`), -- instances of these classes can be decomposed through - *pattern matching* as we will see below. - -Now that we have defined the data-type to represent our arithmetic -expressions, we can start defining operations to manipulate them. We -will start with a function to evaluate an expression in some -*environment*. The aim of the environment is to give values to -variables. For example, the expression `x+1` evaluated in an -environment which associates the value `5` to variable `x`, written -`{ x -> 5 }`, gives `6` as result. - -We therefore have to find a way to represent environments. We could of -course use some associative data-structure like a hash table, but we -can also directly use functions! An environment is really nothing more -than a function which associates a value to a (variable) name. The -environment `{ x -> 5 }` given above can simply be written as -follows in Scala: - - { case "x" => 5 } - -This notation defines a function which, when given the string -`"x"` as argument, returns the integer `5`, and fails with an -exception otherwise. - -Before writing the evaluation function, let us give a name to the type -of the environments. We could of course always use the type -`String => Int` for environments, but it simplifies the program -if we introduce a name for this type, and makes future changes easier. -This is accomplished in Scala with the following notation: - - type Environment = String => Int - -From then on, the type `Environment` can be used as an alias of -the type of functions from `String` to `Int`. - -We can now give the definition of the evaluation function. -Conceptually, it is very simple: the value of a sum of two expressions -is simply the sum of the value of these expressions; the value of a -variable is obtained directly from the environment; and the value of a -constant is the constant itself. Expressing this in Scala is not more -difficult: - - def eval(t: Tree, env: Environment): Int = t match { - case Sum(l, r) => eval(l, env) + eval(r, env) - case Var(n) => env(n) - case Const(v) => v - } - -This evaluation function works by performing *pattern matching* -on the tree `t`. Intuitively, the meaning of the above definition -should be clear: - -1. it first checks if the tree `t` is a `Sum`, and if it - is, it binds the left sub-tree to a new variable called `l` and - the right sub-tree to a variable called `r`, and then proceeds - with the evaluation of the expression following the arrow; this - expression can (and does) make use of the variables bound by the - pattern appearing on the left of the arrow, i.e., `l` and - `r`, -2. if the first check does not succeed, that is, if the tree is not - a `Sum`, it goes on and checks if `t` is a `Var`; if - it is, it binds the name contained in the `Var` node to a - variable `n` and proceeds with the right-hand expression, -3. if the second check also fails, that is if `t` is neither a - `Sum` nor a `Var`, it checks if it is a `Const`, and - if it is, it binds the value contained in the `Const` node to a - variable `v` and proceeds with the right-hand side, -4. finally, if all checks fail, an exception is raised to signal - the failure of the pattern matching expression; this could happen - here only if more sub-classes of `Tree` were declared. - -We see that the basic idea of pattern matching is to attempt to match -a value to a series of patterns, and as soon as a pattern matches, -extract and name various parts of the value, to finally evaluate some -code which typically makes use of these named parts. - -A seasoned object-oriented programmer might wonder why we did not -define `eval` as a *method* of class `Tree` and its -subclasses. We could have done it actually, since Scala allows method -definitions in case classes just like in normal classes. Deciding -whether to use pattern matching or methods is therefore a matter of -taste, but it also has important implications on extensibility: - -- when using methods, it is easy to add a new kind of node as this - can be done just by defining a sub-class of `Tree` for it; on - the other hand, adding a new operation to manipulate the tree is - tedious, as it requires modifications to all sub-classes of - `Tree`, -- when using pattern matching, the situation is reversed: adding a - new kind of node requires the modification of all functions which do - pattern matching on the tree, to take the new node into account; on - the other hand, adding a new operation is easy, by just defining it - as an independent function. - -To explore pattern matching further, let us define another operation -on arithmetic expressions: symbolic derivation. The reader might -remember the following rules regarding this operation: - -1. the derivative of a sum is the sum of the derivatives, -2. the derivative of some variable `v` is one if `v` is the - variable relative to which the derivation takes place, and zero - otherwise, -3. the derivative of a constant is zero. - -These rules can be translated almost literally into Scala code, to -obtain the following definition: - - def derive(t: Tree, v: String): Tree = t match { - case Sum(l, r) => Sum(derive(l, v), derive(r, v)) - case Var(n) if (v == n) => Const(1) - case _ => Const(0) - } - -This function introduces two new concepts related to pattern matching. -First of all, the `case` expression for variables has a -*guard*, an expression following the `if` keyword. This -guard prevents pattern matching from succeeding unless its expression -is true. Here it is used to make sure that we return the constant `1` -only if the name of the variable being derived is the same as the -derivation variable `v`. The second new feature of pattern -matching used here is the *wildcard*, written `_`, which is -a pattern matching any value, without giving it a name. - -We did not explore the whole power of pattern matching yet, but we -will stop here in order to keep this document short. We still want to -see how the two functions above perform on a real example. For that -purpose, let's write a simple `main` function which performs -several operations on the expression `(x+x)+(7+y)`: it first computes -its value in the environment `{ x -> 5, y -> 7 }`, then -computes its derivative relative to `x` and then `y`. - - def main(args: Array[String]) { - val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) - val env: Environment = { case "x" => 5 case "y" => 7 } - println("Expression: " + exp) - println("Evaluation with x=5, y=7: " + eval(exp, env)) - println("Derivative relative to x:\n " + derive(exp, "x")) - println("Derivative relative to y:\n " + derive(exp, "y")) - } - -Executing this program, we get the expected output: - - Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) - Evaluation with x=5, y=7: 24 - Derivative relative to x: - Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0))) - Derivative relative to y: - Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1))) - -By examining the output, we see that the result of the derivative -should be simplified before being presented to the user. Defining a -basic simplification function using pattern matching is an interesting -(but surprisingly tricky) problem, left as an exercise for the reader. - -## Traits - -Apart from inheriting code from a super-class, a Scala class can also -import code from one or several *traits*. - -Maybe the easiest way for a Java programmer to understand what traits -are is to view them as interfaces which can also contain code. In -Scala, when a class inherits from a trait, it implements that trait's -interface, and inherits all the code contained in the trait. - -To see the usefulness of traits, let's look at a classical example: -ordered objects. It is often useful to be able to compare objects of a -given class among themselves, for example to sort them. In Java, -objects which are comparable implement the `Comparable` -interface. In Scala, we can do a bit better than in Java by defining -our equivalent of `Comparable` as a trait, which we will call -`Ord`. - -When comparing objects, six different predicates can be useful: -smaller, smaller or equal, equal, not equal, greater or equal, and -greater. However, defining all of them is fastidious, especially since -four out of these six can be expressed using the remaining two. That -is, given the equal and smaller predicates (for example), one can -express the other ones. In Scala, all these observations can be -nicely captured by the following trait declaration: - - trait Ord { - def < (that: Any): Boolean - def <=(that: Any): Boolean = (this < that) || (this == that) - def > (that: Any): Boolean = !(this <= that) - def >=(that: Any): Boolean = !(this < that) - } - -This definition both creates a new type called `Ord`, which -plays the same role as Java's `Comparable` interface, and -default implementations of three predicates in terms of a fourth, -abstract one. The predicates for equality and inequality do not appear -here since they are by default present in all objects. - -The type `Any` which is used above is the type which is a -super-type of all other types in Scala. It can be seen as a more -general version of Java's `Object` type, since it is also a -super-type of basic types like `Int`, `Float`, etc. - -To make objects of a class comparable, it is therefore sufficient to -define the predicates which test equality and inferiority, and mix in -the `Ord` class above. As an example, let's define a -`Date` class representing dates in the Gregorian calendar. Such -dates are composed of a day, a month and a year, which we will all -represent as integers. We therefore start the definition of the -`Date` class as follows: - - class Date(y: Int, m: Int, d: Int) extends Ord { - def year = y - def month = m - def day = d - override def toString(): String = year + "-" + month + "-" + day - -The important part here is the `extends Ord` declaration which -follows the class name and parameters. It declares that the -`Date` class inherits from the `Ord` trait. - -Then, we redefine the `equals` method, inherited from -`Object`, so that it correctly compares dates by comparing their -individual fields. The default implementation of `equals` is not -usable, because as in Java it compares objects physically. We arrive -at the following definition: - - override def equals(that: Any): Boolean = - that.isInstanceOf[Date] && { - val o = that.asInstanceOf[Date] - o.day == day && o.month == month && o.year == year - } - -This method makes use of the predefined methods `isInstanceOf` -and `asInstanceOf`. The first one, `isInstanceOf`, -corresponds to Java's `instanceof` operator, and returns true -if and only if the object on which it is applied is an instance of the -given type. The second one, `asInstanceOf`, corresponds to -Java's cast operator: if the object is an instance of the given type, -it is viewed as such, otherwise a `ClassCastException` is -thrown. - -Finally, the last method to define is the predicate which tests for -inferiority, as follows. It makes use of another predefined method, -`error`, which throws an exception with the given error message. - - def <(that: Any): Boolean = { - if (!that.isInstanceOf[Date]) - error("cannot compare " + that + " and a Date") - - val o = that.asInstanceOf[Date] - (year < o.year) || - (year == o.year && (month < o.month || - (month == o.month && day < o.day))) - } - -This completes the definition of the `Date` class. Instances of -this class can be seen either as dates or as comparable objects. -Moreover, they all define the six comparison predicates mentioned -above: `equals` and `<` because they appear directly in -the definition of the `Date` class, and the others because they -are inherited from the `Ord` trait. - -Traits are useful in other situations than the one shown here, of -course, but discussing their applications in length is outside the -scope of this document. - -## Genericity - -The last characteristic of Scala we will explore in this tutorial is -genericity. Java programmers should be well aware of the problems -posed by the lack of genericity in their language, a shortcoming which -is addressed in Java 1.5. - -Genericity is the ability to write code parametrized by types. For -example, a programmer writing a library for linked lists faces the -problem of deciding which type to give to the elements of the list. -Since this list is meant to be used in many different contexts, it is -not possible to decide that the type of the elements has to be, say, -`Int`. This would be completely arbitrary and overly -restrictive. - -Java programmers resort to using `Object`, which is the -super-type of all objects. This solution is however far from being -ideal, since it doesn't work for basic types (`int`, -`long`, `float`, etc.) and it implies that a lot of -dynamic type casts have to be inserted by the programmer. - -Scala makes it possible to define generic classes (and methods) to -solve this problem. Let us examine this with an example of the -simplest container class possible: a reference, which can either be -empty or point to an object of some type. - - class Reference[T] { - private var contents: T = _ - def set(value: T) { contents = value } - def get: T = contents - } - -The class `Reference` is parametrized by a type, called `T`, -which is the type of its element. This type is used in the body of the -class as the type of the `contents` variable, the argument of -the `set` method, and the return type of the `get` method. - -The above code sample introduces variables in Scala, which should not -require further explanations. It is however interesting to see that -the initial value given to that variable is `_`, which represents -a default value. This default value is 0 for numeric types, -`false` for the `Boolean` type, `()` for the `Unit` -type and `null` for all object types. - -To use this `Reference` class, one needs to specify which type to use -for the type parameter `T`, that is the type of the element -contained by the cell. For example, to create and use a cell holding -an integer, one could write the following: - - object IntegerReference { - def main(args: Array[String]) { - val cell = new Reference[Int] - cell.set(13) - println("Reference contains the half of " + (cell.get * 2)) - } - } - -As can be seen in that example, it is not necessary to cast the value -returned by the `get` method before using it as an integer. It -is also not possible to store anything but an integer in that -particular cell, since it was declared as holding an integer. - -## Conclusion - -This document gave a quick overview of the Scala language and -presented some basic examples. The interested reader can go on, for example, by -reading the document *Scala By Example*, which -contains much more advanced examples, and consult the *Scala - Language Specification* when needed. diff --git a/_tutorials/scala-with-maven.md b/_tutorials/scala-with-maven.md deleted file mode 100644 index 2b22614c52..0000000000 --- a/_tutorials/scala-with-maven.md +++ /dev/null @@ -1,302 +0,0 @@ ---- -layout: overview -title: Scala with Maven - -discourse: true ---- - -By Adrian Null - -## Introduction -[Maven][1] is a build/project management tool. It favours "convention over configuration"; it can greatly simplify builds for "standard" projects and a Maven user can usually understand the structure of another Maven project just by looking at its `pom.xml` (Project Object Model). Maven is a plugin-based architecture, making it easy to add new libraries and modules to existing projects. For example, adding a new dependency usually involves only 5 extra lines in the `pom.xml`. These "artifacts" are downloaded from repositories such as [The Central Repository][2]. - -You can also check out the official [example project][36] which uses the same Scala plugin we will show here. - -## Jumping Ahead -If you're familiar with Maven, you can go ahead with the [Scala Maven Plugin][5]. - -## The Scala Maven Plugin -We'll be using the Scala Maven Plugin ([GitHub repo][5], [website][22]) (formerly known as the maven-scala-plugin; renamed to honour the new naming policy where only Maven core plugins are prefixed with "maven"), by far the dominant plugin for Scala projects. *Note: the plugin includes Scala from the Central Repository so there's no need to install it yourself if you're compiling with Maven.* - - -## Getting Maven - -### Linux (Debian) -On Debian and Debian-derivatives, Maven is usually available via `apt-get`. Just do `(sudo) apt-get install maven` and you're good to go. - -### OSX -OSX prior to 10.9 (Mavericks) comes with Maven 3 built in. -If you don't have it, you can get it with the package managers [MacPorts][4], [Homebrew][6], or [Fink][7]. -The Scala Maven Plugin requires Maven 3.0+ - -### Manually (Red Hat Linux, OSX, Windows) -You can download Maven from its [Apache homepage][3]. After extracting it (`tar -zxvf apache-maven-X.X.X-bin.tar.gz`, or use something like [7-zip][8]) to your directory of choice (on Linux and OSX, Unix-like systems, [I like to put them in `/opt/`][9]. On Windows I would probably put this in `C:/`), you need to add Maven to your environment Path variable: - -- Linux/OSX (option 1): Create a symlink to `/usr/bin`, which is already on your Path - - `ln -s /usr/bin/mvn /opt/apache-maven-X.X.X/bin/mvn` -- Linux/OSX (option 2): Add the Maven `bin` folder directly to your path, using your [shell configuration][10] file (e.g. `~/.bash_profile`) - - Add `export PATH=$PATH:/opt/apache-maven-X.X.X/bin` to `.bash_profile` (or whatever profile for the shell you use) - - Example: `echo "export PATH=$PATH:/opt/apache-maven-X.X.X/bin" >> ~/.bash_profile` -- Linux/OSX (option 3): Make a `mvn` shell script in an existing path location - - Example: you have `$HOME/bin` in your path - - Put the folder you extracted in `$HOME/bin` (`mv apache-maven-X.X.X "$HOME/bin/"`) - - Create a file `mvn` in `$HOME/bin` - - Add `"$HOME/bin/apache-maven-X.X.X/bin/mvn" $@` to it, and `chmod u+x mvn` to make it executable - - This is probably the least intrusive way; `$HOME/bin` is usually added to the user's path by default, and if not, it's a useful thing to do/have anyways. The shell script simply invokes the Maven location (which is at some other location) and passes on the arguments -- Windows - - Hit Start. Right click on "My Computer" and go to "Properties" - - This should bring you to "Control Panel -> System and Security -> System", giving an overview of your computer - - On the left sidebar there should be four options; click on "Advanced system settings" (fourth one) - - Under the "Advanced" tab, hit "Environment Variables..." in the bottom right - - Note: I recommend creating/editing your User variables (top box). You can do the same with System variables though (bottom box) - - Create a new variable called "MAVEN3_HOME". Point this to your Maven folder (e.g. `C:\apache-maven-X.X.X`). Use backslashes to be safe, and do not include a trailing slash - - Create a new variable called "MAVEN3_BIN" with this value: `%MAVEN3_HOME%\bin` - - Edit your Path variable: being careful not to change anything else, append `;%MAVEN3_BIN%` to it - - You'll need to restart cmd to see these changes - -## Creating Your First Project -The easiest way to create new projects is using an ["archetype"][11]. An archetype is a general skeleton structure, or template for a project. Think back to "convention over configuration"; in our case, the Scala Maven Plugin provides an archetype for scala projects. - -You run the archetype plugin like this: - - mvn archetype:generate - -If this is your first time, you'll notice that Maven is downloading many jar files. Maven resolves dependencies and downloads them as needed (and only once). Right now, Maven is downloading its core plugins. - -Afterwards, it should give you a list of archetypes (in the hundreds). -The Scala Maven Plugin was 339 on my list: "net.alchim31.maven:scala-archetype-simple (The maven-scala-plugin is used for compiling/testing/running/documenting scala code in maven.)". -You can type "scala" (or something else) to filter the results. -As of 2015 January 27, there you can choose version 3.1.4 or 3.1.5 of this plugin; you should choose the latest - - Choose net.alchim31.maven:scala-archetype-simple version: - 1: 1.4 - 2: 1.5 - -Next, Maven will ask you for a groupId, artifactId, and package. You can read the [guide to naming conventions][12], but in short: - -- groupId: inverted domain name (e.g. com.my-organization) -- artifactId: project name (e.g. playground-project) -- version: anything you want, but I recommend you read and follow the guidelines for [Semantic Versioning][13] (e.g. 0.0.1) -- package: the default is the groupId, but you can change this (e.g. com.my-organization) - -The groupId and artifactId together should serve as a globally unique identifier for your project - -When it's done, you should see a new folder named with the artifactId. `cd` into it and run: - - mvn package - -You'll see Maven downloading dependencies including the Scala library (as mentioned above), [JUnit][14], [ScalaTest][15], and [Specs2][16] (the latter three are test frameworks; the archetype includes an example "Hello world" program, and tests with each of the frameworks). - -### Explaining this Archetype -In your project root, you'll see a `pom.xml`, `src` folder, and `target` folder (target folder only appears after building). *Note: this archetype also includes a `.gitignore`* - -Inside the `src` folder you'll see `main` and `test`; `main` includes your application code, and `test` includes your test suites. Inside each of those you'll find a `scala` folder, followed by your package structure (actually, `test/scala` includes a sample package, but you should replace this with your own package and tests). If you want to mix Scala and Java source code, simply add a `java` folder inside `main` or `test`. - -`target` includes generated/built files, such as `.class` and `.jar` files. You can read about `pom.xml` at the [Maven page][18]. - -Example structure: - -- pom.xml -- src - - main - - scala - - com/my-package/... - *.scala - - java - - com/my-package/... - *.java - - test - - scala - - com/my-package/... - *.scala - - java - - com/my-package/... - *.java -- target - ... - -Again, you can read more about the Scala Maven Plugin at its [website][22]. - -### Creating a Jar -By default the jar created by the Scala Maven Plugin doesn't include a `Main-Class` attribute in the manifest. I had to add the [Maven Assembly Plugin][19] to my `pom.xml` in order to specify custom attributes in the manifest. You can check the latest version of this plugin at the [project summary][20] or at [The Central Repository][21] - - - X.X.X - ... - - ... - - - - ... - - - - ... - - - - ... - - ... - - org.apache.maven.plugins - maven-assembly-plugin - 2.4 - - - jar-with-dependencies - - - - com.your-package.MainClass - - - - - - package - - single - - - - - - - - -After adding this, `mvn package` will also create `[artifactId]-[version]-jar-with-dependencies.jar` under `target`. *Note: this will also copy the Scala library into your Jar. This is normal. Be careful that your dependencies use the same version of Scala, or you will quickly end up with a massive Jar.* - -### Useful commands -- `mvn dependency:copy-dependencies`: copy all libraries and dependencies to the `target/dependency` folder -- `mvn clean` -- `mvn package`: compile, run tests, and create jar - -### Integration with Eclipse ([Scala IDE][24]) -There are instructions at the [Scala Maven Plugin FAQs][23], but I thought I'd expand a bit. The [maven-eclipse-plugin][33] is a core plugin (all plugins prefixed with "maven" are, and are available by default) to generate Eclipse configuration files. However, this plugin does not know about our new Scala source files. We'll be using the [build-helper-maven-plugin][34] to add new source folders: - - ... - - org.codehaus.mojo - build-helper-maven-plugin - - - add-source - generate-sources - - add-source - - - - src/main/scala - - - - - add-test-source - generate-sources - - add-test-source - - - - src/test/scala - - - - - - ... - -After adding that to your `pom.xml`: - -1. Run `mvn eclipse:eclipse` - this generates the Eclipse project files (which are already ignored by our archetype's `.gitignore`) -2. Run `mvn -Declipse.workspace="path/to/your/eclipse/workspace" eclipse:configure-workspace` - this adds an `M2_REPO` classpath variable to Eclipse -3. Run `mvn package` to ensure you have all the dependencies in your local Maven repo - -Unfortunately, the integration isn't perfect. Firstly, open up the generated `.classpath` file (it will be hidden by default as it's a dotfile, but it should be in your project root directory; where you ran `mvn eclipse:eclipse`). You should see something like this near the top. - - - - -Change the `*.java` to `*.scala` (or duplicate the lines and change them to `*.scala` if you also have Java sources). - -Secondly, open the `.project` eclipse file (again, in the same folder). Change `` and `` to look like this. Now Eclipse knows to use the Scala editor and it won't think that everything is a syntax error. - - - - org.scala-ide.sdt.core.scalabuilder - - - - org.scala-ide.sdt.core.scalanature - org.eclipse.jdt.core.javanature - - -Finally, in Eclipse, under "File" choose "Import..." and find your project. - -#### Using [m2eclipse-scala][35] for Eclipse integration -m2eclipse-scala is a work in progress, and their website/repository may have updated information. -It aims to ease integration between m2eclipse and Scala IDE for Eclipse. - -Under "Help -> Install New Software", enter "http://alchim31.free.fr/m2e-scala/update-site" and hit enter. -You should see "Maven Integration for Eclipse -> Maven Integration for Scala IDE". - -Afer it installs, go to "New -> Project -> Other" and select "Maven Project". -Search fo "scala-archetype" choose the one with the group "net.alchim31.maven". -The wizard will more or less guide you through what was done with `mvn archetype:generate` above, and you should end up with a new Scala project! - -To import an existing project, simply go to "File -> Import -> Existing Maven Project" and find the directory containing your project. - -## Adding Dependencies -The first thing I do is look for "Maven" in the project page. For example, Google's [Guava] page includes [Maven Central links][28]. As you can see in the previous link, The Central Repository conveniently includes the snippet you have to add to your `pom.xml` on the left sidebar. - -If you can't find Maven information at the project page, try a Google search for "[project name] maven". Sometimes, you still won't find anything. For [scopt][29] (Scala command line option parser), I couldn't find the latest version from Google. However, [manually searching The Central Repository did][30] - -Afterwards, running - - mvn package - -Will download any new dependencies before packaging - -## Other Useful Reading -I'm not going to explain all of Maven in this tutorial (though I hope to add more in the future, because I do feel that the resources are a bit scattered), so here are some useful articles: -- [Maven Lifecycle][17] (explanation of goals like clean, package, install) - -[1]: http://maven.apache.org -[2]: http://maven.org -[3]: http://maven.apache.org/download.html -[4]: http://macports.org -[5]: https://github.com/davidB/scala-maven-plugin -[6]: http://brew.sh -[7]: http://fink.sf.net -[8]: http://7-zip.org -[9]: http://unix.stackexchange.com/questions/63531/ -[10]: http://wikipedia.org/wiki/Unix_shell#Configuration_files_for_shells -[11]: http://maven.apache.org/archetype/maven-archetype-plugin/ -[12]: http://maven.apache.org/guides/mini/guide-naming-conventions.html -[13]: http://semver.org -[14]: http://junit.org -[15]: http://scalatest.org -[16]: http://specs2.org -[17]: http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html -[18]: http://maven.apache.org/pom.html -[19]: http://maven.apache.org/plugins/maven-assembly-plugin/ -[20]: http://maven.apache.org/plugins/maven-assembly-plugin/project-summary.html -[21]: http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22maven-assembly-plugin%22 -[22]: http://davidb.github.io/scala-maven-plugin -[23]: http://davidb.github.io/scala-maven-plugin/faq.html -[24]: http://scala-ide.org -[25]: http://scala-lang.org/api/current/index.html#scala.App -[26]: http://docs.scala-lang.org/tutorials/tour/polymorphic-methods.html -[27]: https://code.google.com/p/guava-libraries/ -[28]: http://search.maven.org/#artifactdetails%7Ccom.google.guava%7Cguava%7C14.0.1%7Cbundle -[29]: https://github.com/scopt/scopt -[30]: http://search.maven.org/#search%7Cga%7C1%7Cscopt -[31]: http://mvnrepository.com -[32]: http://docs.scala-lang.org/contribute.html -[33]: http://maven.apache.org/plugins/maven-eclipse-plugin/ -[34]: http://mojo.codehaus.org/build-helper-maven-plugin/ -[35]: https://github.com/sonatype/m2eclipse-scala -[36]: https://github.com/scala/scala-module-dependency-sample diff --git a/tutorials/FAQ/breakout.md b/tutorials/FAQ/breakout.md deleted file mode 100644 index bd672df922..0000000000 --- a/tutorials/FAQ/breakout.md +++ /dev/null @@ -1,234 +0,0 @@ ---- -layout: overview -title: What is breakOut, and how does it work? - -discourse: true - -partof: FAQ -num: 5 ---- -You might have encountered some code like the one below, and wonder what is -`breakOut`, and why is it being passed as parameter? - - import scala.collection.breakOut - val map : Map[Int,String] = List("London", "France").map(x => (x.length, x))(breakOut) - - -The answer is found on the definition of `map`: - - def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That - -Note that it has two parameters. The first is your function and the second is -an implicit. If you do not provide that implicit, Scala will choose the most -_specific_ one available. - -### About breakOut - -So, what's the purpose of `breakOut`? Consider the example given at the -beginning , You take a list of strings, transform each string into a tuple -`(Int, String)`, and then produce a `Map` out of it. The most obvious way to do -that would produce an intermediary `List[(Int, String)]` collection, and then -convert it. - -Given that `map` uses a `Builder` to produce the resulting collection, wouldn't -it be possible to skip the intermediary `List` and collect the results directly -into a `Map`? Evidently, yes, it is. To do so, however, we need to pass a -proper `CanBuildFrom` to `map`, and that is exactly what `breakOut` does. - -Let's look, then, at the definition of `breakOut`: - - def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) = - new CanBuildFrom[From, T, To] { - def apply(from: From) = b.apply() ; def apply() = b.apply() - } - -Note that `breakOut` is parameterized, and that it returns an instance of -`CanBuildFrom`. As it happens, the types `From`, `T` and `To` have already been -inferred, because we know that `map` is expecting `CanBuildFrom[List[String], -(Int, String), Map[Int, String]]`. Therefore: - - From = List[String] - T = (Int, String) - To = Map[Int, String] - -To conclude let's examine the implicit received by `breakOut` itself. It is of -type `CanBuildFrom[Nothing,T,To]`. We already know all these types, so we can -determine that we need an implicit of type -`CanBuildFrom[Nothing,(Int,String),Map[Int,String]]`. But is there such a -definition? - -Let's look at `CanBuildFrom`'s definition: - - trait CanBuildFrom[-From, -Elem, +To] - extends AnyRef - -So `CanBuildFrom` is contra-variant on its first type parameter. Because -`Nothing` is a bottom class (ie, it is a subclass of everything), that means -*any* class can be used in place of `Nothing`. - -Since such a builder exists, Scala can use it to produce the desired output. - -### About Builders - -A lot of methods from Scala's collections library consists of taking the -original collection, processing it somehow (in the case of `map`, transforming -each element), and storing the results in a new collection. - -To maximize code reuse, this storing of results is done through a _builder_ -(`scala.collection.mutable.Builder`), which basically supports two operations: -appending elements, and returning the resulting collection. The type of this -resulting collection will depend on the type of the builder. Thus, a `List` -builder will return a `List`, a `Map` builder will return a `Map`, and so on. -The implementation of the `map` method need not concern itself with the type of -the result: the builder takes care of it. - -On the other hand, that means that `map` needs to receive this builder somehow. -The problem faced when designing Scala 2.8 Collections was how to choose the -best builder possible. For example, if I were to write `Map('a' -> -1).map(_.swap)`, I'd like to get a `Map(1 -> 'a')` back. On the other hand, a -`Map('a' -> 1).map(_._1)` can't return a `Map` (it returns an `Iterable`). - -The magic of producing the best possible `Builder` from the known types of the -expression is performed through this `CanBuildFrom` implicit. - -### About CanBuildFrom - -To better explain what's going on, I'll give an example where the collection -being mapped is a `Map` instead of a `List`. I'll go back to `List` later. For -now, consider these two expressions: - - Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length) - Map(1 -> "one", 2 -> "two") map (_._2) - -The first returns a `Map` and the second returns an `Iterable`. The magic of -returning a fitting collection is the work of `CanBuildFrom`. Let's consider -the definition of `map` again to understand it. - -The method `map` is inherited from `TraversableLike`. It is parameterized on -`B` and `That`, and makes use of the type parameters `A` and `Repr`, which -parameterize the class. Let's see both definitions together: - -The class `TraversableLike` is defined as: - - trait TraversableLike[+A, +Repr] - extends HasNewBuilder[A, Repr] with AnyRef - - def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That - - -To understand where `A` and `Repr` come from, let's consider the definition of -`Map` itself: - - trait Map[A, +B] - extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]] - -Because `TraversableLike` is inherited by all traits which extend `Map`, `A` -and `Repr` could be inherited from any of them. The last one gets the -preference, though. So, following the definition of the immutable `Map` and all -the traits that connect it to `TraversableLike`, we have: - - trait Map[A, +B] - extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]] - - trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] - extends MapLike[A, B, This] - - trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]] - extends PartialFunction[A, B] with IterableLike[(A, B), This] with Subtractable[A, This] - - trait IterableLike[+A, +Repr] - extends Equals with TraversableLike[A, Repr] - - trait TraversableLike[+A, +Repr] - extends HasNewBuilder[A, Repr] with AnyRef - -If you pass the type parameters of `Map[Int, String]` all the way down the -chain, we find that the types passed to `TraversableLike`, and, thus, used by -`map`, are: - - A = (Int,String) - Repr = Map[Int, String] - -Going back to the example, the first map is receiving a function of type -`((Int, String)) => (Int, Int)` and the second map is receiving a function of -type `((Int, String)) => Int`. I use the double parenthesis to emphasize it is -a tuple being received, as that's the type of `A` as we saw. - -With that information, let's consider the other types. - - map Function.tupled(_ -> _.length): - B = (Int, Int) - - map (_._2): - B = Int - -We can see that the type returned by the first `map` is `Map[Int,Int]`, and the -second is `Iterable[String]`. Looking at `map`'s definition, it is easy to see -that these are the values of `That`. But where do they come from? - -If we look inside the companion objects of the classes involved, we see some -implicit declarations providing them. On object `Map`: - - implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]] - -And on object `Iterable`, whose class is extended by `Map`: - - implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]] - -These definitions provide factories for parameterized `CanBuildFrom`. - -Scala will choose the most specific implicit available. In the first case, it -was the first `CanBuildFrom`. In the second case, as the first did not match, -it chose the second `CanBuildFrom`. - -### Back to the first example - -Let's see the first example, `List`'s and `map`'s definition (again) to -see how the types are inferred: - - val map : Map[Int,String] = List("London", "France").map(x => (x.length, x))(breakOut) - - sealed abstract class List[+A] - extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]] - - trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]] - extends SeqLike[A, Repr] - - trait SeqLike[+A, +Repr] - extends IterableLike[A, Repr] - - trait IterableLike[+A, +Repr] - extends Equals with TraversableLike[A, Repr] - - trait TraversableLike[+A, +Repr] - extends HasNewBuilder[A, Repr] with AnyRef - - def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That - -The type of `List("London", "France")` is `List[String]`, so the types `A` and -`Repr` defined on `TraversableLike` are: - - A = String - Repr = List[String] - -The type for `(x => (x.length, x))` is `(String) => (Int, String)`, so the type -of `B` is: - - B = (Int, String) - -The last unknown type, `That` is the type of the result of `map`, and we -already have that as well: - - val map : Map[Int,String] = - -So, - - That = Map[Int, String] - -That means `breakOut` must, necessarily, return a type or subtype of -`CanBuildFrom[List[String], (Int, String), Map[Int, String]]`. - -This answer was originally submitted in response to [this question on Stack Overflow][1]. - - [1]: http://stackoverflow.com/q/1715681/53013 - diff --git a/tutorials/FAQ/collections.md b/tutorials/FAQ/collections.md deleted file mode 100644 index 8108915363..0000000000 --- a/tutorials/FAQ/collections.md +++ /dev/null @@ -1,377 +0,0 @@ ---- -layout: overview -title: How are the collections structured? Which one should I choose? - -discourse: true - -partof: FAQ -num: 8 ---- -## Foreword - -There's a [2.8 collection walk-through][1] by Martin Odersky which should -probably be your first reference. It has been supplemented as well with -[architectural notes][2], which will be of particular interest to those who -want to design their own collections. - -The rest of this answer was written way before any such thing existed (in fact, -before 2.8.0 itself was released). - -You can find a paper about it as [Scala SID #3][3]. Other papers in that area -should be interesting as well to people interested in the differences between -Scala 2.7 and 2.8. - -I'll quote from the paper, selectively, and complement with some thoughts of -mine. There are also some images, generated by Matthias at decodified.com, and -the original SVG files can be found [here][4]. - -## The collection classes/traits themselves - -There are actually three hierarchies of traits for the collections: one for -mutable collections, one for immutable collections, and one which doesn't make -any assumptions about the collections. - -There's also a distinction between parallel, serial and maybe-parallel -collections, which was introduced with Scala 2.9. I'll talk about them in the -next section. The hierarchy described in this section refers _exclusively to -non-parallel collections_. - -The following image shows the non-specific hierarchy introduced with Scala 2.8: -![General collection hierarchy][5] - -All elements shown are traits. In the other two hierarchies there are also -classes directly inheriting the traits as well as classes which can be _viewed -as_ belonging in that hierarchy through implicit conversion to wrapper classes. -The legend for these graphs can be found after them. - -Graph for immutable hierarchy: - - -Graph for mutable hierarchy: - - -Legend: - -![Graph legend][8] - -Here's an abbreviated ASCII depiction of the collection hierarchy, for those who can't see the images. - - Traversable - | - | - Iterable - | - +------------------+--------------------+ - Map Set Seq - | | | - | +----+----+ +-----+------+ - Sorted Map SortedSet BitSet Buffer Vector LinearSeq - - -## Parallel Collections - -When Scala 2.9 introduced parallel collections, one of the design goals was to -make their use as seamless as possible. In the simplest terms, one can replace -a non-parallel (serial) collection with a parallel one, and instantly reap the -benefits. - -However, since all collections until then were serial, many algorithms using -them assumed and depended on the fact that they _were_ serial. Parallel -collections fed to the methods with such assumptions would fail. For this -reason, all the hierarchy described in the previous section _mandates serial -processing_. - -Two new hierarchies were created to support the parallel collections. - -The parallel collections hierarchy has the same names for traits, but preceded -with `Par`: `ParIterable`, `ParSeq`, `ParMap` and `ParSet`. Note that there is -no `ParTraversable`, since any collection supporting parallel access is capable -of supporting the stronger `ParIterable` trait. It doesn't have some of the -more specialized traits present in the serial hierarchy either. This whole -hierarchy is found under the directory `scala.collection.parallel`. - -The classes implementing parallel collections also differ, with `ParHashMap` -and `ParHashSet` for both mutable and immutable parallel collections, plus -`ParRange` and `ParVector` implementing `immutable.ParSeq` and `ParArray` -implementing `mutable.ParSeq`. - -Another hierarchy also exists that mirrors the traits of serial and parallel -collections, but with a prefix `Gen`: `GenTraversable`, `GenIterable`, -`GenSeq`, `GenMap` and `GenSet`. These traits are _parents_ to both parallel -and serial collections. This means that a method taking a `Seq` cannot receive -a parallel collection, but a method taking a `GenSeq` is expected to work with -both serial and parallel collections. - -Given the way these hierarchies were structured, code written for Scala 2.8 was -fully compatible with Scala 2.9, and demanded serial behavior. Without being -rewritten, it cannot take advantage of parallel collections, but the changes -required are very small. - -### Using Parallel Collections - -Any collection can be converted into a parallel one by calling the method `par` -on it. Likewise, any collection can be converted into a serial one by calling -the method `seq` on it. - -If the collection was already of the type requested (parallel or serial), no -conversion will take place. If one calls `seq` on a parallel collection or -`par` on a serial collection, however, a new collection with the requested -characteristic will be generated. - -Do not confuse `seq`, which turns a collection into a non-parallel collection, -with `toSeq`, which returns a `Seq` created from the elements of the -collection. Calling `toSeq` on a parallel collection will return a `ParSeq`, -not a serial collection. - -## The Main Traits - -While there are many implementing classes and subtraits, there are some basic -traits in the hierarchy, each of which providing more methods or more specific -guarantees, but reducing the number of classes that could implement them. - -In the following subsections, I'll give a brief description of the main traits -and the idea behind them. - -### Trait TraversableOnce - -This trait is pretty much like trait `Traversable` described below, but with -the limitation that you can only use it _once_. That is, any methods called on -a `TraversableOnce` _may_ render it unusable. - -This limitation makes it possible for the same methods to be shared between the -collections and `Iterator`. This makes it possible for a method that works with -an `Iterator` but not using `Iterator`-specific methods to actually be able to -work with any collection at all, plus iterators, if rewritten to accept -`TraversableOnce`. - -Because `TraversableOnce` unifies collections and iterators, and iterators are -not considered collections, it does not appear in the previous graphs, which -concern themselves only with collections. - -### Trait Traversable - -At the top of the _collection_ hierarchy is trait `Traversable`. Its only -abstract operation is - - def foreach[U](f: Elem => U) - -The operation is meant to traverse all elements of the collection, and apply -the given operation f to each element. The application is done for its side -effect only; in fact any function result of f is discarded by foreach. - -Traversible objects can be finite or infinite. An example of an infinite -traversable object is the stream of natural numbers `Stream.from(0)`. The -method `hasDefiniteSize` indicates whether a collection is possibly infinite. -If `hasDefiniteSize` returns true, the collection is certainly finite. If it -returns false, the collection has not been not fully elaborated yet, so it -might be infinite or finite. - -This class defines methods which can be efficiently implemented in terms of -`foreach` (over 40 of them). - -### Trait Iterable - -This trait declares an abstract method `iterator` that returns an iterator that -yields all the collection’s elements one by one. The `foreach` method in -`Iterable` is implemented in terms of `iterator`. Subclasses of `Iterable` -often override foreach with a direct implementation for efficiency. - -Class `Iterable` also adds some less-often used methods to `Traversable`, which -can be implemented efficiently only if an `iterator` is available. They are -summarized below. - - xs.iterator An iterator that yields every element in xs, in the same order as foreach traverses elements. - xs takeRight n A collection consisting of the last n elements of xs (or, some arbitrary n elements, if no order is defined). - xs dropRight n The rest of the collection except xs takeRight n. - xs sameElements ys A test whether xs and ys contain the same elements in the same order - -### Seq, Set and Map - -After `Iterable` there come three base traits which inherit from it: `Seq`, -`Set`, and `Map`. All three have an `apply` method and all three implement the -`PartialFunction` trait, but the meaning of `apply` is different in each case. - -I trust the meaning of `Seq`, `Set` and `Map` is intuitive. After them, the -classes break up in specific implementations that offer particular guarantees -with regards to performance, and the methods it makes available as a result of -it. Also available are some traits with further refinements, such as -`LinearSeq`, `IndexedSeq` and `SortedSet`. - -## Complete Overview - -### Base Classes and Traits - -* `TraversableOnce` -- All methods and behavior common to collections and iterators. - - * `Traversable` -- Basic collection class. Can be implemented just with `foreach`. - - * `TraversableProxy` -- Proxy for a `Traversable`. Just point `self` to the real collection. - * `TraversableView` -- A Traversable with some non-strict methods. - * `TraversableForwarder` -- Forwards most methods to `underlying`, except `toString`, `hashCode`, `equals`, `stringPrefix`, `newBuilder`, `view` and all calls creating a new iterable object of the same kind. - * `mutable.Traversable` and `immutable.Traversable` -- same thing as `Traversable`, but restricting the collection type. - * Other special-cases `Iterable` classes, such as `MetaData`, exists. - * `Iterable` -- A collection for which an `Iterator` can be created (through `iterator`). - * `IterableProxy`, `IterableView`, `mutable` and `immutable.Iterable`. - - * `Iterator` -- A trait which is not descendant of `Traversable`. Define `next` and `hasNext`. - * `CountedIterator` -- An `Iterator` defining `count`, which returns the elements seen so far. - * `BufferedIterator` -- Defines `head`, which returns the next element without consuming it. - * Other special-cases `Iterator` classes, such as `Source`, exists. - -### The Sequences - -* `Seq` -- A sequence of elements. One assumes a well-defined size and element repetition. Extends `PartialFunction` as well. - - * `IndexedSeq` -- Sequences that support O(1) element access and O(1) length computation. - * `IndexedSeqView` - * `immutable.PagedSeq` -- An implementation of `IndexedSeq` where the elements are produced on-demand by a function passed through the constructor. - * `immutable.IndexedSeq` - - * `immutable.Range` -- A delimited sequence of integers, closed on the lower end, open on the high end, and with a step. - * `immutable.Range.Inclusive` -- A `Range` closed on the high end as well. - * `immutable.NumericRange` -- A more generic version of `Range` which works with any `Integral`. - * `immutable.NumericRange.Inclusive`, `immutable.NumericRange.Exclusive`. - * `immutable.WrappedString`, `immutable.RichString` -- Wrappers which enables seeing a `String` as a `Seq[Char]`, while still preserving the `String` methods. I'm not sure what the difference between them is. - - * `mutable.IndexedSeq` - * `mutable.GenericArray` -- An `Seq`-based array-like structure. Note that the "class" `Array` is Java's `Array`, which is more of a memory storage method than a class. - * `mutable.ResizableArray` -- Internal class used by classes based on resizable arrays. - * `mutable.PriorityQueue`, `mutable.SynchronizedPriorityQueue` -- Classes implementing prioritized queues -- queues where the elements are dequeued according to an `Ordering` first, and order of queueing last. - * `mutable.PriorityQueueProxy` -- an abstract `Proxy` for a `PriorityQueue`. - - * `LinearSeq` -- A trait for linear sequences, with efficient time for `isEmpty`, `head` and `tail`. - - * `immutable.LinearSeq` - * `immutable.List` -- An immutable, singlely-linked, list implementation. - * `immutable.Stream` -- A lazy-list. Its elements are only computed on-demand, but memoized (kept in memory) afterwards. It can be theoretically infinite. - * `mutable.LinearSeq` - * `mutable.DoublyLinkedList` -- A list with mutable `prev`, `head` (`elem`) and `tail` (`next`). - * `mutable.LinkedList` -- A list with mutable `head` (`elem`) and `tail` (`next`). - * `mutable.MutableList` -- A class used internally to implement classes based on mutable lists. - * `mutable.Queue`, `mutable.QueueProxy` -- A data structure optimized for FIFO (First-In, First-Out) operations. - * `mutable.QueueProxy` -- A `Proxy` for a `mutable.Queue`. - - * `SeqProxy`, `SeqView`, `SeqForwarder` - - * `immutable.Seq` - - * `immutable.Queue` -- A class implementing a FIFO-optimized (First-In, First-Out) data structure. There is no common superclass of both `mutable` and `immutable` queues. - * `immutable.Stack` -- A class implementing a LIFO-optimized (Last-In, First-Out) data structure. There is no common superclass of both `mutable` `immutable` stacks. - * `immutable.Vector` -- ? - * `scala.xml.NodeSeq` -- A specialized XML class which extends `immutable.Seq`. - * `immutable.IndexedSeq` -- As seen above. - * `immutable.LinearSeq` -- As seen above. - - * `mutable.ArrayStack` -- A class implementing a LIFO-optimized data structure using arrays. Supposedly significantly faster than a normal stack. - * `mutable.Stack`, `mutable.SynchronizedStack` -- Classes implementing a LIFO-optimized data structure. - * `mutable.StackProxy` -- A `Proxy` for a `mutable.Stack`.. - * `mutable.Seq` - - * `mutable.Buffer` -- Sequence of elements which can be changed by appending, prepending or inserting new members. - * `mutable.ArrayBuffer` -- An implementation of the `mutable.Buffer` class, with constant amortized time for the append, update and random access operations. It has some specialized subclasses, such as `NodeBuffer`. - * `mutable.BufferProxy`, `mutable.SynchronizedBuffer`. - * `mutable.ListBuffer` -- A buffer backed by a list. It provides constant time append and prepend, with most other operations being linear. - * `mutable.ObservableBuffer` -- A *mixin* trait which, when mixed to a `Buffer`, provides notification events through a `Publisher` interfaces. - * `mutable.IndexedSeq` -- As seen above. - * `mutable.LinearSeq` -- As seen above. - -### The Sets - -* `Set` -- A set is a collection that includes at most one of any object. - - * `BitSet` -- A set of integers stored as a bitset. - * `immutable.BitSet` - * `mutable.BitSet` - - * `SortedSet` -- A set whose elements are ordered. - * `immutable.SortedSet` - * `immutable.TreeSet` -- An implementation of a `SortedSet` based on a tree. - - * `SetProxy` -- A `Proxy` for a `Set`. - - * `immutable.Set` - * `immutable.HashSet` -- An implementation of `Set` based on element hashing. - * `immutable.ListSet` -- An implementation of `Set` based on lists. - * Additional set classes exists to provide optimized implementations for sets from 0 to 4 elements. - * `immutable.SetProxy` -- A `Proxy` for an immutable `Set`. - - * `mutable.Set` - * `mutable.HashSet` -- An implementation of `Set` based on element hashing. - * `mutable.ImmutableSetAdaptor` -- A class implementing a mutable `Set` from an immutable `Set`. - * `LinkedHashSet` -- An implementation of `Set` based on lists. - * `ObservableSet` -- A *mixin* trait which, when mixed with a `Set`, provides notification events through a `Publisher` interface. - * `SetProxy` -- A `Proxy` for a `Set`. - * `SynchronizedSet` -- A *mixin* trait which, when mixed with a `Set`, provides notification events through a `Publisher` interface. - -### The Maps - -* `Map` -- An `Iterable` of `Tuple2`, which also provides methods for retrieving a value (the second element of the tuple) given a key (the first element of the tuple). Extends `PartialFunction` as well. - * `MapProxy` -- A `Proxy` for a `Map`. - * `DefaultMap` -- A trait implementing some of `Map`'s abstract methods. - * `SortedMap` -- A `Map` whose keys are sorted. - * `immutable.SortMap` - * `immutable.TreeMap` -- A class implementing `immutable.SortedMap`. - * `immutable.Map` - * `immutable.MapProxy` - * `immutable.HashMap` -- A class implementing `immutable.Map` through key hashing. - * `immutable.IntMap` -- A class implementing `immutable.Map` specialized for `Int` keys. Uses a tree based on the binary digits of the keys. - * `immutable.ListMap` -- A class implementing `immutable.Map` through lists. - * `immutable.LongMap` -- A class implementing `immutable.Map` specialized for `Long` keys. See `IntMap`. - * There are additional classes optimized for an specific number of elements. - * `mutable.Map` - * `mutable.HashMap` -- A class implementing `mutable.Map` through key hashing. - * `mutable.ImmutableMapAdaptor` -- A class implementing a `mutable.Map` from an existing `immutable.Map`. - * `mutable.LinkedHashMap` -- ? - * `mutable.ListMap` -- A class implementing `mutable.Map` through lists. - * `mutable.MultiMap` -- A class accepting more than one distinct value for each key. - * `mutable.ObservableMap` -- A *mixin* which, when mixed with a `Map`, publishes events to observers through a `Publisher` interface. - * `mutable.OpenHashMap` -- A class based on an open hashing algorithm. - * `mutable.SynchronizedMap` -- A *mixin* which should be mixed with a `Map` to provide a version of it with synchronized methods. - * `mutable.MapProxy`. - -## Bonus Questions - -* Why the Like classes exist (e.g. TraversableLike)? - -This was done to achieve maximum code reuse. The concrete *generic* -implementation for classes with a certain structure (a traversable, a map, etc) -is done in the Like classes. The classes intended for general consumption, -then, override selected methods that can be optmized. - -* What the companion methods are for (e.g. List.companion)? - -The builder for the classes, ie, the object which knows how to create instances -of that class in a way that can be used by methods like `map`, is created by a -method in the companion object. So, in order to build an object of type X, I -need to get that builder from the companion object of X. Unfortunately, there -is no way, in Scala, to get from class X to object X. Because of that, there is -a method defined in each instance of X, `companion`, which returns the -companion object of class X. - -While there might be some use for such method in normal programs, its target is -enabling code reuse in the collection library. - -* How I know what implicit objects are in scope at a given point? - -You aren't supposed to care about that. They are implicit precisely so that you -don't need to figure out how to make it work. - -These implicits exists to enable the methods on the collections to be defined -on parent classes but still return a collection of the same type. For example, -the `map` method is defined on `TraversableLike`, but if you used on a `List` -you'll get a `List` back. - -This answer was originally submitted in response to [this question][9] on Stack -Overflow. - - - [1]: http://docs.scala-lang.org/overviews/collections/introduction.html - [2]: http://docs.scala-lang.org/overviews/core/architecture-of-scala-collections.html - [3]: http://www.scala-lang.org/sid/3 - [4]: https://github.com/sirthias/scala-collections-charts/downloads - [5]: http://i.stack.imgur.com/bSVyA.png - [6]: http://i.stack.imgur.com/2fjoA.png - [7]: http://i.stack.imgur.com/Dsptl.png - [8]: http://i.stack.imgur.com/szWUr.png - [9]: http://stackoverflow.com/q/1722137/53013 - diff --git a/tutorials/FAQ/context-bounds.md b/tutorials/FAQ/context-bounds.md deleted file mode 100644 index 45bad40b92..0000000000 --- a/tutorials/FAQ/context-bounds.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -layout: overview -title: What are Scala context bounds? - -discourse: true - -partof: FAQ -num: 3 ---- - -What is a Context Bound? ------------------------- - -Context bounds were introduced in Scala 2.8.0, and are typically used with the -so-called _type class pattern_, a pattern of code that emulates the -functionality provided by Haskell type classes, though in a more verbose -manner. - -A context bound requires a _parameterized type_, such as `Ordered[A]`, -but unlike `String`. - -A context bound describes an implicit _value_. It is used to declare that for -some type `A`, there is an -implicit value of type `B[A]` available. The syntax goes like this: - - def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A] - -The common example of usage in Scala is this: - - def f[A : ClassTag](n: Int) = new Array[A](n) - -An `Array` initialization on a parameterized type requires a `ClassTag` to -be available, for arcane reasons related to type erasure and the non-erasure -nature of arrays. - -Another very common example in the library is a bit more complex: - - def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b) - -Here, `implicitly` is used to retrive the implicit value we want, one of type -`Ordering[A]`, which class defines the method `compare(a: A, b: A): Int`. - -We'll see another way of doing this below. - -How are Context Bounds implemented? ---------------------------------------------------- - -It shouldn't be surprising that context bounds are -implemented with implicit parameters, given their definition. Actually, the -syntax I showed are syntactic sugars for what really happens. See below how -they de-sugar: - - def g[A : B](a: A) = h(a) - def g[A](a: A)(implicit ev: B[A]) = h(a) - -So, naturally, one can write them in their full syntax, which is specially -useful for context bounds: - - def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b) - -What are Context Bounds used for? ---------------------------------- - -Context bounds are mainly used in what has become known as _typeclass pattern_, -as a reference to Haskell's type classes. Basically, this pattern implements an -alternative to inheritance by making functionality available through a sort of -implicit adapter pattern. - -The classic example is Scala 2.8's `Ordering`. The usage is: - - def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b - -Though you'll usually see that written like this: - - def f[A](a: A, b: A)(implicit ord: Ordering[A]) = { - import ord._ - if (a < b) a else b - } - -Which take advantage of some implicit conversions inside `Ordering` that enable -the traditional operator style. Another example in Scala 2.8 is the `Numeric`: - - def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b) - -A more complex example is the new collection usage of `CanBuildFrom`, but -there's already a very long answer about that, so I'll avoid it here. And, as -mentioned before, there's the `ClassTag` usage, which is required to -initialize new arrays without concrete types. - -Though it has been possible for a long time, the use of context bounds has -really taken off in 2010, and is now found to some degree in most of Scala's -most important libraries and frameworks. The most extreme example of its usage, -though, is the Scalaz library, which brings a lot of the power of Haskell to -Scala. I recommend reading up on typeclass patterns to get more acquainted it -all the ways in which it can be used. - -Related questions of interest: - -* [A discussion on types, origin and precedence of implicits](finding-implicits.html) -* [Chaining implicits](chaining-implicits.html) - -This answer was originally submitted in response to [this question on Stack Overflow][1]. - - [1]: http://stackoverflow.com/q/4465948/53013 - diff --git a/tutorials/FAQ/finding-symbols.md b/tutorials/FAQ/finding-symbols.md deleted file mode 100644 index ea980e95bd..0000000000 --- a/tutorials/FAQ/finding-symbols.md +++ /dev/null @@ -1,205 +0,0 @@ ---- -layout: overview -title: How do I find what some symbol means or does? - -discourse: true - -partof: FAQ -num: 1 -outof: 9 ---- -We can divide the operators in Scala, for the purpose of teaching, into four categories: - -* Keywords/reserved symbols -* Normal methods or values -* Methods provided by implicit conversion -* Syntactic sugars/composition - -And let's see some arbitrary examples: - - <- // Keyword - -> // Method provided by implicit conversion - <= // Common method - ++= // Can be a common method or syntactic sugar involving ++ method - :: // Common method or object - _+_ // Not really a single operator; it's parsed as _ + _ - -The exact meaning of most of these methods depends on the class they are defined -on. For example, `<=` on `Int` means _"less than or equal to"_, but it might -mean something else in another class. `::` in an expression is probably the method of the class -`List` but it can also refer to the object of the same name (and in a pattern it -definitely does). - -So, let's discuss these categories. - -Keywords/reserved symbols -------------------------- - -There are a few symbols in Scala that are special and cannot be defined or used as method names. -Two of them are considered proper keywords, while others are just "reserved". They are: - - // Keywords - <- // Used on for-comprehensions, to separate pattern from generator - => // Used for function types, function literals and import renaming - - // Reserved - ( ) // Delimit expressions and parameters - [ ] // Delimit type parameters - { } // Delimit blocks - . // Method call and path separator - // /* */ // Comments - # // Used in type notations - : // Type ascription or context bounds - <: >: // Upper and lower bounds - <% // View bounds (deprecated) - " """ // Strings - ' // Indicate symbols and characters - @ // Annotations and variable binding on pattern matching - ` // Denote constant or enable arbitrary identifiers - , // Parameter separator - ; // Statement separator - _* // vararg expansion - _ // Many different meanings - -These are all _part of the language_, and, as such, can be found in any text -that properly describe the language, such as [Scala Specification][1](PDF) -itself. - -The last one, the underscore, deserve a special description, because it is -widely used, and has different meanings depending on the context. Here's a sample: - - import scala._ // Wild card -- all of Scala is imported - import scala.{ Predef => _, _ } // Exclusion, everything except Predef - def f[M[_]] // Higher kinded type parameter - def f(m: M[_]) // Existential type - _ + _ // Anonymous function placeholder parameter - m _ // Eta expansion of method into method value - m(_) // Partial function application - _ => 5 // Discarded parameter - case _ => // Wild card pattern -- matches anything - f(xs: _*) // Sequence xs is passed as multiple parameters to f(ys: T*) - case Seq(xs @ _*) // Identifier xs is bound to the whole matched sequence - -Common methods --------------- - -Many symbols are simply methods of a class, a trait, or an object. For instance, if you do - - List(1, 2) ++ List(3, 4) - -You'll find the method `++` right on the Scaladoc for [List][5]. However, -there's one convention that you must be aware when searching for methods. -Methods ending in colon (`:`) bind _to the right_ instead of the left. In other -words, while the above method call is equivalent to: - - List(1, 2).++(List(3, 4)) - -If I had, instead `1 :: List(2, 3)`, that would be equivalent to: - - List(2, 3).::(1) - -So you need to look at the type found _on the right_ when looking for methods -ending in colon. Consider, for instance: - - 1 +: List(2, 3) :+ 4 - -The first method (`+:`) binds to the right, and is found on `List`. The second -method (`:+`) is just a normal method, and binds to the left -- again, on -`List`. - -If the name ends in `=`, look for the method called the same without `=` and -read the last section. - -If you aren't sure what the type of the receiver is, you can look up the symbol -on the Scaladoc [index page for identifiers not starting with letters][2] (for -standard Scala library; of course, third-party libraries can add their own -symbolic methods, for which you should look at the corresponding page of _their_ -Scaladoc). - -Types and objects can also have symbolic names; in particular, it should be mentioned -that for types with two type parameters the name can be written _between_ parameters, -so that e.g. `Int <:< Any` is the same as `<:<[Int, Any]`. - -Methods provided by implicit conversion ---------------------------------------- - -If you did not find the symbol you are looking for in the list of reserved symbols, then -it must be a method, or part of one. But, often, you'll see some symbol and the -documentation for the class will not have that method. When this happens, -either you are looking at a composition of one or more methods with something -else, or the method has been imported into scope, or is available through an -imported implicit conversion. - -These can also be found in Scaladoc's [index][2], as mentioned above. - -All Scala code has three automatic imports: - - // Not necessarily in this order - import java.lang._ - import scala._ - import scala.Predef._ - -The first two only make classes and singleton objects available, none of which -look like operators. [`Predef`][3] is the only interesting one for this post. - -Looking inside `Predef` shows some symbolic names: - - class <:< - class =:= - object =:= - object <%< // removed in Scala 2.10 - def ??? - -There is also `::`, which doesn't appear in the Scaladoc, but is mentioned in the comments. -In addition, `Predef` makes some methods available through _implicit conversions_. Just -look at the methods and classes with `implicit` modifier that receive, as parameter, an -object of type that is receiving the method. For example, consider `"a" -> 1`. We need -to look for an implicit which works on `"a"`, and so it can take `String`, one of its -supertypes (`AnyRef` or `Any`) or a type parameter. In this case, we find -`implicit final class ArrowAssoc[A](private val self: A)` which makes this implicit -avaialable on all types. - -Other implicit conversions may be visible in your scope depending on imports, extended types or -self-type annotations. See [Finding implicits](tutorials/FAQ/finding-implicits.html) for details. - -Syntactic sugars/composition ------------------------------ - -So, here's a few syntactic sugars that may hide a method: - - class Example(arr: Array[Int] = Array.fill(5)(0)) { - def apply(n: Int) = arr(n) - def update(n: Int, v: Int) = arr(n) = v - def a = arr(0); def a_=(v: Int) = arr(0) = v - def b = arr(1); def b_=(v: Int) = arr(1) = v - def c = arr(2); def c_=(v: Int) = arr(2) = v - def d = arr(3); def d_=(v: Int) = arr(3) = v - def e = arr(4); def e_=(v: Int) = arr(4) = v - def +(v: Int) = new Example(arr map (_ + v)) - def unapply(n: Int) = if (arr.indices contains n) Some(arr(n)) else None - } - - val ex = new Example - println(ex(0)) // means ex.apply(0) - ex(0) = 2 // means ex.update(0, 2) - ex.b = 3 // means ex.b_=(3) - val ex(c) = 2 // calls ex.unapply(2) and assigns result to c, if it's Some; throws MatchError if it's None - ex += 1 // means ex = ex + 1; if Example had a += method, it would be used instead - -The last one is interesting, because *any* symbolic method can be combined with `=` in that way. - -And, of course, all of the above can be combined in various combinations, e.g. - - (_+_) // An expression, or parameter, that is an anonymous function with - // two parameters, used exactly where the underscores appear, and - // which calls the "+" method on the first parameter passing the - // second parameter as argument. - -This answer was originally submitted in response to [this question on Stack Overflow][6]. - - [1]: http://scala-lang.org/files/archive/spec/2.11/ - [2]: http://www.scala-lang.org/api/current/index.html#index.index-_ - [3]: http://www.scala-lang.org/api/current/index.html#scala.Predef$ - [4]: http://www.scala-lang.org/api/current/scala/Predef$$ArrowAssoc.html - [5]: http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List - [6]: http://stackoverflow.com/q/7888944/53013 diff --git a/tutorials/FAQ/stream-view-iterator.md b/tutorials/FAQ/stream-view-iterator.md deleted file mode 100644 index 6c9413474b..0000000000 --- a/tutorials/FAQ/stream-view-iterator.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: overview -title: What is the difference between view, stream and iterator? - -discourse: true - -partof: FAQ -num: 4 ---- -First, they are all _non-strict_. That has a particular mathematical meaning -related to functions, but, basically, means they are computed on-demand instead -of in advance. - -`Stream` is a lazy list indeed. In fact, in Scala, a `Stream` is a `List` whose -`tail` is a `lazy val`. Once computed, a value stays computed and is reused. -Or, as you say, the values are cached. - -An `Iterator` can only be used once because it is a _traversal pointer_ into a -collection, and not a collection in itself. What makes it special in Scala is -the fact that you can apply transformation such as `map` and `filter` and -simply get a new `Iterator` which will only apply these transformations when -you ask for the next element. - -Scala used to provide iterators which could be reset, but that is very hard to -support in a general manner, and they didn't make version 2.8.0. - -Views are meant to be viewed much like a database view. It is a series of -transformation which one applies to a collection to produce a "virtual" -collection. As you said, all transformations are re-applied each time you need -to fetch elements from it. - -Both `Iterator` and views have excellent memory characteristics. `Stream` is -nice, but, in Scala, its main benefit is writing infinite sequences -(particularly sequences recursively defined). One _can_ avoid keeping all of -the `Stream` in memory, though, by making sure you don't keep a reference to -its `head` (for example, by using `def` instead of `val` to define the -`Stream`). - -Because of the penalties incurred by views, one should usually `force` it after -applying the transformations, or keep it as a view if only few elements are -expected to ever be fetched, compared to the total size of the view. - -This answer was originally submitted in response to [this question on Stack Overflow][1]. - - [1]: http://stackoverflow.com/q/5159000/53013 - diff --git a/tutorials/FAQ/yield.md b/tutorials/FAQ/yield.md deleted file mode 100644 index cb579ec155..0000000000 --- a/tutorials/FAQ/yield.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -layout: overview -title: How does yield work? - -discourse: true - -partof: FAQ -num: 2 ---- -Though there's a `yield` in other languages such as Python and Ruby, Scala's -`yield` does something very different from them. In Scala, `yield` is part -of for comprehensions -- a generalization of Ruby and Python's list-comprehensions. - -Scala's "for comprehensions" are equivalent to Haskell's "do" notation, and it -is nothing more than a syntactic sugar for composition of multiple monadic -operations. As this statement will most likely not help anyone who needs help, -let's try again... - -Translating for-comprehensions ------------------------------- - -Scala's "for comprehensions" are syntactic sugar for composition of multiple -operations with `foreach`, `map`, `flatMap`, `filter` or `withFilter`. -Scala actually translates a for-expression into calls to those methods, -so any class providing them, or a subset of them, can be used with for comprehensions. - -First, let's talk about the translations. There are very simple rules: - -#### Example 1 - - for(x <- c1; y <- c2; z <-c3) {...} - -is translated into - - c1.foreach(x => c2.foreach(y => c3.foreach(z => {...}))) - -#### Example 2 - - for(x <- c1; y <- c2; z <- c3) yield {...} - -is translated into - - c1.flatMap(x => c2.flatMap(y => c3.map(z => {...}))) - -#### Example 3 - - for(x <- c; if cond) yield {...} - -is translated into - - c.withFilter(x => cond).map(x => {...}) - -with a fallback into - - c.filter(x => cond).map(x => {...}) - -if method `withFilter` is not available but `filter` is. -The next chapter has more information on this. - -#### Example 4 - - for(x <- c; y = ...) yield {...} - -is translated into - - c.map(x => (x, ...)).map((x,y) => {...}) - - -When you look at very simple for comprehensions, the map/foreach alternatives -look, indeed, better. Once you start composing them, though, you can easily get -lost in parenthesis and nesting levels. When that happens, for comprehensions -are usually much clearer. - -I'll show one simple example, and intentionally omit any explanation. You can -decide which syntax is easier to understand. - - l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length)) - -or - - for{ - sl <- l - el <- sl - if el > 0 - } yield el.toString.length - - -About withFilter, and strictness ----------------------------------- - -Scala 2.8 introduced a method called `withFilter`, whose main difference is -that, instead of returning a new, filtered, collection, it filters on-demand. -The `filter` method has its behavior defined based on the strictness of the -collection. To understand this better, let's take a look at some Scala 2.7 with -`List` (strict) and `Stream` (non-strict): - - scala> var found = false - found: Boolean = false - - scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) - 1 - 3 - 7 - 9 - - scala> found = false - found: Boolean = false - - scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) - 1 - 3 - -The difference happens because filter is immediately applied with `List`, -returning a list of odds -- since `found` is `false`. Only then `foreach` is -executed, but, by this time, changing `found` is meaningless, as `filter` has -already executed. - -In the case of `Stream`, the condition is not immediatelly applied. Instead, as -each element is requested by `foreach`, `filter` tests the condition, which -enables `foreach` to influence it through `found`. Just to make it clear, here -is the equivalent for-comprehension code: - - for (x <- List.range(1, 10); if x % 2 == 1 && !found) - if (x == 5) found = true else println(x) - - for (x <- Stream.range(1, 10); if x % 2 == 1 && !found) - if (x == 5) found = true else println(x) - -This caused many problems, because people expected the `if` to be considered -on-demand, instead of being applied to the whole collection beforehand. - -Scala 2.8 introduced `withFilter`, which is _always_ non-strict, no matter the -strictness of the collection. The following example shows `List` with both -methods on Scala 2.8: - - scala> var found = false - found: Boolean = false - - scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) - 1 - 3 - 7 - 9 - - scala> found = false - found: Boolean = false - - scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x)) - 1 - 3 - -This produces the result most people expect, without changing how `filter` -behaves. As a side note, `Range` was changed from non-strict to strict between -Scala 2.7 and Scala 2.8. - -This answer was originally submitted in response to [this question on Stack Overflow][1]. - - [1]: http://stackoverflow.com/questions/1052476/can-someone-explain-scalas-yield/1052510#1052510 diff --git a/tutorials/scala-for-csharp-programmers.disabled.html b/tutorials/scala-for-csharp-programmers.disabled.html deleted file mode 100644 index 593f74779e..0000000000 --- a/tutorials/scala-for-csharp-programmers.disabled.html +++ /dev/null @@ -1,1237 +0,0 @@ ---- -layout: overview -title: A Scala Tutorial for C# Programmers - -discourse: true ---- - -By Ivan Towlson - -## Introduction - -Scala is a hybrid of functional and object-oriented languages. -Its functional aspects make it very expressive when writing algorithmic -code, and play nicely with the brave new world of concurrency; its -object-oriented aspects keep it familiar and convenient when creating -business objects or other stateful models. - -## The same concepts - -Scala shares many features and concepts with C#, as well as introducing -many that are new. In fact, some of the capabilities that people talk -about a lot in Scala introductions or Java-to-Scala guides are very -familiar to C# programmers. For example, as a C# programmer, you don't -need to be gently walked through the idea of function types -- you've -been using delegates and lambdas for years. - -However, some Scala features and behaviours are quite different from C#, -and even those which are common usually have different syntax or work -in slightly different ways. So let's start out by covering some Scala -basics from a C# programmer's point of view. - -### Classes - -The basic concept of classes is the same in Scala as in C#. A class -bundles up a bunch of state (member fields) and hides it behind an -interface (member methods). The syntax for declaring classes is just -like C#: - - class Widget { - } - -### Fields - -The syntax for declaring fields is like this: - - class Widget { - val serialNumber = 123 - private var usageCount = 0 - } - -You can probably figure out what's going on here, but let's just note -a few differences from C#: - -* Fields are public by default, rather than private by default. -* You don't need to put a semicolon after a field declaration. You can - write semicolons if you want (or if your muscle memory makes you), - but if you don't, Scala will figure it out for you. -* Scala automatically figures out the type of the field from its initial - value, just like the C# `var` keyword. (Don't be fooled by the appearance - of the second field though -- the Scala `var` keyword doesn't mean the - same thing as the C# `var` keyword.) - -Now, why is one of these fields introduced as `val` and the other as -`var`? In C#, fields are mutable by default. That is, by default, -any code that can access a field can modify it. You can specify *readonly* -to make a field immutable, so that it can't be changed once the object -has been constructed. - -Scala doesn't have a default setting for mutability. You have to engage -brain, and decide whether each field is mutable or immutable. `val` means -a field is immutable (readonly in C#) and `var` means it's mutable -(a normal field in C#). - -So the class above is equivalent to the C#: - - class Widget { - public readonly int serialNumber = 123; - private int usageCount = 0; - } - -Notice that C# makes you write extra code to make things immutable, -and Scala doesn't. This may seem like a small thing, but it's -going to be a really important theme. - -### Methods - -The syntax for declaring methods is like this: - - class Widget { - def reticulate(winding: Int): String = "some value" - } - -This is a more dramatic departure from C# so let's pick it apart. - -The `def` keyword means we're declaring a method rather than a field. -There isn't a direct equivalent to this in C#, which figures out whether -something is a method or a field from context. As with fields, -methods are public by default. - -The method name is reassuringly language-independent. - -Method arguments go in brackets after the method name, just as in C#. -However, the way Scala specifies the argument types is different from C#. -In C# you would write `int winding`; in Scala you write `winding: Int`. - -This is a general principle: in Scala, when you want to specify the -type of something, you write the type after the something, separated -by a colon. (Whereas in C# you write the type before the something, -separated by a space.) - -You can see the same principle in action for the return type of the -function. `reticulate` returns a `String`. - -Finally, the body of the method has been placed after an equals sign, -rather than inside braces. (Braces are only necessary when the method -body consists of multiple expressions/statements.) What's more, -the body of the method is an expression -- that -is, something with a value -- rather than a set of statements amongst which -is a `return`. I'll come back to this when we look at a more realistic -example, but for the time being, let's focus on the trivial example and -translate it into C# syntax: - - class Widget { - public string Reticulate(int winding) { - return "some value"; - } - } - -### Classes and Structs - -In C#, when you define a type, you can decide whether to make it a -reference type (a class) or a value type (a struct). Scala doesn't -allow you to create custom value types. It has only classes, not -structs. This restriction comes from Scala targeting the Java -Virtual Machine. Unlike .NET, the JVM doesn't really have the concept -of value types. Scala tries to disguise this as best it can, but the -lack of custom value types is one place where the implementation -leaks through. Fortunately, Scala makes it easy to define immutable -reference types, which are nearly as good. - -### Base Types - -Scala's base types are pretty much the same as C#'s, except that they -are named with initial capitals, e.g. `Int` instead of `int`. (In fact -every type in Scala starts with an uppercase letter.) There are -no unsigned variants, and booleans are called `Boolean` instead of `bool`. - -Scala's name for `void` is `Unit`, but unlike `void`, `Unit` is a real type. -We'll see why this is important in a moment. - -### Function Types - -In C#, you can have variables that refer to functions instead of data. -These variables have delegate types, such as *Predicate` or -`Func` or `KeyEventHandler` or `Action`. - -Scala has the same concept, but the function types are built into the -language, rather than being library types. Function types are -spelled `(T1, T2, ...) => TR`. For example, a predicate of integers -would be type `(Int) => Boolean`. If there is only one input type, -the parens can be left out like this: `Int => Boolean`. - -Effectively, Scala gets rid of all those weird custom delegate types -like `Predicate` and `Comparer` and has a single family of -function types like the C# `Func<...>` family. - -What if you want to refer to a method that doesn't have a return value? -In C#, you can't write `Func` because void isn't a valid -type; you have to write `Action` instead. But Scala doesn't have -different families of delegate types, it just has the built-in -function types. Fortunately, in Scala, `Unit` is a real type, so you -can write `(Int) => Unit` for a function which takes an integer -and doesn't return a useful value. This means you can pass `void` methods -interchangeably with non-`void` methods, which is a Good Thing. - -### Implementing Methods - -I showed a method above, but only to illustrate the declaration syntax. -Let's take a look at a slightly more substantial method. - - def power4(x: Int): Int = { - var square = x * x // usually wouldn't write this - see below - square * square - } - -This looks pretty similar to C#, except that: - -* We're allowed to leave out semicolons, as mentioned above. -* We don't need a return statement. The method body consists of - an expression, square * square, with some preliminary bits - to define the components of the expression. - The return value of the method is the value of the expression. - -In order to make this method look like C#, I used the `var` keyword -to declare the local variable `square`. But as with fields, the -Scala `var` keyword doesn't work quite the same as the C# `var` keyword: - -In C#, `var` means "work out the type of the variable from the -right hand side". But Scala does that automatically -- you don't -need to ask for it. In Scala, `var` means "allow me to change this -variable (or field) after initialisation". As with fields, you can -also use `val` to mean 'don't allow me to change this variable -after initialisation.' And in fact since we don't need to change -`square` after initialisation, we'd be better off using val: - - def power4(x: Int): Int = { - val square = x * x // val not var - square * square - } - -Incidentally, if you do ever want to explicitly indicate the type -of a variable, you can do it the same way as with function arguments: - - def power4(x: Int): Int = { - val square : Int = x * x - square * square - } - -Notice that you still need `val` or `var`. Telling Scala the type -of the variable is independent of deciding whether the variable should -be mutable or immutable. - -### Tuples - -Everybody hates out parameters. We all know that code like this just -isn't nice: - - Widget widget; - if (widgetDictionary.TryGetValue(widgetKey, out widget)) - { - widget.ReticulateSplines(); - } - -And once you start composing higher-level functions into the mix, it gets -positively nasty. Suppose I want to make a HTTP request. Well, that's -going to produce two outputs in itself, the success code and the response -data. But now suppose I want to time it. Writing the timing code isn't a -problem, but now I have *three* outputs, and to paraphrase *Was Not Was*, -I feel worse than Jamie Zawinski. - -You can get around this in specific situations by creating custom types -like `DictionaryLookupResult` or `TimedHttpRequestResult`, but eventually -the terrible allure wears off, and you just want it to work. - -Enter tuples. A tuple is just a small number of values -- a single value, -a pair of values, a triplet of values, that sort of thing. You can tell -that tuples were named by mathematicians because the name only makes sense -when you look at the cases you hardly ever use (quadruple, quintuple, -sextuple, etc.). Using tuples, our timed HTTP request might look like this: - - public Tuple Time(Func> action) - { - StartTimer(); - var result = action(); - TimeSpan howLong = StopTimer(); - return Tuple.Create(result.First, result.Second, howLong); - } - - var result = Time(() => MakeRequest(uri)); - var succeeded = result.First; - var response = result.Second; - var howLong = result.Third. - Console.WriteLine("it took " + howLong); - -The reason this keeps getting verbose on us is that C# doesn’t provide any -syntatical support for tuples. To C#, a `Tuple<>` is just another generic -type. To us, the readers, a `Tuple<>` is just another generic type with -particularly unhelpful member names. - -Really, what we're really trying to articulate by returning a `Tuple<>` is, -"this method has several outputs." So what do we want to do with those -outputs? We want to access them, for example to stash them in variables, -without having to construct and pick apart the tuple one value at a time. -That means the language has to provide some kind of syntax-level support -for tuples, instead of treating them like every other class the compiler -doesn’t know about. - -Many functional languages have exactly this kind of syntactical support, -and Scala is no exception. Here’s how the above pseudo-C# looks in Scala: - - def time(action: => (Boolean, Stream)): (Boolean, Stream, TimeSpan) = { - startTimer() - val (succeeded, response) = action - (succeeded, response, stopTimer()) - } - - val (succeeded, response, timeTaken) = time(makeRequest) - Console.WriteLine("it took " + timeTaken) - -Notice the multiple variables on the left hand side of the time call? -Notice the `(Boolean, Stream, TimeSpan)` return type of the time method? -That return value is effectively a tuple type, but instead of having to -capture the returned tuple in a `Tuple<>` variable and extract its various -bits by hand, we are getting the Scala compiler to (in the time function) -compose the tuple implicitly for us, without us having to write the -constructor call, and (in the calling code) unpick the tuple into -meaningful, named variables for us without us having to explicitly copy -the values one by one and by name. - -(By the way, a proper implementation of the `time()` method wouldn’t be -restricted to `(Boolean, Stream)` results: we’d be looking to write a -method that could time anything. I’ve skipped that because it would -distract from the point at hand.) - -How would this play with the dictionary example? - - val (found, widget) = widgetDictionary.getValue(key) - if (found) - widget.reticulateSplines() - -We don’t actually save any lines of code, what with having to now capture -the “found” value into a variable and test it separately; and it’s not as -if the original C# version was horribly unreadable anyway. So maybe this is -a matter of taste, but I find this a lot easier to read and to write: all -the outputs are on the left of the equals sign where they belong, instead -of being spread between the assignment result and the parameter list, and -we don’t have that odd Widget declaration at the top. - -## New and different concepts - -Scala's primary platform is the Java virtual machine, and some of the -interest in Scala comes from Java programmers' interest in features such -as type inference, comprehensions and lambdas, with which C# programmers -are already familiar. So what's left that might be of interest -specifically to C# programmers? - -### Mixins and Traits - -#### Motivation - -Interfaces in C# and Java play a dual role. -First, they are a set of capabilities that an implementer has to, well, -implement. Second, they are a feature set that a client can use. - -These two roles can be conflicting: The first means that interfaces want -to be minimal, so that implementers don't have to implement a whole lot -of superfluous and redundant guff. The second means that interfaces want -to be maximal, so that clients don't have to clog themselves up with -boilerplate utility methods. - -Consider, for example, `IEnumerable` (and its sister interface `IEnumerator`). -This is a very minimal interface: implementers just need to be able to -produce values in sequence. But this minimalism means that clients of -`IEnumerable` need to write the same old boilerplate again and again and -again: foreach loops to filter, foreach loops to call a method on each -element of the sequence, foreach loops to aggregate, foreach loops to -check whether all elements meet a criterion, or to find the first member -that meets a criterion, or... - -This is frustrating because the implementations of "filter," "apply", -"aggregate," and so on are always the same. Of course, we could put -these methods into concrete types (`List` includes several), but then -those concrete types will contain duplicate code, and users who only have -an `IEnumerable` will still miss out. And yet we can't put these methods -into the interface because then every implementer of `IEnumerable` would -have to implement them -- and they'd end up writing the same boilerplate, -just now in all the zillions of `IEnumerable` classes instead of their clients. - -#### The C# and Scala Solutions - -We could resolve this tension if we had a way for interfaces to contain -implementation: for example, if `IEnumerable` required the implementer -to provide the class-specific iteration functionality, but then provided -the standard implementations of "filter," "apply", "aggregate" and so on -automatically: - - public pseudo_interface IEnumerable - { - IEnumerator GetEnumerator(); // must be implemented - IEnumerable Filter(Predicate predicate) // comes for free - { - foreach (object o in this) - if (predicate(o)) - yield return o; - } - } - -C# 3 addresses this using extension methods: the methods mentioned above -are all in fact included as extension methods on `IEnumerable` as -part of LINQ. - -This has some advantages over the approach described above: specifically, -the "standard methods" aren't bound up in the interface, so you can add -your own methods instead of being limited to the ones that the interface -author has included. - -On the other hand, it means that method implementations have to be packaged -in a different class from the interface, which feels less than modular. - -Scala takes a different approach. A Scala trait can contain a mix of -abstract methods without implementation as well as concrete methods with -an implementation. (It can also be a pure interface, with only abstract -members.) - -Here's a Scala trait that represents objects that can be compared -and ordered: - - trait Ord { - def < (that: Any): Boolean - def <=(that: Any): Boolean = (this < that) || (this == that) - def > (that: Any): Boolean = !(this <= that) - def >=(that: Any): Boolean = !(this < that) - } - -Orderable objects can extend `Ord`, but only need to implement the -method `<`. They then get the other operators for free, implemented -automatically by Ord in terms of `<`. - - class Date extends Ord { - def < (that: Any): Boolean = /* implementation */ - } - - // can now write: myDate >= yourDate - -A similar trait, called `Ordered` already exists in Scala, so there is no -need to actually define my trait. - -#### Scala Traits vs. C# Extension Methods - -Okay, so Scala has a different way of packaging standard implementations -from C#'s extension methods. It's different, but why is it interesting? -Well, there are a couple of things that you can do with Scala traits that -don't fall nicely out of the extension methods approach. - -First, you can override the default implementations of trait members, -to take advantage of additional information or capabilities available -in the implementing type. - -Let's look at another `IEnumerable` example, recast as a Scala trait: - - trait Enumerable { - def getEnumerator(): Enumerator - def count: Int = { - // Shockingly bad style; for illustrative purposes only - var c = 0 - val e = getEnumerator() - while (e.moveNext()) c = c + 1 - c - } - } - -This (ignoring style issues for now) is the only fully general -implementation we can provide for count: it will work with any `Enumerable`. -But for collections that know their sizes, such as `arrays` or `List`, -it's gruesomely inefficient. It iterates over the entire collection, -counting elements one by one, when it could just consult the `size` member -and return that. - -Let's fix that: - - class MyList extends Enumerable { - private var _size; - def getEnumerator(): Enumerator = /* ... */ - override def count: Int = _size - } - -The `count` member of the `Enumerable` trait works like a virtual method: -it can be overridden in classes which implement/derive from `Enumerable`. - -Compare this to the `Count()` extension method on `IEnumerable` in LINQ. -This achieves the same effect by trying to cast to `ICollection`, which is -fine as far as it goes but isn't extensible. - -Suppose you create an enumerable class that can count itself quickly but -isn't a collection -- for example a natural numbers range object. -With a Scala trait, the `NumberRange` type could provide an efficient -override of `count`, just like any other virtual method; with C# extension -methods, `Enumerable.Count()` would have to somehow know about the -`NumberRange` type in advance, or fall back on counting elements one by one. - -Second, with Scala you can choose a trait implementation when you -instantiate an object, rather than having it baked in at the class level -once and for all. This is called mixin-composition. - -Suppose you're creating a `MyList` instance, but you want it to puff itself -up to look bigger so as to frighten other `MyList` instances off its territory. -(This example would probably work better with fish, but we're stuck with -`Enumerable`s now. Work with me here.) In C#, you'd need to create a -`PuffedUpMyList` class and override the `Count` property. -In Scala, you can just mix in a `PuffedUp` version of the trait: - - trait PuffedUp extends Enumerable { - override def count: Int = super.count + 100 - } - - val normal = new MyList - Console.WriteLine(normal.count) // meh - val puffedUp = new MyList with PuffedUp - Console.WriteLine(puffedUp.count) // eek! - -As you can imagine this gives us much better granularity and composability -of traits and implementations than we get from the extension methods -approach, or indeed from single implementation inheritance type systems -in general. - -So Scala traits have some distinct advantages over extension methods. -The only downside appears to be the inability for clients to add their -own methods to a trait after the fact. - -Fortunately, you can work around this in Scala using so-called implicit -conversions. They enable Scala programmers to enrich existing types with new -functionality. - -### Singletons - -In C#, if you want to create a singleton object, you have to create a class, -then stop evildoers creating their own instances of that class, then create -and provide an instance of that class yourself. - -While this is hardly a Burma Railway of the programming craft, it does -feel like pushing against the grain of the language. Nor is it great for -maintainers, who have to be able to recognise a singleton by its pattern -(private constructor, public static readonly field, ...), or for clients, -who have to use a slightly clumsy multipart syntax to refer to the -singleton (e.g. `Universe.Instance`). - -What would be easier for all concerned would be if you could just declare -objects *as* singletons. That is, instead of writing class `Universe` and a -`public static readonly` instance of it, you could just write `object Universe`. - -And that's exactly what Scala allows you to do: - - object Universe { - def contains(obj: Any): Boolean = true - } - - val v = Universe.contains(42) - -What's going on behind the scenes here? It pretty much goes without saying -that the Scala compiler is creating a new type for the singleton object. -In fact it creates two types, one for the implementation and one for the -interface. The interface looks like a .NET static class (actually, the -.NET 1.x equivalent, a sealed class with only static members). -Thus, a C# program would call the example above as `Universe.contains(42)`. - -Singleton objects are first-class citizens in Scala, so they can for -example derive from classes. This is a nice way of creating special values -with custom behaviour: you don't need to create a whole new type, you just -define an instance and override methods in it: - - abstract class Cat { - def humiliateSelf() - } - - object Slats extends Cat { - def humiliateSelf() { savage(this.tail) } - } - -Obviously this is a frivolous example, but "special singletons" turn out to -be an important part of the functional idiom, for example for bottoming out -recursion. *Scala by Example (PDF)* describes an implementation of a Set class -which is implemented as a tree-like structure ("left subset - member - right -subset"), and methods such as `contains()` work by recursing down to the -child sets. For this to work requires an `EmptySet` whose implementation -(state) and behaviour are quite different from non-empty sets -- e.g. -`contains()` just returns `false` instead of trying to delegate to -non-existent child sets. Since `EmptySet` is logically unique it is both -simpler and more efficient to represent it as a singleton: i.e. to declare -`object EmptySet` instead of `class EmptySet`. - -In fact the whole thing can become alarmingly deep: *Scala by Example* -also includes a description of `Boolean` as an `abstract class`, and -`True` and `False` as singleton objects which extend `Boolean` and provide -appropriate implementations of the `ifThenElse` method. - -And fans of Giuseppe Peano should definitely check out the hypothetical -implementation of `Int`... - -### Pass by Name - -> You're only on chapter 3 and you're already reduced to writing about -> *calling conventions*? You suck! Do another post about chimney sweeps -> being hunted by jars of marmalade!" - -Silence, cur. Pass by name is not as other calling conventions are. -Pass by name, especially in conjunction with some other rather -theoretical-sounding Scala features, is your gateway to the wonderful -world of language extensibility. - -#### What is Passing By Name? - -First, let's talk about what we mean by *calling convention*. A calling -convention describes how stuff gets passed to a method by its caller. -In the good old days, this used to mean exciting things like which -arguments got passed in registers and who was responsible for resetting -the stack pointer. Sadly, the days of being able to refer to "naked fun -calls" are consigned to history: In modern managed environments, the -runtime takes care of all this guff and the main distinction is pass -data by value or by reference. (The situation on the CLR is slightly -complicated by the need to differentiate passing values by value, values -by reference, references by value and references by reference, but I'm -not going to go into that because (a) it's irrelevant to the subject at -hand and (b) that's -[Jon Skeet](http://www.yoda.arachsys.com/csharp/parameters.html)'s turf -and I don't want him to shank me. Again.) - -In *pass by value*, the called method gets a copy of whatever the caller -passed in. Arguments passed by value therefore work like local variables -that are initialised before the method runs: when you do anything to them, -you're doing it to your own copy. - -In *pass by reference*, the called method gets a reference to the caller's -value. When you do anything to a pass-by-reference argument, you're doing -it to the caller's data. - -In *pass by name*, the called method gets... well, it's a bit messy to -explain what the called method gets. But when the called method does -anything to the argument, the argument gets evaluated and the "anything" -is done to that. Crucially, evaluation happens every time the argument -gets mentioned, and only when the argument gets mentioned. - -#### Not Just Another Calling Convention - -Why does this matter? It matters because there are functions you can't -implement using pass by value or pass by reference, but you can implement -using pass by name. - -Suppose, for example, that C# didn't have the `while` keyword. -You'd probably want to write a method that did the job instead: - - public static void While(bool condition, Action body) - { - if (condition) - { - body(); - While(condition, body); - } - } - -What happens when we call this? - - long x = 0; - While(x < 10, () => x = x + 1); - -C# evaluates the arguments to `While` and invokes the `While` method with -the arguments `true` and `() => x = x + 1`. After watching the CPU sit -on 100% for a while you might check on the value of `x` and find it's -somewhere north of a trillion. *Why?* Because the condition argument was -*passed by value*, so whenever the `While` method tests the value of -condition, it's always `true`. The `While` method doesn't know that -condition originally came from the expression `x < 10`; all `While` knows -is that condition is `true`. - -For the `While` method to work, we need it to re-evaluate `x < 10` every -time it hits the condition argument. While needs not the value of the -argument at the call site, nor a reference to the argument at the call -site, but the actual expression that the caller wants it to use to generate -a value. - -Same goes for short-circuit evaluation. If you want short-circuit -evaluation in C#, your only hope if to get on the blower to Anders -Hejlsberg and persuade him to bake it into the language: - - bool result = (a > 0 && Math.Sqrt(a) < 10); - double result = (a < 0 ? Math.Sqrt(-a) : Math.Sqrt(a)); - -You can't write a function like `&&` or `?:` yourself, because C# will -always try to evaluate all the arguments before calling your function. - -Consider a VB exile who wants to reproduce his favourite keywords in C#: - - bool AndAlso(bool condition1, bool condition2) - { - return condition1 && condition2; - } - - T IIf(bool condition, T ifTrue, T ifFalse) - { - if (condition) - return ifTrue; - else - return ifFalse; - } - -But when C# hits one of these: - - bool result = AndAlso(a > 0, Math.Sqrt(a) < 10); - double result = IIf(a < 0, Math.Sqrt(-a), Math.Sqrt(a)); - -it would try to evaluate all the arguments at the call site, and pass the -results of those evaluations to `AndAlso` or `IIf`. There's no -short-circuiting. So the `AndAlso` call would crash if a were negative, -and the `IIf` call if a were anything other than 0. Again, what you want is -for the `condition1`, `condition2`, `ifTrue` and `ifFalse` arguments to be -evaluated by the callee if it needs them, not for the caller to evaluate -them before making the call. - -And that's what *pass by name* does. A parameter passed by name is not -evaluated when it is passed to a method. It is evaluated -- and -re-evaluated -- when the called method evaluates the parameter; -specifically when the called method requests the value of the parameter by -mentioning its name. This might sound weird and academic, but it's the key -to being able to define your own control constructs. - -#### Using Pass By Name in Scala - -Let's see the custom while implementation again, this time with Scala -*pass by name* parameters: - - def myWhile(condition: => Boolean)(body: => Unit): Unit = - if (condition) { - body - myWhile(condition)(body) - } - -We can call this as follows: - - var i = 0 - myWhile (i < 10) { - println(i) - i += 1 - } - -Unlike the C# attempt, this prints out the numbers from 0 to 9 and then -terminates as you'd wish. - -Pass by name also works for short-circuiting: - - import math._ - - def andAlso(condition1: => Boolean, condition2: => Boolean): Boolean = - condition1 && condition2 - - val d = -1.234 - val result = andAlso(d > 0, sqrt(d) < 10) - -The `andAlso` call returns `false` rather than crashing, because -`sqrt(d) < 10` never gets evaluated. - -What's going on here? What's the weird colon-and-pointy-sticks syntax? -What is actually getting passed to `myWhile` and `andAlso` to make this work? - -The answer is a bit surprising. Nothing is going on here. This is the -normal Scala function parameter syntax. There is no *pass by name* in Scala. - -Here's a bog-standard *pass by value* Scala function declaration: - - def myFunc1(i: Int): Unit = ... - -Takes an integer, returns void: easy enough. Here's another: - - def myFunc2(f: Int => Boolean): Unit = ... - -Even if you've not seen this kind of expression before, it's probably not -too hard to guess what this means. This function takes a *function from -`Int` to `Boolean`* as its argument. In C# terms, -`void MyFunc2(Func f)`. We could call this as follows: - - myFunc2 { (i: Int) => i > 0 } - -So now you can guess what this means: - - def myFunc3(f: => Boolean) : Unit = ... - -Well, if `myFunc2` took an *Int-to-Boolean* function, `myFunc3` must be -taking a "blank-to-Boolean" function -- a function that takes no arguments -and returns a `Boolean`. In short, a conditional expression. So we can -call `myFunc3` as follows: - - val j = 123 - myFunc3 { j > 0 } - -The squirly brackets are what we'd expect from an anonymous function, and -because the function has no arguments Scala doesn't make us write -`{ () => j > 0 }`, even though that's what it means really. The anonymous -function has no arguments because `j` is a captured local variable, not an -argument to the function. But there's more. Scala also lets us call -`myFunc3` like this: - - val j = 123 - myFunc3(j > 0) - -This is normal function call syntax, but the Scala compiler realises that -`myFunc3` expects a nullary function (a function with no arguments) rather -than a `Boolean`, and therefore treats `myFunc3(j > 0)` as shorthand for -`myFunc3(() => j > 0)`. This is the same kind of logic that the C# compiler -uses when it decides whether to compile a lambda expression to a delegate -or an expression tree. - -You can probably figure out where it goes from here: - - def myFunc4(f1: => Boolean)(f2: => Unit): Unit = ... - -This takes two functions: a conditional expression, and a function that -takes no arguments and returns no value (in .NET terms, an `Action`). -Using our powers of anticipation, we can imagine how this might be called -using some unholy combination of the two syntaxes we saw for calling -`myFunc3`: - - val j = 123; - myFunc4(j > 0) { println(j); j -= 1; } - -We can mix and match the `()` and `{}` bracketing at whim, except that we -have to use `{}` bracketing if we want to batch up multiple expressions. -For example, you could legally equally well write the following: - - myFunc4 { j > 0 } { println(j); j -= 1; } - myFunc4 { println(j); j > 0 } (j -= 1) - myFunc4 { println(j); j > 0 } { j -= 1 } - -And we'll bow to the inevitable by supplying a body for this function: - - def myFunc5(f1: => Boolean)(f2: => Unit): Unit = - if (f1()) { - f2() - myFunc5(f1)(f2) - } - -Written like this, it's clear that `f1` is getting evaluated each time we -execute the if statement, but is getting passed (as a function) when -`myFunc5` recurses. But Scala allows us to leave the parentheses off -function calls with no arguments, so we can write the above as: - - def myFunc5(f1: => Boolean)(f2: => Unit): Unit = - if (f1) { - f2 - myFunc5(f1)(f2) - } - -Again, type inference allows Scala to distinguish the *evaluation of -`f1`* in the if statement from the *passing of `f1`* in the `myFunc5` -recursion. - -And with a bit of renaming, that's `myWhile`. There's no separate -*pass by name* convention: just the usual closure behaviour of capturing -local variables in an anonymous method or lambda, a bit of syntactic sugar -for nullary functions (functions with no arguments), just like C#'s -syntactic sugar for property getters, and the Scala compiler's ability to -recognise when a closure is required instead of a value. - -In fact, armed with this understanding of the Scala "syntax," we can -easily map it back to C#: - - void While(Func condition, Action body) - { - if (condition()) - { - body(); - While(condition, body); - } - } - - int i = 0; - While(() => i < 10, () => - { - Console.WriteLine(i); - ++i; - }); - -The implementation of the `While` method in C# is, to my eyes, a bit -clearer than the Scala version. However, the syntax for *calling* the -`While` method in C# is clearly way more complicated and less natural than -the syntax for calling `myWhile` in Scala. Calling `myWhile` in Scala was -like using a native language construct. Calling While in C# required a -great deal of clutter at the call site to prevent C# from trying to treat -`i < 10` as a once-and-for-all value, and to express the body at all. - -So that's so-called "pass by name" demystified: The Scala Web site, with -crushing mundanity, demotes it to "automatic type-dependent closure -construction," which is indeed exactly how it works. As we've seen, -however, this technical-sounding feature is actually essential to -creating nice syntax for your own control constructs. We'll shortly see -how this works together with other Scala features to give you even more -flexibility in defining your construct's syntax. - -### Implicits - -Scala implicits offer some features which will be familiar to the C# -programmer, but are much more general in nature and go far beyond what can -be done in C#. - -#### Enriching types in C# and Scala - -Scala, like C#, is statically typed: a class’ methods are compiled into the -class definition and are not open for renegotiation. You cannot, as you -might in Ruby or Python, just go ahead and declare additional methods on an -existing class. - -This is of course very inconvenient. You end up declaring a load of -`FooHelper` or `FooUtils` classes full of static methods, and having to -write verbose calling code such as `if (EnumerableUtils.IsEmpty(sequence))` -rather than the rather more readable `if (sequence.IsEmpty())`. - -C# 3 tries to address this problem by introducing extension methods. -Extension methods are static methods in a `FooHelper` or `FooUtils` kind -of class, except you’re allowed to write them using member syntax. -By defining `IsEmpty` as an extension method on `IEnumerable`, you can -write `if (sequence.IsEmpty())` after all. - -Scala disapproves of static classes and global methods, so it plumps for -an alternative approach. You’ll still write a `FooHelper` or `FooUtils` -kind of class, but instead of taking the `Foo` to be Helped or Utilised as -a method parameter, your class will wrap `Foo` and enrich it with -additional methods. Let’s see this in action as we try to add a method to -the `Double` type: - - class RicherDouble(d : Double) { - def toThe(exp: Double): Double = System.Math.Pow(d, exp) - } - -(We call the class `RicherDouble` because Scala already has a `RichDouble` -class defined which provides further methods to `Double`.) - -Notice that `toThe` is an instance method, and that `RicherDouble` takes a -`Double` as a constructor parameter. This seems pretty grim, because we’d -normally have to access the function like this: - - val result = new DoubleExtensions(2.0).toThe(7.0) - -Hardly readable. To make it look nice, Scala requires us to define an -*implicit conversion* from `Double` to `RicherDouble`: - - object Implicits { - implicit def richerDouble(d: Double) = new RicherDouble(d) - } - -and to bring that implicit conversion into scope: - - import Implicits._ - -Now we can write this: - - val twoToTheSeven = 2.0.toThe(7.0) - -and all will be well. The `Double` type has apparently been successfully -enriched with the `toThe` method. - -This is, of course, just as much an illusion as the C# equivalent. -C# extension methods don’t add methods to a type, and nor do Scala -implicit conversions. What has happened here is that the Scala compiler -has looked around for implicit methods that are applicable to the type of -`2.0` (namely `Double`), and return a type that has a `toThe` method. -Our `Implicits.richerDouble` method fits the bill, so the Scala compiler -silently inserts a call to that method. At runtime, therefore, Scala calls -`Implicits.richerDouble(2.0)` and calls the `toThe` of the resulting -`RicherDouble`. - -If setting this up seems a bit verbose, well, maybe. C# extension methods -are designed to be easily – one might even say implicitly – brought into -scope. That’s very important for operators like the LINQ standard query -operators, but it can result in unwanted extension methods being dragged -into scope and causing havoc. Scala requires the caller to be a bit more -explicit about implicits, which results in a slightly higher setup cost but -gives the caller finer control over which implicit methods are considered. - -But as it happens you can avoid the need for separate definitions of -`Implicits` and `RicherDouble`, and get back to a more concise -representation by using an anonymous class. (As you’d expect, Scala -anonymous classes are fully capable, like Java ones, rather than the -neutered C# version.) Here’s how it looks: - - object Implicits { - implicit def doubleToThe(d1 : Double) = new { - def toThe(d2 : Double) : Double = Math.Pow(d1, d2) - } - } - -Well, big deal. Scala can enrich existing types with new methods just like -C#, but using a different syntax. In related news, Lisp uses a different -kind of bracket: film at eleven. Why should we be interested in Scala -implicits if they’re just another take on extension methods? - -#### Implicit Parameters - -What we saw above was an implicit method – a method which, like a C# -implicit conversion operator, the compiler is allowed to insert a call to -without the programmer writing that call. Scala also has the idea of -implicit parameters – that is, parameters which the compiler is allowed to -insert a value for without the programmer specifying that value. - -That’s just optional parameters with default values, right? Like C++ and -Visual Basic have had since “visual” meant ASCII art on a teletype, and -like C# is about to get? Well, no. - -C++, Visual Basic and C# optional parameters have fixed defaults specified -by the called function. For example, if you have a method like this: - - public void Fie(int a, int b = 123) { … } - -and you call `Fie(456)`, it’s always going to be equivalent to calling -`Fie(456, 123)`. - -A Scala implicit parameter, on the other hand, gets its value from the -calling context. That allows programmer calling the method to control the -implicit parameter value, creating an extensibility point that optional -parameters don’t provide. - -This probably all sounds a bit weird, so let’s look at an example. Consider -the following `Concatenate` method: - - public T Concatenate(IEnumerable sequence, T seed, Func concatenator); - -We pass this guy a sequence, a start value and a function that combines two -values into one, and it returns the result of calling that function across -the sequence. For example, you could pass a sequence of strings, a start -value of `String.Empty`, and `(s1, s2) => s1 + s2`, and it would return you -all the strings concatenated together: - - IEnumerable sequence = new string[] { “mog”, “bites”, “man” }; - string result = Concatenate(sequence, String.Empty, (s1, s2) => s1 + s2); - // result is “mogbitesman” - -But this is a unpleasantly verbose. We’re having to pass in `String.Empty` -and `(s1, s2) => s1 + s2` every time we want to concatenate a sequence of -strings. Not only is this tedious, it also creates the opportunity for -error when the boss decides to “help” and passes the literal -`"String.Empty"` as the seed value instead. (“Oh, and I upgraded all the -semi-colons to colons while I was in there. No, don’t thank me!”) We’d -like to just tell the Concatenate function, “Look, this is how you -concatenate strings,” once and for all. - -Let’s start out by redefining the `Concatenate` method in Scala. -I’m going to factor out the seed and the concatenator method into a trait -because we’ll typically be defining them together. - - trait Concatenator[T] { - def startValue: T - def concat(x: T, y: T): T - } - - object implicitParameters { - def concatenate[T](xs: List[T])(c: Concatenator[T]): T = - if (xs.isEmpty) c.startValue - else c.concat(xs.head, concatenate(xs.tail)(c)) - } - -We can call this as follows: - - object stringConcatenator extends Concatenator[String] { - def startValue: String = "" - def concat(x: String, y: String) = x.concat(y) - } - - object implicitParameters { - def main(args: Array[String]) = { - val result = concatenate(List("mog", "bites", "man"))(stringConcatenator) - println(result) - } - } - -So far, this looks like the C# version except for the factoring out of the -`Concatenator` trait. We’re still having to pass in the -`stringConcatenator` at the point of the call. Let’s fix that: - - def concatenate[T](xs: List[T])(implicit c: Concatenator[T]): T = - if (xs.isEmpty) c.startValue - else c.concat(xs.head, concatenate(xs.tail)) - -We’ve changed two things here. First, we’ve declared c to be an *implicit -parameter*, meaning the caller can leave it out. Second, we’ve left -it out ourselves, in the recursive call to `concatenate(xs.tail)`. - -Well, okay, it’s nice that `concatenate` now doesn’t have to pass the -`Concatenator` explicitly to the recursive call, but we’re still having to -pass in the `stringConcatenator` object to get things started. If only -there were some way to make the `stringConcatenator` object itself implicit… - - object Implicits { - implicit object stringConcatenator extends Concatenator[String] { - def startValue: String = "" - def concat(x: String, y: String) = x.concat(y) - } - } - -Again, we’ve done two things here. First, we’ve declared the -`stringConcatenator` object implicit. Consequently, we’ve had to move it -out of the top level, because Scala doesn’t allow implicits at the top -level (because they’d pollute the global namespace, being in scope even -without an explicit import statement). - -Now we can call `concatenate` like this: - - import Implicits._ - - object implicitParameters { - def main(args: Array[String]) = { - val result = concatenate(List("mog", "bites", "man")) - println(result) - } - } - -And we’ll still get “mogbitesman” as the output. - -Let’s review what’s going on here. The implicit parameter of concatenate -has been set to our `stringConcatenator`, a default value that the -`concatenate` method knew nothing about when it was compiled. This is -somewhere north of what classical optional parameters are capable of, -and we’re not finished yet. Let’s build a `listConcatenator`. - - object Implicits { - class ListConcatenator[T] extends Concatenator[List[T]] { - def startValue: List[T] = Nil - def concat(x: List[T], y: List[T]) = x ::: y - } - implicit object stringListConcatenator extends ListConcatenator[String] { } - } - -This is a bit vexing. `List` in Scala is a generic type, and has a generic -concatenation method called `:::`. But we can’t create a generic object, -because an object is an instance. And implicit parameters have to be objects. -So the best we can do is build a generic `ListConcatenator` class, and then -create trivial implicit objects for each generic parameter type we might -need. - -However, let’s not worry about the implementation, and see how this is used -at the calling end: - - val result = concatenate(List( - List("mog", "bites", "man"), - List("on", "beard") - )) - -This displays `List(mog, bites, man, on, beard)`; that is, it concatenates -the two `List[String]`s into one. Once again, we have not had to pass -`stringListConcatenator` explicitly: the Scala compiler has gone and found -it for us. We can use the exact same calling code to concatenate lists and -strings. - -#### Why Should I Care? - -Isn’t this pointless? At the call site, I have access to -`stringConcatenator` and `listStringConcatenator`. I can easily pass them -in rather than relying on spooky compiler magic to do it for me. -Aren’t implicit parameters just job security for compiler writers? - -Yes, implicit parameters are technically unnecessary. But if we’re going -to play that game, C# is technically unnecessary. You could write all that -code in IL. Extension methods are unnecessary because you could write the -static method out longhand. Optional parameters are unnecessary because -you could read the documentation and pass them in explicitly. -Post-It notes are unnecessary because you could fire up Outlook and create -a Note instead. - -Implicit parameters are about convenience and expressiveness. Implicit -parameters give you a way of describing how a function should handle -different situations, without needing to bake those situations into the -function logic or to specify them every time you call the function. -You don’t want to have to tell the `concatenate` function whether to use -the `List` or `String` concatenator every time you call it: the compiler -knows what you’re concatenating; specifying how to concatenate it just -gives you a chance to get it wrong! - -Consequently, implicit parameters – like implicit conversions – contribute -to Scala’s ability to support internal DSLs. By setting up appropriate -implicits, you can write code that reads much more naturally than if you -had to pepper it with function objects or callbacks. - -#### Conclusion - -Scala’s `implicit` keyword goes beyond C#’s equivalent. As in C#, it is -used for implicit conversions; unlike C#, this is the idiomatic way to add -operations to an existing type, removing the need for the separate -extension method syntax. Implicit parameters have no equivalent in C#. -They are like being able to add default values to a method: just as a C# -using statement bring implicit methods into scope, a Scala import statement -can bring default values into scope. If implicit conversions are a way of -extending classes, then implicit parameters are a way of extending methods, -creating simple, reliable shorthands for complex generic methods, and -making up another piece of the Scala DSL jigsaw. - -#### Method Call Syntax - -C#, like most object-oriented programming languages, is pretty strict about -how you call methods: you use the dot notation, unless the method is a -special ‘operator’ method such as `operator+`, `operator==` or a conversion -operator. The special operator methods are predefined by the compiler: you -can write your own implementation, but you can’t create your own operator -names. You can teach the `+` operator how to handle your custom type, but -you can’t add an exponentiation operator: - - int a = b ** c; - -C# has three problems with this: first, it doesn’t like the method name -`**`; second, it doesn’t like that there’s no `.` before the name; and -third, it doesn’t like that there’s no brackets around the method argument. - -To get around the objection to the name, let’s compromise and call it -`ToThe` for now. So what C# insists on seeing is `a.ToThe(b)`. - -Scala, like many functional languages, isn’t so strict. Scala allows you -to use any method with a single argument in an infix position. Before we -can see this in the exponentiation example, we will enrich the `Double` -type with the `toThe` method as we learned earlier: - - import Implicits._ - import math._ - - class RicherDouble(d: Double) { - def toThe(exp: Double): Double = pow(d, exp) - } - - object Implicits { - implicit def richerDouble(d: Double) = new RicherDouble(d) - } - -Recall that this is just the Scala idiom for extension methods – it’s the -equivalent of writing -`public static ToThe(this double first, double second) { … }` in C#. -(If we were wanting to use infix notation with our own class, we wouldn’t -need all this malarkey.) So now we can write: - - val raised = 2.0.toThe(7.0) - -Okay, so what do we need to do to get this to work in infix position? -Nothing, it turns out. - - val raised = 2.0 toThe 8.0 // it just works - -This still doesn’t look much like a built-in operator, but it turns out -Scala is less fussy than C# about method names too. - - class DoubleExtensions(d : Double) { - def **(exp: Double): Double = Pow(d, exp) - } - - val raised = 2.0 ** 9.0 // it still just works - -Much nicer. - -This sorta-kinda works for postfix operators too: - - class RicherString(s: String) { - def twice: String = s + s - } - - val drivel = "bibble" twice - -Calling methods in infix and postfix nodadion is obviously fairly simple -syntactic sugar over normal dot notation. But this seemingly minor feature -is very important in constructing DSLs, allowing Scala to do in internal -DSLs what many languages can do only using external tools. For example, -where most languages do parsing via an external file format and a tool to -translate that file format into native code (a la `lex` and `yacc`), -Scala’s parser library makes extensive use of infix and postfix methods to -provide a “traditional” syntax for describing a parser, but manages it -entirely within the Scala language. - diff --git a/tutorials/scala-for-java-programmers.md b/tutorials/scala-for-java-programmers.md deleted file mode 100644 index b8618b431c..0000000000 --- a/tutorials/scala-for-java-programmers.md +++ /dev/null @@ -1,723 +0,0 @@ ---- -layout: overview -title: A Scala Tutorial for Java Programmers -overview: scala-for-java-programmers - -discourse: true -multilingual-overview: true -languages: [es, ko, de, it, zh-tw] ---- - -By Michel Schinz and Philipp Haller - -## Introduction - -This document gives a quick introduction to the Scala language and -compiler. It is intended for people who already have some programming -experience and want an overview of what they can do with Scala. A -basic knowledge of object-oriented programming, especially in Java, is -assumed. - -## A First Example - -As a first example, we will use the standard *Hello world* program. It -is not very fascinating but makes it easy to demonstrate the use of -the Scala tools without knowing too much about the language. Here is -how it looks: - - object HelloWorld { - def main(args: Array[String]) { - println("Hello, world!") - } - } - -The structure of this program should be familiar to Java programmers: -it consists of one method called `main` which takes the command -line arguments, an array of strings, as parameter; the body of this -method consists of a single call to the predefined method `println` -with the friendly greeting as argument. The `main` method does not -return a value (it is a procedure method). Therefore, it is not necessary -to declare a return type. - -What is less familiar to Java programmers is the `object` -declaration containing the `main` method. Such a declaration -introduces what is commonly known as a *singleton object*, that -is a class with a single instance. The declaration above thus declares -both a class called `HelloWorld` and an instance of that class, -also called `HelloWorld`. This instance is created on demand, -the first time it is used. - -The astute reader might have noticed that the `main` method is -not declared as `static` here. This is because static members -(methods or fields) do not exist in Scala. Rather than defining static -members, the Scala programmer declares these members in singleton -objects. - -### Compiling the example - -To compile the example, we use `scalac`, the Scala compiler. `scalac` -works like most compilers: it takes a source file as argument, maybe -some options, and produces one or several object files. The object -files it produces are standard Java class files. - -If we save the above program in a file called -`HelloWorld.scala`, we can compile it by issuing the following -command (the greater-than sign `>` represents the shell prompt -and should not be typed): - - > scalac HelloWorld.scala - -This will generate a few class files in the current directory. One of -them will be called `HelloWorld.class`, and contains a class -which can be directly executed using the `scala` command, as the -following section shows. - -### Running the example - -Once compiled, a Scala program can be run using the `scala` command. -Its usage is very similar to the `java` command used to run Java -programs, and accepts the same options. The above example can be -executed using the following command, which produces the expected -output: - - > scala -classpath . HelloWorld - - Hello, world! - -## Interaction with Java - -One of Scala's strengths is that it makes it very easy to interact -with Java code. All classes from the `java.lang` package are -imported by default, while others need to be imported explicitly. - -Let's look at an example that demonstrates this. We want to obtain -and format the current date according to the conventions used in a -specific country, say France. (Other regions such as the -French-speaking part of Switzerland use the same conventions.) - -Java's class libraries define powerful utility classes, such as -`Date` and `DateFormat`. Since Scala interoperates -seemlessly with Java, there is no need to implement equivalent -classes in the Scala class library--we can simply import the classes -of the corresponding Java packages: - - import java.util.{Date, Locale} - import java.text.DateFormat - import java.text.DateFormat._ - - object FrenchDate { - def main(args: Array[String]) { - val now = new Date - val df = getDateInstance(LONG, Locale.FRANCE) - println(df format now) - } - } - -Scala's import statement looks very similar to Java's equivalent, -however, it is more powerful. Multiple classes can be imported from -the same package by enclosing them in curly braces as on the first -line. Another difference is that when importing all the names of a -package or class, one uses the underscore character (`_`) instead -of the asterisk (`*`). That's because the asterisk is a valid -Scala identifier (e.g. method name), as we will see later. - -The import statement on the third line therefore imports all members -of the `DateFormat` class. This makes the static method -`getDateInstance` and the static field `LONG` directly -visible. - -Inside the `main` method we first create an instance of Java's -`Date` class which by default contains the current date. Next, we -define a date format using the static `getDateInstance` method -that we imported previously. Finally, we print the current date -formatted according to the localized `DateFormat` instance. This -last line shows an interesting property of Scala's syntax. Methods -taking one argument can be used with an infix syntax. That is, the -expression - - df format now - -is just another, slightly less verbose way of writing the expression - - df.format(now) - -This might seem like a minor syntactic detail, but it has important -consequences, one of which will be explored in the next section. - -To conclude this section about integration with Java, it should be -noted that it is also possible to inherit from Java classes and -implement Java interfaces directly in Scala. - -## Everything is an Object - -Scala is a pure object-oriented language in the sense that -*everything* is an object, including numbers or functions. It -differs from Java in that respect, since Java distinguishes -primitive types (such as `boolean` and `int`) from reference -types, and does not enable one to manipulate functions as values. - -### Numbers are objects - -Since numbers are objects, they also have methods. And in fact, an -arithmetic expression like the following: - - 1 + 2 * 3 / x - -consists exclusively of method calls, because it is equivalent to the -following expression, as we saw in the previous section: - - (1).+(((2).*(3))./(x)) - -This also means that `+`, `*`, etc. are valid identifiers -in Scala. - -The parentheses around the numbers in the second version are necessary -because Scala's lexer uses a longest match rule for tokens. -Therefore, it would break the following expression: - - 1.+(2) - -into the tokens `1.`, `+`, and `2`. The reason that -this tokenization is chosen is because `1.` is a longer valid -match than `1`. The token `1.` is interpreted as the -literal `1.0`, making it a `Double` rather than an -`Int`. Writing the expression as: - - (1).+(2) - -prevents `1` from being interpreted as a `Double`. - -### Functions are objects - -Perhaps more surprising for the Java programmer, functions are also -objects in Scala. It is therefore possible to pass functions as -arguments, to store them in variables, and to return them from other -functions. This ability to manipulate functions as values is one of -the cornerstone of a very interesting programming paradigm called -*functional programming*. - -As a very simple example of why it can be useful to use functions as -values, let's consider a timer function whose aim is to perform some -action every second. How do we pass it the action to perform? Quite -logically, as a function. This very simple kind of function passing -should be familiar to many programmers: it is often used in -user-interface code, to register call-back functions which get called -when some event occurs. - -In the following program, the timer function is called -`oncePerSecond`, and it gets a call-back function as argument. -The type of this function is written `() => Unit` and is the type -of all functions which take no arguments and return nothing (the type -`Unit` is similar to `void` in C/C++). The main function of -this program simply calls this timer function with a call-back which -prints a sentence on the terminal. In other words, this program -endlessly prints the sentence "time flies like an arrow" every -second. - - object Timer { - def oncePerSecond(callback: () => Unit) { - while (true) { callback(); Thread sleep 1000 } - } - def timeFlies() { - println("time flies like an arrow...") - } - def main(args: Array[String]) { - oncePerSecond(timeFlies) - } - } - -Note that in order to print the string, we used the predefined method -`println` instead of using the one from `System.out`. - -#### Anonymous functions - -While this program is easy to understand, it can be refined a bit. -First of all, notice that the function `timeFlies` is only -defined in order to be passed later to the `oncePerSecond` -function. Having to name that function, which is only used once, might -seem unnecessary, and it would in fact be nice to be able to construct -this function just as it is passed to `oncePerSecond`. This is -possible in Scala using *anonymous functions*, which are exactly -that: functions without a name. The revised version of our timer -program using an anonymous function instead of *timeFlies* looks -like that: - - object TimerAnonymous { - def oncePerSecond(callback: () => Unit) { - while (true) { callback(); Thread sleep 1000 } - } - def main(args: Array[String]) { - oncePerSecond(() => - println("time flies like an arrow...")) - } - } - -The presence of an anonymous function in this example is revealed by -the right arrow `=>` which separates the function's argument -list from its body. In this example, the argument list is empty, as -witnessed by the empty pair of parenthesis on the left of the arrow. -The body of the function is the same as the one of `timeFlies` -above. - -## Classes - -As we have seen above, Scala is an object-oriented language, and as -such it has a concept of class. (For the sake of completeness, - it should be noted that some object-oriented languages do not have - the concept of class, but Scala is not one of them.) -Classes in Scala are declared using a syntax which is close to -Java's syntax. One important difference is that classes in Scala can -have parameters. This is illustrated in the following definition of -complex numbers. - - class Complex(real: Double, imaginary: Double) { - def re() = real - def im() = imaginary - } - -This `Complex` class takes two arguments, which are the real and -imaginary part of the complex. These arguments must be passed when -creating an instance of class `Complex`, as follows: `new - Complex(1.5, 2.3)`. The class contains two methods, called `re` -and `im`, which give access to these two parts. - -It should be noted that the return type of these two methods is not -given explicitly. It will be inferred automatically by the compiler, -which looks at the right-hand side of these methods and deduces that -both return a value of type `Double`. - -The compiler is not always able to infer types like it does here, and -there is unfortunately no simple rule to know exactly when it will be, -and when not. In practice, this is usually not a problem since the -compiler complains when it is not able to infer a type which was not -given explicitly. As a simple rule, beginner Scala programmers should -try to omit type declarations which seem to be easy to deduce from the -context, and see if the compiler agrees. After some time, the -programmer should get a good feeling about when to omit types, and -when to specify them explicitly. - -### Methods without arguments - -A small problem of the methods `re` and `im` is that, in -order to call them, one has to put an empty pair of parenthesis after -their name, as the following example shows: - - object ComplexNumbers { - def main(args: Array[String]) { - val c = new Complex(1.2, 3.4) - println("imaginary part: " + c.im()) - } - } - -It would be nicer to be able to access the real and imaginary parts -like if they were fields, without putting the empty pair of -parenthesis. This is perfectly doable in Scala, simply by defining -them as methods *without arguments*. Such methods differ from -methods with zero arguments in that they don't have parenthesis after -their name, neither in their definition nor in their use. Our -`Complex` class can be rewritten as follows: - - class Complex(real: Double, imaginary: Double) { - def re = real - def im = imaginary - } - - -### Inheritance and overriding - -All classes in Scala inherit from a super-class. When no super-class -is specified, as in the `Complex` example of previous section, -`scala.AnyRef` is implicitly used. - -It is possible to override methods inherited from a super-class in -Scala. It is however mandatory to explicitly specify that a method -overrides another one using the `override` modifier, in order to -avoid accidental overriding. As an example, our `Complex` class -can be augmented with a redefinition of the `toString` method -inherited from `Object`. - - class Complex(real: Double, imaginary: Double) { - def re = real - def im = imaginary - override def toString() = - "" + re + (if (im < 0) "" else "+") + im + "i" - } - - -## Case Classes and Pattern Matching - -A kind of data structure that often appears in programs is the tree. -For example, interpreters and compilers usually represent programs -internally as trees; XML documents are trees; and several kinds of -containers are based on trees, like red-black trees. - -We will now examine how such trees are represented and manipulated in -Scala through a small calculator program. The aim of this program is -to manipulate very simple arithmetic expressions composed of sums, -integer constants and variables. Two examples of such expressions are -`1+2` and `(x+x)+(7+y)`. - -We first have to decide on a representation for such expressions. The -most natural one is the tree, where nodes are operations (here, the -addition) and leaves are values (here constants or variables). - -In Java, such a tree would be represented using an abstract -super-class for the trees, and one concrete sub-class per node or -leaf. In a functional programming language, one would use an algebraic -data-type for the same purpose. Scala provides the concept of -*case classes* which is somewhat in between the two. Here is how -they can be used to define the type of the trees for our example: - - abstract class Tree - case class Sum(l: Tree, r: Tree) extends Tree - case class Var(n: String) extends Tree - case class Const(v: Int) extends Tree - -The fact that classes `Sum`, `Var` and `Const` are -declared as case classes means that they differ from standard classes -in several respects: - -- the `new` keyword is not mandatory to create instances of - these classes (i.e., one can write `Const(5)` instead of - `new Const(5)`), -- getter functions are automatically defined for the constructor - parameters (i.e., it is possible to get the value of the `v` - constructor parameter of some instance `c` of class - `Const` just by writing `c.v`), -- default definitions for methods `equals` and - `hashCode` are provided, which work on the *structure* of - the instances and not on their identity, -- a default definition for method `toString` is provided, and - prints the value in a "source form" (e.g., the tree for expression - `x+1` prints as `Sum(Var(x),Const(1))`), -- instances of these classes can be decomposed through - *pattern matching* as we will see below. - -Now that we have defined the data-type to represent our arithmetic -expressions, we can start defining operations to manipulate them. We -will start with a function to evaluate an expression in some -*environment*. The aim of the environment is to give values to -variables. For example, the expression `x+1` evaluated in an -environment which associates the value `5` to variable `x`, written -`{ x -> 5 }`, gives `6` as result. - -We therefore have to find a way to represent environments. We could of -course use some associative data-structure like a hash table, but we -can also directly use functions! An environment is really nothing more -than a function which associates a value to a (variable) name. The -environment `{ x -> 5 }` given above can simply be written as -follows in Scala: - - { case "x" => 5 } - -This notation defines a function which, when given the string -`"x"` as argument, returns the integer `5`, and fails with an -exception otherwise. - -Before writing the evaluation function, let us give a name to the type -of the environments. We could of course always use the type -`String => Int` for environments, but it simplifies the program -if we introduce a name for this type, and makes future changes easier. -This is accomplished in Scala with the following notation: - - type Environment = String => Int - -From then on, the type `Environment` can be used as an alias of -the type of functions from `String` to `Int`. - -We can now give the definition of the evaluation function. -Conceptually, it is very simple: the value of a sum of two expressions -is simply the sum of the value of these expressions; the value of a -variable is obtained directly from the environment; and the value of a -constant is the constant itself. Expressing this in Scala is not more -difficult: - - def eval(t: Tree, env: Environment): Int = t match { - case Sum(l, r) => eval(l, env) + eval(r, env) - case Var(n) => env(n) - case Const(v) => v - } - -This evaluation function works by performing *pattern matching* -on the tree `t`. Intuitively, the meaning of the above definition -should be clear: - -1. it first checks if the tree `t` is a `Sum`, and if it - is, it binds the left sub-tree to a new variable called `l` and - the right sub-tree to a variable called `r`, and then proceeds - with the evaluation of the expression following the arrow; this - expression can (and does) make use of the variables bound by the - pattern appearing on the left of the arrow, i.e., `l` and - `r`, -2. if the first check does not succeed, that is, if the tree is not - a `Sum`, it goes on and checks if `t` is a `Var`; if - it is, it binds the name contained in the `Var` node to a - variable `n` and proceeds with the right-hand expression, -3. if the second check also fails, that is if `t` is neither a - `Sum` nor a `Var`, it checks if it is a `Const`, and - if it is, it binds the value contained in the `Const` node to a - variable `v` and proceeds with the right-hand side, -4. finally, if all checks fail, an exception is raised to signal - the failure of the pattern matching expression; this could happen - here only if more sub-classes of `Tree` were declared. - -We see that the basic idea of pattern matching is to attempt to match -a value to a series of patterns, and as soon as a pattern matches, -extract and name various parts of the value, to finally evaluate some -code which typically makes use of these named parts. - -A seasoned object-oriented programmer might wonder why we did not -define `eval` as a *method* of class `Tree` and its -subclasses. We could have done it actually, since Scala allows method -definitions in case classes just like in normal classes. Deciding -whether to use pattern matching or methods is therefore a matter of -taste, but it also has important implications on extensibility: - -- when using methods, it is easy to add a new kind of node as this - can be done just by defining a sub-class of `Tree` for it; on - the other hand, adding a new operation to manipulate the tree is - tedious, as it requires modifications to all sub-classes of - `Tree`, -- when using pattern matching, the situation is reversed: adding a - new kind of node requires the modification of all functions which do - pattern matching on the tree, to take the new node into account; on - the other hand, adding a new operation is easy, by just defining it - as an independent function. - -To explore pattern matching further, let us define another operation -on arithmetic expressions: symbolic derivation. The reader might -remember the following rules regarding this operation: - -1. the derivative of a sum is the sum of the derivatives, -2. the derivative of some variable `v` is one if `v` is the - variable relative to which the derivation takes place, and zero - otherwise, -3. the derivative of a constant is zero. - -These rules can be translated almost literally into Scala code, to -obtain the following definition: - - def derive(t: Tree, v: String): Tree = t match { - case Sum(l, r) => Sum(derive(l, v), derive(r, v)) - case Var(n) if (v == n) => Const(1) - case _ => Const(0) - } - -This function introduces two new concepts related to pattern matching. -First of all, the `case` expression for variables has a -*guard*, an expression following the `if` keyword. This -guard prevents pattern matching from succeeding unless its expression -is true. Here it is used to make sure that we return the constant `1` -only if the name of the variable being derived is the same as the -derivation variable `v`. The second new feature of pattern -matching used here is the *wildcard*, written `_`, which is -a pattern matching any value, without giving it a name. - -We did not explore the whole power of pattern matching yet, but we -will stop here in order to keep this document short. We still want to -see how the two functions above perform on a real example. For that -purpose, let's write a simple `main` function which performs -several operations on the expression `(x+x)+(7+y)`: it first computes -its value in the environment `{ x -> 5, y -> 7 }`, then -computes its derivative relative to `x` and then `y`. - - def main(args: Array[String]) { - val exp: Tree = Sum(Sum(Var("x"),Var("x")),Sum(Const(7),Var("y"))) - val env: Environment = { case "x" => 5 case "y" => 7 } - println("Expression: " + exp) - println("Evaluation with x=5, y=7: " + eval(exp, env)) - println("Derivative relative to x:\n " + derive(exp, "x")) - println("Derivative relative to y:\n " + derive(exp, "y")) - } - -Executing this program, we get the expected output: - - Expression: Sum(Sum(Var(x),Var(x)),Sum(Const(7),Var(y))) - Evaluation with x=5, y=7: 24 - Derivative relative to x: - Sum(Sum(Const(1),Const(1)),Sum(Const(0),Const(0))) - Derivative relative to y: - Sum(Sum(Const(0),Const(0)),Sum(Const(0),Const(1))) - -By examining the output, we see that the result of the derivative -should be simplified before being presented to the user. Defining a -basic simplification function using pattern matching is an interesting -(but surprisingly tricky) problem, left as an exercise for the reader. - -## Traits - -Apart from inheriting code from a super-class, a Scala class can also -import code from one or several *traits*. - -Maybe the easiest way for a Java programmer to understand what traits -are is to view them as interfaces which can also contain code. In -Scala, when a class inherits from a trait, it implements that trait's -interface, and inherits all the code contained in the trait. - -To see the usefulness of traits, let's look at a classical example: -ordered objects. It is often useful to be able to compare objects of a -given class among themselves, for example to sort them. In Java, -objects which are comparable implement the `Comparable` -interface. In Scala, we can do a bit better than in Java by defining -our equivalent of `Comparable` as a trait, which we will call -`Ord`. - -When comparing objects, six different predicates can be useful: -smaller, smaller or equal, equal, not equal, greater or equal, and -greater. However, defining all of them is fastidious, especially since -four out of these six can be expressed using the remaining two. That -is, given the equal and smaller predicates (for example), one can -express the other ones. In Scala, all these observations can be -nicely captured by the following trait declaration: - - trait Ord { - def < (that: Any): Boolean - def <=(that: Any): Boolean = (this < that) || (this == that) - def > (that: Any): Boolean = !(this <= that) - def >=(that: Any): Boolean = !(this < that) - } - -This definition both creates a new type called `Ord`, which -plays the same role as Java's `Comparable` interface, and -default implementations of three predicates in terms of a fourth, -abstract one. The predicates for equality and inequality do not appear -here since they are by default present in all objects. - -The type `Any` which is used above is the type which is a -super-type of all other types in Scala. It can be seen as a more -general version of Java's `Object` type, since it is also a -super-type of basic types like `Int`, `Float`, etc. - -To make objects of a class comparable, it is therefore sufficient to -define the predicates which test equality and inferiority, and mix in -the `Ord` class above. As an example, let's define a -`Date` class representing dates in the Gregorian calendar. Such -dates are composed of a day, a month and a year, which we will all -represent as integers. We therefore start the definition of the -`Date` class as follows: - - class Date(y: Int, m: Int, d: Int) extends Ord { - def year = y - def month = m - def day = d - override def toString(): String = year + "-" + month + "-" + day - -The important part here is the `extends Ord` declaration which -follows the class name and parameters. It declares that the -`Date` class inherits from the `Ord` trait. - -Then, we redefine the `equals` method, inherited from -`Object`, so that it correctly compares dates by comparing their -individual fields. The default implementation of `equals` is not -usable, because as in Java it compares objects physically. We arrive -at the following definition: - - override def equals(that: Any): Boolean = - that.isInstanceOf[Date] && { - val o = that.asInstanceOf[Date] - o.day == day && o.month == month && o.year == year - } - -This method makes use of the predefined methods `isInstanceOf` -and `asInstanceOf`. The first one, `isInstanceOf`, -corresponds to Java's `instanceof` operator, and returns true -if and only if the object on which it is applied is an instance of the -given type. The second one, `asInstanceOf`, corresponds to -Java's cast operator: if the object is an instance of the given type, -it is viewed as such, otherwise a `ClassCastException` is -thrown. - -Finally, the last method to define is the predicate which tests for -inferiority, as follows. It makes use of another predefined method, -`error`, which throws an exception with the given error message. - - def <(that: Any): Boolean = { - if (!that.isInstanceOf[Date]) - error("cannot compare " + that + " and a Date") - - val o = that.asInstanceOf[Date] - (year < o.year) || - (year == o.year && (month < o.month || - (month == o.month && day < o.day))) - } - -This completes the definition of the `Date` class. Instances of -this class can be seen either as dates or as comparable objects. -Moreover, they all define the six comparison predicates mentioned -above: `equals` and `<` because they appear directly in -the definition of the `Date` class, and the others because they -are inherited from the `Ord` trait. - -Traits are useful in other situations than the one shown here, of -course, but discussing their applications in length is outside the -scope of this document. - -## Genericity - -The last characteristic of Scala we will explore in this tutorial is -genericity. Java programmers should be well aware of the problems -posed by the lack of genericity in their language, a shortcoming which -is addressed in Java 1.5. - -Genericity is the ability to write code parametrized by types. For -example, a programmer writing a library for linked lists faces the -problem of deciding which type to give to the elements of the list. -Since this list is meant to be used in many different contexts, it is -not possible to decide that the type of the elements has to be, say, -`Int`. This would be completely arbitrary and overly -restrictive. - -Java programmers resort to using `Object`, which is the -super-type of all objects. This solution is however far from being -ideal, since it doesn't work for basic types (`int`, -`long`, `float`, etc.) and it implies that a lot of -dynamic type casts have to be inserted by the programmer. - -Scala makes it possible to define generic classes (and methods) to -solve this problem. Let us examine this with an example of the -simplest container class possible: a reference, which can either be -empty or point to an object of some type. - - class Reference[T] { - private var contents: T = _ - def set(value: T) { contents = value } - def get: T = contents - } - -The class `Reference` is parametrized by a type, called `T`, -which is the type of its element. This type is used in the body of the -class as the type of the `contents` variable, the argument of -the `set` method, and the return type of the `get` method. - -The above code sample introduces variables in Scala, which should not -require further explanations. It is however interesting to see that -the initial value given to that variable is `_`, which represents -a default value. This default value is 0 for numeric types, -`false` for the `Boolean` type, `()` for the `Unit` -type and `null` for all object types. - -To use this `Reference` class, one needs to specify which type to use -for the type parameter `T`, that is the type of the element -contained by the cell. For example, to create and use a cell holding -an integer, one could write the following: - - object IntegerReference { - def main(args: Array[String]) { - val cell = new Reference[Int] - cell.set(13) - println("Reference contains the half of " + (cell.get * 2)) - } - } - -As can be seen in that example, it is not necessary to cast the value -returned by the `get` method before using it as an integer. It -is also not possible to store anything but an integer in that -particular cell, since it was declared as holding an integer. - -## Conclusion - -This document gave a quick overview of the Scala language and -presented some basic examples. The interested reader can go on, for example, by -reading the document *Scala By Example*, which -contains much more advanced examples, and consult the *Scala - Language Specification* when needed. diff --git a/tutorials/scala-with-maven.md b/tutorials/scala-with-maven.md deleted file mode 100644 index 2b22614c52..0000000000 --- a/tutorials/scala-with-maven.md +++ /dev/null @@ -1,302 +0,0 @@ ---- -layout: overview -title: Scala with Maven - -discourse: true ---- - -By Adrian Null - -## Introduction -[Maven][1] is a build/project management tool. It favours "convention over configuration"; it can greatly simplify builds for "standard" projects and a Maven user can usually understand the structure of another Maven project just by looking at its `pom.xml` (Project Object Model). Maven is a plugin-based architecture, making it easy to add new libraries and modules to existing projects. For example, adding a new dependency usually involves only 5 extra lines in the `pom.xml`. These "artifacts" are downloaded from repositories such as [The Central Repository][2]. - -You can also check out the official [example project][36] which uses the same Scala plugin we will show here. - -## Jumping Ahead -If you're familiar with Maven, you can go ahead with the [Scala Maven Plugin][5]. - -## The Scala Maven Plugin -We'll be using the Scala Maven Plugin ([GitHub repo][5], [website][22]) (formerly known as the maven-scala-plugin; renamed to honour the new naming policy where only Maven core plugins are prefixed with "maven"), by far the dominant plugin for Scala projects. *Note: the plugin includes Scala from the Central Repository so there's no need to install it yourself if you're compiling with Maven.* - - -## Getting Maven - -### Linux (Debian) -On Debian and Debian-derivatives, Maven is usually available via `apt-get`. Just do `(sudo) apt-get install maven` and you're good to go. - -### OSX -OSX prior to 10.9 (Mavericks) comes with Maven 3 built in. -If you don't have it, you can get it with the package managers [MacPorts][4], [Homebrew][6], or [Fink][7]. -The Scala Maven Plugin requires Maven 3.0+ - -### Manually (Red Hat Linux, OSX, Windows) -You can download Maven from its [Apache homepage][3]. After extracting it (`tar -zxvf apache-maven-X.X.X-bin.tar.gz`, or use something like [7-zip][8]) to your directory of choice (on Linux and OSX, Unix-like systems, [I like to put them in `/opt/`][9]. On Windows I would probably put this in `C:/`), you need to add Maven to your environment Path variable: - -- Linux/OSX (option 1): Create a symlink to `/usr/bin`, which is already on your Path - - `ln -s /usr/bin/mvn /opt/apache-maven-X.X.X/bin/mvn` -- Linux/OSX (option 2): Add the Maven `bin` folder directly to your path, using your [shell configuration][10] file (e.g. `~/.bash_profile`) - - Add `export PATH=$PATH:/opt/apache-maven-X.X.X/bin` to `.bash_profile` (or whatever profile for the shell you use) - - Example: `echo "export PATH=$PATH:/opt/apache-maven-X.X.X/bin" >> ~/.bash_profile` -- Linux/OSX (option 3): Make a `mvn` shell script in an existing path location - - Example: you have `$HOME/bin` in your path - - Put the folder you extracted in `$HOME/bin` (`mv apache-maven-X.X.X "$HOME/bin/"`) - - Create a file `mvn` in `$HOME/bin` - - Add `"$HOME/bin/apache-maven-X.X.X/bin/mvn" $@` to it, and `chmod u+x mvn` to make it executable - - This is probably the least intrusive way; `$HOME/bin` is usually added to the user's path by default, and if not, it's a useful thing to do/have anyways. The shell script simply invokes the Maven location (which is at some other location) and passes on the arguments -- Windows - - Hit Start. Right click on "My Computer" and go to "Properties" - - This should bring you to "Control Panel -> System and Security -> System", giving an overview of your computer - - On the left sidebar there should be four options; click on "Advanced system settings" (fourth one) - - Under the "Advanced" tab, hit "Environment Variables..." in the bottom right - - Note: I recommend creating/editing your User variables (top box). You can do the same with System variables though (bottom box) - - Create a new variable called "MAVEN3_HOME". Point this to your Maven folder (e.g. `C:\apache-maven-X.X.X`). Use backslashes to be safe, and do not include a trailing slash - - Create a new variable called "MAVEN3_BIN" with this value: `%MAVEN3_HOME%\bin` - - Edit your Path variable: being careful not to change anything else, append `;%MAVEN3_BIN%` to it - - You'll need to restart cmd to see these changes - -## Creating Your First Project -The easiest way to create new projects is using an ["archetype"][11]. An archetype is a general skeleton structure, or template for a project. Think back to "convention over configuration"; in our case, the Scala Maven Plugin provides an archetype for scala projects. - -You run the archetype plugin like this: - - mvn archetype:generate - -If this is your first time, you'll notice that Maven is downloading many jar files. Maven resolves dependencies and downloads them as needed (and only once). Right now, Maven is downloading its core plugins. - -Afterwards, it should give you a list of archetypes (in the hundreds). -The Scala Maven Plugin was 339 on my list: "net.alchim31.maven:scala-archetype-simple (The maven-scala-plugin is used for compiling/testing/running/documenting scala code in maven.)". -You can type "scala" (or something else) to filter the results. -As of 2015 January 27, there you can choose version 3.1.4 or 3.1.5 of this plugin; you should choose the latest - - Choose net.alchim31.maven:scala-archetype-simple version: - 1: 1.4 - 2: 1.5 - -Next, Maven will ask you for a groupId, artifactId, and package. You can read the [guide to naming conventions][12], but in short: - -- groupId: inverted domain name (e.g. com.my-organization) -- artifactId: project name (e.g. playground-project) -- version: anything you want, but I recommend you read and follow the guidelines for [Semantic Versioning][13] (e.g. 0.0.1) -- package: the default is the groupId, but you can change this (e.g. com.my-organization) - -The groupId and artifactId together should serve as a globally unique identifier for your project - -When it's done, you should see a new folder named with the artifactId. `cd` into it and run: - - mvn package - -You'll see Maven downloading dependencies including the Scala library (as mentioned above), [JUnit][14], [ScalaTest][15], and [Specs2][16] (the latter three are test frameworks; the archetype includes an example "Hello world" program, and tests with each of the frameworks). - -### Explaining this Archetype -In your project root, you'll see a `pom.xml`, `src` folder, and `target` folder (target folder only appears after building). *Note: this archetype also includes a `.gitignore`* - -Inside the `src` folder you'll see `main` and `test`; `main` includes your application code, and `test` includes your test suites. Inside each of those you'll find a `scala` folder, followed by your package structure (actually, `test/scala` includes a sample package, but you should replace this with your own package and tests). If you want to mix Scala and Java source code, simply add a `java` folder inside `main` or `test`. - -`target` includes generated/built files, such as `.class` and `.jar` files. You can read about `pom.xml` at the [Maven page][18]. - -Example structure: - -- pom.xml -- src - - main - - scala - - com/my-package/... - *.scala - - java - - com/my-package/... - *.java - - test - - scala - - com/my-package/... - *.scala - - java - - com/my-package/... - *.java -- target - ... - -Again, you can read more about the Scala Maven Plugin at its [website][22]. - -### Creating a Jar -By default the jar created by the Scala Maven Plugin doesn't include a `Main-Class` attribute in the manifest. I had to add the [Maven Assembly Plugin][19] to my `pom.xml` in order to specify custom attributes in the manifest. You can check the latest version of this plugin at the [project summary][20] or at [The Central Repository][21] - - - X.X.X - ... - - ... - - - - ... - - - - ... - - - - ... - - ... - - org.apache.maven.plugins - maven-assembly-plugin - 2.4 - - - jar-with-dependencies - - - - com.your-package.MainClass - - - - - - package - - single - - - - - - - - -After adding this, `mvn package` will also create `[artifactId]-[version]-jar-with-dependencies.jar` under `target`. *Note: this will also copy the Scala library into your Jar. This is normal. Be careful that your dependencies use the same version of Scala, or you will quickly end up with a massive Jar.* - -### Useful commands -- `mvn dependency:copy-dependencies`: copy all libraries and dependencies to the `target/dependency` folder -- `mvn clean` -- `mvn package`: compile, run tests, and create jar - -### Integration with Eclipse ([Scala IDE][24]) -There are instructions at the [Scala Maven Plugin FAQs][23], but I thought I'd expand a bit. The [maven-eclipse-plugin][33] is a core plugin (all plugins prefixed with "maven" are, and are available by default) to generate Eclipse configuration files. However, this plugin does not know about our new Scala source files. We'll be using the [build-helper-maven-plugin][34] to add new source folders: - - ... - - org.codehaus.mojo - build-helper-maven-plugin - - - add-source - generate-sources - - add-source - - - - src/main/scala - - - - - add-test-source - generate-sources - - add-test-source - - - - src/test/scala - - - - - - ... - -After adding that to your `pom.xml`: - -1. Run `mvn eclipse:eclipse` - this generates the Eclipse project files (which are already ignored by our archetype's `.gitignore`) -2. Run `mvn -Declipse.workspace="path/to/your/eclipse/workspace" eclipse:configure-workspace` - this adds an `M2_REPO` classpath variable to Eclipse -3. Run `mvn package` to ensure you have all the dependencies in your local Maven repo - -Unfortunately, the integration isn't perfect. Firstly, open up the generated `.classpath` file (it will be hidden by default as it's a dotfile, but it should be in your project root directory; where you ran `mvn eclipse:eclipse`). You should see something like this near the top. - - - - -Change the `*.java` to `*.scala` (or duplicate the lines and change them to `*.scala` if you also have Java sources). - -Secondly, open the `.project` eclipse file (again, in the same folder). Change `` and `` to look like this. Now Eclipse knows to use the Scala editor and it won't think that everything is a syntax error. - - - - org.scala-ide.sdt.core.scalabuilder - - - - org.scala-ide.sdt.core.scalanature - org.eclipse.jdt.core.javanature - - -Finally, in Eclipse, under "File" choose "Import..." and find your project. - -#### Using [m2eclipse-scala][35] for Eclipse integration -m2eclipse-scala is a work in progress, and their website/repository may have updated information. -It aims to ease integration between m2eclipse and Scala IDE for Eclipse. - -Under "Help -> Install New Software", enter "http://alchim31.free.fr/m2e-scala/update-site" and hit enter. -You should see "Maven Integration for Eclipse -> Maven Integration for Scala IDE". - -Afer it installs, go to "New -> Project -> Other" and select "Maven Project". -Search fo "scala-archetype" choose the one with the group "net.alchim31.maven". -The wizard will more or less guide you through what was done with `mvn archetype:generate` above, and you should end up with a new Scala project! - -To import an existing project, simply go to "File -> Import -> Existing Maven Project" and find the directory containing your project. - -## Adding Dependencies -The first thing I do is look for "Maven" in the project page. For example, Google's [Guava] page includes [Maven Central links][28]. As you can see in the previous link, The Central Repository conveniently includes the snippet you have to add to your `pom.xml` on the left sidebar. - -If you can't find Maven information at the project page, try a Google search for "[project name] maven". Sometimes, you still won't find anything. For [scopt][29] (Scala command line option parser), I couldn't find the latest version from Google. However, [manually searching The Central Repository did][30] - -Afterwards, running - - mvn package - -Will download any new dependencies before packaging - -## Other Useful Reading -I'm not going to explain all of Maven in this tutorial (though I hope to add more in the future, because I do feel that the resources are a bit scattered), so here are some useful articles: -- [Maven Lifecycle][17] (explanation of goals like clean, package, install) - -[1]: http://maven.apache.org -[2]: http://maven.org -[3]: http://maven.apache.org/download.html -[4]: http://macports.org -[5]: https://github.com/davidB/scala-maven-plugin -[6]: http://brew.sh -[7]: http://fink.sf.net -[8]: http://7-zip.org -[9]: http://unix.stackexchange.com/questions/63531/ -[10]: http://wikipedia.org/wiki/Unix_shell#Configuration_files_for_shells -[11]: http://maven.apache.org/archetype/maven-archetype-plugin/ -[12]: http://maven.apache.org/guides/mini/guide-naming-conventions.html -[13]: http://semver.org -[14]: http://junit.org -[15]: http://scalatest.org -[16]: http://specs2.org -[17]: http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html -[18]: http://maven.apache.org/pom.html -[19]: http://maven.apache.org/plugins/maven-assembly-plugin/ -[20]: http://maven.apache.org/plugins/maven-assembly-plugin/project-summary.html -[21]: http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22maven-assembly-plugin%22 -[22]: http://davidb.github.io/scala-maven-plugin -[23]: http://davidb.github.io/scala-maven-plugin/faq.html -[24]: http://scala-ide.org -[25]: http://scala-lang.org/api/current/index.html#scala.App -[26]: http://docs.scala-lang.org/tutorials/tour/polymorphic-methods.html -[27]: https://code.google.com/p/guava-libraries/ -[28]: http://search.maven.org/#artifactdetails%7Ccom.google.guava%7Cguava%7C14.0.1%7Cbundle -[29]: https://github.com/scopt/scopt -[30]: http://search.maven.org/#search%7Cga%7C1%7Cscopt -[31]: http://mvnrepository.com -[32]: http://docs.scala-lang.org/contribute.html -[33]: http://maven.apache.org/plugins/maven-eclipse-plugin/ -[34]: http://mojo.codehaus.org/build-helper-maven-plugin/ -[35]: https://github.com/sonatype/m2eclipse-scala -[36]: https://github.com/scala/scala-module-dependency-sample From eb36bec09a817f66a829c0dd7515aecb03017130 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 15:05:28 +0200 Subject: [PATCH 071/112] Cleaning up dependencies --- Gemfile | 9 +++ Gemfile.lock | 167 +++------------------------------------------------ 2 files changed, 17 insertions(+), 159 deletions(-) diff --git a/Gemfile b/Gemfile index 61e9b59ad0..3b040ad134 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,11 @@ source 'https://rubygems.org' gem 'jekyll-redirect-from' + +# group :jekyll_plugins do +# gem 'hawkins' +# end + +# ^ Useful for live reloading the site in your +# browser during development. To use, uncomment +# and do: +# bundle exec jekyll liveserve --incremental diff --git a/Gemfile.lock b/Gemfile.lock index c77940101f..61c9c6cb3d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,173 +1,34 @@ GEM remote: https://rubygems.org/ specs: - activesupport (4.2.8) - i18n (~> 0.7) - minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) - tzinfo (~> 1.1) addressable (2.5.1) public_suffix (~> 2.0, >= 2.0.2) - coffee-script (2.4.1) - coffee-script-source - execjs - coffee-script-source (1.12.2) colorator (1.1.0) - ethon (0.10.1) - ffi (>= 1.3.0) - execjs (2.7.0) - faraday (0.12.1) - multipart-post (>= 1.2, < 3) ffi (1.9.18) forwardable-extended (2.6.0) - gemoji (3.0.0) - github-pages (143) - activesupport (= 4.2.8) - github-pages-health-check (= 1.3.4) - jekyll (= 3.4.5) - jekyll-avatar (= 0.4.2) - jekyll-coffeescript (= 1.0.1) - jekyll-default-layout (= 0.1.4) - jekyll-feed (= 0.9.2) - jekyll-gist (= 1.4.0) - jekyll-github-metadata (= 2.4.0) - jekyll-mentions (= 1.2.0) - jekyll-optional-front-matter (= 0.1.2) - jekyll-paginate (= 1.1.0) - jekyll-readme-index (= 0.1.0) - jekyll-redirect-from (= 0.12.1) - jekyll-relative-links (= 0.4.1) - jekyll-sass-converter (= 1.5.0) - jekyll-seo-tag (= 2.2.3) - jekyll-sitemap (= 1.0.0) - jekyll-swiss (= 0.4.0) - jekyll-theme-architect (= 0.0.4) - jekyll-theme-cayman (= 0.0.4) - jekyll-theme-dinky (= 0.0.4) - jekyll-theme-hacker (= 0.0.4) - jekyll-theme-leap-day (= 0.0.4) - jekyll-theme-merlot (= 0.0.4) - jekyll-theme-midnight (= 0.0.4) - jekyll-theme-minimal (= 0.0.4) - jekyll-theme-modernist (= 0.0.4) - jekyll-theme-primer (= 0.3.0) - jekyll-theme-slate (= 0.0.4) - jekyll-theme-tactile (= 0.0.4) - jekyll-theme-time-machine (= 0.0.4) - jekyll-titles-from-headings (= 0.2.0) - jemoji (= 0.8.0) - kramdown (= 1.13.2) - liquid (= 3.0.6) - listen (= 3.0.6) - mercenary (~> 0.3) - minima (= 2.1.1) - rouge (= 1.11.1) - terminal-table (~> 1.4) - github-pages-health-check (1.3.4) - addressable (~> 2.3) - net-dns (~> 0.8) - octokit (~> 4.0) - public_suffix (~> 2.0) - typhoeus (~> 0.7) - html-pipeline (2.6.0) - activesupport (>= 2) - nokogiri (>= 1.4) - i18n (0.8.6) - jekyll (3.4.5) + jekyll (3.5.1) addressable (~> 2.4) colorator (~> 1.0) jekyll-sass-converter (~> 1.0) jekyll-watch (~> 1.1) kramdown (~> 1.3) - liquid (~> 3.0) + liquid (~> 4.0) mercenary (~> 0.3.3) pathutil (~> 0.9) rouge (~> 1.7) safe_yaml (~> 1.0) - jekyll-avatar (0.4.2) - jekyll (~> 3.0) - jekyll-coffeescript (1.0.1) - coffee-script (~> 2.2) - jekyll-default-layout (0.1.4) - jekyll (~> 3.0) - jekyll-feed (0.9.2) - jekyll (~> 3.3) - jekyll-gist (1.4.0) - octokit (~> 4.2) - jekyll-github-metadata (2.4.0) - jekyll (~> 3.1) - octokit (~> 4.0, != 4.4.0) - jekyll-mentions (1.2.0) - activesupport (~> 4.0) - html-pipeline (~> 2.3) - jekyll (~> 3.0) - jekyll-optional-front-matter (0.1.2) - jekyll (~> 3.0) - jekyll-paginate (1.1.0) - jekyll-readme-index (0.1.0) - jekyll (~> 3.0) jekyll-redirect-from (0.12.1) jekyll (~> 3.3) - jekyll-relative-links (0.4.1) - jekyll (~> 3.3) jekyll-sass-converter (1.5.0) sass (~> 3.4) - jekyll-seo-tag (2.2.3) - jekyll (~> 3.3) - jekyll-sitemap (1.0.0) - jekyll (~> 3.3) - jekyll-swiss (0.4.0) - jekyll-theme-architect (0.0.4) - jekyll (~> 3.3) - jekyll-theme-cayman (0.0.4) - jekyll (~> 3.3) - jekyll-theme-dinky (0.0.4) - jekyll (~> 3.3) - jekyll-theme-hacker (0.0.4) - jekyll (~> 3.3) - jekyll-theme-leap-day (0.0.4) - jekyll (~> 3.3) - jekyll-theme-merlot (0.0.4) - jekyll (~> 3.3) - jekyll-theme-midnight (0.0.4) - jekyll (~> 3.3) - jekyll-theme-minimal (0.0.4) - jekyll (~> 3.3) - jekyll-theme-modernist (0.0.4) - jekyll (~> 3.3) - jekyll-theme-primer (0.3.0) - jekyll (~> 3.3) - jekyll-theme-slate (0.0.4) - jekyll (~> 3.3) - jekyll-theme-tactile (0.0.4) - jekyll (~> 3.3) - jekyll-theme-time-machine (0.0.4) - jekyll (~> 3.3) - jekyll-titles-from-headings (0.2.0) - jekyll (~> 3.3) jekyll-watch (1.5.0) listen (~> 3.0, < 3.1) - jemoji (0.8.0) - activesupport (~> 4.0) - gemoji (~> 3.0) - html-pipeline (~> 2.2) - jekyll (>= 3.0) - kramdown (1.13.2) - liquid (3.0.6) - listen (3.0.6) - rb-fsevent (>= 0.9.3) - rb-inotify (>= 0.9.7) + kramdown (1.14.0) + liquid (4.0.0) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) mercenary (0.3.6) - mini_portile2 (2.1.0) - minima (2.1.1) - jekyll (~> 3.3) - minitest (5.10.2) - multipart-post (2.0.0) - net-dns (0.8.0) - nokogiri (1.6.8.1) - mini_portile2 (~> 2.1.0) - octokit (4.7.0) - sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.14.0) forwardable-extended (~> 2.6) public_suffix (2.0.5) @@ -181,24 +42,12 @@ GEM sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - sawyer (0.8.1) - addressable (>= 2.3.5, < 2.6) - faraday (~> 0.8, < 1.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - thread_safe (0.3.6) - typhoeus (0.8.0) - ethon (>= 0.8.0) - tzinfo (1.2.3) - thread_safe (~> 0.1) - unicode-display_width (1.3.0) PLATFORMS ruby DEPENDENCIES - github-pages jekyll-redirect-from BUNDLED WITH - 1.15.1 + 1.15.3 From 00d73a5b4b388bc943c2d00e8e8cb0763e57bd6f Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 15:38:11 +0200 Subject: [PATCH 072/112] Scala for Java programmers tutorials, good to go --- _config.yml | 12 +++++++++-- .../tutorials/scala-for-java-programmers.md | 7 +++---- .../tutorials/scala-for-java-programmers.md | 21 ++++++++++--------- .../tutorials/scala-for-java-programmers.md | 6 +++--- .../tutorials/scala-for-java-programmers.md | 10 ++++----- .../tutorials/scala-for-java-programmers.md | 8 ++++--- .../tutorials/scala-for-java-programmers.md | 6 +++--- 7 files changed, 40 insertions(+), 30 deletions(-) rename {de => _de}/tutorials/scala-for-java-programmers.md (99%) rename {es => _es}/tutorials/scala-for-java-programmers.md (99%) rename {it => _it}/tutorials/scala-for-java-programmers.md (99%) rename {ko => _ko}/tutorials/scala-for-java-programmers.md (99%) rename {zh-tw => _zh-tw}/tutorials/scala-for-java-programmers.md (99%) diff --git a/_config.yml b/_config.yml index dcd1d588a9..34430c308e 100644 --- a/_config.yml +++ b/_config.yml @@ -54,7 +54,15 @@ collections: ko: # Korean translations output: true permalink: /:collection/:path.html - + de: # German translations + output: true + permalink: /:collection/:path.html + it: # Italian translations + output: true + permalink: /:collection/:path.html + zh-tw: # Taiwanese translations + output: true + permalink: /:collection/:path.html defaults: - @@ -68,5 +76,5 @@ highlighter: rouge permalink: /:categories/:title.html:output_ext baseurl: exclude: ["vendor"] -gems: +plugins: - jekyll-redirect-from diff --git a/de/tutorials/scala-for-java-programmers.md b/_de/tutorials/scala-for-java-programmers.md similarity index 99% rename from de/tutorials/scala-for-java-programmers.md rename to _de/tutorials/scala-for-java-programmers.md index 9336cd9e83..451cc0a940 100644 --- a/de/tutorials/scala-for-java-programmers.md +++ b/_de/tutorials/scala-for-java-programmers.md @@ -1,10 +1,10 @@ --- -layout: overview +layout: singlepage-overview title: Ein Scala Tutorial für Java Programmierer -overview: scala-for-java-programmers + +partof: scala-for-java-programmers discourse: false -multilingual-overview: true language: de --- @@ -631,4 +631,3 @@ Dieses Dokument hat einen kurzen Überblick über die Sprache Scala gegeben und Beispiele verwendet. Interessierte Leser können beispielsweise mit dem Dokument *Scala by Example* fortfahren, welches fortgeschrittenere Beispiele enthält, und die *Scala Language Specification* konsultieren, sofern nötig. - diff --git a/es/tutorials/scala-for-java-programmers.md b/_es/tutorials/scala-for-java-programmers.md similarity index 99% rename from es/tutorials/scala-for-java-programmers.md rename to _es/tutorials/scala-for-java-programmers.md index d833018565..71a7ba6023 100644 --- a/es/tutorials/scala-for-java-programmers.md +++ b/_es/tutorials/scala-for-java-programmers.md @@ -1,13 +1,14 @@ --- -layout: overview +layout: singlepage-overview title: Tutorial de Scala para programadores Java -overview: scala-for-java-programmers + +partof: scala-for-java-programmers discourse: true language: es --- -Por Michel Schinz y Philipp Haller. +Por Michel Schinz y Philipp Haller. Traducción y arreglos Santiago Basulto. ## Introducción @@ -75,7 +76,7 @@ La declaración `import` en la tercer línea por lo tanto importa todos los miem Dentro del método `main` primero creamos una instancia de la clase `Date` la cual por defecto contiene la fecha actual. A continuación definimos un formateador de fechas utilizando el método estático `getDateInstance` que importamos previamente. Finalmente, imprimimos la fecha actual formateada de acuerdo a la instancia de `DateFormat` que fue "localizada". Esta última línea muestra una propiedad interesante de la sintaxis de Scala. Los métodos que toman un solo argumento pueden ser usados con una sintaxis de infijo Es decir, la expresión df format ahora - + es solamente otra manera más corta de escribir la expresión: df.format(ahora) @@ -129,7 +130,7 @@ En el siguiente programa, la función del temporizador se llama `unaVezPorSegund unaVezPorSegundo(tiempoVuela) } } - + _Nota: si nunca tuviste experiencias previas con programación funcional te recomiendo que te tomes unos segundos para analizar cuando se utilizan paréntesis y cuando no en los lugares donde aparece *callback*. Por ejemplo, dentro de la declaración de `unaVezPorSegundo` no aparece, ya que se trata de la función como un "valor", a diferencia de cómo aparece dentro del método, ya que en ese caso se la está invocando (por eso los paréntesis)._ Note that in order to print the string, we used the predefined method `println` instead of using the one from `System.out`. @@ -163,7 +164,7 @@ Como hemos visto anteriormente, Scala es un lenguaje orientado a objetos, y como Esta clase compleja toma dos argumentos, que son las partes real e imaginarias de un número complejo. Estos argumentos deben ser pasados cuando se crea una instancia de la clase `Complejo`, de la siguiente manera: new Complejo(1.5, 2.3) - + La clase contiene dos métodos llamados `re` e `im`, que proveen acceso a las dos partes del número. Debe notarse que el tipo de retorno de estos dos métodos no está expresado explícitamente. Será inferido automáticamente por el compilador, que primero mira la parte derecha de estos métodos y puede deducir que ambos retornan un valor de tipo `Double`. @@ -198,7 +199,7 @@ Es posible sobreescribir métodos heredados de una superclase en Scala. Aunque e class Complejo(real: Double, imaginaria: Double) { def re = real def im = imaginaria - override def toString() = + override def toString() = "" + re + (if (im < 0) "" else "+") + im + "i" } @@ -224,11 +225,11 @@ El hecho de que las clases `Sum`, `Var` y `Const` sean declaradas como clases ca en lugar de `new Const(5)`), - se crea automáticamente un "getter" (un método para obtener el valor) para los parámetros utilizados en el constructor (por ejemplo es posible - obtener el valor de `v` de una instancia `c` de la clase `Const` de la + obtener el valor de `v` de una instancia `c` de la clase `Const` de la siguiente manera: `c.v`), - se proveen definiciones por defecto de los métodos `equals` y `hashCode`, que trabajan sobre la estructura de las instancias y no sobre su identidad, -- se crea una definición por defecto del método `toString` que +- se crea una definición por defecto del método `toString` que imprime el valor de una forma "tipo código) (ej: la expresión del árbol `x+1` se imprimiría `Sum(Var(x),Const(1))`), - las instancias de estas clases pueden ser descompuestas @@ -377,7 +378,7 @@ La última característica de Scala que exploraremos en este tutorial es la de l Los tipos genéricos proveen al programador la habilidad de escribir código parametrizado por tipos. Por ejemplo, escribir una librería para listas enlazadas se enfrenta al problema de decidir qué tipo darle a los elementos de la lista. Ya que esta lista está pensada para ser usada en diferentes contextos, no es posible decidir que el tipo de elementos sea, digamos, `Int`. Esto sería completamente arbitrario y muy restrictivo. -Los programadores Java cuentan como último recurso con `Object`, que es el supertipo de todos los objetos. Esta solución de todas maneras está lejos de ser ideal, ya que no funciona con tipos primitivos (`int`, `long`, `float`, etc.) e implica que el programador tenga que realizar muchos casteos de tipos en su programa. +Los programadores Java cuentan como último recurso con `Object`, que es el supertipo de todos los objetos. Esta solución de todas maneras está lejos de ser ideal, ya que no funciona con tipos primitivos (`int`, `long`, `float`, etc.) e implica que el programador tenga que realizar muchos casteos de tipos en su programa. Scala hace posible definir clases genéricas (y métodos) para resolver este problema. Examinemos esto con un ejemplo del contenedor más simple posible: una referencia, que puede estar tanto vacía como apuntar a un objeto de algún tipo. diff --git a/it/tutorials/scala-for-java-programmers.md b/_it/tutorials/scala-for-java-programmers.md similarity index 99% rename from it/tutorials/scala-for-java-programmers.md rename to _it/tutorials/scala-for-java-programmers.md index b13512e1e7..35205679c9 100644 --- a/it/tutorials/scala-for-java-programmers.md +++ b/_it/tutorials/scala-for-java-programmers.md @@ -1,10 +1,10 @@ --- -layout: overview +layout: singlepage-overview title: Un'introduzione a Scala per programmatori Java -overview: scala-for-java-programmers + +partof: scala-for-java-programmers discourse: false -multilingual-overview: true language: it --- diff --git a/ko/tutorials/scala-for-java-programmers.md b/_ko/tutorials/scala-for-java-programmers.md similarity index 99% rename from ko/tutorials/scala-for-java-programmers.md rename to _ko/tutorials/scala-for-java-programmers.md index 919b8783e6..4342d2e9c6 100644 --- a/ko/tutorials/scala-for-java-programmers.md +++ b/_ko/tutorials/scala-for-java-programmers.md @@ -1,7 +1,8 @@ --- -layout: overview +layout: singlepage-overview title: 자바 프로그래머를 위한 스칼라 튜토리얼 -overview: scala-for-java-programmers + +partof: scala-for-java-programmers discourse: false language: ko @@ -295,7 +296,7 @@ Scala 클래스의 경우 파라미터들을 가질 수 있다는 것인데 아 실수 부분과 허수 부분에 접근 할 때에 마치 그들이 필드인 것 처럼 함수 마지막에 빈 괄호를 붙이지 않을 수 있다면 더욱 좋겠다. 놀라지 마시라, -Scala는 이러한 기능을 완벽하게 제공한다. 그저 **인자를 제외**하고 +Scala는 이러한 기능을 완벽하게 제공한다. 그저 **인자를 제외**하고 함수를 정의하면 된다. 이런 종류의 함수는 인자가 0개인 함수와는 다른데, 인자가 0개인 함수는 빈 괄호가 따라 붙는 반면 이 함수는 정의 할 때도 사용 할 때도 이름 뒤에 괄호를 붙이지 않는다. 우리가 앞서 정의한 @@ -365,7 +366,7 @@ Scala는 **케이스 클래스**라 하는 이 둘 사이의 어디쯤에 놓여 클래스 `Const`의 인스턴스 `c`에 있는 생성자 파라미터 `v`의 값은 `c.v`로 접근 가능하다. - 함수 `equals`와 `hashCode`도 공짜로 제공된다. 이 함수들은 - 레퍼런스의 동일함 보다 **구조**의 동일함을 확인 하도록 구현되어 있다. + 레퍼런스의 동일함 보다 **구조**의 동일함을 확인 하도록 구현되어 있다. 다른 말로, 생성 된 곳이 다르더라도 각각의 생성자 파라미터 값이 같다면 같은 것으로 여긴다. - 함수 `toString`에 대한 기본적 구현이 제공된다. 이 기본적인 @@ -679,4 +680,3 @@ Scala는 이 문제를 해결하기 위한 제네릭 클래스와 제네릭 함 보았다. 흥미가 생겼다면 *Scala By Example*도 함께 읽어보자. 더 수준 높고 다양한 예제를 만날 수 있다. 필요 할 때마다 *Scala Language Specification*을 참고하는 것도 좋다. - diff --git a/_overviews/tutorials/scala-for-java-programmers.md b/_overviews/tutorials/scala-for-java-programmers.md index b8618b431c..eb9ad8776b 100644 --- a/_overviews/tutorials/scala-for-java-programmers.md +++ b/_overviews/tutorials/scala-for-java-programmers.md @@ -1,11 +1,13 @@ --- -layout: overview +layout: singlepage-overview title: A Scala Tutorial for Java Programmers -overview: scala-for-java-programmers + +partof: scala-for-java-programmers discourse: true -multilingual-overview: true + languages: [es, ko, de, it, zh-tw] +permalink: /tutorials/:title.html --- By Michel Schinz and Philipp Haller diff --git a/zh-tw/tutorials/scala-for-java-programmers.md b/_zh-tw/tutorials/scala-for-java-programmers.md similarity index 99% rename from zh-tw/tutorials/scala-for-java-programmers.md rename to _zh-tw/tutorials/scala-for-java-programmers.md index 596463df6a..15d95b0b27 100755 --- a/zh-tw/tutorials/scala-for-java-programmers.md +++ b/_zh-tw/tutorials/scala-for-java-programmers.md @@ -1,10 +1,10 @@ --- -layout: overview +layout: singlepage-overview title: 給 Java 程式設計師的 Scala 入門教學 -overview: scala-for-java-programmers + +partof: scala-for-java-programmers discourse: false -multilingual-overview: true language: zh-tw --- From 4c49b4ad1c5677d3e3ba3accfedc35ce8ba5af3f Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 17:26:44 +0200 Subject: [PATCH 073/112] Updating drone image (syncing Ruby versions with production server) --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 22fdde9af3..0955732d81 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,6 +1,6 @@ pipeline: build: - image: scalaplatform/jdk8-ruby2-coursier:0.1 + image: scalacenter/scala-rvm-jvm-coursier:1.0 pull: true commands: - bundle install From d7447d5b6f37e43ebcc2b962c67679ac23ef1bad Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 17:39:14 +0200 Subject: [PATCH 074/112] Glossary finished --- _data/doc-nav-header.yml | 2 +- _includes/sidebar-toc-glossary.html | 10 ++++ _layouts/glossary.html | 31 ++++++------ _sass/layout/glossary.scss | 75 +++++++++++++++++++++++++++++ glossary/index.md | 12 +++-- resources/css/style.scss | 1 + resources/js/functions.js | 35 ++++++++++++++ 7 files changed, 145 insertions(+), 21 deletions(-) create mode 100644 _includes/sidebar-toc-glossary.html create mode 100644 _sass/layout/glossary.scss diff --git a/_data/doc-nav-header.yml b/_data/doc-nav-header.yml index b720d7f923..514c8fbc68 100644 --- a/_data/doc-nav-header.yml +++ b/_data/doc-nav-header.yml @@ -30,6 +30,6 @@ - title: Cheatsheet url: https://www.scala-lang.org/contribute/ - title: Glossary - url: https://www.scala-lang.org/blog/ + url: "/glossary/index.html" - title: SIPs url: https://www.scala-lang.org/blog/ diff --git a/_includes/sidebar-toc-glossary.html b/_includes/sidebar-toc-glossary.html new file mode 100644 index 0000000000..03159bdb6f --- /dev/null +++ b/_includes/sidebar-toc-glossary.html @@ -0,0 +1,10 @@ + diff --git a/_layouts/glossary.html b/_layouts/glossary.html index 73f5f8207b..ea5d5dde0f 100644 --- a/_layouts/glossary.html +++ b/_layouts/glossary.html @@ -1,20 +1,17 @@ --- -layout: inner-page-parent +layout: inner-page-parent-dropdown +includeTOC: true --- -{% include glossary-header.html %} -
    -
    -
    -
    -
    -
    Glossary from the definitive book on Scala, Programming in Scala.
    -
    - {{ content }} -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    +
    + {{content}} +
    +
    + + + {% include sidebar-toc-glossary.html %} +
    +
    diff --git a/_sass/layout/glossary.scss b/_sass/layout/glossary.scss new file mode 100644 index 0000000000..e55ef1c9aa --- /dev/null +++ b/_sass/layout/glossary.scss @@ -0,0 +1,75 @@ +// GLOSSARY +//------------------------------------------------ +//------------------------------------------------ + +.glossary { + + .filterbar { + position: relative; + margin-bottom: 24px; + + #filter-count { + visibility: hidden; + font-size: $font-size-xsmall; + text-transform: uppercase; + color: $base-font-color-light; + transition: $base-transition; + } + + .icon-search { + position: absolute; + left: 14px; + top: 4px; + z-index: 30; + } + + input { + padding: 8px 18px 8px 40px; + background: rgba($brand-tertiary, 0.1); + appearance: none; + font-size: $base-font-size; + border-radius: $border-radius-base; + width: 100%; + box-sizing: border-box; + + &:focus { + outline: none; + background: rgba($brand-tertiary, 0.07); + @include box-shadow($box-shadow-inner); + } + } + } + + h5 { + font-size: $font-size-medium !important; + color: $base-font-color-light !important; + } + + ul { + padding-left: 0 !important; + } + + ul li { + list-style-type: none !important; + padding-left: 0 !important; + } + + li { + h4 { + text-transform: none; + margin-bottom: 2px; + font-weight: $font-black; + } + } +} + +.glossary-toc { + #toc { + ul { + li { + // margin-bottom: 0; + margin-top: 2px; + } + } + } +} diff --git a/glossary/index.md b/glossary/index.md index 732a1f1ccb..6959c4048a 100644 --- a/glossary/index.md +++ b/glossary/index.md @@ -2,10 +2,16 @@ layout: glossary title: Glossary --- + +
    Glossary from the definitive book on Scala, Programming in Scala.
    +
    - Look up a term - - + + + +  
    * #### algebraic data type diff --git a/resources/css/style.scss b/resources/css/style.scss index a72d7a29a3..8a5a024f09 100755 --- a/resources/css/style.scss +++ b/resources/css/style.scss @@ -30,6 +30,7 @@ @import 'layout/nutshell'; @import 'layout/overviews'; @import 'layout/toc'; +@import 'layout/glossary'; @import 'layout/courses'; @import 'layout/documentation'; @import 'layout/upcoming-events'; diff --git a/resources/js/functions.js b/resources/js/functions.js index a0382e7691..3500125667 100644 --- a/resources/js/functions.js +++ b/resources/js/functions.js @@ -461,3 +461,38 @@ function updatePointer() { pointer.css('top', (target.y)); pointer.css('left', (target.x) * xScale); } + + +// Glossary search +$(document).ready(function() { + +$('#filter').focus(); + + $("#filter").keyup(function(){ + + // Retrieve the input field text and reset the count to zero + var filter = $(this).val(), count = 0; + + // Loop through the comment list + $(".glossary > .inner-box > ul li").each(function(){ + // If the name of the glossary term does not contain the text phrase fade it out + if (jQuery(this).find("h4").text().search(new RegExp(filter, "i")) < 0) { + $(this).fadeOut(); + + // Show the list item if the phrase matches and increase the count by 1 + } else { + $(this).show(); + count++; + } + }); + + // Update the count + var numberItems = count; + $("#filter-count").text("Found "+count+" occurrences.").css('visibility', 'visible'); + + // check if input is empty, and if so, hide filter count + if (!filter.trim()) { + $("#filter-count").css('visibility', 'hidden'); + } + }); +}); From eb5eb7e2857e63bac62ffbf67c44f909efa1f46b Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 18:13:06 +0200 Subject: [PATCH 075/112] WIP documentation index page --- index.md | 127 +++++++++---------------------------------------------- 1 file changed, 20 insertions(+), 107 deletions(-) diff --git a/index.md b/index.md index 5c563961da..71f2bef8be 100644 --- a/index.md +++ b/index.md @@ -1,9 +1,9 @@ --- layout: inner-page-documentation title: Documentation -redirect_from: - - /what-is-scala/ -includeTOC: true +#redirect_from: +# - /what-is-scala/ +#includeTOC: true # Content masthead links links: @@ -27,110 +27,23 @@ links: description: "Books and online exercises" icon: "fa fa-book" link: /learn.html + - title: "Learning Resources" + description: "Books and online exercises" + icon: "fa fa-book" + link: /learn.html + - title: "Reference" + description: "Search the API, read the language spec, and the glossary" + icon: "fa fa-database" + link: /reference.html + - title: "Learning Resources" + description: "Books and online exercises" + icon: "fa fa-book" + link: /learn.html + - title: "Learning Resources" + description: "Books and online exercises" + icon: "fa fa-book" + link: /learn.html ---- - -# What is Scala? - -## A Scalable language - -Scala is an acronym for "Scalable Language". This means that -Scala grows with you. You can play with it by typing one-line -expressions and observing the results. But you can also rely on it -for large mission critical systems, as many companies, including -Twitter, LinkedIn, or Intel do. - -To some, Scala feels like a scripting language. Its syntax is concise -and low ceremony; its types get out of the way because the compiler -can infer them. There's a REPL and IDE worksheets for quick -feedback. Developers like it so much that Scala won the ScriptBowl -contest at the 2012 JavaOne conference. - -At the same time, Scala is the preferred workhorse language for many -mission critical server systems. The generated code is on a par with -Java's and its precise typing means that many problems are caught at -compile-time rather than after deployment. - -At the root, the language's scalability is the result of a careful -integration of object-oriented and functional language concepts. - -## Object-Oriented - -Scala is a pure-bred object-oriented language. Conceptually, every -value is an object and every operation is a method-call. The language -supports advanced component architectures through classes and traits. - -Many traditional design patterns in other languages are already -natively supported. For instance, singletons are supported through -object definitions and visitors are supported through pattern -matching. Using implicit classes, Scala even allows you to add new operations -to existing classes, no matter whether they come from Scala or Java! - -## Functional - -Even though its syntax is fairly conventional, Scala is also a -full-blown functional language. It has everything you would expect, -including first-class functions, a library with efficient immutable -data structures, and a general preference of immutability -over mutation. - -Unlike with many traditional functional languages, Scala allows a -gradual, easy migration to a more functional style. You can start to -use it as a "Java without semicolons". Over time, you can progress to -gradually eliminate mutable state in your applications, phasing in -safe functional composition patterns instead. As Scala programmers we -believe that this progression is often a good idea. At the same time, -Scala is not opinionated; you can use it with any style you prefer. - -## Seamless Java Interop - -Scala runs on the JVM. Java and Scala classes can be freely mixed, no -matter whether they reside in different projects or in the same. They can -even mutually refer to each other, the Scala compiler contains a -subset of a Java compiler to make sense of such recursive -dependencies. - -Java libraries, frameworks and tools are all available. Build tools -like ant or maven, IDEs like Eclipse, IntelliJ, or Netbeans, -frameworks like Spring or Hibernate all work seamlessly with Scala. -Scala runs on all common JVMs and also on Android. - -The Scala community is an important part of the Java -ecosystem. Popular Scala frameworks, including Akka, Finagle, and the -Play web framework include dual APIs for Java and Scala. - -## Functions are Objects - -Scala's approach is to develop a small set of core constructs that can -be combined in flexible ways. This applies also to its object-oriented -and functional natures. Features from both sides are unified to a -degree where Functional and Object-oriented can be seen as two sides -of the same coin. - -Some examples: Functions in Scala are objects. The function type is -just a regular class. The algebraic data types found in languages such -as Haskell, F# or ML are modelled in Scala as class -hierarchies. Pattern matching is possible over arbitrary classes. - -## Future-Proof - -Scala particularly shines when it comes to scalable server software -that makes use of concurrent and synchronous processing, parallel -utilization of multiple cores, and distributed processing in the -cloud. - -Its functional nature makes it easier to write safe and performant -multi-threaded code. There's typically less reliance on mutable state -and Scala's futures and actors provide powerful tools for organizing -concurrent system at a high-level of abstraction. - -## Fun -Maybe most important is that programming in Scala tends to be very -enjoyable. No boilerplate, rapid iteration, but at the same time the -safety of a strong static type system. As [Graham Tackley from the -Guardian says](http://www.infoq.com/articles/guardian_scala): *"We've found that Scala has enabled us to deliver -things faster with less code. It's reinvigorated the team."* -If you haven't yet, try it out! [Here are some resources to get -started](./documentation). +--- From b2603c49216c866f80776149f120f1af5d8af470 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 18:47:55 +0200 Subject: [PATCH 076/112] Drone updates --- .drone.yml | 3 ++- .drone.yml.sig | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 0955732d81..c40ff9c9af 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,8 +1,9 @@ pipeline: build: - image: scalacenter/scala-rvm-jvm-coursier:1.0 + image: scalaplatform/jdk8-ruby2-coursier:0.1 pull: true commands: + - source ~/.profile - bundle install - ./scripts/run-tut.sh - rm -r tut-tmp diff --git a/.drone.yml.sig b/.drone.yml.sig index e492c55065..8970e5e353 100644 --- a/.drone.yml.sig +++ b/.drone.yml.sig @@ -1 +1 @@ -eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgYnVpbGQ6CiAgICBpbWFnZTogc2NhbGFwbGF0Zm9ybS9qZGs4LXJ1YnkyLWNvdXJzaWVyOjAuMQogICAgcHVsbDogdHJ1ZQogICAgY29tbWFuZHM6CiAgICAgIC0gYnVuZGxlIGluc3RhbGwKICAgICAgLSAuL3NjcmlwdHMvcnVuLXR1dC5zaAogICAgICAtIHJtIC1yIHR1dC10bXAKICAgICAgLSBidW5kbGUgZXhlYyBqZWt5bGwgYnVpbGQK.uCV-tIDp9xbL2u2y27B9id6SL89dBfiiiTvVXYxHAbw \ No newline at end of file +eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgYnVpbGQ6CiAgICBpbWFnZTogc2NhbGFwbGF0Zm9ybS9qZGs4LXJ1YnkyLWNvdXJzaWVyOjAuMQogICAgcHVsbDogdHJ1ZQogICAgY29tbWFuZHM6CiAgICAgIC0gc291cmNlIH4vLnByb2ZpbGUKICAgICAgLSBidW5kbGUgaW5zdGFsbAogICAgICAtIC4vc2NyaXB0cy9ydW4tdHV0LnNoCiAgICAgIC0gcm0gLXIgdHV0LXRtcAogICAgICAtIGJ1bmRsZSBleGVjIGpla3lsbCBidWlsZAo.MbpSnd-LG8P_paAfoa9uSejokPFrvMKR9TRZ5zAHOP8 From 79b003699d103b1887388b168592c5085de86997 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 18:54:20 +0200 Subject: [PATCH 077/112] More messing with drone --- .drone.yml | 4 ++-- .drone.yml.sig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index c40ff9c9af..8f1fcfdc7d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,9 +1,9 @@ pipeline: build: - image: scalaplatform/jdk8-ruby2-coursier:0.1 + image: scalacenter/scala-rvm-jvm-coursier:1.0 pull: true commands: - - source ~/.profile + - source ~/.profile - bundle install - ./scripts/run-tut.sh - rm -r tut-tmp diff --git a/.drone.yml.sig b/.drone.yml.sig index 8970e5e353..7a63399b3e 100644 --- a/.drone.yml.sig +++ b/.drone.yml.sig @@ -1 +1 @@ -eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgYnVpbGQ6CiAgICBpbWFnZTogc2NhbGFwbGF0Zm9ybS9qZGs4LXJ1YnkyLWNvdXJzaWVyOjAuMQogICAgcHVsbDogdHJ1ZQogICAgY29tbWFuZHM6CiAgICAgIC0gc291cmNlIH4vLnByb2ZpbGUKICAgICAgLSBidW5kbGUgaW5zdGFsbAogICAgICAtIC4vc2NyaXB0cy9ydW4tdHV0LnNoCiAgICAgIC0gcm0gLXIgdHV0LXRtcAogICAgICAtIGJ1bmRsZSBleGVjIGpla3lsbCBidWlsZAo.MbpSnd-LG8P_paAfoa9uSejokPFrvMKR9TRZ5zAHOP8 +eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgYnVpbGQ6CiAgICBpbWFnZTogc2NhbGFjZW50ZXIvc2NhbGEtcnZtLWp2bS1jb3Vyc2llcjoxLjAKICAgIHB1bGw6IHRydWUKICAgIGNvbW1hbmRzOgogICAgICAtIHNvdXJjZSB-Ly5wcm9maWxlCiAgICAgIC0gYnVuZGxlIGluc3RhbGwKICAgICAgLSAuL3NjcmlwdHMvcnVuLXR1dC5zaAogICAgICAtIHJtIC1yIHR1dC10bXAKICAgICAgLSBidW5kbGUgZXhlYyBqZWt5bGwgYnVpbGQK.RkACGrOMM3mrV25zqRfwWrEPzRbb6HliSYaTFotsUw0 From a03e0a6da753ff94f89a025770c020a07dcc158b Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 20:11:30 +0200 Subject: [PATCH 078/112] More drone BS. This is the last effing time. Gah. --- .drone.yml | 8 ++------ .drone.yml.sig | 2 +- scripts/ci.sh | 6 ++++++ 3 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 scripts/ci.sh diff --git a/.drone.yml b/.drone.yml index 8f1fcfdc7d..d36025ce89 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,10 +1,6 @@ pipeline: build: - image: scalacenter/scala-rvm-jvm-coursier:1.0 + image: scalacenter/scala-rvm-jvm-coursier:2.0 pull: true commands: - - source ~/.profile - - bundle install - - ./scripts/run-tut.sh - - rm -r tut-tmp - - bundle exec jekyll build + - ./scripts/ci.sh diff --git a/.drone.yml.sig b/.drone.yml.sig index 7a63399b3e..8240a38b07 100644 --- a/.drone.yml.sig +++ b/.drone.yml.sig @@ -1 +1 @@ -eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgYnVpbGQ6CiAgICBpbWFnZTogc2NhbGFjZW50ZXIvc2NhbGEtcnZtLWp2bS1jb3Vyc2llcjoxLjAKICAgIHB1bGw6IHRydWUKICAgIGNvbW1hbmRzOgogICAgICAtIHNvdXJjZSB-Ly5wcm9maWxlCiAgICAgIC0gYnVuZGxlIGluc3RhbGwKICAgICAgLSAuL3NjcmlwdHMvcnVuLXR1dC5zaAogICAgICAtIHJtIC1yIHR1dC10bXAKICAgICAgLSBidW5kbGUgZXhlYyBqZWt5bGwgYnVpbGQK.RkACGrOMM3mrV25zqRfwWrEPzRbb6HliSYaTFotsUw0 +eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgYnVpbGQ6CiAgICBpbWFnZTogamRrLXJ2bS1jb3Vyc2llcjpsYXRlc3QKICAgIHB1bGw6IHRydWUKICAgIGNvbW1hbmRzOgogICAgICAtIHNvdXJjZSB-Ly5wcm9maWxlCiAgICAgIC0gYnVuZGxlIGluc3RhbGwKICAgICAgLSAuL3NjcmlwdHMvcnVuLXR1dC5zaAogICAgICAtIHJtIC1yIHR1dC10bXAKICAgICAgLSBidW5kbGUgZXhlYyBqZWt5bGwgYnVpbGQK.7Rp37FEwRqAo85EdFYZh1PoyU8mxpFdEnpchWaQkHCc diff --git a/scripts/ci.sh b/scripts/ci.sh new file mode 100644 index 0000000000..13e23eb863 --- /dev/null +++ b/scripts/ci.sh @@ -0,0 +1,6 @@ +#!/bin/bash - +. /usr/local/rvm/scripts/rvm +bundle install +./scripts/run-tut.sh +rm -r tut-tmp +bundle exec jekyll build From 384e6b90bb15e0511d883b62581132bf4c6d43c1 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 20:13:27 +0200 Subject: [PATCH 079/112] !!!!! --- scripts/ci.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 scripts/ci.sh diff --git a/scripts/ci.sh b/scripts/ci.sh old mode 100644 new mode 100755 From f8c2786e1c257d14e1e168d320825b8e636b58e3 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 20:19:24 +0200 Subject: [PATCH 080/112] Get rid of cruft --- tutorials/index.md | 47 ---------------------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 tutorials/index.md diff --git a/tutorials/index.md b/tutorials/index.md deleted file mode 100644 index d4ae517d70..0000000000 --- a/tutorials/index.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: overview -title: Tutorials ---- - -
    - -
    -

    New to Scala?

    -

    Tutorials geared for people coming...

    - -
    - -
    -

    FAQ

    Frequently Asked Questions (and their answers!)

    -
    - {% for pg in site.pages %} - {% if pg.partof == "FAQ" and pg.outof %} - {% assign totalPagesFAQ = pg.outof %} - {% endif %} - {% endfor %} - - {% if totalPagesFAQ %} -
      - {% for i in (1..totalPagesFAQ) %} - {% for pg in site.pages %} - {% if pg.partof == "FAQ" and pg.num and pg.num == i %} -
    • {{ pg.title }}
    • - {% endif %} - {% endfor %} - {% endfor %} -
    - {% else %} **ERROR**. Couldn't find the total number of pages in this set of tutorial articles. Have you declared the `outof` tag in your YAML front matter? - {% endif %} - -
    - -
    -
    -

    A Tour of Scala

    Bite-size pieces of the essentials...

    -
    - {% include tutorial-tour-list.txt %} -
    From 2ae27bfa13ee5de502ea94617bfe39e3a632f271 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Thu, 27 Jul 2017 23:59:08 +0200 Subject: [PATCH 081/112] Cheatsheet styling --- _includes/headertop.html | 4 +- _layouts/cheatsheet.html | 26 +- _sass/layout/cheatsheet.scss | 55 +++ cheatsheets/index.md | 420 ++++++++++++++++++---- resources/css/highlightjs.css | 102 ++++++ resources/css/monospace.css | 42 +++ resources/css/style.scss | 1 + resources/glyphs/Consolas-Bold.eot | Bin 0 -> 97182 bytes resources/glyphs/Consolas-Bold.ttf | Bin 0 -> 97008 bytes resources/glyphs/Consolas-Bold.woff | Bin 0 -> 59492 bytes resources/glyphs/Consolas-BoldItalic.eot | Bin 0 -> 107150 bytes resources/glyphs/Consolas-BoldItalic.ttf | Bin 0 -> 106948 bytes resources/glyphs/Consolas-BoldItalic.woff | Bin 0 -> 66592 bytes resources/glyphs/Consolas-Italic.eot | Bin 0 -> 101014 bytes resources/glyphs/Consolas-Italic.ttf | Bin 0 -> 100832 bytes resources/glyphs/Consolas-Italic.woff | Bin 0 -> 63412 bytes resources/glyphs/Consolas.eot | Bin 0 -> 95274 bytes resources/glyphs/Consolas.ttf | Bin 0 -> 95104 bytes resources/glyphs/Consolas.woff | Bin 0 -> 58472 bytes 19 files changed, 556 insertions(+), 94 deletions(-) create mode 100644 _sass/layout/cheatsheet.scss create mode 100644 resources/css/highlightjs.css create mode 100644 resources/css/monospace.css create mode 100644 resources/glyphs/Consolas-Bold.eot create mode 100644 resources/glyphs/Consolas-Bold.ttf create mode 100644 resources/glyphs/Consolas-Bold.woff create mode 100644 resources/glyphs/Consolas-BoldItalic.eot create mode 100644 resources/glyphs/Consolas-BoldItalic.ttf create mode 100644 resources/glyphs/Consolas-BoldItalic.woff create mode 100644 resources/glyphs/Consolas-Italic.eot create mode 100644 resources/glyphs/Consolas-Italic.ttf create mode 100644 resources/glyphs/Consolas-Italic.woff create mode 100644 resources/glyphs/Consolas.eot create mode 100644 resources/glyphs/Consolas.ttf create mode 100644 resources/glyphs/Consolas.woff diff --git a/_includes/headertop.html b/_includes/headertop.html index bfc55927d5..7ef0bcea80 100644 --- a/_includes/headertop.html +++ b/_includes/headertop.html @@ -13,10 +13,10 @@ - + - + diff --git a/_layouts/cheatsheet.html b/_layouts/cheatsheet.html index df7b1f4ca7..d3796ae2e6 100644 --- a/_layouts/cheatsheet.html +++ b/_layouts/cheatsheet.html @@ -1,19 +1,13 @@ --- -layout: inner-page-no-masthead +layout: inner-page-parent --- -{% include cheatsheet-header.txt %} -
    -
    -
    -
    -
    - {{ content }} -
    - -
    - -
    -
    -
    -
    +
    +
    +
    +
    + {{content}} +
    +
    +
    +
    diff --git a/_sass/layout/cheatsheet.scss b/_sass/layout/cheatsheet.scss new file mode 100644 index 0000000000..691a34b38b --- /dev/null +++ b/_sass/layout/cheatsheet.scss @@ -0,0 +1,55 @@ +// CHEATSHEET +//------------------------------------------------ +//------------------------------------------------ + +.content-primary.cheatsheet { + code { + font-family: 'Consolas'; + } + + pre.highlight { + margin: 0; + code { + padding: 0; + background: #fff; + border: 0; + } + } + + .h2 { + display: block; + font-size: $font-size-h2; + font-weight: $font-black; + color: $gray-darker; + padding-top: 26px; + } + + h6 { + color: $base-font-color-light; + font-family: $base-font-family; + text-transform: uppercase; + font-size: $font-size-small; + } + + .label { + display: inline-block; + // position: absolute; + // right: 0; + position: relative; + // float: right; + top: 3px; + color: #fff; + text-transform: uppercase; + font-size: 11px; + font-weight: 700; + padding: 1px 5px; + } + + .important { + background: $brand-primary; + } + + .success { + background: $brand-secondary; + } +} diff --git a/cheatsheets/index.md b/cheatsheets/index.md index 6a79afa732..99c2c2380e 100644 --- a/cheatsheets/index.md +++ b/cheatsheets/index.md @@ -6,80 +6,348 @@ about: Thanks to Brendan O'Connor, this cheat languages: [ba, fr, ja, pl, pt-br] --- -###### Contributed by {{ page.by }} + -| | | -| ------ | ------ | -| variables | | -| `var x = 5` | variable | -| Good `val x = 5`
    Bad `x=6` | constant | -| `var x: Double = 5` | explicit type | -| functions | | -| Good `def f(x: Int) = { x*x }`
    Bad `def f(x: Int) { x*x }` | define function
    hidden error: without = it's a Unit-returning procedure; causes havoc | -| Good `def f(x: Any) = println(x)`
    Bad `def f(x) = println(x)` | define function
    syntax error: need types for every arg. | -| `type R = Double` | type alias | -| `def f(x: R)` vs.
    `def f(x: => R)` | call-by-value
    call-by-name (lazy parameters) | -| `(x:R) => x*x` | anonymous function | -| `(1 to 5).map(_*2)` vs.
    `(1 to 5).reduceLeft( _+_ )` | anonymous function: underscore is positionally matched arg. | -| `(1 to 5).map( x => x*x )` | anonymous function: to use an arg twice, have to name it. | -| Good `(1 to 5).map(2*)`
    Bad `(1 to 5).map(*2)` | anonymous function: bound infix method. Use `2*_` for sanity's sake instead. | -| `(1 to 5).map { x => val y=x*2; println(y); y }` | anonymous function: block style returns last expression. | -| `(1 to 5) filter {_%2 == 0} map {_*2}` | anonymous functions: pipeline style. (or parens too). | -| `def compose(g:R=>R, h:R=>R) = (x:R) => g(h(x))`
    `val f = compose({_*2}, {_-1})` | anonymous functions: to pass in multiple blocks, need outer parens. | -| `val zscore = (mean:R, sd:R) => (x:R) => (x-mean)/sd` | currying, obvious syntax. | -| `def zscore(mean:R, sd:R) = (x:R) => (x-mean)/sd` | currying, obvious syntax | -| `def zscore(mean:R, sd:R)(x:R) = (x-mean)/sd` | currying, sugar syntax. but then: | -| `val normer = zscore(7, 0.4) _` | need trailing underscore to get the partial, only for the sugar version. | -| `def mapmake[T](g:T=>T)(seq: List[T]) = seq.map(g)` | generic type. | -| `5.+(3); 5 + 3`
    `(1 to 5) map (_*2)` | infix sugar. | -| `def sum(args: Int*) = args.reduceLeft(_+_)` | varargs. | -| packages | | -| `import scala.collection._` | wildcard import. | -| `import scala.collection.Vector`
    `import scala.collection.{Vector, Sequence}` | selective import. | -| `import scala.collection.{Vector => Vec28}` | renaming import. | -| `import java.util.{Date => _, _}` | import all from java.util except Date. | -| `package pkg` _at start of file_
    `package pkg { ... }` | declare a package. | -| data structures | | -| `(1,2,3)` | tuple literal. (`Tuple3`) | -| `var (x,y,z) = (1,2,3)` | destructuring bind: tuple unpacking via pattern matching. | -| Bad`var x,y,z = (1,2,3)` | hidden error: each assigned to the entire tuple. | -| `var xs = List(1,2,3)` | list (immutable). | -| `xs(2)` | paren indexing. ([slides](http://www.slideshare.net/Odersky/fosdem-2009-1013261/27)) | -| `1 :: List(2,3)` | cons. | -| `1 to 5` _same as_ `1 until 6`
    `1 to 10 by 2` | range sugar. | -| `()` _(empty parens)_ | sole member of the Unit type (like C/Java void). | -| control constructs | | -| `if (check) happy else sad` | conditional. | -| `if (check) happy` _same as_
    `if (check) happy else ()` | conditional sugar. | -| `while (x < 5) { println(x); x += 1}` | while loop. | -| `do { println(x); x += 1} while (x < 5)` | do while loop. | -| `import scala.util.control.Breaks._`
    `breakable {`
    ` for (x <- xs) {`
    ` if (Math.random < 0.1) break`
    ` }`
    `}`| break. ([slides](http://www.slideshare.net/Odersky/fosdem-2009-1013261/21)) | -| `for (x <- xs if x%2 == 0) yield x*10` _same as_
    `xs.filter(_%2 == 0).map(_*10)` | for comprehension: filter/map | -| `for ((x,y) <- xs zip ys) yield x*y` _same as_
    `(xs zip ys) map { case (x,y) => x*y }` | for comprehension: destructuring bind | -| `for (x <- xs; y <- ys) yield x*y` _same as_
    `xs flatMap {x => ys map {y => x*y}}` | for comprehension: cross product | -| `for (x <- xs; y <- ys) {`
    `println("%d/%d = %.1f".format(x, y, x/y.toFloat))`
    `}` | for comprehension: imperative-ish
    [sprintf-style](http://java.sun.com/javase/6/docs/api/java/util/Formatter.html#syntax) | -| `for (i <- 1 to 5) {`
    `println(i)`
    `}` | for comprehension: iterate including the upper bound | -| `for (i <- 1 until 5) {`
    `println(i)`
    `}` | for comprehension: iterate omitting the upper bound | -| pattern matching | | -| Good `(xs zip ys) map { case (x,y) => x*y }`
    Bad `(xs zip ys) map( (x,y) => x*y )` | use case in function args for pattern matching. | -| Bad
    `val v42 = 42`
    `Some(3) match {`
    ` case Some(v42) => println("42")`
    ` case _ => println("Not 42")`
    `}` | "v42" is interpreted as a name matching any Int value, and "42" is printed. | -| Good
    `val v42 = 42`
    `Some(3) match {`
    `` case Some(`v42`) => println("42")``
    `case _ => println("Not 42")`
    `}` | "\`v42\`" with backticks is interpreted as the existing val `v42`, and "Not 42" is printed. | -| Good
    `val UppercaseVal = 42`
    `Some(3) match {`
    ` case Some(UppercaseVal) => println("42")`
    ` case _ => println("Not 42")`
    `}` | `UppercaseVal` is treated as an existing val, rather than a new pattern variable, because it starts with an uppercase letter. Thus, the value contained within `UppercaseVal` is checked against `3`, and "Not 42" is printed. | -| object orientation | | -| `class C(x: R)` | constructor params - `x` is only available in class body | -| `class C(val x: R)`
    `var c = new C(4)`
    `c.x` | constructor params - automatic public member defined | -| `class C(var x: R) {`
    `assert(x > 0, "positive please")`
    `var y = x`
    `val readonly = 5`
    `private var secret = 1`
    `def this = this(42)`
    `}`|
    constructor is class body
    declare a public member
    declare a gettable but not settable member
    declare a private member
    alternative constructor| -| `new{ ... }` | anonymous class | -| `abstract class D { ... }` | define an abstract class. (non-createable) | -| `class C extends D { ... }` | define an inherited class. | -| `class D(var x: R)`
    `class C(x: R) extends D(x)` | inheritance and constructor params. (wishlist: automatically pass-up params by default) -| `object O extends D { ... }` | define a singleton. (module-like) | -| `trait T { ... }`
    `class C extends T { ... }`
    `class C extends D with T { ... }` | traits.
    interfaces-with-implementation. no constructor params. [mixin-able]({{ site.baseurl }}/tutorials/tour/mixin-class-composition.html). -| `trait T1; trait T2`
    `class C extends T1 with T2`
    `class C extends D with T1 with T2` | multiple traits. | -| `class C extends D { override def f = ...}` | must declare method overrides. | -| `new java.io.File("f")` | create object. | -| Bad `new List[Int]`
    Good `List(1,2,3)` | type error: abstract type
    instead, convention: callable factory shadowing the type | -| `classOf[String]` | class literal. | -| `x.isInstanceOf[String]` | type check (runtime) | -| `x.asInstanceOf[String]` | type cast (runtime) | -| `x: String` | ascription (compile time) | +{{ page.about }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    variables
    var x = 5variable
    Good
    val x = 5
    Bad
    x=6
    constant
    var x: Double = 5explicit type
    functions
    Good
    def f(x: Int) = { x*x }
    Bad
    def f(x: Int) { x*x }
    define function
    hidden error: without = it’s a Unit-returning procedure; causes havoc
    Good
    def f(x: Any) = println(x)
    Bad
    def f(x) = println(x)
    define function
    syntax error: need types for every arg.
    type R = Doubletype alias
    def f(x: R) vs.
    def f(x: => R)
    call-by-value
    call-by-name (lazy parameters)
    (x:R) => x*xanonymous function
    (1 to 5).map(_*2) vs.
    (1 to 5).reduceLeft( _+_ )
    anonymous function: underscore is positionally matched arg.
    (1 to 5).map( x => x*x )anonymous function: to use an arg twice, have to name it.
    Good
    (1 to 5).map(2*)
    Bad
    (1 to 5).map(*2)
    anonymous function: bound infix method. Use 2*_ for sanity’s sake instead.
    (1 to 5).map { x => val y=x*2; println(y); y }anonymous function: block style returns last expression.
    (1 to 5) filter {_%2 == 0} map {_*2}anonymous functions: pipeline style. (or parens too).
    def compose(g:R=>R, h:R=>R) = (x:R) => g(h(x))
    val f = compose({_*2}, {_-1})
    anonymous functions: to pass in multiple blocks, need outer parens.
    val zscore = (mean:R, sd:R) => (x:R) => (x-mean)/sdcurrying, obvious syntax.
    def zscore(mean:R, sd:R) = (x:R) => (x-mean)/sdcurrying, obvious syntax
    def zscore(mean:R, sd:R)(x:R) = (x-mean)/sdcurrying, sugar syntax. but then:
    val normer = zscore(7, 0.4) _need trailing underscore to get the partial, only for the sugar version.
    def mapmake[T](g:T=>T)(seq: List[T]) = seq.map(g)generic type.
    5.+(3); 5 + 3
    (1 to 5) map (_*2)
    infix sugar.
    def sum(args: Int*) = args.reduceLeft(_+_)varargs.
    packages
    import scala.collection._wildcard import.
    import scala.collection.Vector
    import scala.collection.{Vector, Sequence}
    selective import.
    import scala.collection.{Vector => Vec28}renaming import.
    import java.util.{Date => _, _}import all from java.util except Date.
    package pkg at start of file
    package pkg { ... }
    declare a package.
    data structures
    (1,2,3)tuple literal. (Tuple3)
    var (x,y,z) = (1,2,3)destructuring bind: tuple unpacking via pattern matching.
    Bad
    var x,y,z = (1,2,3)
    hidden error: each assigned to the entire tuple.
    var xs = List(1,2,3)list (immutable).
    xs(2)paren indexing. (slides)
    1 :: List(2,3)cons.
    1 to 5 same as 1 until 6
    1 to 10 by 2
    range sugar.
    () (empty parens)sole member of the Unit type (like C/Java void).
    control constructs
    if (check) happy else sadconditional.
    if (check) happy +
    same as
    + if (check) happy else ()
    conditional sugar.
    while (x < 5) { println(x); x += 1}while loop.
    do { println(x); x += 1} while (x < 5)do while loop.
    import scala.util.control.Breaks._
    +breakable {
    +  for (x <- xs) {
    +    if (Math.random < 0.1)
    +      break
    +  }
    +}
    break. (slides)
    for (x <- xs if x%2 == 0) yield x*10 +
    same as
    + xs.filter(_%2 == 0).map(_*10)
    for comprehension: filter/map
    for ((x,y) <- xs zip ys) yield x*y +
    same as
    + (xs zip ys) map { case (x,y) => x*y }
    for comprehension: destructuring bind
    for (x <- xs; y <- ys) yield x*y +
    same as
    + xs flatMap {x => ys map {y => x*y}}
    for comprehension: cross product
    for (x <- xs; y <- ys) {
    +  println("%d/%d = %.1f".format(x, y, x/y.toFloat))
    +}
    for comprehension: imperative-ish
    sprintf-style
    for (i <- 1 to 5) {
    +  println(i)
    +}
    for comprehension: iterate including the upper bound
    for (i <- 1 until 5) {
    +  println(i)
    +}
    for comprehension: iterate omitting the upper bound
    pattern matching
    Good
    (xs zip ys) map { case (x,y) => x*y }
    Bad
    (xs zip ys) map( (x,y) => x*y )
    use case in function args for pattern matching.
    Bad
    +
    val v42 = 42
    +Some(3) match {
    +  case Some(v42) => println("42")
    +  case _ => println("Not 42")
    +}
    “v42” is interpreted as a name matching any Int value, and “42” is printed.
    Good
    +
    val v42 = 42
    +Some(3) match {
    +  case Some(`v42`) => println("42")
    +  case _ => println("Not 42")
    +}
    ”`v42`” with backticks is interpreted as the existing val v42, and “Not 42” is printed.
    Good
    +
    val UppercaseVal = 42
    +Some(3) match {
    +  case Some(UppercaseVal) => println("42")
    +  case _ => println("Not 42")
    +}
    UppercaseVal is treated as an existing val, rather than a new pattern variable, because it starts with an uppercase letter. Thus, the value contained within UppercaseVal is checked against 3, and “Not 42” is printed.
    object orientation
    class C(x: R)constructor params - x is only available in class body
    class C(val x: R)
    var c = new C(4)
    c.x
    constructor params - automatic public member defined
    class C(var x: R) {
    +  assert(x > 0, "positive please")
    +  var y = x
    +  val readonly = 5
    +  private var secret = 1
    +  def this = this(42)
    +}
    constructor is class body
    declare a public member
    declare a gettable but not settable member
    declare a private member
    alternative constructor
    new{ ... }anonymous class
    abstract class D { ... }define an abstract class. (non-createable)
    class C extends D { ... }define an inherited class.
    class D(var x: R)
    class C(x: R) extends D(x)
    inheritance and constructor params. (wishlist: automatically pass-up params by default)
    object O extends D { ... }define a singleton. (module-like)
    trait T { ... }
    class C extends T { ... }
    class C extends D with T { ... }
    traits.
    interfaces-with-implementation. no constructor params. mixin-able.
    trait T1; trait T2
    class C extends T1 with T2
    class C extends D with T1 with T2
    multiple traits.
    class C extends D { override def f = ...}must declare method overrides.
    new java.io.File("f")create object.
    Bad
    new List[Int]
    Good
    List(1,2,3)
    type error: abstract type
    instead, convention: callable factory shadowing the type
    classOf[String]class literal.
    x.isInstanceOf[String]type check (runtime)
    x.asInstanceOf[String]type cast (runtime)
    x: Stringascription (compile time)
    diff --git a/resources/css/highlightjs.css b/resources/css/highlightjs.css new file mode 100644 index 0000000000..1db8a26ed4 --- /dev/null +++ b/resources/css/highlightjs.css @@ -0,0 +1,102 @@ +/* + +github.com style (c) Vasily Polovnyov + +*/ + +.hljs { + font-family: 'Consolas'; + display: block; + overflow-x: auto; + padding: 0.5em; + color: #333; + background: #fdfdf7; + border-radius: 3px; + border: 1px solid #e7e7d6; +} + +.hljs-comment, +.hljs-quote { + color: #998; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-subst { + color: #333; + font-weight: bold; +} + +.hljs-number, +.hljs-literal, +.hljs-variable, +.hljs-template-variable, +.hljs-tag .hljs-attr { + color: #008080; +} + +.hljs-string, +.hljs-doctag { + color: #da322f; +} + +.hljs-title, +.hljs-section, +.hljs-selector-id { + color: #900; + font-weight: bold; +} + +.hljs-subst { + font-weight: normal; +} + +.hljs-type, +.hljs-class .hljs-title { + color: #2f8ad2; + font-weight: bold; +} + +.hljs-tag, +.hljs-name, +.hljs-attribute { + color: #000080; + font-weight: normal; +} + +.hljs-regexp, +.hljs-link { + color: #859a00; +} + +.hljs-symbol, +.hljs-bullet { + color: #990073; +} + +.hljs-built_in, +.hljs-builtin-name { + color: #2f8ad2; +} + +.hljs-meta { + color: #93a1a1; + font-weight: bold; +} + +.hljs-deletion { + background: #fdd; +} + +.hljs-addition { + background: #dfd; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} diff --git a/resources/css/monospace.css b/resources/css/monospace.css new file mode 100644 index 0000000000..67e622e269 --- /dev/null +++ b/resources/css/monospace.css @@ -0,0 +1,42 @@ +--- +--- + +@font-face { + font-family: 'Consolas'; + src: url('{{ site.baseurl }}/resources/glyphs/Consolas.eot'); + src: url('{{ site.baseurl }}/resources/glyphs/Consolas.eot?#iefix') format('embedded-opentype'), + url('{{ site.baseurl }}/resources/glyphs/Consolas.woff') format('woff'), + url('{{ site.baseurl }}/resources/glyphs/Consolas.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'Consolas'; + src: url('{{ site.baseurl }}/resources/glyphs/Consolas-BoldItalic.eot'); + src: url('{{ site.baseurl }}/resources/glyphs/Consolas-BoldItalic.eot?#iefix') format('embedded-opentype'), + url('{{ site.baseurl }}/resources/glyphs/Consolas-BoldItalic.woff') format('woff'), + url('{{ site.baseurl }}/resources/glyphs/Consolas-BoldItalic.ttf') format('truetype'); + font-weight: bold; + font-style: italic; +} + +@font-face { + font-family: 'Consolas'; + src: url('{{ site.baseurl }}/resources/glyphs/Consolas-Italic.eot'); + src: url('{{ site.baseurl }}/resources/glyphs/Consolas-Italic.eot?#iefix') format('embedded-opentype'), + url('{{ site.baseurl }}/resources/glyphs/Consolas-Italic.woff') format('woff'), + url('{{ site.baseurl }}/resources/glyphs/Consolas-Italic.ttf') format('truetype'); + font-weight: normal; + font-style: italic; +} + +@font-face { + font-family: 'Consolas'; + src: url('{{ site.baseurl }}/resources/glyphs/Consolas-Bold.eot'); + src: url('{{ site.baseurl }}/resources/glyphs/Consolas-Bold.eot?#iefix') format('embedded-opentype'), + url('{{ site.baseurl }}/resources/glyphs/Consolas-Bold.woff') format('woff'), + url('{{ site.baseurl }}/resources/glyphs/Consolas-Bold.ttf') format('truetype'); + font-weight: bold; + font-style: normal; +} diff --git a/resources/css/style.scss b/resources/css/style.scss index 8a5a024f09..796ccfcf17 100755 --- a/resources/css/style.scss +++ b/resources/css/style.scss @@ -26,6 +26,7 @@ @import 'layout/navigation'; @import 'layout/doc-navigation'; @import 'layout/twitter-feed'; +@import 'layout/cheatsheet'; @import 'layout/ides'; @import 'layout/nutshell'; @import 'layout/overviews'; diff --git a/resources/glyphs/Consolas-Bold.eot b/resources/glyphs/Consolas-Bold.eot new file mode 100644 index 0000000000000000000000000000000000000000..198111964874cd796438f4b982a8369e94be3101 GIT binary patch literal 97182 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

    r{Wic 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 VWhSt$U|`t&SXu)_??2HL1px3rt4#m^ literal 0 HcmV?d00001 diff --git a/resources/glyphs/Consolas-Bold.ttf b/resources/glyphs/Consolas-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..231576b0e9637e0a35973bcdcef0d7083ec35e41 GIT binary patch literal 97008 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 literal 0 HcmV?d00001 diff --git a/resources/glyphs/Consolas-Bold.woff b/resources/glyphs/Consolas-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..1899dbd063c6c7e86afc8e61f15ea78f8bebe896 GIT binary patch literal 59492 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 QkMG(eqW_QY9Sb%1Ke_cnO8@`> literal 0 HcmV?d00001 diff --git a/resources/glyphs/Consolas-BoldItalic.eot b/resources/glyphs/Consolas-BoldItalic.eot new file mode 100644 index 0000000000000000000000000000000000000000..107dc0bef7b78428dd039c27563eeedae5d15416 GIT binary patch literal 107150 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

    ?G_W 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 literal 0 HcmV?d00001 diff --git a/resources/glyphs/Consolas-BoldItalic.ttf b/resources/glyphs/Consolas-BoldItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2a98047bd3ec3bff1ee01400cca0b67c4ec6ddc3 GIT binary patch literal 106948 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 z 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 AO#lD@ literal 0 HcmV?d00001 diff --git a/resources/glyphs/Consolas-BoldItalic.woff b/resources/glyphs/Consolas-BoldItalic.woff new file mode 100644 index 0000000000000000000000000000000000000000..828c0282dcdc11473ef502fdcf00df936228b7de GIT binary patch literal 66592 zcmZ5nQy(?k zxHtd^0001i5(1F_llg=HtN+~o|0XW}TlR+s`eVoa5ArkY)e@p&|M`>t=y*TC2ap5L zN+`%H1Astgesty^2(nGd)k-L<2>1t(CL#b6Kw@U?Vfxe6S^!|C2mmB3L-yDNYHng+{4>y`9}fF}*seL@ zF#j?B*d~5-;vbO0vH%&G+c>-boTc-x7s1}n$r%86_y7Q4&;S6msTizi zVtYrEA7A*DpICkX0D>1TI%zim=qJh#7+@^K^bmL-0R)gg1^}RAABX%u*)tkw@D2Ng z2G$Re0{{U)0)YRs0s!t=M*7D3`kyb)Bu2Q>YkoO?(&`9IMU0RvgdpgEZowb+|HcI# z2ZRD300ut*{+T5PfIa{K`3Z#n6AS3S7^ad&YXiOidV9GO7t;a)6yb$g^z=Cg2YdU* z1}CS+1_%3x2J8I;PP;&Y0y#iXU%+W787Y}5KTHk(FSQpeN+K}GfOj13J%XwQVR3IS zD4?_se$_q8CCScw(g1a&XZq5RuCY<`TtZWG!`3NmskklwV;rWghz*;eQCu=zV-!YG zvz=~ta!!^-mllelaS+DhGK92h=uSAHv#qXkM%!xS*yCyB%3ki+BWL?QYn$uXYsw?X zJ<=zlg)%O8)3rq&#(KkcC04x%%4U(mMpe@zwf6m=c{H)gI>KVPMon9*OI~yx8*_NeMUP^88EWu4w;@-}NJXC&_%tJ|9ZF$_w2W{EnR2vRHf~J1 zS+C(_+aGJF*|)=F)v{~YqPGOv&<{m?BiN9X2>cd{YJl9vV<9-KCE!5SSQiy9ZE9eT zI#8FD3dJSs=Okk1 zGbH-3!|hqkJ`VRF+m>wmiWS9vm0yT~go$^L^$*G3eU} zzWviOU+&x)^wZ~{a_MT8uGBeg$}48mwQ5na>F=Ox*&c(lL_9gMJQGFbxb7Psj)-r_ zD6wsyIKpmX$6u?JCX z(~=oSxk-j+ms;30aq=39%`Ul5Q_WgAGv0wUD9^lePUq7bzP||A?1bUKNKWq@9rX(A z7qM{R=3RUul5{1XL;=Jdg1uTpl|9&O0_@g(w9|og*w6n#_nn<16~(s8?vskyFF=dy9Zfa zq^*3zR|>DqH=nWyDy9&a)0XAYa?A{ArJgzn_b$92sT;s8sugr0M+S(35opyLpq@A3p4~0wP1+77apE<81Ct0;qkhjy7;)ty7z&oZM`EU z32w%DAT=?IZ4%smo{NWO%@Zsc^@hqIZJ57cmZva%HNAr58kKGCWiukzfU|0IVcikQ_m95zc;(z%v70k@U;3WSJ__Y1m?vJYVBdu~ z8`sRArD*CZk{okEJM%EZt#2?BFT<^t*DmWwcD|EqNeUq+AcrZ3*e?IM@kng-*0tUy z^{|P48pjMH<#6bWq^2bCMr})fGEtvV9k_2#-;FXGTV0vExlzQz)k3wGfD@AwafM(7 zV+BVYT^&0OXOtmbG2vjO>p<6jy5o4G;Y#wk)oX(9!qz!*^U*dwbGM{Jmt!pY$?dM? zx#flf!mmVN(GHK#<6&_36z%@sUoZKxCGzNRpRs&DpL)zuLU5K~p0>z*SR+oib2xFt z{7t+uN^zFZx+s#|cg$%}a^{G!jcE{6#t>or1ELb<=u_SaSo~c@Q|8E3-eKG$r9s}K zdrlbjQ54=WV)T1d`G(+P-T~&6hCnloF>2L@&|~+Urq15TN+Z6Z&OvbY`iNAI5Vh3_ z^yWM4b@yxz_r#W!srAl*j?R&H?_PMii=(4r~24W+#MGFW0>v(LOqA@ z8mrcS5q27&*fB- zwv@(`Ol(h}ee z4`Wm=t{m5{d%TVwNxFvp?kdWHj`hS77o%28r~X}xy;v^Ps-&3st00^_RDCa;B0x03 znYd_&Ol9(6S0Qq3fhfAYNI4dkxm-SFDk7A%fJxR|iX~${Fr~48&Bk1sr`|#yBAX(+ z_0N=ZKF#Kd9DLhD9!1xQEWzE0v_SO{cJ(25^)a~Z0lDoFy6qvmZK(iAtH?D$fkS8l zr+m6rK`@=#uqu^FMRIM^KlSk1$Nw(TQp@CrRt?C_vZqSTGM$bD^@e4Vzt2+3p$23n zT=dgUi<20b+oIUJWC>izx1HHHWz?vc`4%!InH8zF{_4US2am~p=$aov^98BNgreRW$%qKE&~4w3z7*W>Y$=hL&eVdZYy_0-hRaliPK zs-3U41cUiBsk-hD1vXHa65uq!oV|iH5;5Qg z84(~+=CBCvo;UHLA`L;8tKHyiC~an2j^&+cqeioVg1glkDBn~VGx%s%eZ!Si@hH+H zmzty?+E+gl`!-<&Y+7QH55$Ohk2Pk76*r>mc#p`N?v5Urf;5oFHuf2(l6p zw-^<}AGuv|tfgQWtLbmuK01VaVr_(fz(Gw2M^LPzE1^5HJfR0euL5fEj6&$v`*?+9 zHV(Sv3)4%mZA2m)xh6=dsmft^g@FB-D3hs}NwtYp%J)2oA`iZKZ}(prw=yhl2*F}i zcYy~!?3w|$Of{;tZ)Z)nVtir0sW`7&vpg3DGgc!I!OweXnuWMu1uFOcoJXe%5L^IDVhR< z32%ThyuGzQ;L?dIv>$_0WU9NUOR-ADZ(V2h3RiOR(pbtQR&4bOITgqTnSppB9>6Xi=CjCNA3WKs>80xSNP0GyXd*hTVek+suqJB~!Xz>GV;#qe;Guvx&@lgt zi4?WGH`_RPZOKZ1^9U0QnN;Jh#P}YH3*#;B!KLyWEiuPZGLvCJ_}EA5uT?VREsU}= z2;7Qn(@**-5AXP+NldIgpEP>7MFu-^De-lw)eu8`^kpW(?ne5`d5?(|jqTVmnp0M$ z-VJcnIK@*XDinxH_`jNiVB5+?QYC&$X;Vpf;RLsuM03L^Zj=J+Y4adTKr^M=N>dw2bnv(l*6L;+{`LL|iIt?_eFW(+=a7VvIzbFmzPyxIcF$BsU5f^gb05>WGMBfh+q|gBM0^(g5Dk|8J zG7!nuJSn`FA2GDcfD<+-pAi&%K$j5`ydQ9a!2S>;nH6; zO(KL_acY674M?d2sr4n-;%01gMi^qzhLG8VX~U=X8MdO*2Sn}?x6Vvv)6)Cj8z9jJ z-|uL1LQI7=?8tC}$c4@81F;H&WI>nqdZ$593`p7&X8}e!d{Fi{Inj^$ne1670#?a2!Y=PF`q(r8DT^F5iw!C`Uo*0LGlDvW)PtB0m6vE-lIXqCs?RkE))+rP_7H^FqfN-ea?;bXWP7 zLQTc;Ld6-{W4)*3rNa6mdy`66S##Os8Q5c|ryO5J&aKLtd`0O(;u#d$=4wfsBwsbY zGR|V*8Pa2|r3ykh#3IO)=VG}a{ISwOXfuR<`PhsD0^i^;=+3%yWh#XPBqU!*~w zB;XP^G8ZLb{Y4U26K^ua>mkl;=bK}3jDFaB?0R4->hypm-%F52ppH5^Iv33j`UV$X zY?p>N3OD@r9{H)*v%If&#o=>W@s*(HRV{ae5Iaaa-aH{yv6W%wqKTEVXkFF9ZV{nb zI5JF|d&wd_@04iQQL~xK)u(n^@$8Z5J;^`cF?)lFFJCgkn9{N^II#O?sXAMkT50od zNomwjdEf0rlq!Xi4tgoCoZ(v|`hdFXx?$QOTlvMGGtgrU2Awl1+KOLj!=-Ioixn{A zmNZI{b6%|NX4f*;_+7=#`3^!lBCT@4^DKN(_C}t&=Gu8W$2m9O2aI2r1UGs-55_py zb8Kgj=N6?c_5%W`_h(<=ZRvR(KlQix_oaNa0dBYNan^BC%dd#rXoRJSiHY|x*v!;j z2J>Ola#P(D+4((?k%-Qy#m=U*<%ed{fAI;FP`iW?kjcmiEu9x0+Rsi?IT`B5_`*bWbFDJa`~vy$i&K|;#u~@V5JUdD(*lc!Y0+U@slst z@;CvEz2@^qE@4R3E@!fyMLuN)%a%>#R;gyPPK2VdESh8GEd4fYo<%@jNl3QN>LtW0 zseLK_j3E|v86xA$8%tnsh)p7bZvwbhXy_#1nYAG<6b3ZS20X5WIlqYOKsn2%@rw)@ zo^}ai3@8%Deu-gAlymx{jOO{KkBY-PI>3t!!lR52Bh|CSg?vUy4-gelaH(~{U?@aI zQVy65qcZ+QJeI8lO_gcHRXY(R<7rOYkm8ZI@K*YTTzVoCT?(eSEQvsYX+b|s!ozH8L7*dI%J z84h&rV$th2YZWIvrXI0!-Y=7hp|)yY_@xE>dno+-G~K%p&H*c;C>@}V)saITZ(us) z`yHu9NENXZK1DSTiC;-Ch3gpa<^U}#GF>pxemnRl^CYt*lc)T#oWf5vQGpbO%2C?g zieoY*MtZr}u7n`5=>WjxjnmzgY0!z{1<~o024!lyMc29Ry(~?YM@dDJ1WA?=GWBoD z(0H}pw0^0%V(J9IDLO${Cv>e;M?e;0}mrINMd1);udEq z4B9n*ah!Jy&{nOcRLiNDVmqj2T$BNsA8cesD&b(@N61Z0fjFqU6ZN>yL#+h3=#!WO zUFJdH0)gz{VuCK;J-*ueYIT$NQTnm^{Sh;*s&iE*QVqr%h%wt=wXpV|G>lwvY>vk} zYIL;D0-92rB0a`2JLP}mefwbll+BD6+;!nFpN=(RuSnlv`&_WEEdsV+{9X*r16*9hCZ8elLk)6 z{1-xB99X>wzBwqgppydPn!jsa5VK5rk&J2y?R*klYE`O@G)j@AQo(N`6=@x%8fUF` z20l-PXMk@lMdIi`6Ksv_N{uBVr+?lE`vlwsp@%MaCnWoY$&MDU>aV*-hx@W%Vn_-T z{A0d*-i`$e)FVmpr6r|iXm5XV(ae%h{tf-%q-^%Tm$i*DL4|y7c`lwgGaD?N#C)+3D}JfIDcaqcnvai98UG#k5!>CGRTP_H6er2-FLqx$1-9QDm^hj^ z)^HWkNy8x=MI28YT8v<-{dlz~PksA0u$_=K{p&YG-b=w&QWgp98kt8jqRJJgJ9MX2 zkC&n>bztkjjlZ;9wTS5yS=ORhP$rC7^uZ@XOh+pjttP+3p~9jaA}`q{PkK2fiuAFXph&QWd!-09h&Q-T;3 zR;MVti32J6R*f3gu9+k3hA=Bf)_t}rEj90E&*^wJ@{OfyRIVjmGu`?K&q>d)k5Nz7 z4vE~>eA5hME#NX> zUT7iDqt*%$i}&`1H%{Wz!Gry)4t_Q)g@0cl)oO-K_I9bf9Bd#u$>SJ zLC7+Fh8bX%&In~Gku}_MRtcN}EJ-PNH4sotcM^bq$owsW1!s`52s8np9DmW$iH*!+ z)ZriS82;~Lck0wt5G6vc)+ZgSo*Vv`41T23uz2MNQPP4-41{bjzcF7rx_F@$SnLf{ z*1z#;d(k5WY-OF5m?4B;X_3LEu|1uY;PExi89lrT@MPwDZGB_MGb7zG>s}jLz%b*P z!k)^j3vah{H`!9w}RquP#Cx)w?OQU!os($60 zzmx2IDd(7dZh*9A%JHFm!MxnA9SA=%-5gsV*1_YAZInJ7Tw-k5?Bp8>MmW#BV$ zp(qGr+(?UH33UfMR*IOX0?8-QXNJXOGm#La&YhShUNP)B>o9md({!?)ytlv&4WPL+ zFtqj*kH-v2-DkCvnwV}TQ~mcH<#^88?%kllF~CsN+=I-hq87gg$0((!jq*g@UslWH z5K!X#Qk$;3R~h)TI^!-6Jx7K2%ehCDcy{zW*gj~w8M<-u2!_0O0Q5J+9P8Hg>q*e% zqE&Iy>TnfKM{`jr2I^Jfx%&liKN?qu#JT|4HU>fe-3hxDm< z4d8U~-L@^O$uIhF!%5 zyE+hKE``{6DqgC*H~LgHebPV0_~wsdzx3V^19XpKcSvdVtMP8R*z`dU>tXrFeuLcP z8=mjuVQ$8q^VhZ?$Dvzork|!aAB@g)KhuZBooCATj>Zf6HxOW9rZ+ALf7~0ih!7fi zXJjM*BV7COfN+*6riLm=;eg?LnMr@yoKe%P< zpK+HCBtg^%{`{RB%!=Ny^26IXxp$Nj+rKcb{>IF!xfjc3XmC+k(Zk_)T-!A$^2KCK z3{gztY;=E7xY609|8?TC*)SVsiwx9!jt71hE{(hz3h3Xp?dEcsh2%E|o5(~72+mS* zn-pLvhZF}IP}3%1;|3Az6NJQtHI#jVEB{mKJ4#Z>7o-X*C1y&SSIG*cRW)Os7Xno! zG#O0jy?_A2hNdl*N6bM9QSQ+2`GYm&kQPab?j{J=z|DY98LF`<;jUm<7T{pcI#S>u zCELd=K+Z@Ur6w|a__TFk@W#Js49IA;qAUVRe@zcjSFi{)kZm>zt}4T3T6s-RU%j+C${vwAvV!s+KB~ zg>u>M<69d0P3&p}(o<1w1bop# zm!cqj&!bT<3j66>S zK~hUi)uW+DO;}+}n7f3ePvd?Q2|<&EAW%|(5-;@KVJt$eX){XJ-Iq3D6dud(n+8}W z2x1P?snd+mQBy+YNgr!}@Sf~#Yq9R|9^LZjwIk0*|!tsF4YhLA#z7Aa5=7tSiPcomk` z1@NOqLT^Ma9(uSlTSlA9i<1-}IgD(s*te3I9}gms-#vrwT6%|81l@4ugG4b!n-YLK-W13thT=hlKfvaUtQ|WG;MWb&3a8H+?nKLLvyd4! zXI{p)>nrN4@LrcAEN6>)9J=Uw)iS0KltpsuB%=&)B%m^eDQ zSa2?=Av%{Jof!Rrj_D(ggUMo?I1Pf=W5gineXDcKc{hTm1uPZ$7^*hya|m@_)p4y%0=hkxy7qsUGzhccwY0Mmq0Kz=9YfQ%M6A<*_A2{!vhGEMu+Y zqfC!9wm3Q8@AY<|oITj7Oz$$wfBIORpH+WIih=A3dT;I-Fp4#>H3R1FzbPhKX^$1a z531?@gm(_vD#;)axSzt1*g}6h?Ny%*J7&b#pn1CI92J_sCHRdPqz}Pbsz_Cutbh!; zfZQ|)rA?iT0+V|kt^w?PpT`0RBQ8Lo+(Yn;KCLINg9utSJ`ujZ)ACj~>i8jQ8@VK$^=72rudO41om%{yC<-Fz$JPRMbc)XIuG<0W%jgl}VJhi+hsM z&nhrvP53wFi-<$_VBb{lbz6JwL9K9$ZFxBZS z`Duov6F5>XzLyWsW@7o5ZJlT3$xFg5eYRdh77VyOWu_N*8vB#&JmK^rBDqGs)Fa(t zxMjVCSW|BQOr%0p$|#Ejl>lTfg1`D^sEd{U>4QHxTE+lFL&=h;GGh?;BLwmUtWuzp zWFbY1*E+^#4}qJh_e9zTJ)$m2#|&&=ss;xKiuv1Ki0OYP{jE7+An@i5#}8of_2v$? zsYjs?bNq6m>=BH{;`9l)gL{IyQ;P8@LyDb-xx$ceqg*~}3}Ow;+S2D(x!H|7K3?N@ zZNr5%b(t0l5srTCywT_|s(y>bO;k~3>OUoR9)fkJu1JF%?3 zX@|3g!Y4HaJL{UF_`WT&+88X!QW#oVO4lku)vUm9gAMSS(IW}+qq5U|NyJgUsiDc~ zDUCKYnaH%%asRkTgXowzS&e<2zNgswe)zB`rsH%qWc411=^pA3c~|rHv#5%Y-B@G(yuym_8*g&5x`z!Z z-|Jj6?k8503GnNBdbQa!)1%UC@*N-6D%?Fv+kZ@up4|E(@;th>Qncauz-kWyM z6M|$CkMFJe5PP`Vc~UytLgv035kPJ$=v5E6Dqe}jh9M7JXG!7+{B;NVp$7|$d_#a) z!)WiH*zy~#dF3Ymws_Y-4r7+I0-B_;^J1XWS(mc`_pbTDO~Qj zmW0qfOM@j6Skx&}Iq-Nu(3A|r1S?hp63WrKP)3diE6$)NzHoJHEuv|b2EIv}suy)Hm^h7Oh1A3( znPyc81CML4=NCZsG-!NKLEyfAsq5G_jMds;9ylK|YZNelc6TdzJS~+_iy{Hgk4Ej^R`bPU8;rSu(R8NV32nDg|&)`+;4sYQu>L+&hRpZJ2j$=SHpQm^?ywYp>)) z#+T*NG#iOR^EeSp%GVO51Wj)Ndy7wp(gbRU8vcKhVJzKv;u~%+N;Z*dNE3 zN3{)O4Cunny<6j{+ce_=Y>L8&HGlT=Jhx~kFBJe@O*fTo@NdT5<}suOIFYwC{Z@35 zw_M-W7;!bX>&UYjkyk|80am;`F91lV#OLAfY34;+VTn zMAVX9QMr^6SgGQYPB{_mC0s_A_%`Aa8J?GnUS(oO;!3jN7Fazj4*YcPHLW`D>pyc& zM(i9DWwo3*YR`?Lh63(JHdxpH=nln0jDygz8!tO8h@Eh^aLdwn+?I#<2-x-9o(0cS z0~^gxfS=x<8NCD%)PY?IAntb%0fxi%q6* z7K@uA?;yuDX0XNY$cSyQR+_2@#0@FHhNOduiCinLeV8<5J5hr;)a1;C7#%J_Wh)g{ zw3Zn8Mwg0zugFz_|0`Znm!zDwpOm^wev1b<1`|p>3V#X zJDuV2y`iKlxtcts&Ny8eCyiFEPn*iE(f!&d(NdF%$KfH3>rrgGnRR~2CvDqq;NTS@ zp2G2RwQ}Tdxlz@TyTW!9Pmw@_|1~t0IMVLx%ZSVWTj6)pQCrip_sgxF-lm_I)90Gr zO(~Y-WY*$%DOMHs`=D112IGm*iaxx42W=mvv)eGUgncYfoI+)QnByNYW~!lI_mrCC zeqjUVNh&)6OOp3RtOF&Kf2v7Z<3-I-l3!#_a8aV&Cu}<~$skdO8JSyh(D-3izB)Fc zX#6-r5Kh?jKO*SUU;Z_T0Bi#ItX_RS*6o=^dF9dBPG)xY)v+UT+2LcypuGv9n@z>Z zrfH(v3+OSy@>%O0nX{E3Zh=TGjE^SR^CsRI6rihqTTt2S_?MsX;FjT-1c%!Z)h61q zzAc!B8Go<~P)uPvBXnScV$SGNhEs{XK*3AX9s1LH8xK`$gam}9C-0UhWwOt;-3~tQ zfSl=%uSlG?EHM0izTx4nwwho`k(1(!F)$SxsD!=>U6u8;FqjO^^P2+Ri|sU{ze{`R z0;)EeQBdcgN57LkigK0*Cjbi)U%nrWi7!Vw0?f}uhAp(4CbGdr z`?PD)HZ7545dK!~^ZuxdEj*KW*AB7^c?&@!0A7vxjO6HDxQ$L>XMq}xoJ*-YU)V~Q zf41i9t{1%;Bs|GaY>|6+P%bi+IKQxoJOj~`J)Fb3im2?uik6qX*L7Q-x^$z* zv|{lyzh$lIyxwXGC|JcigbvandDF`^Ss- z7!CqMr^8FKOI~&oa;w#3q^o86uQBfRCm&bK5|6#a=3K{hmvZlPc56+fgC3f`L};!~ z)Nk=|WB*Xclw=WTpwKSJqUm4p$q750{hrX{^ygj43i!2R?h1^prDMzM`?zsCr?lKoBuSoYHbMuzDq1rg;o9F9p+HAQUgBwfyG&k{z{B*HEe&t^aYUV-0#U12(o-A$1O8-M|nwx zoj809^k9DkJ7l34rmv6-kiWn^o4l1!<^`fQFF~f=$r0j}bVD^XW3A+cD4)T2_eG*E z8NO*=@TtFXHuDdF2J)1U5J4(ZgN6_}PTXF5L(F}*W@4wnzDXx2B5~xc0lV9tXJM%7 z_}SZ3HQa{gK3}J=Eu)uzjy;^Nt2K(#RkvG>pi$hPDc&shmFUN5Io*UtO!qh+bdTA- zhc)Xz2Ewg3Qz+kKcS3_%lYOu|V@=QV2W;m|++~6)(hf1BPBKch$q{7L=;p zxh(s@uTH1-9%J}T>4``5u|yf&*!^;OC1IW(Ox2)@qT5e);%W?xA4dblum43ol5^@O zeQq8C{J{~XgcUOqOJ;`!KFCN?0m8cN-o@!t;5@hqUIweABJ|ZYFoTBa&IRC;IEm5w zwA@M2`~0&e^M;Q5MuOMt(-5#Fvu%wYDQg=fUZ`Ok9Q9g2NA}$${LVjOjC#^x$cVA; zx=~mr#}F#Ow(U%JiUK6Cmr<6dDLI;}S#sX}SsGc`KoL8>TEIYEbK5b(kz&PuA3(?r zq2^-K(JXaMKelOi!`;|CSV{h{QW0odZ{XQYHCT5w`mjnlKn>3no^L^Aio?~*JHXHJ z;6;ejK1h`xRZJ-fO(Y1V{704_yf!(x4zk3S7MTxiXrAUK2p$=l;G?;RJPH8DPiTR9 z!u0j?z25(#*Ybr+29_VZZ6e%L5 zLPR;m3_W%4W-y(wD9rhZx~d#**jMs8ay>WU7$GDj;ZgxOj41}RjxI{aQ{pC(%iXC{ zEfDobM!K2)j>!WN><5G%MD91jSC=nNr-7G+19Q-eqMXLsLfG%)x%m?j;0(*U;rESc%Kt ziK@Qzg>NeVoZhLj&%eW<{Ld$-=djKO@8zCkWll%h8MNQ=1*!{r9e8iq%>*%FOsqT{ z%^pDDTUr}rcbT}14aNy_CV?pY{VHRAa zVUXLcOpGHHwBwtrImu<1hhq-thiQmpjbKkyoJl7LtdcU~Xy>rvN|m$OM0qi#P)Ysw z^2>%r3C3asD@(rVb|=XvNC<3Z$_9akKd};Q?ou~MF2d7z5IqC2V}f;aQ7~4ot8Iz3 z1#rW_U^Qvt4+j@?iM$uh5cN){m1jEN`(O&$dT8*n46P-nBOf~|DC2ffQ{7&cTOlB; zrncMxgzbbA#jv8 z$=>=9u)->QEv(iz$2D2c6gDeS{cv@hRUS#PIejx*>7!~phnadvdl~AjoW#mt)%~%P zozCvE0O0=)(Y^IWK5$uq5gg=YCUXF)dts3agr(>e_ZuYG*f`>vDWN3>9(cN{QsKH& z~ce1{d#jtB7!+?n#*FQzlSAyGLjgp2Q(6X4CL3Ro1t`Bhwii zPTs9oIWP#JCC<^~a9moo$McP~ODep+OCK8hkrdxMEh$-9+ZGp|Z7do87%F4X#rF1( z7n=l}MS?{0F(_!xXbC9AC;0C_6>lRk_4LMt-_b^*!Hoa%2y8G${E-(*e zYQmJRS*#-1p=_eI>V&G<0sIcdMWyo3n+eA3Em5P8ko+GUNPSI@JLqdt>JS{$AG{|z zIHruc$?ofyzeBN8FsHt6ugX$J22PEZs>%^Z<$k>lR=HhNip-Z!OKVWS&wJ}ydPaa;d+fi9^Y}VK;jk_e4K_)IV3SHnsi;FHBhB?3^n!iYiD!3pX?pIc z4XT11c@Uio(LS8C@*20?A+({QS9Blmc^4ELZ@fR`{7ChqO6z^m#2cB@g^G&^XB3i# z36p<4aI1c9%tQ(@J$Ow?9V$Yzf!9bSE1AsfN*FB!1uQ}SlY_=2459_q&qomwqfHr8 zLiO~8E&EQ^`a=Tq3(~ZdH0=vib+9b^gFyJ~isLml$M5IpBJhikN%p15$$lj9-5qun zBIo^N`3Bc+<=g77THgB%0?Udr5WU&(<=Dt~9jpQ)ESU)P7(`JKij}zp;aM=SfV1K>kmU z!pdScI!nMF)724N0ZQujbT zs~zg1r(3%l4E>dnngmjL?P~sjYX}k%Ay*4bvRH?Y1-zk+YLVv-FY{-T}@c1x-27P4m z7ag?RRmd^+@H(85wryKBhS8%f+2g#KhmSqoA;I_Yg4bKT5SQ)?;s+yB8LPwYP-aL% zt*(eNP>d$z(iyhsHX4x{EJITyScH1v1uZmwer!eF%sDBlsbJDhn620EdR?}B z>MW_uB(ixMBnKwA?^~Yz4S{TZ>674o_ICds)ark)F-oSc`iNSXTj@Zqo~pR#ptFcR zq~1>+TAr|0(wGzQ0aY5QW-IpzGP~DIxzJ2`YW@vY1_fdA(vnF!g*J%$t$R}*>=`)Xwg&K2OU9NImq@!~4Tp-%X}-4JcHHt@nlhb<9r!kl zoq@OSc_d2U9UQCHes-2{=xZUV%SS#aDg)W*$1zRNp<2RLk#6ykXcv4YZGGzO)naD7 zzL35v5b3N=TlxGM;X0$V17^jHpSN7$mLq6jefY>Ic?WG!DL)TC$LK1$Kh(UEnHq}b zvq}Uz(kx-PKhm?05R%5pmJq>*{DPudT9yf%*sgU??|WdQ-uc*@u$7Ir`_D@`TAF(g ze{ymh`&u@HO||ObVeDzdZDUKzcG)LsA8zS+;^L8^bJzw#1HT7ix(*IC0H>a}I_?){ z@w)6fQ!D{%+o#+_c9!^dxnUBX494am!Jgpz4ecq4q(Ptri~DxVJ5stPFQxSpi}mH? z4L8}H3-iFyN!=eg`SF>rTAhOn0ckei zLKrJR9z^W+uZp@FT%c()Qw{R@Ud<~O`pWIy*ZAYSu=c5jtsqYM4zuPk zVEn3M-|2mPO%V_N;tgAsT>;w5J0dLAP-lYDGu&Ie5PkZb+2mzC`9P3%>!X|?nzwHk zJ)q@&hANF1sYWy%2BK#OIYIM9m;GGZbAB$btDIsZa=y1rS!MR;l%)-yTc*RN^UTWZ z$6G8}Cb*4KXRMOxhBWy!Cn?h{j`J(&krXsmhO(SiSIbjht32A$PVyDb25oie)&A+c zY37bv2yB4BcmY(!zoEbC=^zB2J<8NL^)$a#;m+^4aj_W)*E*+pH2h$Jg@_dhN>B0e zH+L+u(})7UV~1)HZV!7azP7u_N-_p{alfJ>sa4|`RY@w%Az`r?8&gz+HI;|{c)hlO z%v{6z+-4r&%?etZ7!FZ4pllKB)T|rke06m>L04RI=or%#SD~GRpdfVq4**<1qrds8 zPWggLy?XHjCk7{6zh`zKMKL=G&p9!zh+Xl53UDApE2TzTUXic%LbW=-n3xu|)ULO9 z+spGy14FL%QX)($i}Y`5i%h13jHOeVh5##!$-@>yOq9{~6S+zV{>AD3^F$)o3Y!zu znHbK$cT-}HM4||Za}}6oqoh`_P|THKtH4DuC=dN<|ZUqu|W#nl5#hssnUn94!*PAea`rIXVnlVi`+CFygoPov;8=A=o2{ z5PlX+R4&3mc*90*FqYR?&y@wFlRtxYVw%SH%rEO$$!YZ)(ax@^qS@hFG4c>?QTe>B zJ%cMSpFBDA3H36wA2_gX+L^V#IcPGc^s~?ol}3%Gv?P><6ArtrrU9c4PadaKLXDl4 zTiYcAEG5jQymL7AkGqOb+IPhitkb86vKB1yQ4snU?n0CfJEC#?ae)Szd|I=_aP^+Q zZF+xYLlb|tDUdQ0#EVxorygv5YLkC+PNdr79aCt@DXH<~CxT|!J!@jnd(0a? z@DgkmKIRwookVN$Yr-~LFrJ%V9k$yFE5vo&-B_OVm`-`14D8Fw6JwK_^K52(sNHEa z>DiE4{Y#Ce+=#U(Co@ZJ3{5E~mtf-c`6)p3MILaFfl}?+fojFE3W37mX6KKD;Tg&bnZ0-@>=oKBOc^L%aSq^rMy*g~??=wyVh~$`8TK z`Psb*oX!jL0aP1QYK>l_(d)FiX_Uv=SfJJTj8F??P(#T$Tf0u*rR~TUBtnR~+s|T? zBxq@@vCmKJ*N%vjkcBMjZu7UjiLj=V$#2J`87*EyToIxp{QIjPn_HNyELq%Jubi0? zvX&|H%Ia2jhD)ceod{V3G)0w-g=0$^m)=>;`-WxBTQ`>*f6ENcz%LW^$n#j{c49hc z!2Apyv>G*3s@1F#D&<7_C1iG4>?L0*6-i+Y?$q!C(X;>j@-NFz48Ea`*0$_jzTlO` zNAB^z`8kX?Hx(7WP*`;MMLIxyqBrq8-GFUJDR?2%zrx%cYe?#L8VpvGR3%pwsH`ev zR+L5%P$;4?sll3C5Mjd#L>BYmp>VrOp}`-pHuJPQ+sqw`E3}zTz-2njsA8JtX*_wS zbcxKGOH`MQ{D>i|fBEJk8k0=vs82OiJSGt>vB}70pgx8EdXE}9%*mQ^#iDcfs$q_y zhO1C6d}A4_P-zv1SY!3lcrr{s824}IE)FObasqu zOe*Z^a6`oM#NNTN!n}uw{lGXd58RlwA10ebE>{{fs=QpRWmG1VJ8TE+?HZM_ZP*TA zFq<_Xm;o@wUWyV@3CmN5zs>KiG$0}ot~4RJ^>xWIzijen;*ihCEKR*DQ(v|snL!>U zz&B$^1e{B98zOjo7Ch34;s{>=Iuyjttp~p|SX-6%DcgN%d>ZjXvl*+{Kz#MzziQ{u z@B)aik@SsOyOxacH7(lwqggW_!Jith+4SV#oaq%kO=TY4TzqcCXG>((_PLXP^67Xy=S-@HbUK%xDkaLoBkC1;!Y6_v6hwphr@ta2t`U7%^d$k0sF6ABE?F?WS>JMbDRFTXr{(5` zX)Eidtf^O7(6Z{zQd8RHtPZ&TLc`;?A21RX%y}Y z97h<)-9`b68u^-Ctg}rM5S^_@S&Bf z)OwkG$S9#SN^Ki(Skq97(OuRXKbwq^Xgu~QPbV^gW_==~NW_W_!bc(W_wDaAk`XC5 zs0N>q$Cg=drtWCiQZ;{Dx5+$>|I`Py!i*i`0G&fhCLlXy4&sjSSIY=dC z6#5RiqAeG+W!4`s8;BxtDzp5T0SFnfpuf$Jg+&22xKGVuGMg{@c>g zv7e5WDUE&;b!l*M*Hj|nuy62#k$_LdyscBv1DRMnmiu-*PIY#TgG4YxoQzYmj4V$o zk&#>&r3^4^heRq>tHu5aYgHr-Sx90&8;x4e3w_0-l6~X#0;H2emXzEeQckyPr`?lY z{+Czyt&P`pmC2|}JNV!7|NHSY>e9eK-A`Zs@0-N^uk;SwM;mx8Ytalwy-}}|uu|42 z#fO3cY4|%%W0Pw%DkY~-s@imFz2>>0_koe*@@R}wg|tAUhbl@h)k}d~(ZO~IT}x&% zc`%%=Bm!4isY}GjvVr}q;QdYPk7i1d5N?Grfm887kNn$@7)6RJ7WoKOcF5wrcTPDO zmdE$;{Z$_Z)eTQRSE3Jdg%+B+w1t14|LY$o!P@Iq@h9;dzx4gBu$(`;y{Ye|(_~#^ z=s8TU-I!hqXwJ^5Wa#&t-|Lmiyb1uVUY2B(GBOTPWRsapDwVY41}dSBomkSgbxb&)fZ+iqbaF)Kj8zw?_OaQ@S9f( zue=6S3IVTgGn&kLpbTc#1CRfRe}jJw{trI@bAJfE(8*UrIh@Qtg#f(7_rX?PKFAAe zsavrhO=Kk>JdyQ%EH+ziZob{=%+GN+^dv1h^lZM~?sMd{>5jotpa&)xImpSiP9jG# zr(8h+^<08?a^2GZB@PXB`yO)T}s_{`<*-X&R%n5eOIxbpNcm^MiPJT~-u z{4*(xALKws!EX6REDBStknV5DG~kEE0IUBgBa^$+N5b1$N0Kq6uur= zbc}e>Lf#jo0^Y%W@PCS)Mr+RPAKZFD&~w``-W$jCfg&)83woV)zum6!BSxt~8Vw5J z!zgvjd5zuZM^Y&;4;q(-28A_1F^o(?qW#0>Jt6w*x~%2G3ITfjNm$_*_7hbk;i%1} zSY_uz4a8OnH=zx$KN%`Bja$M6xMjQ$CY%}m8d+L;?$JNYg(LTDs^~V z3FqTioqL;K)cgE3*FLjo*Zk;ld*#&R{_%?&{Zi%j2l-T2iQza||CI6zsg3MHKmju+ zqY%U**-KUdN3DLw@Ktcn2mE|~p%h#KqVD(dtEfL=niON26!=^@1vxpo0>-P>Azc{Z zLu&R)c~nrGqm#+ZMT5mlt%K&TCCO;eo25zAjK^)xP=bVVGMf|QMD-U#OfX>w7X0Kt ze-!c>TL@7qIubs4g=vC7mZU(Im?ly?rE*EL)2mVyk1I*~n7(sw@55xVm$jAgmly3? zP%2Oc(&Mjfz@*vn;6AQfAPpX40H)|xio}vj39a_x4?4K_S$)1&@6}s!kHd1zrAqvc z(OLgRF@{A7MhN>51w%^&}N7N*tR@i9~Qb)){lKk1LgJ1CCxhMILQK?WXta4;y8k#$xV`yJZkOaYa%T5VG@3J9xfd6yiiS%^!RIR~76kEn&Qd z%%)##1I02^0jdVZO;~aaYD&Pk@Rj}Brd(g=$V)A_r*HYv{nvRats!TV@6?zjmF|}D z<2*Auu5ZqhSVH-i;a>fOIV&nA?zwNx^fK7U?`PEV3koYcc$Bgk&ZvxP38Y-|wiWBf z7%Ssp8P@9`VLi5vDFrUDfGbvERI8Tex)9~0oN7v{mP-Lm1F4)gv1Z%<(Yutd$((Av zuqlDU>PUTntpmxyQ%P~@?*SYAOGXR)AOUVa4&w0FKne2Tueh3UKJfXS_8f;%XV9}Ui4>Fs zbTUTiw}f<=#^HJYkZ}MzIL=*X0XDnE zU^418YK7b&hcqLVVasNa8RV?iC};76%Z*ybt^@|V9+n`u9^H=+I;_>uS~XL`!u9O^ zEMgBU6;frJc9s@tv@UH#`wQ(0+Sj$LlGB-+wMwm0Zcmv~Mv_TBEG?nlmDpF-_V=z7 zs@r8gBod>TNT2x1I(MRR#O#zgym5Et$Rweo6D;()_;(%T{i6M^4*s27=)>>y!rc#%&%L74L()iMlS1 zUdHSKP1txXI}W-~7>i^T3LOc6=61?eN1S!VcvoQj_<)O2lv1R&p~Y-2Rg?~vL}9e@ z3p2hhzlxO>cPrAc_>fZng>(kl1S_t|ytOh@E<%?rq@v4asrp8I=xnNKu|zCK07o(l z#C+3SMl4JK%ZsUi8k%w`DkN3b+;;S~<gVt z%}XAbJ$rX$z&&nUlauR+=wRLa8|Kw|lhck>RHKT{1tm4rFh2R3w{(%5=I_lNV>13) zW!A{-!Kl?)7L6uSZL`KCmh?`{v6bYwCroIH=b4npWojvH3xth6u71MoTifFcr?;&Q zO^$^cQz?H@cX_CROX1lA)FnE|>=bhK3qav)MJD{Bn#i8TfVr!7+vK7vnfAfb5#V) zW^%srJO8Rsm%@#G?lnRUs0c=QQ;&l2a62cf=-{}H3d$8q!zF!8mkXabyRI|Uk?Lrz zf3CXqc&r*9*8F|Sdh0$=E8r7dwz8itYtOaWvUDT$1?cRCG0VsvN?NEZp@x!7MoGpT zW#VqwD51w!9lW`^V8W_xPtCgV(2Zk*knEYR{PIw|06`7O$x!CsY-*tC`XYN7%uXPTqO;<^?bR;-RJ0ZTG)&%c3&}cdbb7 zDV=)D)<;^W>{^nHO}gctM49_he792;DdK^1x1XLp>+7jfkn=Oyrb5FqiRNBmCLQvF#u=7^^k%& z4(mKDw6XKOVg`HSyhyX`E<&Pq2*F5jg@hi;1$Ca4h`1yhg~}{tFCLuWMu%)zbIK*V z!Z+8?UuFoVX0B;XblKPZR^xP*O)Rr}oVKdUhzfS@gD33qmbnefYpZX$sbb}|y^RH0 z*7yiTRrFk6Ra{uyd0idGDz|_;=pc0*gER#QACyQGY#RRgkPJcwyA^*vf}L1ej--HW z?17QD2!M-d2hR$1?Fl= z)@T&J)8i-P+$T5ETXW^Pq)uI~oc4xyrM$c1-I%k_kJQ5@`EPnCd|OKbj@E3&+^AS| zFi;Fb7!b-4O)Pr$9(c6f4=09fJ(q=S5-fK!`rHfB$h(Yb1=`p`DoaM9<|Bw7(M=fgJ99 zHrLz7*!nag1>slgHpy_A8IdZqGj-h~Jw3N~1h|#ErcPPc5qLxGju!?SN*$`ac(AY` znnSNz@W{fllCGsy^LH&QEA3j+ky9SD=EOVWw$gyrQ9;Jx!c5UfpT*p(0aG|1SwmCG zLX^CUV9?)Xups*>LqE{ zq0gWWwtTUIUMJ*@4h;>R!@hWox}-<&UcpCOf$jb)_}*^7aSrSW>D(%v-e!{;^imx( z8X5hNyG!~@nKBLO15C!Ci!La3jb0ORcLrnlMkU`6KhsNPSKr*!8^(_H&#YSc)7o{B zl^vy%>PuCxZrHZzm$z4&DrW9@?H;)6fPEHdtOZdsAo0>YK0c zsUUv#TDXEj=yS|prss$2C3XV2P+##Y>eisftI8#!&&6_yaaZnexkP+lmVX;f6D**E zaP4|b5(<8D2vHrdM=S{{QqvHF&mCIv5&t#}2=ipYbN38$AD*Y;%sdIUfTjdnU;{x= zbrg8u_QPsYLlrBs?VQODeaB@o(})Rh9yiS+a?v}S$069vtkxeH4lPQFf0V}0DEynF zi}$W-UfEt~Dr%T>dt39Ww!+uARKc`u?*cnx2XqU_b%M zJEea3^z;0_ryqRu#WVa5_o6W5y7?Y{>3moZ%e(n`{EZ9nv*@9R0ATW%doXmB0~1!o zw;u%-xRR^ZsnmM2*`QM!OeSpdnXZaBAZt^Nmf) z1iNIu?@I%CMCbdXcuYda_2RBTaXh+M6nmY8P%S9d1Mgo%Fs7@zrjks3G?|&#(UUJa!YdQn5lkvDL*`O!$1(Y?Uis4R3mdlgN=@Ope$Eu^M{%Wb4tPy=a{Z? zo5>Tgc|vyi;HO{BWVC$#N=VV@URLJGlWFDbX1UI)(dZP44OmDiQ+{cVx7=@KXp77H z#m86iAp%K$6?Kh}lTZv=gj!C|N~A`KQKEJnLz75q6&l;8CY78fkV;OxRBFy^7b-c4 z;YP8N(`m7i6QN>)5XT}GCeLmHL_(Oc7{Sq5z4&`ltmKrqt6_TF>}n+VOMGitRcUZN<&HgA@vd(nBxy$ra5Yq%SKI zV?fv7d%VFou!+)xRoqyaQYBTXfK;nPl$K(&6s?CO<|o(6)mlKawCsXX38cSaQ#$=Q zsAUj#?NfA}>NlW{%zzl&B4y??Tc1x^gpF>I$lA@LYVHqzC@m2SP6+iPC0>k6NC^6) zGXCxR2GHkdb&&t8ejqR~0Yg&$kNiU)VH0?nah&1z|B}D!4Bb7rOuz-x__u``JVjv9 zaSV($W5YnfQwLeL$dg8Pl7`>)eUX}2$?RU$$naigVq<-3vW;I1M}AF(owf&B9zL{U#@Y$)e^O*8*Te9i zz<_!Bt=q?cL#dsiAu?fjz6pO6jOHhyeebCDO{3aJU%&Z^_T9tncaLgcDO_LlHTN(2 z()~wYzxj&x-NWs|{jq%0nL0swEXwVi(W%Sz+o28WklumxdgP$|EGZYHH)x^#qSK($ zq1^Yib+-3WityG4#Dc^~<(h1rHr+^J@X``P%-|L_lCxg7VYnC+SwF_!dTiNZIPcQn zaOI#AtCmo$q-xNL+TREMZ&PQcauAD5@4cU`9)uNzDmkat%O8D}-%+=W6cEC)CDdLJ zWU+L#EaKNE^eTZC(JN_(+xL!Y-}E)tZywby%6jxG?!SAu{qD@#Oaqutmr(CxBvj7@ zWQ-h2Sw@Z|6sro`e2J`TxVe{Q&gw@9cp-Sh$^FN#9Tq`?7xpxp=y5+Q30i)JPsXNi-w z0usf6 zoDDmF*yc~#2i`^5xqL>L`^qoz%INk@U(>$%iuT>Z?W5)RvT%LX$bB+(ERtCMw7>~k zIJLrJWhHWnL@Jlbq(Tu3gHCFBS7CjZRp0|RrzBGGUs@UUFJK5|yN3V4hEt-0Od<#{ zCW&2Dv?580R%|M{1er_uYeuS8Y?{4Zs9ND5RVzMa9EU1rTv4@xtfBA5zF#xOX)R#Y zQBa5SIhnPnsH7wq^8>Q0L#gvyNAAD#_NN!!6oTPX7p^q6s@?3tf}IpSOl0%ahq` zO|1E88dWZTa;d$%G^7o9Cx<#GyQ5>H?r?ouec0%YIA-2r`L($|`Zr5qzEgJA)O+WX zz1;P0iSGp9K>^&|UVtj$)1Z!S(IWw7=L+wYRgu zPWxYM=FeQ69e-il$NmN1mqc^L2!0~cD{M`QkqEVDv$KehWMD@!&Q(-cZ`A390QC3H7;E z&NON`oMX$exx#4_;1yEVJMo#Vb(5&0j4}2a9eyPSQfY){XfQo}mPA@SVeYkyLbIo~u75IXvqW9f@g-do5op@^ zHFMngr8ypT{a5I+&ab%t&f)u9E9xwe1S}n+#|n8DAu#qh2%t7>HVvr#S(y_b2w2mw zrOyM@$MG|0fS?TjE`mitOQhj!)?j98Az!tPl)CDBGDy7|)%=ZBK4v)X`3hcH7VouMPLW?C(p6jkGv!VzyiHlG{@ z#wI-tup*dBPc4q%rURf;D8C>ilnMWoSY@RUfXtS4wIr?pXhQnNHv%)lZN9RAn9~2Y zJz`mQ_P(aJ{Dy@ae>5u+UUqhCbBFMS|FcRBtM!Q)&E;O~IN14@43)xX3roEM>EiES zFK&Ne&+vTYu~p&@2)NVF*-%rcsdvGmS6fR4j}_ui3y0@_tiUflU&CKLU*fM(*I&V3 zV+B6y8LdNvTtM+YGe+)%{q8q0z9BUMEEqF)aw?g}tx?PIp;9Rbz&T=7gr!=>A?%ef zwaYXLwW>?5nB*6LOnO&k7)$K5W_W5yNqATf(LJAXK4anBW)V(u`eSTXAQdUo6J_4j@aW*MKM z>tp<4p$>B%@PZWw@}z0_Dp$tY?K-2{>sA>IUIQ|yjBbxc>(=VE?mV5T&RFO1zN2;< z{vT^^0v}a%_K)9lm)Yme%$0eJPTQbqQa0I-QZ3k>k?G=rNFBs1=2HSyqh^8e#0Yk3K(oUXvz&WPNMaG% z69_oVUX+)FB_8Vdan#f2O!XCORe{Dt9{6%~w9+ZJdQ~A+`V$1hoZco?SPLuc){5HZ zKn9aJ+F0UcH9Cf3%o7ju@}oAr64#~_L#kbPuvmgpn>FFE-|u!eI!lxWmThb-smAyk z27fQ>Zltu%o|3xKNo!BtIEX!~?NjzfU!i8osG$fPQ%DkKd@C|WED=|drj2bD6}BSn5xtq`IJOP`Wn!vN-UnOLt1Q98L? zs_T_Vwn=wONoh)=Xp{CAP4cRDWCWpyR${)NI5!fHXTVBu$o?-<=(Q*Oc)&eY0cA`2E@O_e>doAUnQO@bUD)bG{Pyzgd0IhdEl%p*0veqmkv5 zDzn3+a+q{FlLKX#nVYm{A2xss24FC-+&EBlU+)xIfp1B(At_jAMkB?v{so%o9)7NjNOgLho|sA0%2|7@$FOe9)A4p?D)I> z#rTdXGA!yN=kjd|W<@LA;gY3ogGd+PCT^8ag zIA1s+7Fenr=XyMNZF<$JFP>c(S{V2u2<{F3PhehX-m?qb>1$o2d*TCX@5!IR|4v?B zMgINCC;9V;){KO*qlkzRcceI{$LsL=3qn?#-DkJ^ybc#yJf}j{<_fq14v|Q%P^cXR zPS=-yPaxonsYPMRVZ_)dM6Ac_>$f|4<%;}z4!(!;`{!_#d}S;PPZPu58C>6ZJPUfj z@Dt6YPw)kx*-l)!X^Y(|P)GSGQSah%PO4DQOFv&q%cWwG-erx=oi$C}_4$YQ`|mS; zZUi!a`U|cqIK6v#B1HYMq_f}Uud1)nPFx0Gam6K3y*6N1HLkg*E4_uDcXZvlql?}u zZiyPAIhx{q^@US>^~l~|fNf3Q574d4jbDf5O5qf_g2pSbT;cr#Yo?6fogIHy-gsM{ zpP@s?%ZXyhKi6S7(I&_VmqY0=aqz)H8%u4_u<+-Mu0d@)YtBgn9IittYuK%ON|Wu) zb)^JIx-szRR0*&kUjp1+09;4X9~KDE?CyUd08UanUw{4brju9j|Hq;3j&NE^Ax~QQ z98pHjJY3I}VzpMKEGqK*U3Qns$Z^x#n^6eZUS&01GU_H(F3wIXtvw-+GH}`NxuonX z$#8gt&{Fxea4j(uPk)VfgyhY!2wVZaLW!&?fZ@b?U4!hDoc&PTOpy%)5W2qO;x#TW`811Rj(Twm zl?2($uEYh@!2tZ#{@eY%Hx=zL*Ous`1#ao_FBt1(-2(>-!(&E2>#ht_D7Tm<{7|ku z$~;AA;60)K;me*Q!sNiAQng{697q{ldQPv^0S&8{Nc1eF)@gOU2==-orHWbg90yoN zt?8DWM@eYJL1reAy<2#PC}|qwq+*SWN32z2hfzuKR5U1U0&(&GEv=ebZydbgf#KfU zi2?nf1wV1Flp;ZpqTf1*wxWMz2(Y5ME>f&Vy zbrCpywJ39;-0?4-1-;?#LXJXoN{#};4#K`qa;o3W4CVLR&>jguDUvV0{qTAO^RTWd z*NfpM?4mAmB5=nQvFF%TF|qwvo_Aw;{w*laBhL}q$(Ij9Y0e?ZB!e%24#{<$AlG#| zUapr$BIq;brwPxyMb zaJ>wFP6gi|1C2iouM-==8fcGAqP}eOdj9jD;jgcQlm^g>QZHg$|M{s>rIb)GGASV! z;Zjzbk|-Fdgn+LV3b}-$0V5JIinDUjSw;>Y2-KDs2daY;PRQ(hyHNQSG{0udhuItk-@%`%Sc^%e zpf-JPXFdi{n?!aEf{k+X_|iYJ%(?B^4Z+p7SX_!pouvymy2RQ~>GQYco5vcDqWeH)zd35|l7z_>jXl4$2@8 zN&wA3l|-Zn&A;_^+XX^71oAv-uhMK7$z6?fRzr4Dk5f+3|Nx8J~+^bmXox zi}LbCcIREsAKydpx|DtL6Z-E=l<*JsxWN}5ePT;1Lp4uJEGenn zSQ{Tqg`z5TLBL*CT&Q&f3v8vug&??KqtfJ1yOOa2)wf*LVbiV$%eJ&_dUL@X9WDy$18j!702FeSTZs=iiq1`8B!E&+*D^ zj#c?pIs$(`Gp~x_H0O=a&!-XP#`8K1!cR}1#yv4=h&?YrA-0nc6AXA`Od>HTN{^AH zgp)9kWho7PSm5Yy3mv!6-7pP8J>Y`#TmfxN{kcY>_9Mti@^WZ{DS(pZvFmc z`^J_Hf3M^Jq5lPVeCF;o?dtTO07qMm{||jI__gawpT?L=v!?pUQ2!D9`N{mxpB#lU zgTwmjEJ0r#CGz`~pbDf2iwb+iGO-Bio&cf1hzNyTM94p2W#D}=O-O)PA`!6-s4E55 z-jM4mfzhl$?02U!Qb8?5%g_T=U?3MicGL(xOxivPeX{cM%5$M}ZJAys>Ak<2_?R^Q z3amPHiuXea*it^iPu!box0;Mv&dzZfyI3MsDAgLV#_ZFGSyrs^nHinTsAMN~TD5K) zw9|ANomp(BRGgC|C7j=+Fi%>gG8yIXCNBHjs8EO!62mh7C-w1pC|80%eHuPJKUZO` z&crgZDa^;QeuQ~>JwOhq48WA0ueBCXra%s6O7>U(Un5hYrXrxrpFSkZ08U-rCO}Rl zUi>wWIrReh>le!s1H~qdf{NvG{~pff{^jS1@_7zJSLHc8gY}b&tMrp5 z^^=OL^ppJfcgK|Rr?TUB<&VcXVMDo`uqpaX#lO&JhVt|oK-8pH(oaL$EG1?gEXBU9 za1mRe4F)}alh&Xw@_35$luv8&XoIxU?`tsV8$uz+`^x5$Ow0qa)dW^}^SX5k4c@MD zb)m%WE8E&o`7R*xTN#&+GZ|TM*X*$7$%{Wy=%Ov9bwkrj)CazJBKXVOV-*hG;gjdY zul3NSvp022ZGUs^466O|Yp;G%6w7mn0UQrO*ka|x!m&ssSdKkmp5kJoAI{ohwi#KE z$87c)BfXxW(Z(`bpTFB|>2_pdyNO(Em!En4l@Mb{ioC|Vcdm@?7U4NhK@`h5{jLt_ z@x{p z_hrZD=g{&xMeV<^2S=vr6sX4v_P^Cmjp-rmBOKQlnG)CF$LBu(YI|@b$NM2I=+f&U z{XYcN)y9;SvRYMgxk@IHs2EOVQx2&uIO!?dG2wX2d@?MtpYZaoxZzPp?Ui_BZJ1O_9VMTQA2l`KQ#`(Z_FS zFa}C2*e=~TsiR+I9wZ{fttp2=&uP>~b+8~pfB*;>fl(dOszV{QmU74($AB2>M;;(L zWG&ERl7=Ukq(8rS5ZjH)v)1O+^sy(@sy zPlZo`teK>}b2r{Qhl@rd8o#UCHEV5XXZrI1@HTtqZ+>7-aB(Q;?ej<5Dx=y4&>T!3 zcxdeda{?42rhhJxtlbQrc;+JWQ-Q#Ps)%PriKE?{CA;?g0O6G~(x!)L?4sPpQ@XRSCyGvn>BxnMBdX zRQGgKWEZ+DmA7?af0wkatE-#$ca=sm|8b@KdBAl)nEt+%pBI61b$R;UKgHishI0Jz z_y>{BKz@t&aZR3p*RNqdg4f4|>nA4X!TFLfOx_$VbysTsgfE`kE%;6O^7B3%?Eky4okG>jNSQCLnzuIh|4VmXIXd$r!6 z*eeB8>&tH}+WR4hOsq$pX|`<1^rk`ITPb{R5By$c9Q^rcibmf_j)Nyt@{}3?z<=#H zYMV-t4ElEFWOAmM!V8b_(Au zf#0nqbVMPl=)D!bl!2R5YPozqs&`gzQseU4Hp#W=G|cj!0qWXoVr@&?L*yl!co4Rm=hWy(Ux<-wVl51=*kGMU*Fdk+w)-tewu)KHPk#@QZiWQ3%AZG zi4W9xzD*?x3MwhUnmxLL^2={B)#bBOK2KFwG&#H8L4HP#EL}ImP~Cz640L|DcR;72Y}b{)hlVf^iG@uf~P3<=B;ice%6h#=gq#x}?ir z?_4_gNM#rQ^0JjN_s;NyJw=79zUK*fV-d8`cps21g>iW##K?<;c=944Cc7Z9)8Ic- ziWpPRkZ>m`Ac5!W4`cPn)*UAs#!@Z{U}{%VVxefC0TOhZ=&V61tblZWcH~@z`kjgq zXjfI>s&ls%{s405^jpBN?d%sI_SKuXmjA1~vql|Yp2ye#t``VY){iRK|9Uh`CWQ12&PYeSNII(Y39u=z5w_Kx(Mz@_yY5(H58e{7Z}Om z3uqI7PM^5#+*f_@j{4F;UZ%d4UP?wLcOg8K7a(cejX&vcWALL%NC@iR2nF>FG+4;U z-V^KA9r=#Z@l(;J$Zmrn<}(flznHw8mPS!QzRR>jt0t?zRwrZ~pWi@&p)#bgD)DTAx0w zBoKfAT%O}R0}u;NCMej@|Ktg2Xxj2tcdEKn*EKEbY@gBT)_WZmIT&3u_Q+io7LQKS zSdxl5Z3d@XU*3CtAJ31Q(+^V-n&d=;BTK6!oEutS8^UjB#;fwl7@+XFzH=%EGvEf4 z|Gysw!Aog+9SEi8q4*j4`{&``YjNImAyIMIW6~SP$%;b;J#mbzhm+8O{fG5NRd5`% zj4^h7A^OyJ;uG;-gBS|aLNomjr@jVL2!7vHxI*;v#)2}tJ?7CG0|~pm#H&m9oLT~8 z>FrlupLmm@U1b4--dALCmZRs`Qy_Hz6_?UyGC8O3F`2qrG5G}XI9e<49#u+>(8oA2 zv5}YqPVn*)$9QtJluz*aEV!TI0eqd$LwQ1wqervj`8e$pe5`-+6&w@F)%QK&Re(%ThhBj{} z?%(ZCNohcl0E#VuUBC^v|6qN6{kgop3;>ty(sL)?I`ah(QC_!?i6E&4_${(=pgV}&%kg+zi0PgZ`fca3PfLou2&YU8 z9(am1|Pgdw_O^4i!uW;eSYE%ehvCiejT1$0e&y^KZNgVb2&oz zJ=_~yzxXk|5o2MX4l&J#DM}wbMrsHtp(iydg%<#?v>2SFC^|3>jEB^XHu!XqZq7hl zo&)N4j#u6kGx11LsGT0tFSSH;l5nNXgx0Ggx-C}Jfv>hme0ku+$cnh1c}4rUv}nq!q*uGSXrhFgw0sg{7&9qBJ> zbho5;r$0ei$`l7T;=<+2c%6lQ?eKexb1^Xdz67s(gx@DDpX5XmwE&fgAbrp@DS`T* z>aa*eLK4v)k~9(U=^$SRoU1HH4jCTfC;P)jsaW*#!x8e8$mLxTsyRY8z4zY4=Wo3= z@%nr3@p-nmA33~-I9`i?!h3iU_vo!jslT~5J3``iztagPKn^mjfW)-G(3b>KB|k-bht_&M05EA!iT#MN~9DmfE$48 z$=>MYb@QaJHT;QHeq&w#hVQ!l-rUV;6Hyq^!=OA^zE(7zPEuOToGpL`0Q zhc%FHe2|ukiPb5S&L5LW^-{6JZZt>?5~)EC-|3aaG4LK?Aqv5}hm`7Uc0Xhn zDVe2EsyFD24KdXGdrTq~BMigDDV+Oo3YCh$)x*H4Sp>i<3Pm92r&Yi!K7U+v4YzKc<-M`NYju}j(#>3V^G&NS8E37% zY4b||oV6Ss%kgmj-0;0k5MTKN?ybBkRW6cCq*B&sFza*%gG#Qnt5t-GRI-3ju|TPQ z&#n`h<+O?<0jp9O;aGza^&^JVlfa-T2_an?(^5Q<@s(n~qA8Zh+3b+G5YhnejX^E` z>L|e~x(Y*eQU=s?PZ-CW>$Gsdt4Leq>8~)ph%`xlmW&5Bxh{4~idQnRyGA znX{(&I??qVQ?IAj(=5Ke^Q!S=p6t!PUV>_2znv&W3EWLz*(sg^sal^|fs`R6TN#W+QR%I_;fl7DJBx?*7aoMLO$oNqk7Z1Y}2^{0>bt%+D-`pyMlu&Zjgx!A7k zo;Ll4{^+ju;i5Trj!b`e-yJLJMtUDiuYP^$M(HVusE=$|FgM;kx@4|v!ARfsMQgCk)i@|Qq#KB%sfjZY z*BFM5<|md($j%}%mM6=(JT==v;rb?#3I|zvzpYk?twMF2HXF-^wjbIs`;G;14eQZtRT&TNYG=`|5+22WC2}JlnOLAzT}5T0JA|EG{k^?x-_w*}I+Wif+5@TPqq9 zD<50eu&ePfFupwMZ<=3OJh!`6BMwbl+CR8dDsc>ary@1G($c#2PI9xWG}?cCo8620 zpDu;;vYu8DA)@LzLPQoFVeDF!O8zr&fzX1#LM6ol%2QHzkDL&x21QUE##;(y+{C#A z(kfmyS4y7#)}#+Bhr80;yy4zF+PTS@oY8Z8Dv~WH4D2 zN={PXDCV54$wH4q{hM{adi~YK_oqKiUrtZlS#@~Pwa4y%>YtA+FSm(F@}!heMP}SI zc5F|kK6Me?LwMiB;B(M&3?KCrdBP4eJ5GjlBxN(s+GIQa5kQ9rY`gH!Qma^ZNv% zVR$|(h+7Xwj1@9SzA0&u+UZmmQ5DFhNreJE>ILfGsy|fIgc>YRuT@`AQ|b!@2h^O4 zdxm?NyTCCVSLUsF9*O+Pms2{Osf}R1EbDQlohi=P=Cpm~#Sy5;Is6ZD$*;Q(l_X>{ zEY9&o>P1r#j4}v>naro)R1{iTXYZOdC)~Td#%L<-t@^9yFAF!fSgh3}O@l{A7o1vl z{%;`IzG~XEPOhl?#wXW~9Bijz5|vyGjUcNjg~}ZEA{VDK{*2S#wJSbnM!8%* zr_JBdR$px6ShPN)={@vA%nqm{3?na6gisJxa$ZW|bSi9Og+gqj9D#AN@`%AuT;jAH z0}Qk=BY;ZD0tN@cabicmn80O27$RPsL6kiuphffLvy4u}lRU+TY>^ZJJ``mWBV_=^ zYbk02#V8xgzBCtk^-6bnc;xOy@%G1n=!JARNLBbB|Lm#NDWkc3u(oS$tGia?*VV79 zaMjf{csZLLzfRj?Q08p%frJMkFJW^PDZpTG6* z^O8{Y=)cKGB8dS=yC<24pk%2hW{#;y1yOR0^g^Y#igZC<6AUWUYJFTkPWr~+D{*oh z$PX0`SWun6xR3Nq#tF!Wkj}=lVML4=I{~4PO`PHxvDhmjGlHg^caibnxKj&dygX_K z{v~}fu*9+Qq5s%9^R{^jH}{oFt6+5gGI#lKTV=$f2;Y9}hSJJ456$nrXKB?xwf_3T zwsu2l*IMf0i^tQiy}vQO;s>i2zPORG^6>?xwpb)cj7s z2Ip%g1VtKZkkCrNDkM}L5$0zCw=p4qoIbPc{K@kJ;2>z7_y_1AMH6397t?dW<7wZ- zpP?@J)Z{1BSCHQq5KB`&qtT%C7X*U70^n8yts7bv;b6!Q->Ml!u-9!@7`5Y|c+B2s z>XjaYH!2|*D7h5_0?rC<5{%79^$A4xF#)oUS8&y@#bW|b6V?kJWbG8x4Js%`233W` zz25hhhK ziU@@Vov7nr?69v-$BhG7N@?lS_eP-Is#GYH@POceT0B;daQ)#i$%L1M7%N|ootZE` z@q9p)=EKPj0695Jday5_&;N#Y4*(zR(D=&Cbq%SCuzcwATNDzh&E)hO?0T&-(HfM< z>|Mk2qM>@$RQNftQWqz#9h~hbZyy>h1!EJNDyN6kFEAp?Ro)&hY_D*db+4Vp*oOec za_BuY3cfWyM=+%Ch)86!Nr_`*9lT^F?T0l|4NAI9N%cKkw+*)?s;{rC$Gs)C{>FsW zFmtSu#~c)T66Nb5q?0V?lJuV9n;#lm_*D_B=M)?#7ps*4XG9mDQ4!KA*sVWnq%M9d z{Y5(cKkL9vEAL%gX%~~!2{GeZ_Opl9KX(vp{VlrJ1yHtZf%jcU%p0pJE#<`HWcX0h zZAIAbia@eDAeRTKlXNXNP8J@DmCE61J<3^?N_RCe4w$30{l-tiy>2L8&S9fD#`hq} zw}OC(uQuB&c+DblgDDpcmP z+Jm)o8UtM&t+wK}>Jp11U^Ba-)0Rzd`R3YLCjEkzfqP=dk7w%TVl)eF&^~Ohy)yZC z+_NbJt#WB7B(bnYjov*@x>E{?K5Uej;3I5-R-7%RRO&Pqy&k?}StGO~L@A|&vUKZu zL;eY9J0R{c@fzYE*qGy6dLwJ@l+c?ai;2Xcq(&yc4iXxlZn)EENxT`hU!J7STN*7r zOY2N>o7?zLUA%TMnVz`rwxV4@+E_4Vbea@WBUBx1T9HWNsp*f$Ha;-E=9)Kt{rc$7 zz@2|PabnF+zPZF5@oGdA#rqM39+d_nNVKQqGQU^u^|EqS0ie=3W>@%)Zrl!(^(tgy zu1BsIG(wIf7!L{DN|LWaMsyp`rm6Aj5(am#qElN!MrgVN|+{yONTJ%j9dzEiOgC4?T6F9H`sU?v;oqxot8jZ-1Hn#NJhp8hB0}Mkx_yrOrZMc&E|W|q}c{# z?~y`=(Dwx4ULBJ55}11798ixG9$<%$FJvflDz1(|xZopOkxLV985$XC3ES!x?|xy^ z@V-Q>;;x3~YkMoa>TvVWoT28h${F{j_XHa2;!bCgUhgmMySb-z&z@VC)mqhOE%tEh z;K)$8rXylEc`N7F6;(I4N3hKrhxB+1?uiE)a5YCtf-1sJzexItQfSVF2o>P~^1~K; zcL4P_H6TQqvMeG^f=`(}j&iH=;AgT84^rmoTL{UKE}S)KlqHm0wc*BW=Q0|Pzlr+YnA!hd~r!##IxTUc2l#0)1F{m*r=Am`&f>A1@JzcMEP^jr1F8R9vLKq@|4nK?PDUv3Ys-r z28$3yuswW@3MxK8Zb7V7z03pJ@>zAjq{cRXLa5N=yZdPKto;o9CTU z^Oo3`yB^uzy1u8d?XHvGoc`bKotp=vWh1xDxLmuq)gSV^3R!Bj;M%RXH5Ip&*hHlC zk^RXfchB1L(fDRndMCI+Sv7C_tR=rZbYJ}PGwkp!-|Op|)!Bm2^5nGiTzWUO<)g&) zse*7cQmBctAgV14fRNr8&~rL20EN6rTNn=MH5{Xh6b{soCWU;A+5|bXcWqaKdspEu@8A04^__2aKKi6$!+%^aqNn$K z_2vO?2_&!od@z0S%+uStRq0#6HqB4(fh?wV;(1%Kp{rL)pXK>A^4BeaXD~r*evSx` z1GsRi0Hj7-YFxl-6 zr^#utm__5HEX8TePK))7-FTV+z-(_YHz`oToIHzMoH)lP`)1&jUWubVWHI!NTB-@0 zCzv$wy)YsGXELKqGR4!Y_AM+N{{Fi+g<98kh79q(+I_cue)f$B`L{^=J!(~Qczw-1 zzrVX)_Lj&uW8E}gL$Q5gi0*s+^{Kgr;7~5t5aAlbwYi5Wf!fc)l$7vO(ttEzqmGdl zf+K9e_H2)Rft|FwP!u&KT_fElC8bi43%!$>181QQr8GB+P^ePbEWl$|;s9lAAQ!7sdMF?#a*TVYufW_z6o=I;~A@bGuc>;v&Km^Tb$>iY-8i!d6kSP3tkL zRO+`qMQ;-X>-IF5ers%sz0C@hO$i*pI2Y&Z2GqZsIDR~yLqa*Fsk{l7A?ApHjsUG! zM)JhKa7i~6&(Z`%8i%&ba4UVO<$JgO{SQlPm(QEMU`=x&=L-}Rmp67sNzwDQGYa)G zdm>~Sq(ZUYRdu0j)~%jdMh{OC={wRB-|Juu&yf;O!NIpftWQ<+Ux5Cnf&&tcvg!>jw-~I+*&W? z1qmvlO~^ssGMIar)02f3filV9qT6#qu+wwH1B*^h@CMQCpPowbxxc zvy>c|c#5%3d_{`V?8JlhbFYb*BQ6fl86dtx*T6YDh^48x#bPzvY<34>FglC|htX(q znAMC_E>$Sh@TKL9O?z6Z785i@nQRTFCPkCrjl;Dw@l2N#xWzAH%ZEUXI+;wj6ydyA z37d_bbGS$=47|A$Up!p4@UGbruItkoBJqsgqU8~E;6vo!Lg>K91~*QdJ}bQe>~U6_ zdk2NXF3|Sdn6DM(=M_(0%+!a1XQ%SJ$&VS4Xd}G-bA0`$Ja3r1l>Ug`kK@N(DfllS zWn!raiD|Kzq$vqSGmJ#qAQqu+j8ZvC%1}suIxe7x^G33kZ@re639mkZYUpuZPXhHB z25wUPmw5UBU@rb1jJ$(Eu3a&w1u~6S(?=(s4K~e=Tg$?h z^v18vftjDpf$^oJBn4=ip-}jp8)F(s3YWQ@Lu*!fMGDM+{QiFcLwV`uOE18`CMU0h zd-DK%C57K`P0D4JS>z_ORLLq-v>q7cgi3FuSecAqrLHmjuY z{SmpM$tvTwix0Kp7ueN@7YS()xe#%tah{iD#BM9PFD+CZD8!c%6m|WDa8p$x7Po-= zsLF|Zs{hm{iFS3i4=kuAPo5cFPq*&>a%}5hg)yxLR=B?xjGgy<4n{9sdOofGntU46 z-6|gx`~@6mXmuYR^gvl$l;0IsGb_iqA0HITl5@J zOqz~JToPAJV4T#abo!cz`ajfAqkWjAF0g=Qy~!Gs;-H*QtbwFXolQ2oPCLgZUw)R= zv^g#Bc^k8VoDm$z8CiEu@ILUJas*Hc92ti;V&)|hue{G#h+}39<6v;jkvYRRPIG7y zBc17Yg1|huw$Z|dI_rwMdxxurUmyL=@4Sr*YEkDs~`PO5PI>ekLbp)(sxfj*hU$M zug!Uyl56%g`A3)F?;pqcN2MtxMU#w}5HSRxQSKeo>O>7pgE+U)^?c@0E)y%8cl6}% z)1AM+2-(o%=}UCur5C?8?+CshMtFrx6uu7-SLFhsSQnBv@P5ohYsv}DSuHS_IfGVf zl4x~0y@3NXV>X%04U#4Vabh{hK-Cg?5g`fi=T|tpxggh+PFwXL({XfneDSjD8p1<~ z5?j61T~#-|kV^j<*tSI0e+2v>*t4m}uhzUFk?1PsH!;-w^uUo*;9f$=Px_d?0iIcu zpHu?ZaYM>viiBCESqYpjxq^WEPnh95vsQGB6cHA}Pl^s{QDaEYO~fH>2_l&K^<<^>lrDBTq2W-7%>e9l9W&kd`M}iUBIIumk?qk=pdz$s0ca1XJiWdiH(G~ zfLJ5Ax~Qz4K+*2&|5ADRQ+T4!fJc9S@yxqvHlrn6dI8eZ_XXZ!C3;c;D`7FSO10bs z;Qq;_N&@bcRIUa}mbIA9Lc`3`U=<+IP|63kho>}DpVHITtqo@bkQq-v0%~qr#AimM z>#J9MXGyU{()eY)L^Lp3xw&)`WH{hr(eUO@&-4IjPQT`@v-I|nLx}aD^+LM!@9=u{ zrh-zL80l1E_#jAxVIq-JBt?nDXJqmws9{M_Q+4SSA%>+jh@IsFa+(b7U@oY|N;3KZ z?%#fRcI@o?@aMzyrAttOy7V;Nhkmm60r%|%T#LITm4Jk*f_sOyjv`3FNK`5bWXDp1 zhB^b>Kp7xOxQPn6Tq1@;&ZzJL;#uY4uaQ57hm_}s?zjDO?;?zPXB1< zx^^GQg$)x5Q`lw<{Oi^E_7)K10gW%5V_71qTMO_;nU77v>Fn=3utr@Pe)3&k-ypX;`1*_+* zipSTjSu&%Dv8ELh-;Z@IDd&npw)El?n8jywZo4qQ%9I?>%C2HrB{NPMQ>;O$vYl3| zmAOfvDa3@tJps{p>b*`;w1+nQcuhG}w7+?1L%ItvpN}THubb{w7te0{Q9l(-%-%FD zv3kY)wlIH#aM9!Z@?&|zKwjMnd3A{BJt8fT7C5+ZGJc41K(o|&biQ+qlXQBZ97##7 z1!v(>TbpI$z%bSWHAoTa#iYb4)kvnd7xr2REE(Q$d1ONPGR(Ys@LhjHSG0!pMbDfaCMc+LboHCvzETz%W_ zdv>fJD6u&{4%#Xf@9a5=?d~_=T=v4b*r6;PNGa_mv(0R_SuI9F?>+|YGZ|3`ex5Sw z^)idybXHFg7Hfm0DR!1?_MK%K1>jZeh2f=OJ8 z{m&6*Qk4o&dT7FEIJFCyB%FkEnJAt1ZL>tJ2PUUeZEE70Tpy?#a=pNC3@)By-~!X( zP^bc$)kz^M6l+}`G35q0ze$Ux`gFmn7FSETfs1#4)afwwMZ1>cD@4-y!6`D28Y4dr4p zJ~Q*+`yI@aL?MwPnhtqOYmSjt0%cnS#!5>~vLvdZt||3W1qEX3IH)~j7>FX=`&?q8 zvGQDQ6SF}@1bu_bam2VlM|?$mkbtoZA+l~;$+3_*0@_Wt*w3A+)`t-fxNwT?|pZzYW&5UuIqO{^4NE8 znWvL0AiMogEQimEirHIxiyFha`0$pAKg4D%S~B0?Fwk_(&gO=pQUliaguF~n1a9SX znkPS|{)=XbGNLb~aH+x!9o51IVQr9+HPC|fktsN?2ow=fA`B!)q&`DQpY|A#L$0p^ z;Gm%=Q>0s&!^GiXH=w9k^#@&4rdV!)*?&-xt)wW2iRhPbzfbwgu%atA<+a zoXZn5tDPp5!TxKV#ag>0lgn7bJcRR6TgEIFnT~)nY$Uuyj10kzx561(2qP#Na}4Oj z1>>OTkfvv9E@~F^rxFLmb32axq*DSjAeZ}$C0L26a^%iqn_oL_ET6gUhpQUrn|0gP z+nT*0Z;$KRpKNMK|3w>4MUz7q2{$ch1A>yr;b1K&90Zml6$^$?}oOJ>*m`; zqF+nI9edtQKOAg|+oSCZmMx7enBB7-}Z@P(1!Jg-SF(5pe_=%#1B)mWPzYG zD3t`op#q7dz~r_ii;5D(B{sLisa10tvYJR#*Q%Xvs-PA=G$l!sSau;42!%?51qCH; zn^sM84yTi>P889_CA|`@B&DR?M7Dm6!a zAe7$gEplpTkvY;>oIVO7;e<_=?gLMYbzWQgDR7g&)N7PVt)Z$QC?PWPjrxjKLF;Mz z!AS8q8BR%MC__|6B?}Ppet|I=`7QS$M-tq_I3bi?z^&yja1?hzt#+wV`Y5edd&?lh zp;O8q5I-eeA}E6Ss;tk2QbwJVFF&g@wK;7Uy**br7uKO9(aKymymRQlWg&oa9YFcB zm#s$>@%{yPF<9i8Gd!<&Xmf|#QoE${3*V;$*VGy{#WP!bf80aPIyQo^&$eDy+@7$M z-}YaJUODoXoKuSJv1+?1~e~IWewiCo0m5P34tc7 z5tbv7!$_;mVSA8I3vmvV^R^Pws)JaYN~$@|sM9OOGAU`Z1H0V;#8AG%ht#3d>m6$H zf>_=P%AbY>FEzdTOG2 zTdHMX>E^b#DO1ng>lgoU>okMrvYm2E#o_*&`qn;nZJS?(dB#JNU()m7KG=!nsWQfF z6w_9-nG{QGq@9pyC0f=%7zjYJ2BVSInk6DqEV9WMhPB$d)jwx@?I<-OE`(DmaSIr{ z{&9W-^3J|lFeQ=wmK0N7D#$V@l7c*z*gHUxm;0hKPwt7Sy!8!jL)SF;t%Z?jvNZ-& zK2^_tV7Z+%e)&lHJ7D9U$NxFDp;4vz5hY8kcmdc>rq4e95Qtos5CHp&y5M}|#JrS4 zCY2Lnk`hrAEn*}9iZ%v5u>C6&OHhPf0j($+k`i)k^$LzH92Jb`5mJtX^1{+Q=r(T* zllKm$Hw<1ld7%^B*?ImXcq%;rDP=pj3Et9``CJGu65b<=-}hqs{S2S;1mE}L-)}^H zBxnhI-->_#HXMIF)?<2dxF-0F&BZl!GWhqy%nrDR2&MFn=Li|;NV&Z?JGCloVTamd^R>8!)oY4(cKT%JEcp&7L1c zP&td%S8LL3pPJTphI{G-C>@+@CeXZ%=uR1Igw>*!C>4OPC?wDvktmc(s}5%Zk`gs# zWf+UK+r}T;1db}4%evpW%A0nwQ(Y~Y?1OStlb5waP;X^&Z@a(#nH3M;CzVDol!!$Y zmE{{(^mB>}C&9)oojv#8>FqE4;G(VCP}*F-Zr|J_JPVNDd@}iWoa-DXmZhvArOgJp zy~AYE8k7>bOrkB;%Lp=Ftb)rAQl_H5;*f(>nj~6@!PjT+mC5CCLT|_^A-H!9^4YzU zi2v@mrZPX*UX$1w%gevzp^bETYKAwNwYcyz+?RECvB0LUuP;?Bee+J%rmahrE0(^6 zQH|tdBAv6MqRQDmUF52+srRiO_S_A3sI#-PX5NNMlFoa@gBm?<4^;)Y)_Q|I+7dH9 zvY$MG=TFcZ;QT9z>r+XYJ6@6~Ev+mp42In{rz2?7gd949R#RG96;I5C>Q`8+(*!pggU=5sy^)6o*G2K%&AgTE?&PRL7!+pxqELonU6wcB_ zIi7YhS%atj7G9?6suKCKH_=ONRdqtX?5$Ngj{{JquODqoG=N|H;_@L! zywRr%x%H%Ii7}|DEQP#UJ$Zq?6VA;HZL1PuRZ3qRD=7*Z4Pk>pAJ%I1MfxI5%!zV) z?MzG)^jWk<1##XP_W2ZW`my5flA>PAc}1on7$3pskt6xJZhgmej&G!pjihBnR@U2- zbDq|LtLMz1;(-HwH+Bb9{`v*qn%%Re)g_f||ESAQRuio+;3TFZzpYefl!!v$#giZX zirf-xU0CHEoY`9!aM#YRur>S1d(&GOD(&`HP4}sMPCY}d@RW10&_f7z9Pja5C=)rn z4l!azN*>hfLhzy0szgYiHaS&r>W~+tLry6DeJYt&*KJAjS)~afS2=Hgzs9FU=FgJ4 zYL*U?T(IrH;)2Rse*56E2S-aJqFc|kn~F-x%Ns_UJX~}}RsXUAg+!!{Oe6Qj`&U-9 z?ONO7Y`UgXSLq>lruU%A`Lx23X!M(kyatsx=B zfziARj1pX6ppI!Pi?+-eS>H*8^QgOB)=@duG0S^4%V=-CGm) zTa<+X{+V3*=DUTx*SFO_y#EJVI}3Z)wzSSj_aSXQohlnzcfmv)WBr*c+}Qju2kiAaN;tl@0vov50!YE|Re_9ijMw?^vzEq-^lc+WTi*TQ$j=5( zo^PLD&{AD$yeEAc{K;50P`j$qR_*IvT4Mykf^v_R(XZ~EzH?cfL~>aIHpuOX;AiBc zu9$-b(2}+lr*ipt|Ej0zRXtJ>vIf*YX{c{jKq}P$1$S?An zi5JO2LIv4CHDMvdpyVjx1tQE0QmkQtf32VNUmysXp@+&fdd1v8onsFfb)5G}<|9q# z68fl2Lf`gPI|skrIrHe;bw9j5X{{dJbNm~_k2gkL(>gjl&DYPY){q7-cy#k>r{fKW zJF(({CAIhLxovs9QFm6UXT{$7S&%azOgG5m-o-Y{Ogv{?bH_Xl-*;X?JO9YfA;mo- zI6Q6!<}dpu&(W38UM@%dUsB0NqP(TNT;HN~hHcSs)Th^KYkYvE&zF}p*4ML9Ti8ka zN?NpfiZy*!(i8a1*PA@g_GQ4zQBNUbC0|{Nt3ZzPg{B#eFpF*v#Q6#U#_i6T)d6Ie z(n$d!k;*jo%E(9ZV~NtMn0`&GCp>4*vRk)GMb|QFsWt5PaGu8Ij=@EhL34r66DY9Q z8s}{4x1}2DZF7TVPBxgZS``k4agz`GNz&K6uy)$6b*<~yT56mmOOjl2w0H0OyL#L- zJ4Q0znpO8-v-bWu0a`3$F8?Un*kLa!CCTsWBHDm6!<#>$Yq1Xn_4(5m7ex!Dk|K#j zTx55M#CE$_Y}Jf|Kc`B3R)V1cd{7i=wU(C^Iz!STgW`NBBypaHGKZ!mjObN|-3mWd zEa@guxdX3P2!=2p!Q*udoVb5H%d|6D`;}9SWNbws-PVfuZkqjimZ=Za- z>%@$=XXwk#ZOfZo@zHND>{?cDQCKf~Dgt`3-fy?ZReFg;uC;+*q$?o{-w19`-^4K9 zhPf4;d)H6%)eRPxmfx0sD^SxJ)c9;#iD*Tj(qMLIm`pAn3D2g8`0G?`PjGq)_TeX2 z_K5w2_&Q9{tUt>cJcDt6!L$NIA=15dqud3t`rLZ zNrpIp>_oH;k^vflK>xBe8@I0TF7i>c5 zo*2I$>itYD;UtQoZhTkDVl>9R5yEM+My*ke)nK-0EEbK|Zr4B~z?UlYYEVY#|5w|0 zz{gcw{m$I7z4zYxYPHg?w5wIUvSeAZWHrlOlI14%jvKaPx-r<;rsELs2MHmB5LY2= zVwy1l;_&z=4lgl;5RyPp;t;~)fpqm|?%iD#obr3W_gJL8cXWMc=FFKh=gge*KTc#7 zu=c1cG)$^vfF$MX?QlH_Eq0`NSV+0GxXqQd3NVg(JRa}qQF8#{Y7Qhk>wLyJzpAs& z2IF=!xJBnG_ChMn0a~YVt^;ijydN<#1~!23qN}@L!^8dY=6%myv-FMyQJP*n;2$U* zuH}%Qlh^K2$J(kZx|~`;0~;gO({!L=apl}QmZiM)OBiFZ1%CK54g&gTBkQ}{5zniI z^jx>CqKs_Cbjtb@0WG3b1k?|>%n=J^m!Bn6zys`rijkA9%N~jSiUnFMz&{8LCnQsJ z*eM+3@vC~Ugyel)@Tm=*Kh(&gR^ea+&Hzf~4k6*27k}+(Saa_S+p^zX)hoMd9U~vn zWFP#!V9~0IB^|X6)2^O1}J zJ!oLGLK&@)i6ml$NTe6j3Xulm^x7GjUNc216Nx3YLJ^Y{k`6GIZ8`@A zHr~N68I_xUWI}_^Zb`wnTGCqq1kE?lU{L6H(F^{M((7Ov6IZX+XTqH@MfDi&4f+--W?5cM$>ur~J z;K56nJB=)l;YYrFov5yQqniv4zlS6)o4guU_4t^Taah%3I`yRIZ}}(BUo+wPBl+iV zoACU-`R7-TD<6-Aoo&zLb&GEg-aCW?Kh zc?ZDJ&)@)c^FMi(M9=D?)H?22RA2U4jex)o#Sp$hyhK&e_al4Xa+21WC4)rwDV-9$ zjO_oR^DsVgp2Ir{@TcYa@tBWP)r_69;|8<70wm=zUMH?88_zR7v#S@1X6&33 zH&{?jb>V_KRMQh5?I9NvuCbaDc2{~vZ0k8x69j4YDDfEFg6eT{_4MI-o=4U`>^M#% zZW_r~XS`Z<>8sTC0^usH8>`G&maUAly9UW9b&2a&5)@{sIPIh^=`0*18X2)KZ256g z34Y9xl%wiU=Iyut1!d{0lKWCcU%g+YRhgm{zCd|M-?sn7%_Cpr3oE^Wim;*mz)PDc z7HR`2x6$hjJKW_3W^3`(@RFmOs*hZ~nD4{&s1HZ8eJD82wfv;Thp9J>kpKzmK^XOb z8!CRZMl$bmB-CDwg#N1)UO%p_{tCVWglv3aW!TVm$4i^ZmkAwismB}PTbihkEXEzU zdNHSSKf+!lUw|2|{TZ(PGqdgQM5Ax{Su#8bTTeSXz|SxrwC4CA%L}>A2>;{G^p1@N zBK$w;3?2>C=wQClQABmV(GOg`n49~@~_%EcHol}m7JC;Bn1aQKgj;c4^64Rl|s9pd|{-Dc%i57o-f=P8!kq7Ju9f`%JXmM8xw$60iASK#$9|OlJk2$H>A2>z zAHg@zu%AHV8F(ez2?*Wlen~#qM%A=HTBXk(_-0l_iq4JClB$Ih?XPO9Fa$W4gv`3Dlsra+?~-n7zN>a^Ej$JYX)OS? zQi3urD(!UMZS2a$n#Z%gnkt;627mbIeWdxnJ%oSWAHuQb>NFI>iNFMeaLjp&1C{&M zpZOq;+~58G=I>tWly~FP#4ZXb!vLP9?3d=<&DjYR^1Q zE6n<_ryNS+_3@7tPVJri82?QOo`idzgwyRaW_B<^ccwYwZ0&5V^1I5KsyvBUU9&^! zFe)NU^YCl7)=vJBaQ2aOQni!xt;u9fClE15Yprz;XMgxis0a~ zFAwpaR9c}J8VSMgPq6R6ckdyL?8B?!Z{aV#!^x%Tyzlh`>dwSr=RK9mH8LA=@G)r~?Mz0wrJvW20$8X0;iB)Yl0Ju>^`G$TXKg^ogp@ z5@o;y+{EmYQ2>hu;S5F|Fxad%LM8}wN~FXfEI%$l(r@S&2_9WU^@3{Tmy^N-G$6yd z0jr{8!iF>31Ds2a5h+Iw&N)zDr>CM|s>$Sr)W>oreo zy&|z!IOkhxs{`!c9L0>slMWf|MKvBzS=b2gC8hA!@Rj#O=_L=&nSIOBsRFpQbU{+g z?jSA6sl)%vUllW(B57y#8=t@4gKQKRhYzoP8W1ETXF{y^qFIqh^2v0U{SWHQoAk}2 zqfkXGr=0Zt8X*w_k7Lsl>6c#<+sH5Iy{N1>R|aFmO7bhNERiou63fX?xw2%wEKY2r ztXx@1zRZj209+lVk}X*DX# zi{)5^PF-MuWLnB02 zmI!wR!-2J1u33=rPFph_Uv;CawVi=tn@ieN6{3d~)Eze=4TomjA_6Kz3Xv5m9+Iz@?vWDGS4B|0 zi0HF!uoBkSxC(plL>cM9j*}O&5ga%N8||)v*`0b}FdzA*gl^P=9p0~)mayCto4u^B z@drJ(-)F7pt=kD}E}eR+peF878eKJa6=gQ9j5IgRh_-QglF(TBi55@_T8#7+SgS8hm+HO#<(cZ%0+rlbUHn2f;qxFm$VA+Y=A4}@B`Intme=~&!d>{NACZ=73l+YPqAx58#b(qO0SGgCiVz3@&Yup}lY2;ld|l8- z^kT@79$o{+$}X}t#Lx|ql1sL-UF^s3DfY1-u@UYbd6H;-k=u9OiRd#I&2ulZY3jh- zlMa=}HVE%K;Zqi%uTvU@OM-@ZfDxBCOEl=?tHgIoNo6|jqv+s6#zi#`=cqOY`HGb; z4(cSqeUFXv1gzJbdn%bzw+zN=d=t#0zmnG2g8d8IH#K>*1=G`!ve2ws7PhXbcg~*c zEU?J#4=4R`t0bEA#VwNBcx52rkQZl+@qo!%+S?GeC+dUlin6rL*qVvXnHsegR(q$c zN@{(f0@z`&D)eTlGi=toZD#g>J#5#yoGy*YE!KPRdT=wMbr+(G3n}60XMh_Hp0*YM zpcM_mXBbt1Ht?v`;h^IZ|E+V*z17{jcCFo2{jKY%z1OpTA@UlzQ=a|%0`MEJ zz!Quun}Hl;2iZ+;#Zz5ZU6YYix=3?d2pc~4_Q6`IoJ?@HGA$h|ML`kelg?819kXkG zlSj4as65rTB#6rrsYKPYV=N$5+Ot*8ghk+uu}g`eP~`4xh;b4OXFQXbo+AZFf?<{1 zG|DDU{u(q1A2@L&YE5)`ZJbX2+<76$1%&Y*9@sI4WG};*J|f0v*(hF|KwT%;o}juH zsHL80RI5F^izugex(9}(LO3NHqCk7cuaf{QljtT03G2M`Rdd8m03bH5WJg*j1_+y2 zxV+fis`PG_zSu$2LS ze#)FhJAajeMPq`i0dEEg&&@?J3=RqDaH1ISu;0K3*!w$@?e_b9;G-YbnjdHdyq@z- z=zWWB_l-&aKc^@5nu`vF{_#FfHFyNQInhw4 zZnF5PWX=-V>uiT{TO8_1G;($9*_9!dA6_)XnLzk+)C7Eqe1!TvqnuS`qs-3`Rp0mS zcY!gsjau!Kf4nZ*L%~yBaV#TPFKcwZ`Tt?l5F zWRZh_!;<|`B0>y-$lpooM{&n!BlhUxH_OLKS~F1KXb-}`Q;X*gm^R_qmc*N&VX7>VS+XaM3b3`VH~C$IDSv)C0%sx&yPkjltX z*$HT|b9MGOP~{jztS2YpE8^~IECRY>eZ3T0HKJ?zd^*pXm&R7_Zo>v&=BX=M)8AI_ z#ls|A;}vBy6CHo1o!q1bX61qes_%{T$2djvjRR-TN*XU36KuT@(ZUkfk6I@R5ds#R zAt419B9x6RJs>RGDvv#r?AoI3k&bYlNhggEixMJ&!*`M=3`#1M6IfPs1#&vZ!oe^u zHaoct=2g7i9!Zh zQUf>5&5~~&p$cnBK_g)SB4)s%Gxcop8)3GhF?(Kk8C#mL8dw=tw~%(|w^Vs(=yNao z1Z{(vY&4g0w4tu?a+W2ddwKo}a;>Ox5tx%c!Qw{maCQeG{G~`^ADbYn1voZ_BdD7q zMP^1BW>6@mqhfI>tdB$C>lyL`Ecu(!#aT)jKgDzsdm&z?8grGmmgZ`s6VeM_>S87y zgt8NiX_k5b10e^ApDJAxRRyM@CHJajO$Jj;14C_b;h|C#4C5PweLx;|jFdQg?uH(! z)vEVRc(As-Z51SY4fn%-dUQw1Bt_Gkxy`x7T302lDNN(#i=D?&&Z_8aLyMnIPwmDX zmX;~aRNSWC$tfu`Z z2(yVl2EwfG2Cr*V1K%V$&E!_r2WC|d0aNJ1)+dHaLcF7cFxvmBWB^FF$pVG*SPKfE zB1(0F^zj*oEHly*EcG&zNW8K_fF*~LRhijFkOYu;r5H4-CK1q|M910FBBpeyY`7l2 zT#11;0rM?THpF9OYZEY})C^r-I!MA^aUz0@r5&Iz1Ij0KDK5e%v#DLrct6GP4eP5E z%e8CYx1^}rYOg^qBU_(B*HB!%=J*8ahV>qt9KGrKpf~!ed4Bv?>K|wbf?by@4Zm`o z*x!5R{O)tHZz0x%SVG;&UrfW6$Xb8F7nl$w%cC{?l_bEd{3Wdts35a3B9kK>@j_5B zty7U#(t{f^3E0ve?7}?wvr$@k^BRAVAPZcRE!X(nPofUQ#CLJ~ex0TGh_ABKH5?lr z;`0ah?v{i-Uv--InP9+%h{eq~LbsU)1d-Kivm25x>cxaE?J69_9Pgt-R z6XnnpZtR2$_oXCmNKcw#!IU780j8B>ET1inONz>cbSb~-ttg{DFC004%-V*7kQwd8 zh{+y5thc!z%M#bffbke~>@arT7zyr32$)GYU5q^QwrvhWGLPv_88Oky9`*yeJ zV3kr8wKMbc3*UNvWb@qCw)g3RyPnj+f>VZQd&3kXC4mwOLbf3E%?A!&!ke8Z0-0eR z%2TvkeU~67T2uyVdmq2SL1U25UhMvD7pxOaNsH7yK6y1p*RFFpKnu|H}kW z%y#~8ac^*4sQ?a=iVjMMiI%kp83kju&-r6M@0;2S@IqW&5pnL7Z`4OzgS^O&sugP+ z_Afzj$(?FsC`Q79iv!y;@BPzv@z@hViqSmXHAPQ96k7&`59x&{Wgy2MJf6EvYK)q zbIaC-2I3?udd7+kQdytmd3UuSE^e;=(WRtIQz^rxbC?JvId$a`9M`f{jZ(Iqm{n7c z3N`IG2}JA7d(R_zYK=C6H4P(p_D6NEW7p}b*atc&_9Tvg`4qP?9J3}ujlI&GBN|Ht zRy7eSD_N&du;k>s7exAoO$q&hHf}8pM+taVtS(`azB2d0f52tHB^W*^61*skMx{`v zlxrJB{}87hY~)II7t^!kcV8KUepoW@j#s7p$I22ibyYpcQX8W;u`mNjaPpYEwf%SEvFXB&GPGC1?Kq^|@4#|1}xM z)$#s)wZgjBGmd^&rqU*p>21y-Gg*<6fS*=g()7#vt>YLKee#};L0#T@a%=LIpYhAS z`xW^MoO;4BcMJUy)$4jvU$9TyqBcdd@r?vhh3207z&4*hf z6Z;QB)YR)X43qCxR66*2=BqaEY5IVf#$;ZkAo9|;U39;%J%+E~;nDDW9cA?ySk%A) z=Dz_Ia$%wY4YlyAwk)%(1VVRG0*HZe95MURVq3hi&jl*Nsg8Zadj%wVc7rn%A#(hy zm-^gyT(`4B@zggvF!TcaI04uA=K5xSL(9UxuF0NgF%_XmALE__A`IGa>jH)1G}w@E z9bdudrPP%nk9dKo2qm31!;UIAXjvGO1U&+OMd10sizEt@!k~aj(Vzxd#MS|h?IPuD z6=z4kGwOFCkI6;&%!6@UtOpff!cRS5bb)%+ZScMJ5V8a>h_7%~p<`=xFT`F*u9yGB zZ)Q#QA_GIDA!SZl)p2FfHAoYwma!T|57CIRzd2w4Fe#Hfi=<4OqPYs`>ZU7a$4&Tr zUT9GA)Mw*7)|QLrrxU0>1Sd)kM-5p>oJUwSO$Kx(PF3%$kL6fiIBO1ejy7I(_EwI$ zs8I=24a>_j&n9T>WXD~D{C36!pB@84D8qX&iTOHScAf*anXx?KvsMo*?JAqR?-5*O zD&rt!iW(|X;cCdYu)?d_XZjDZeuDK{zyg71klNz980Ay35p_HNL39tRlvALqio}CA z%$fuo!6{KBD;b6B0>vR`vRmxWj*bXuduFkN8=2KPPWx7klg*Fq?xebzywGbd6S zGYIk7spp9}Pr(@OlO#NBc-puQCr)0JsCOr!pS5gSEWrJOSh+E=DrME*mg|yC$YiJG zg7}m;!Z+*~6{M4&Q~@s=fz~1_5dHB6RXR}XDV;e4wapPQ&7)9e!iniFDdL1O-tpyb z9O#<3>$Lub2df_i;_&|6nJH;xI1>4RlDqdy@{KxDPsjBwWe0D*9+doTd)+6tm_QYK z#Ub)f4H47eGLN$$<*N;Y=iX~PFL$m6?)GQvl(njEOQc6*nPj=Um1B+}OOHdh2YYKs zU4;o%I#SvLSL6?GBO=)-a;|_m4}Q*JGf-io6yZCguwJpNkH`%J77HtS^)EoW1ZUCZ zCLHmP*ilVRgdK zH}}$5K?hu8Xs&?Y*_(%To;;1JWQcQ2n6l?TOfPQNmqHhUQepgD#0G$vX)1@9u28z_ zVZJDl(&AzNJKRC*XNXJ;Avw)_qE5~ICvK=r?qy+)x~>QGji!x+_-@9-KD-CkDm{q8 z5d%%fzKH(t-4lv|tCHS!XHj+8E3JdAc z2Ke@r272)DdOPjTv3+WIY+KPqJo`T@KplHch|#a+N{hGRyEo1=rxh{jle>-m>ZlEA z#FTs?z>kM!vVTuL)pRCy9S@hIsL`X6`->e9X&*pe@0v$%Sy!K<`d7(%3eb-B88vF-iw-rGkI`uUP#`I)8IRc(g%;HMbrMdP~k$}84H?;ixZ$g zHi+RT;3tAx=y18V%y#uOvvP~Im<~@`!E4*B;+&6-?z7RIwh!ARvrhH{*ykqUi~s)S z++5|k0P&m)bMizR&OJ~Mot>CR&X_`t;uo1$BV_fGGyF@bt~jrb1zl+&TB7Ph|s+ zbc3(k*o^qJZ)vv|`}fC&-%Fu~#)e<;hG!kUdyI&-0ZuvB>M~EdlXssH;oh-1=+Rz& zG1q{wYerhe?&%5hzv#Cr2dR3^Zawe9Rk5@gVoJ_z9-f}(&2ADK`b9i9) zSK9a-Vpf77NZAhcYZ+qb2loR#G+>FMemh2|1qq~?X7P;Qz$TtAc<7~1U~9_lxK7)6 zqty!wf%gXG8QUq5Pn8pNf!YVEazd;-%n2z%N?dT}&o6$sn1H5w=nPd0FB%sjW< z7++WxwclK=3%3<+X}%POw{4JHV3t}Y4XKUDI{UU0zeNR%t_}6u@JMt@VUx!no%2U6 zb4d(4cgjXR0;I##xc)InMT?cr3li(A=| zM=D2uBx*}rRd2DKAEcP;U29P0~kAiwt{JdDu9RaWI zC;Q@wo`)Okm@F9>?r4*l?7xl`v#o&?V8XQfR0-Z-<6`;zl=3Z-c6q&_+W$g}(b%cv zt2iy5beR4Vhj4~@!W+E_mPM>Wu-c0GE~VD_K(iQd&~2hi!r{SOHGc2ry(d_j-oMqmlx&(`|iD;$14d{ zsZX)qF(|G5+q*so-w|(C$L}t9kEk}pm0=+kgaXz>(GQ3QU~o~MI(&key8II*NJ*y3 zP;OA6!{mq)#Rvyzii>_3f(}D3umPc8lHh`Sv{!S(rU(Q3`9kfze|Ipf-pPC`RSkKT z)cF-*A6onmSDvA^9MIH?8jmrL9RB3XEnCN?p+-Yjg5V13&z*O==5q+ zp)_ec;WH5Rs`T=2%O9Zg!~&33!v$p9MNa^Vpf#>5Mm!L}4nS};V*79gjDUM8iS)Iq zXRZakCiSBe6!^&2#nf@CmoEi0fO`KRp3|$KDzS#naWfZ_l!$VX(kP3%q#`L`gC?@a|yF#C0%0rB2~H=X<`{mp)R4@2`%(HflTf%fsodso5G@O zWHI^kHIIZDZ7N#e$-=u*mQ?4wAr?)j7R z6N3^m#)dN)kbU?sb(CF1Swd;!N+OAKp9xW0R^XbO zi8ST9Lnor2)`&ZMXbV5zOxS~!GFBE{@-((2Yte63)y=o;q9IHD8YQojW!TB@?GIe9%7Zz*4MENVID*rftK)_ZTw)*##4(L;E# z_DGe&&~5`N81X1lK_1kyyGm3#=7!+H4#Es^E&%yDcz0E8iYN&g7E62eXre!)*o)2R zvFYv`9P(R#qjRV6AdVcH8YsJ+k%%L&$#Cp92;C4)Zp2(OUc6?HRIa0GJzWEvJ<6x( zU`GIJl+y>}kC|R2C7(v=zsjna&4@s2EPCN^YfD;^38oN(D$Ed- z=he@V!nP7AK_@P{O>fUUY&ER2Z6I`?X}L@NDtY9mJMKH3HaR?zbnMcyAMo;k4h`O! z3E;kmN_` zEa@;=ojb+mMpE*d;q4S(_gSX5(|YP^O7GL;*1m-5>300FA;GAN28cEwt9CWz$c~5m z)IB}AGNYk2*OYSHZy>7@FF$|%pm%0F&)2l=)_K+`;LS6Pc*U1gp$6&0g1^QvcaP|D z`2IF0Q2+7*R{AaQp(%hwn+CUAg9#@LBxY#wYZ+Kb6oCkYT-~FmW!Q-kdOroR^A##u zkDuhq^Z5FL`{J*`btrzl`|(!zJ$!VRGdSJB7L??N+#MH?Y8*l@)HTlDR=5Zp$@}*g zRMyv|_Z#C^@~Zn1z#aN8f=l*J!IPe;3G7x8F-0G#KnfMlSQ_R@pSX4qkUDYeZxAU_ zL?Wq?a&t63X5v7_WN04+ytE7bUWUr(kCxniL%H_*h z#CAaep}C+l&ldC2WA>BK%p>muNdDeeX2>+|z`qNRb+*~*OJ|ML3a2`l;>)%tYx<_& zN3j7GUKP|Q5#s7D6Fc3j+HK6t8!4&*#5y#RkRmx?TtuUy@g(w~2r(YB17*CH#lo%Q z;W}tn?xEdP0QneH{{cqj(g7>_CyQwR!cDb{`CjH?g}I^RUx{owcavQpKRK@!Gw*w5 z<`LgHF4MjV_zjH)l*6HCz$7rfz#Jj_@_4tHWN_OzNXlK?-eT}Pj|=X@FBb%7nZHqt zrt3FPltA2>u*y(MPFDgQQ^0|uS-fJ#hftLixkYaM=sb^aG@GK-?NbNBj_CLRqN)VEN?_+5cvO43ko`?EqbQK zQ)eDope#_M&4@4icr{nn=ljRAE8qBl@N++D>O(W*EiVG> zvmCVJ0=fdM&!C5F^PktF-;<+6R!dd5;`QVxFx@*k;4x9`YHF4`!nc90dud~jv|?cQ z#=nbSwd80%NHzFtI2bF2uyeTh<4?-nHgVyD=LYi?PUrabLJqereg$|q&sou#1Psnc z(jIBZZeT)&&cOfT$6w7p)b(T}&kPdywUiCEaH9T~sF-=LwN`9}jO6xdVj44DX$J+t z;tXUV*nBq<2+5LULp3!7=KM_|jMl6szF1#0Mh-rM42Iud8a@g?+40 z{fZW?O*CW05hH2}#?_J*HuX0L9A*?G%R5A+B1ED&&E9C84H2UUODCw!@G-PN%2KP& z5dGM(oSm_!gRwm9GRF5Zyy(BR=WmW1kQ`!Hh3&Ilpxc3TS>jH746xm64D}1oeqp-MALitacS~ID zUzu)v3O?~lkuv1JTU~YM`ow;ub(puli_dO*wY}?I@kL)jU3JSZ$2P{huS}|t`|ua| zl4|r)%$KR8)8{F@FYsk+5o3|`mAHtCV*O>LpmCE$&Bjwj9rfW-rXqE(E2e8QQ*%+t z`RNG85*C>jSOmkPBE8-Uet=)JhC)td`x{C5S5qK^;53-?8uaTX_ycW%e(|*IvO=r7 z*!P<|lu8}V)fc#Gs5=uco%?)gYhZc4gv`w3V=t6-)+63?8G>C-QtJ8nt1p&QjO~PbTD;% z{45m!@akaddHKe7%RCzyzDz>>SdlCWYErtmZ1j1wo4G^CZTCKbOXH47S|A-Il{(|$kp zTfXac)Sb?s!hg5kJ}ujomT9USr>osdPVP}|4Pm{Rx#eCSUfX|f&T}tv_*Gqg?63Q= zJ5+hTG+OmGy<3~pDk6-vt=m^*w+QtP(ke%*K-Dkaa-H|Rgo?P(*NM#_@zZ1f$yP{T;Z zhD4Zu__cn=Q9@qNaXuoo#h&ta{#p`WF&DC|VLI?AYosi8UZiU~6dzaLT2|L+x@h-9 zp~*?-8nQW|DYqua&7E89L1bt85{qjV}!8Le~y61T)k%ONg-@%OWgwumwF zXtOxMD#@RR_;g)v5lTlELiy&{@M9GER`)( z;U-r$B{ET+v6Zp*6OZ%Z6=~P{EqX=wp>E>CQhggb^ z+G-H<$yi@*_XyXazAg=t(U$`4AvMgl)RmrWrQcxcTkiBe zYJ19!_tqQOLR-Uvs`A#E3}(hX&);>>!T)tz)!-sUAF588XLTY?04){w%lieBfILDT zWngxXeXLb#yf2-pL$)UJ{hS3r%a){^SyU34QzU(=k03(>g-OB(OPbiUctR9& z7r&3dhWf2ROdp{oa!d*=gW>I@fZ2ui0~|Ben7FrBZCAgQV9Xw^%SX^vFngG1q>3dA zfUdN~8R8_+8Hh;I&;}HQk$^S4f%#f^hT;||W|SN$$A-}aouHGe`Qx5)TehRyZX{54 z-|6x?;^TB*qh+mo>(B0xe&>#8s#~J{Zff5r@3ESwG&o90Uu7TL-&AETpgLpLqV*Ln zD_I2=K7A;HvwgP7;hLD!dV~-Q1*)D)WudFScXw-62`QED=ESODY_OlJ?qe!oiDMH= zpFxh^f5bet*BpD@5*LJPJvxOOTt>yGB8!9&&*cw~df5{!@K^Mb*$qU6S4TjFr7ADY zTQuK=gajqW;S^*B3#^T=qgJ*p3#L>`cPgj}EDKe3p#qcfOQF5}z@!-mHL^X(y5R() zjllH@Dd5FGwwY}uWc^s*q2K0vrAs}tIM<$=TH;~E-lzLtQpb6JoGSxuRni#M{N#2eONv9jial`ZLPNHdyqeb-Mn16 z_6_u9rq33v>}LPRe-wqP>6OZeOs+b*MDd_-n5FtVDu^y^W?bi+>APD0Tao{(-wohM zBW7z;^c%p|E(qNE&XFJ_^3gbPksKVq28kIcjn)9xC`1P9hZTY!y!&>O!+?kv1U$5O z6yWFD3rAP#!l~k0SGpB7+9Suw#oR`@M)|q33>6@_B!y zt9@zx(-aoIcz>m>J-C!ipQVS!1Ow@u<^!ngphT{N7XPx*5YW;5_(*iGu8yMj^6;5n zCBN2GN7jFu$&fIoTTBW}8qsHt% z%5EDyoh*W^4F_*3d`V7x$y?O7miVe_k5y@GD7VzJUMkjqrwv@IlTNueS667X`W&;y zSDlZ0wOp+sy|XnSBcGsfp0>mg0};og%A6c>=zTA5zQK3g;x}QPN@2467|Ga)ys`~p zi3(TaNuoa`p(s^_o8w_56L5lDOM*U$gm@r7hkNJh5PTNb(Wfig5GduarS_C%Z~)h6 zW(BoA^zT++oiE$dLgo*=d-3o3UynBz&oB3o)aCauBIxWV-e{2FgA$6&{nd$RS`o$S zh=DC2g-DTW1!$5qW+q)P@)e{{hf0N&!7)u&+!q@b*J+KzC9OW8XNlaU|sO!{RKyukh)k4mqRNH3o*9WcoJ@M z{jfagM1nqY(F!km8nlAJ#z}>h7EAh)kwM?Q$iyG|!*^DYiQYgHnN>ImUIQu&KcBZ+ z^KY9)R+d!st=C@+^cPfH3LIy?H)Aew^QX3&L6z6>uTHz19hp{ErYkoK|+*;ltNUb14%k$1&E0+a>M|*A0OQbrGIU%M(^QS7RFAj46yP?IpdeN zH{I`B^oY83I%XASCl)Cj|9kun1k)aau3A6Tcbv*kRJqZ2>N$R=gyS+rW>axnmw+Q> zv1mbSToOp|Q$1+7w-X7#5j@K=_ z#%zy6rnlttgf2p)Sj&O zisx-_mr?qz2x7K)aDzE076}FK$r0o)jrI_!v>9>Gn{8D*SyQf~{DZI9 ziwbLBdVMytWpj4EdWMSCM6XAU%E=Wu^w=V<5O9L5Fi-`FC}3cP`^)I{<7e8c_OC@y zm}}j5-#dx@jk8U2w`u;?%7^=yKk1x@PsOLFTkZdvhBSOCyK9EFn0pG`BT}^#>%CVK z3MN3E&2YLL&Ag9_-*mqLfRin-$>%GJ@aD=aWv0ng%NrFs8V5B7Sz;Jo)6v#dZSEpI z23NJxaQR~!#{RvSTjj{mrBDn&`z`ih`*MVw8h?RAEtMuC%a`hN`B)Krj27X$nTN^ByV#bR zTF7VXzmv0ZL)FyBS5TwJG2ScPsFk%X6DYNzt~>F!{EVFhhqX!X-DMUzapS9Xak2Hf z-0d2#{hLfHcJ9irnnan~2dn@EZbg2JLN@Z-te(N%K`zc3{E80Y>&qMgX(Zvr?Qs+n zsh7g0)UC&Rul3loJ+|aOUVRGv5ca#4-A|JO4t+P92WKa8^^}oRxIDj(6l&?Cak)Id zcLa5{1bQ!6UUy=3`_q;88E3E7dK7(d3Ir^lxbos>XLAV2xRCb5(}cN&ofC@0UwPbc z($ZiI*+QbD9xNx+F~OgF;lt#vYpPs{lhrdAG;Y9fm>?iE+C+WdbPW1rPM$u!3JWo6 z(<6yD=Vhhh= z-tMq;PH-{4dz*ap-<3BQkJmYiX8aavH1wj`TPvT=dK166<{NjNxHcA zI|d{0<7qM?oo)*=j^9aaAa0wy3Tz&gpWj#6*8Sg5EQJ=l_!bqMQOhIJ~z`vyC(ih;8uJ{;+s(P0>f>$Npp-uZi%B z{)#{3-m4N$JnRs{^G!Tlw&Umg&`!AyKYsqoI3&JZnysn-cW&RG}Q$`DsB0^e|7$2KVCLKzlXly7Rs->Gjw1x05w1}m$657Z zDxSEe6nxL)y-7^<7dvMabIyPt#KNe+u+=eahF|OEt1pPVx{{~TU8B%B3O0=qk5auDXx}weYTRi zf7e`8p8!Nv?oN37al*3nu~?m1Bz1LB!F|vzRTixyV^sV{nlsl~)R5!aBf*sJlAtf8 zm@X0fAx@lFgu+Pj1*e$*c+gZbM#8py1Ao$FQk`kc`f+8+&{zbk?Y@RH33qiEt9HrC zY}-TeSP^R4qHPSKXsL=Q)rwLzlzD%;v}SA^>G7O%5|-Zl9@u(p7Cg5BY{P8w)Yq{T zr23u9R9DX1hP{zed8Y>&_^th`_VG#cpKL4Vi23sgUnuo4Tcf%lePUjb7LyirkR%3Q8l59u9)6l8CzH}=QpKZQE}QM@~-+pmq z2E;NBZ;})L`}np^%0)y`be*-s?NFWa?ZnC+m<*smuM*lRKP5w4UZE<5T6uUd+-9OE#U(%NT&|`wKasp%H8p5Z zWF?JTT{xOK*=zz=j-vt#oc%Jej_cP1&9&=ERu$B)2467ld>^CE_`&hek{v42&{|Nn zqIFruP*rUigGWHRHbDdoG?*B=EZvC>&Xp?@mu0rSA>$c3A|A!K^U@4ur5=os1YdRs z9`OP)5;P|!OjM66r0${Ch@xyJYcZ!wueOZM$#QdXom>h-rSM_Xkm>!r+yJA* zNbEd4*|w)Ki5U5ZZAvfnw*sf{@D2@~|EdhfjZDd9TQh339G$VX>Z&5lclbKY;8X1} zt=#v>^iDZI*cx=^lCP8pqLx34KUs2PZkIrFB*Ht+uoYKvC%1XUv@h2s(~8yF)z#JQ zfI7PE?eEkb;mG0sBzTA0)aV3D`rWZ^8qiEbxB^Njr_Q8gU`q7AT?G`2A};Q2CGLHi z$5;QQ-E0Gp# zX&i5}X{r;o7=uzPwmf(ZU~3E#)J7`{1O8PaDH#>sQI9;q{d3Mi4wLNg_PIHdGJ=m!WO)jzG~<#wj#whGE9gjLONJM^{&GxUVrCW|o0!-ro|u@p?rN^tXj$tr zrsSPI*7(leO#kk1H&bUOB71u$FW^s|?|<<7@&H4`fLjoVM#&=xw!mOjWNuQRZc^li zD?wyQ5Sz8Y60VpjZ^WvSv!2OlM5~fLpJ{KztCGi_328*MmC2kbZp5^e)1FCbM75P& zpV3%L;gTTEhGn9ZL5xs}`eD)8%5%@eG$LQhbSRL!DzZldenpo+vBY~?AjuX}vLqKP z(#w|UT7pj&YA%R(%-UoKJ6qyi%6ZM?G@@V1zQ>7+R+V7LP0@{CWt+au_T zj8AB}lH^2*L0ovUCB z^{?ws*MX)}y+jFyxf9o6L?vel?x+1@63xKJV!l_h> z2U#TaXOXnTq%HN=a-RU)r$$_Hzw-i54BkSz0Ud~a3@rJWv=&ADr}T~axc!U)U*Lu$ zVumb#88yHvj94pUSZm3a`qnfc&W(8Af_(?yR)DaLh;5=|Ybn|Kk5)jmji_y+Z5hzk z=jIf;fS{5kR~bi8o?Str5Is$3F6PKKm6{J#-GW2;CmYMUBQRg5x@xw zbcFmpXvl#hz9WGX3~+>v8Fb{pBi|9p3QBZ@mK(I>z%}2I$qH_8{5>`JhXWsdM@T0q z(h(|UUr8rq+!3sLK(!TXc}Ghp^xP5DZa}sbvt>tZBV^qX;%Y$Gh3;yIw-x<)*O%un z#4Q5e5U~eJ?5-n^Klv?6&JZ+Dp!qFM&M=GzBK@vuXMp-GmfkS72eSRHZD*kSE#B5J z#0vucuCaH3{4FNmF!~FU{;sump#3dw_b~hm`d9S#j`&AV?ydeufc`DE?=b!gvfr*f z88|b$12F{02qE5(?8XG`#+dEKRR5n}u?LDg8N)YJom4eL757-3WHp1<_gtNHHN)rk z;GKl_1K9Uuos{-NnfGX&r1pc__iQ{VEr-9CG|U5Ke>(>OT$nfe7~8Y2^2ATo+N1FR z!W;Gp#HU!ESyv?x9CFWhZk@c>|G4kuI+?HgvNr}dd18=xa>#E)u68AP(o1esa)!Ei zvfU3cc@oVX@vny({`|%jN59kIimWblKCyYrp#vzN@U#cW70#cyv_~G_UUfoTk9ys1 zb;4_JOiRkuP2O`(Vou$G=#6krIs4<{?TG3eAf>kTMgECPy;0H|dcX7S2YH=q7L z^gB^+M*Y6=SEcSK`eW)BsqToHBkLEf?wFcm>=&`_aQh?e7q#wa`(y4Gx$em8Bd-^| z?%3;Nh!;WMF#IF&7e(JF{A2PLN#BUvBh(LA{_*MCpG4B05&GNl4=ILhZLYXk?^)qw zp=JO;x{MGD-u0-;WR^0BMn5q7Q`D;tw~+_RmCLfkfF4$}yV-@jVbFw>pWmF`QgUhU z=qgFMIYF{R!A|aXaId3eg;n~UY1X-YvM?0z8i>q`z!--q9r04^O<->dFn{Xjp1CfRQzsQ>WkO+ zOVh8U51)x;qpxiaa}U<&zkj&uz- zYfoNqDshO?&L^7lnV4e|U$qq5G(?oR*Yc}0fZq|!`X6O-_L`L`@)9iRN0)?{{@ zf7D^HtXePSIw!Yv6B)RaCcEMFU9E`b)iVcD)6-GI;^`B%k85_O*S*`?H4D?Zn|?Uz zjYhPt+bXn7?cJYoc^bQrpkCq4p z(3~9Gw0K7l=7!VGMTYV6Ad<|G({vl*mUsTHq({c=+ohcLs1RxKMZ4w@vVz5G`fasC zbJbU4Dr)%0$onDoC-q2a^+=e)3!PQyaP|FqE6c@9)0)O|O@n-OttskB`TB}X7*xFk zSAEHv?hvCPsFdGp3s<{cIC?k3-<&~M^NwG!V&s>g~W*s6@w zkkwhj^6;~QkWxx4wCFG~QR4!~S-DxdS-V-s0;t*BZmYiKAo}UnGAMoIZl3#MGH=_ee;+&yKg~z& ze2%mO5b=7e_q5@ zQCOvwad#*P8`)0g|C=C1hfB@gVr6_wbzmVzv#l1@d+kuImz^cw>VBohRlG5Ld#@Wx zAwX3S6ae+vF(0kzOXRAJ==#UO{3l;*OW)?=E? zYgHeAb!pnfx7(TEWZNvqMAVI4lcTC<+KI5BO(^_?No`7l;0|pl%;#-JTdmNC$f^=} zH}n!*+;@|hPg=z3%2jd&-Op9j#fqp`bw+t>51pPq>fJ3LW{Qm1vMHC_4=*~E_1ZGs z%a*DJ=?XW5vj<-DJsP>GHOWD#w|Q(kMm|5bJIBctfp)oU$O8y)@CxHWW8!oZ8pJVTz zVeg+~6LN_*>y3M{G`#jo+ zzycO|ci}hfXyKRMnp2st=b?m!qjoQx}GkH(X$viZ5j^XZ{H_|6VyT zO{L;^HC*KB%*zmFwRn16tQ|FeS4=*CtYGT$<(r1_Nm-uV_rUE^0%W}MH>M;x{?x@l zz(Alt&_CC2Ac*g8Ab5`~V*?WdgB@TaMglVUwe){p72{(i^Tjfb!vDe`uJ*b*G>r&v8(n+8>YDbgb~Xemz^IQ7BY|0E zsn*Z5UHzNkLX;{fho=2w%*Iju_j->(s}G9UOOdpLl_bRsZv1UZ?bK{xflb z@2hlMkxCO|eCGF}gW2^0t*yxvKb23Gexo%rlVxcy>`av@QNX&HBSU;#J8$Stww*HA z>;`pl^P${<^YrJOnexHwl?*;mue-mW@vz{G&WV%ww{$SN)BE<3_eaFemJf{^$B?E< zI=AEQWZ!R)D5B!Rto`WOq&5To=Du2LB5CmxJVlhzND`F4g4`=K`|IA zhD@BXP$3^@ik8r~U4iQR0nZ^|suaTd?kdu8m3Ei(|a* z3G~xvOy~Eie<-piz_KNXvJ%F~?X`<%8dK0)1L5V%3_pQf%oOg)jaqtb7ENl@u3fC{ zyqo_g{s960h*E2^l&zD!!}3xw@Y_Gh>Bg#oAAPbL^IfYYs z3#&NI?fg1SaJx0dyp+4bu_ER;s#DSw^pKNuD&oASU*>)7Rs4Ky%=coPaqVK5U%8yc zn&ze4#C(k7EZ)lLBUQ$UIg2Ubga29=nL1!B-bJc?zg6OU5jQ5KtZ z(|WL7cdHkDIDi2h!6^)D5AX7ra4DAwmvfCU%`76^%qRGizG~PgT)~8JC7XnGY!=os zDXeFUa1~R+)m$rV;5uO&Jz+b=OlFu9KF&?T4(h+l0-xm5l3m=&XCynhh265A;Ct$# zQr$nS`j=oi260gTg~Rw6zu;H=hTrig{=(m)9_eQtls z&-jVh^*O)b8GgyLj%On<@*fGq*V1^LV_;-pVBlh~VX$LhPOdDpdz(e)V3k~w4{erJ((Ktg=TD6$CKv&eMPiTxllo=hPNaBjL-*Gf`J5=p_Akg$Kc z-g?K?sKk0^z>h$z<>mEbYF^9iCv?kM91R&+T2@Y?x!)1Gp_DZ@vbLr! zBkLN9FZ7xtalVTXxwWP)Ic?^_g#&2x2+p^4&TgM)-W0!*5C!&SPj)U@WZiP)?k@-l zM|*rh_q?gIt%st|5aNsLRr;yz3+ItgVn=)FxL!YX*7EM1-Br_QA9%?OhjL zpNv8K2{@lQ4F@zYuzOKlPa>zyUbL*?ALWI>y%P7^KWlDh`-5XXDJLXiBli1dw=bKg z{4Hld{mrOvozp&h%Hh4&f(V_6T|?)*xeFIFq z^1O&Epep`p)49pI{J)WK+yhvIN2)2eh`7uO9;3f?*8S_m$X&r z#`(XJfo$Z%w@%!ww$CCLAd zbmLa4Thw-daDR0XYvpj;W9qC{TyOoBllq`&;W(>EdYFUHWERx35>Dv$Tot;dYZ#<0 zbTQEqz2{+K;G8s=Orip*BDKU#^E_{n7BZS7k`W#^j?E{1_^qbZIC?K>Cab8?bHQ_n zWTF3Z(m}dO1#nT0dCedjJTGJ9BXDj6DMkAiaPAPXd5)3;sB?felBuN6Q-OZ8;LJAs zc6<6r6v-h+(Bf$Fjpu7@*Lpt3nGRs8hfKuv6`qTpJ!tU&jYF@psDa$;`39`gg8guD ze9x0TWHxyL<0>cN)Cb3j=VLOP-bW48MxG~ao`>0&ba)Qk_MMC4pG!FLyx{p9->(U% zhaD65zQG7bu=C>H4U*;AFV*&5^D#!?{owkySS>P6y54)Z3Vj!k$PpBn+^d!e~4Od+E?%Rmk16*PTJ3Obn#~9FJM%d>9>GAxJ zXEb?}G>iBD8o#Wt@w3k;GEwZ)*BGya%a0{U9G<=6=;s0#t)AAKEsB?M!6INk-(Uvo z(OY~xhv;8P2r#3=@2lcGG1}YU`5h=kDUP+AWO(+Alve>-iJ5c7Wb{$TDK_EJs@!X zWU2%u*}y%|KoZ@D`7n}t%$~*bKCW3R(pm@PN;tlAJ)hxwh-fgPS6RPr`@D)HjkoQy z%|_DbImo`C{W#A;{FW0nc>;26B7S>G1o%Syii;`1@55pX4-+OA|HX#}(=p@$j4&Cs z7hx1i`b(M!Irtb^iBjbGkj%ggxH$(Iiy4W+><)7h0i8x+7AJy>*_TOC1Ln$xdF>`u zo~xj?L%_^Y(B3on{+T>UPLWu#BRqat!CJ`n`F?R-VEY1pKMu@+ULnKcznZS0yQqh= zaKGk$!@a{B_y9hdU(B!Od-<*W&-hn_$wH^FSlAiX6ZVx=Zw;`Ttzp&(YqT}Rnrj_l zEw#?GuC{Ks9<}+~0&NktXj@VQ7a@<(MHnK?5n&OIh>D1|h$&Goeam?~xUUs*cNe|R z1##zazdw5Kw+Q#!i2H5le=ZQALs%f}2wNL=75DSE23bR_R`Gs0-uo@Gt`+ZR`p*6Q zB7$$;uM7919uH`Oc#e5S65`%4blAPnUFaU+P8_;4^v9t;4E=uScSElYJv8*wp?ilG zzrX2y%O!I8#^vjm|8e=+<-cA2?D7Yf-@E+w<>Qx+U4G>9j?3|vV=sqYwp=z|D!No~ zDf1Gz?vmqD_$B`0XBYqSc8{V)#PckMYc87o{=fU7fBL8Pc*u=T@W>RbkNaE zsCu&@C+0xCn+Fmjbr*Q=ezF_u;R9H=A0&IpPsl# z@-q1?IZs{zcfLk`M}ANKKwc+rU~RZS{z%>;e*%raP2M5zl8Xe>McyOtlMl#;uKQ+<-YNCNO2%K!D78*iBX&4QsR%)XW)J`KI!=fS2 zVrd+Wrw*Dx6KN7nrYSU)rcozNrx`R8(k+|j&|JuYd^&;_&_X(rR}pG&1KM}6j&ovkX{;SHojOpbf5}9=mY+cmjT2?0$~gU z6Eh@N2(;)hNJ}f^N(7{TBxFuBiGdu6Bk{yR5+HSwpkJoISV$vIl8zN86KhX4U3Nf9ZA5m8FYAU8)suNVc1Rf*Ma4AzJmtN~*o!|EWz8(@Y^fV6FboSq2z zHVIOGGS;bf42x0YCQ#x|(A+*y*a2~M`8jCpm!PjBB85Ey8e}hd~OU^4W3!dm2l-;8QsB+2H&ip%R$A1 z;EN72o2#HMu97PSH?8C>keqA5C%YLna5_%U1#l+L!1 zetMm+My*mRfLBK#yzGCH9gj>cj+cT&=;T23!5H_Ibx?YylNF zxY)AX<1lM{*=_szsIvC1{@SsPWu+FItw}saO2oGM3V`t6`p9yGSXVwm%xp{e;9hfb%m$iBSmP`xkU)y0#Ou)>F ze^LEH6wX`w`RKNe&S?y`PwBOnVuZjYsc-Cem14uy?nP$V@swoLZ*RjOrZY5*ZR}6B z&+9kZizRI102+xZn_kx_ULqYW>o=D4!^Zbs)t_93Ye6Dqy=-Dw2U%P8v5hB*({t%~ zy4CV16Esb%AN{5hOiFZFZ(~T}?n=lf7|D6PqQwzCZ2~Zif#h zP`nCbyQB7SWemGt5v8;?au&Xc%@z(=%kg6`&c_LTzg*g7Gg_Q)ZG^TuTn%^e)?i!T zX$L!eR7nLc2E?T$6&4$aTm1Z+E?J}jpd0iR8s$)J?VV*&2!$Zey#)rSnd!fmCdQRKT_Pkxm$6^ z=~2U{t-VTnWnC}3v)$Vuv4VXtGxF$B?e}CG(!r_akdpTDb~{wf^4|7A&)SaO<1Sb4 zyt1}wx!{3b`>3v7dtGBbCQ@YjRhAX()&^2ZE9;9B6CikskK5_yvBzC>b6s=eNf>F? z&Gn4~9OX*dikpr{;#}iND+IfElw(KPK~}}ec34Z}u%i^~TTZ$NSu36u!~^1f=O88G zF{u)cQPMfcNyqfV^*9_8q+>4eSQ9>Aub^qbKZJ0ZwTsQoT}{(^+nN|>5fkPSf3%<4 z3rIg_FE~y)xlg~^KBc%{V=rdM3)%5P>A0L7SJ;dDp%4K%gAho)ZFUGL&|4$1&?bHmWgYZ*>Kkq6EmxXs;D;8J=Ei=N1B-wxYBcJNWTg#pxTq6zg9#L`UyGgowN3p^4tCSV>8wXqtG=I9*mE)M zQk$}9cBAB`UV}Z2Q8lXetD`nCphDkBU88iwf?eDU(w2=#;e(!c+HtD0&59WiNM{}B zToR;eHg7mG1@cgco+1{t#e0&lVewHKUvkD%i+iD10to6h4P53ZI@htzl|ncf*v#u7=LUj)wNM zwua=k!ZvPlT5Cgi^XX=8Uh}19ZhT@x! zEfSb0fo2IbNuW^z6C^NR0u2(Vmq6V>;7E*UoCL;7pjHAk5~!BI7ztEKpi%;(B``_? z6%rUJfpQ6yNuX2$B@!r>K#>Go5-5~FfdocKAYTG`6387eHK4mW63CW7mIN{-kRgF| z2{aWS@lApGe)^ zE4A{V1p1^49*{uPfRV9Nqy+2|h#2sX!am%5->?1Kmx`AeJQH=OTZujKMCk1pp$@B0=^RPk$^@5Y6++$pp<|@0&)q+Bp^tD zAJA3ewwwg01c=LnqpY}5hEQ&xe2a1&C~u+s5#<8P zn<#IfypHk*l;3N|h5ruaHI!FTUO_pJ@>`UbQC>p%4a$oszeYKS@&d|Pl;=^NLwOd( z2>);2 z+VF<~{|n^^$}dq4qx=Hp5X#R{4x;=Fhq4Q0C&~_#do{h`+flZmY(>$V={)mVGdC}IZ7`P{R2Wnf#D@na2NnkM4+Z`z zkaGoE!pr9w*BW0lx>T2p!dicNKu-rfXCBojq?7}??E3JC@_9OXT1W5IKBWDXmj9LZ zw3a)qeN}r=%e%A%#pV1d7B_~M2chEQ^>q$M<)FeduCiZQJF%Z`?vJWtzpk;({qoIF zE1D-Z9;bAB6EwJz`hGvA4vYIe+qRL=;>!Ney2b(iz=6=>rpo@cY}@4$w>@l|ph}Zt z;iAP07dspa7cQiZg^L$0T)2oh{(t+S|DW}YAq6;E1OzR_)}lp=9g7wLIH0G_UA;TQb^KFUy9b?buv{T8CQ zVqDVQ=lRwXC%fwY(A^>r^vGowJVyRA>71;G__(`0-?-Pf-}1x>&%0YZMBeR*^L)X% zh;q0EVI|@bah_|kZrM875!vrBE<_$N!tX>je`HS^;R75458(`8Lh=cAVQaeR$6g3u z-mw3A6?}U0ft#hGH+v4?MtJr%i$3jjqVI6ngFWoymi*h_^=_{bz2D5&$o$^-h#qgr zPq)GAf0TURd%S+{fA0;Kyp#{aXa27|<~zNf^kF~xK6uVgdj04JG3Q6%(_|h^wtbZJ z!>@S^e(%dDFTfZ6CHWdOp@s+2jF`Fu)ZwCIXf?e2OW@;YUj5a;0UO~~^ni_Rlh>0! z?8o1YaqRz&$Ns+W`1SYU?Du>0zu%iLd1C*yum6^(pZWO*;h&W}{x4ueFT$(;F+B9l zfB!N3^*7*;zafs#fe{Y-+1V(oFv1n!f}J<{fDzLk@Apmk?)P&Q+-LkgevtoE2oYuo z2ZTQgpUcd$OxanvR=z}GP~4^XOgT$=NL8+SMIEAEqTZ!Gp?+OcptGojbT^AH->*|U2nZ^>$W`^F+Sp~ zz1V(#q)%jQCM{A=uM}HF&AM<)_Z0uuk(Q%97cEzW}kB+}T{@wUb;;%YF z9FdL%j)RWp9KTJ7Pgs_4HE~)}Y|{3my-A0Y29lmhdMWA6-=wWg+nUywb||es?Nr)} z&h5_6o!8Ul>4x;M^!W75^y2jD^h@cVrC-YsGV~cC8L=66X9i_PW~OB3XI5m^XP(V` zE%WWnPcpwoXnIk$Jll{RmK~p+nO&S+o!y+>oxLD?b@tskK{=5*DLMH$6*=`eZ8_(1 zyK@)huFmbv-IIGT_gLr#uwt`s&%L+CY>?r6fI8<<|;KhR13oaFWR&cFQD6A?xQTSZpD}{e5{HXA1q1&Z) zwYUadXI-zk-gbTB`nre|Eh;t@M-(R&=N6Y2k1K91o>9D{czyBqlAx0PB@dUJD0!~r zm6AV|d{mlJ`f%xq(&tKFDg9IFN2OOw-DT=BQ&~jWo^nHZSb2PTW_fXWb$N4nclm< z^;Zp6U9Gx4CVEWLm>FXhj9D?}dNo(wQTs>a4o*DVBDDClw6t`!Y;5_chb$s4g z-`Uc6wkxG8yX(^_v!*PXvUbV`-Ra#2yWi{nwEJrJ^{L!c^;E;uy;BcPBhy-^eKdW= z^iOAW&3I{M#LQhY-LurQOtX&6dTh3Owqdq;cJ%C|*)Po*Kc{uhj5$l@te*W>0NUFlKo4LEE!mGYN>u{@zUO< z*O!$q>su~gp0K=sMbL`s6R!v)Vbalw;$ki>Y_pKgS z{ob0cHT`Q%Yv-*!u=cfe#&x~x$@=E?&uu8)aA-sShEp3}-0=E_OB+7haBbt(jpuqo zdSZLhdt5zLJ&iqGJ@a~Q+?{lH-`#(@`=d>UP4SyDHx+N%x9P}cVROjl*v;vipWFP} zmar}HTQav4Z`rYB-%J%Up00Zi-1F|%_^qW|@7j89TlBV$ zZM(L;wC(k6?{52K+ZWreZ7186+x6Rnwp+KyZco{sz1_9FVteiOckgxG``Ep2-g{|B z+m2Z~7VS8*GkRy*&VrqH?L4uoe%ICemfZKs{n_^q?3VA&-F^2SVbAzIM;|agkpIBB zzRW&X-{`*jzSh2JeGB^T>btvdN8jED7Z3sY2t+sv-GE2*F#@r|EvJc|rXswkq_GG_ zM$>67bCSxG6lzLJGKKOog@TjGc>yt4!l~?o)aVLvMBJ|rRI39Wf?^kk_-u)3cc5H$ zAK`gK7Yoyp^+Rc^-VM4^=oGhJ@yiKHHsoB<54}Q@&*tR#IsKez_%Q_LBqt~P8FKVj z*f%vrMQtkTZ{w*w%b8ZdWuzx@4vNh%F2Jbe?20s#2qU61(hE4@sQbo^(Ny5NFAfP8 z>Ao&UYfh!ZFQTw1E71_svS~tmMAeF>rBTVcV5=c2SsQGVS+0L5vwT}5bbPy?w%xF% z<(i}O;!It!WfRj9+G?EFoOXjXNShYnZwu106^x@Tp3nJeXjwcWvB@W)%8z=KLrC!{ zx*msAa2EM)!a_E{E3aQ+BT7x7yxpH4?t4TMhEu@t)t^4tzb7Oq!rv5? zY6(pSj82pFe0yDxe>*?Go@@?HjSL8gNHT|}MFrsgi}`*bSiHZ2q?{r$Iu=1=f%Xh2 z2z?4W9N}rt;|NR(2-y~N`mr_-swl9mRFxVeD}pcIijST(;K{i;o8@zIOFeiQ`sPn$rv9fK81meU_3 zvpEu3#&-lH2?+nj$I@i5Z zD(h+MdOF&Df|d2|gS6Fs5L2%A7=;+w1;pFa5li2765O)brA_DA4eoec-^U2%~|DBpSnqVfwU! z4$OqTEFASu;u#HQDYP`e7{&$i1=vsGVtD=DVrNHuc>2`Loh7r!#OocEOOGt5eXw@y z7N;$5%ftr?x<|*3-1y8==j!`>Z8?t2hJ~e}y6A!w*@ZJQf6ZR4N#?E&FRE(Fw2z-q zSCSxn_L)3bd(apnqb~**R8Gtq*V{cpzDG5yH89Z;XBgTgBox)em=YXuNNkiLNAs#o z4dLOBYz7CgT1O@nX-ERy3zzk1{ALn0e#3~7?sbJ?FO$T^#X9249?7eDBz0W;SVwG~ z{SjI4Bf5H02<9tv7BWDS&!;&>3e#gxKlCPJ2qs0moXDh#0;9J_Xdzv~I3{pZ$P?jz zpbqy~bnWu`5e=C^@#9w1X4Ped+@~g@K{@h=_tvV_USAfw!EmbZHkd;Np z@Ir}D5+DS~3ZJ5LAic-BbGHdFt zmND~3MdnZ4GUiM~s(z?(W>@jroR8D$@*}uWkgX~A+`us@g8UJ=hdeD^yIS7#TD zaEx7ARkv|Qz1@6{8R0I4{&dK-l&7v$eBSDsK0?H}v#(Ka+?|BIRVVTP}uRK~TiCWa7!R{78 zD?5+8p5RCQ{8aoPo%NVjIUCtEg@w54Tuw3&6^-S=Oi5OtTutQxMnfQ1zPoSK&nS7K z{=Y8vWG-6mZutdW^wKV5Fh$Xb?K8Kz|Kk4J_4&u#&%ubNWRkmuuluKaj3={izlX^X zU{q70h{-h5%#3s{hKt5M4tm%mrR0=6uaT*h ziuaL1Mv|T9zkI<-lU~S4&S7f^6P|1#K`+=sY0F~kg-uYqmojaAX&CLTJz7h*V9A_D z`}*i^_e|WY*8MQIjus;?%XVC5K&0tJu$r6-Hbd~+kITkE2ubq#N=|A@ph;3=B${XH zi(s#H&TGl{^Y^pFr<+R}Lh~oCdZ_(87nEL;X^x7B4UNi;F*Z&}n=`w!Dv{A0JPEJz zi@Aj$H|=9QPTF63PgTJg#QF z0P-t1IZcSAkEe`h5Ea6k*WBSxEtEekQ*)}O$HU3$B1IbD`#l(J6D*x zkeXsSMeINRy3~{=nI!q;UfCm$Nh=VtXc$i;w1R>r@$}XDE$tb;p(!IL-?ex0t?i#w zl+0@`OAC_^ZID@QK>s^C`1!)fzw?~b*Tv&HR=yB=iQ_vN;) zzjsfWdrJEFLc0*0J$cQx`%CGAsgP2bD$Fag$oK;?R{8qZ0hr3+lXnW@H1n=v!fXyf?=3~j)|M<98_IU8DT^Z< zGTI4by#=)COLlsx)8ZpnXnX};!)uhhigHT1Ov!O-nU6~DJ4mTZ>!KQ^An*!h2j9gp zYK3qBz6?1|@Gum7MzzT~h8!k!hgEn{mk075{n4!eZK9%Y%Yw{y!ekVLi#dG1&>S5}6!&EYutp9Kh6dU!>Ru z2V2y^?yxXfC?f=w+0voWdt5R}1Pz@lbTZus(|#6XfPKdnGW7fLAHIZj*sD}xvO(VB z3_D=xDlh=ZWB%F6M?TtYD=#Ss3Nw!=EDUYkH@|S#gV#54&xQ|uFnU_kLrcr)-W(S*$K`oc!BjULlBU_16p|+A zH8t3n`Kz_}{(9-rL0@Z5)2d%y*t99EXXDcw#|0}#?ysHLGd}K#_NlS;W2?e_)D~xD z%9y*xC(&y&p4ndi%nKP?`X1W7u`{Qyc>bO`gFP{!c20dmi!Rt^NSc#g){+%e(7hh> z3q?TKg7M2qg_mMA(9k#tG&#&<$k4}J@(zhE89hqW=$*#n-%|nOV@nzqSz+1`d5t#= zjkfZOh2`IF5|%StkkQi!$QD1WY$M4^>7I`gLjbs8!DBIznK31UbjgW;7<|K1pP~y8 z3(SMLs1MJ}Br?IQ{5GMp!0j^MTI85k4P!-=pP(?2%yOVbSX{b)eF>WeulSTKnZPiZ zcee~3Sbw)`+%hf9Lg?F)Ucxm`TvQU=-N`IGqoeG#sdI`qJioqe`=lgm&D~R5Tk@Bm zSTS*Pb7I)2RqnsG-&J8*FgNblos{qJL;)FPjHD}_w;~(r?9Nu;JYkeI-^3tEw zcW$bWtlIkPyZ-Ab_lB`|)jRx?Yw~03Ye$7?7zy)-QaA3|zvu3*yuO0j4>bAP6VvMF zgOBVjaR zz*q~UL`dqKsL;LsJu035ohsgE)~r}la6+!lmKhTOt!_ZvcVItD+^4pn_{^}d9ET++ zHZwFL*AdLUBOb|4xOoI~)#83w>{~FNG)Oy%M=o~{#NDR&VEOC|AnqBtN*7yQ#>>J1^L* z@#u(^Zha!&e2RnEwZIMegi;qZ&i7*-)N-b&48ifrRxF4V(N6ceCGGyh+<%*MRdJ`M zlC$)=x+l!aGdd@(skDX#uYYb$Vb-hz(~;lCoR%qYUfP-S5)Y=WNgY)m>6%m!wy$XR zwyL^q(?{^`b^qkdWc=>A;BFJP$#x=w+(st3QW}k!r9P7<+p2A);R&R)dh8RH@QH)E zs6mBI7p_w%bm4qX@-N~tiz@32at;^4V#Y#@v0RZXO0VbmO(x7=f9ISZ^FhF>d*@At zU1n-QOUM`z!JnG?#<^qbD}&=RLdJ|VCXUX` z-*x}iS;YobxZ7AZDJQ6C-u;JvRd;-F`lEk!f9?LT=Y=!sfUtmdRoA}xDJ{Z7O!DXF zeeQ1Z@z0yQbjfskw9Q(#eJ=0}YYdCrgLp<>93U`fhg2b4gEll--!B8Mkl3_01NG6xerI9c>XQl~lSd>T{f zAbOAgADV(5_Q;@v9TodD_Ruyl?x9*-Lx#B z(2L4E(^Rj(!|L@5h|BK3y9gb2QkxW$P)G{8or)m~QOD!cqaBoO!N`=;na`dLEuK}K zP?cjf=TBUJbQZl3l-X1?IPT&0)sY*%Qa2zf{^6A!hFb=HqRHra+w46+OYnCG-}IJcuGo+ppNee zogek({h;p22Yid+>eHZRC{+x&_)-x8%%mB56BEnC1Tdg9ND?S)<l*F_zy+04|HznBtL=l2+RmT-)y6U5 z)?dZMxn^$r$wL+HQ!#c?24WOMsKq>M5u+F>&GO@T&W}*c>H?QWM*=XProZ1dpKL0{ zNo4b<`lr+0NdsSN<+sh2T#h-axIu|J#5r!s&tB`{COgKjAOFyAmUrx3I(|fSfI?=u z@Luea&AYd+ZOh@C$Ih>?-3UmknYL)z+{u`S4sSm-WTBT9BjhSI+}fO)Dl`fWPvu`B zUKGizR8;mASET-%i{4Ix(hMLr#$5c#l4FVu!2gFY!-(Q2WwwM^_|Zc_RL*@8KJ@Z9 zEXM=zArRXz^H`ZV1*Fy5p8{;``$k3OIo}dcu zvarG3xO?bH^pN6B3ZDxbpdTiZ{a(EK1e#42OOP4gL?2l0E+4zeFYpUXnAsd324hEi zLPtRJjO~>t;*0&RyTT+=A0~9flNh7>F2wwz1zzif#OCyAGq3n`M2#e9ia zYP@Qt$QMGJwP<8@;*>+HTk=DqYgbM1Pf1Pmt9O4BLZ9kseztMmz{+CBgthgu`lpxV%R8yobQYruLLL^QmFOrpj{Md}*1AK@woq3}%onG-rgV?55 zuDgss=5nCH1}7?$;9}ry=fLuzKQAAk@<-=#QF9*U8i$U^EJGJKG5L(*=~E&!W0o~! zg16tnP_rnn(tzrST!X*AsY+zzD;HNJN$a#iw$dlFNW*#kdC^Q_OrrVTiXx3=dHEvC z#x=YD5YB7E-G6wICWha*8b%X^o;`bR`1kbzhjzqHe9)Hw((dhx*=SVx`*8&C`|*?e z|2?2c^}O2m?=E>MBHTcmc?=VsOs_(R&N4TgC_G($KTwyylP%u< z>eUL4m-BL#Z>m)Bf_l*N#eiC+81!6nX%(CxlgZ_(A|76Fk&(p+tP_lolkJz2O_S5k zrhyE+%uJd2e-bZg`0*`>b5F&oM?x5X=P~@!TnXDjKkX* zh_DCk8^~kc_D#M@rH@>u(`lgO7}Od{{0v1h9ndqN3gQMmuOV(=6|B5cP;e@p!bdLG z`1%y#Syn3Yi4(FFnzGQEW@>KhrRBOl6ZydI1{Cuu@4YCWF7|)TW;-Dvl_f((}ts`q29I z&&=Fkv%D!UQpd79WR|Gv6%7Te7Ick@r(ZXB41HwF8d;5CCh+g>6Y(Dj&-h%a|IETH zL4YzU6|Ta^jfP00Dr}Ipy9`mr_-mHp%rE>(m9lSKK4s#ZFadau1%xq{p|hYF=C6|6 zFZzF1oz0b;t&y2ofzt9Q`KQIW48aGTZUwmSCX4`&q`jO4e{EX5$)$w=EEQg^HArSK#47=iP z@4TAAl&Vlws79rR8b?e)YP^#nNbM)4d6?Cvu-^vpN~JpVD;L(t;YC%NXc($q(}L-B zVzlPF_J!XMT`e*fpTxtGxJd;Z%kc0|my8RJ?>{rw*Pc8grhXBlhjH!qwkD=cTwm+V z39$d+==h$os~QsN!IZ)%JyWToM@}sovvGQ*(SKSsSGLbxn^4)sbOXec9yu8s?JFePlt9YPd!s>CcDK$7=lTx|sErZfWuR0oHjV!27Nh--s zPRp(ADz4f#yGX9lDi4?fZMijxi6xmhK4wbk_}#M$8ICT1Hu^yu3Oq_I#t&S6h>7z8 zbSxRgswFSthnZY-E;HTOyLR1imT|RBiZ=^8-K#!+>|^(;aQ;bQ!?&x14Prc->Aib# zuRzl2y;rDK<)fj2MvYNpQUw;7ii|3%6=a$s!&R4TSV|%BK`nz-e$xhsCw6kzNhIHnU)+N!oc5_;@W`s-Q0T3O;+d}sHq;M@tN%VkfTi;z~cf&|{g0Ne1m6c@0(9Wy6_~r6>UuJje>7x3n9k(U`@!vN+5%9X{gP^!=4F0Gl1Dnkw|1seB*5^#}L(54kM& z)t6tSJKh-jE2kg&g4@TfaU1DpqS}Y~Ish7xqpxZ*(K}xuDwrx9CysBEu^d-#ZU^P+XcJ!hgNA65B*SFPrfWOs-h1!@ z@;WG)>h9)W9PWppfVr(eV$m$&w0stSnl`Y0Fy(-yhTQ5$7J30gtlsS(Ld$5r`>7Cm z#ofJC9=}x_TOYSiFk&9$B>4%MASZayDso(1ILFia6G-k6V7MYZ@_ZWTLG)<{!+tR! zJ_b?2^kl*K^QYbS{F4548@G@C&8^|quy#Ea=aXxaQ87tJrJnJI05`Bp}c_H*GHtw@5=L06kFbnwqh|54>RPufE3i~aG zI436F6$qGRwwwywzM>_W`{xTPAA`@K;IbDMR?IAqsN8kIeWQ4^WBj;LBNBY#k~@+1 z*qCke<*)haV}@S!FPQ$&qK}uYOKG~RYRAwsbA}ox&x$RdJbP*0E5^X==7q}^h&ZV5 ze2o`g^}xQ4C$l8ZKS}&B><|+#54S?Ou;G13w#eZ3eQITOLS5t;7mtU`;Bf+fF@k39 z8YFZ=Vb&rK7|E|Jq-1gF|Lr9hHw=RqkgnoZE`^k1h`d-S3_zcqGgwD`j#U?h*G`_9 z;~g>;io8SCIFh?C{NZX9q^46IPvfpCkU9=3# z68K{q{0U0_I;?~F(r6zI4b=t?()lj4T>BTIqdLPyO)Ys>7aAT4uY0KZT^G-W%iIdg z7YL{9Z7x|?@Q=O*L%!*ag_do)r7B4|mM!%=Dqs9#+u?UtRqQMZ&)Qlv+d-}X~l{ViEWA1_xhwAZ4ZJkD;uw%yXRx8177tD-; z0|P?e(~t8-wl(n?%O)`@_F2p#BVwkWC#g*5=*UWZewGshPP(r3&` zrrC?ke)k%KpMJFW>nCP{g|jLa)g|6-Ny@i|q(z$?HS@|4SL#D9>v_cQB1sfE?xlq+ zr2cDtjlNbJ-WnZFsc*P3!sx5VvjP!9u+kj*w#jCQcso*O3^!36mYM2Rnu{*kc!}Iu zJP!#quo;|Ae=AlJ+|0a*f zb>ccn-wLQ&{IeOxY-31L?9%P#``k~5(h^(jC%?;BQxKlBFbBtM^kk^JV(31@v=?s;rs89O>Z0aA8&VKEl1$jojUVH;jiu*`gWe(c>H znFWz+V^SOyt+bCVF*il7w8TK>BBMl*Lrw*e1zt=?1QN;z>tMKR@%8ngygrckkzIBL z)N1Q|>wL!I2^!G_&a^lwryVxQtu==Ebw#%Pf$M-FIKDH*crx_);q}I(;MJ+`1LB8@Kg0x_gi*KJ|_Ch7qui0+0E_ z8vm&@k`q}ei7A#a#B1+$8A()FSaweIhfxu4$5~Rs5{0-xSm9wVz0Rc%tkA{P8b8cR zf7=y!OV+>`XDRzEZPCv_bCS>Dfe5yko@0a4{x4f%lIV8H0|OG0NwOm|nE;Y2G8lUy z7mSx1* z2vG-z;On2n5UG#!HyX18-w9TSAY)uRK8_(#=VVB9^5lKH^=%g~X<)#c9ynyn7vneP zzJt7Cp*laOgv0+`LqH*(e~?flv6tlS$jtKhV(?B3UvD}&w|;h(+`lJa(we>Xc~d7; zgw9J?S2=fI#42AJoMxYJWFr0Dok->n$0f&4xO>vjv|0P7J5ABfu=y$Zdw$8yb=4&q zGUMpicOaO_E-63R4StFy4|_2f5Y6EkK!r)~X9&Qv1O|URzvFKkgnAsR@DJpCE{C`j z`Vjx`Z1Rwt8&cDTgcD|Yz}+GB)J9h9M)_t|8a{VABxFv z-UaX$FG`OPD)@mvyN_--J~K%do@{lknJu!V`PkU(km}YMIj+u9d)e$QrJP*lD6B~c ziVyd{gGHlC@sQzyx^y4l=Nr#eejD)PPa1~tqgJW>eH1pvGe4 zQqYt+5L~){R}VuG^*G5ZBjv0hn!c^=bXV)Ld@ktM17{5?Q%1t*$LE&DIFhD6ypq3g zBd%#du{k~oHqY0Pg>_hWa>zs8zGX*RQ`A1uQ9+hai`B{P@P$ zgnG)qwV8hmyK zhvXNHj(3)X3&xNU4f)8TC>4(LkBp41D=tcByOSHEdZCEf;ZBf%^!- zHPLGUEQu@1zrREj4C3&ae!>uQb8$KB)o06}U+_>*^t#*E7tTA1YL%okH(!##=ugX$DRbAI}$$YRMQ4qMR>r*4h8e-4f8m$!eVp(O9 zY-c9619|{cH({Y*${Y@h%k(4Lknj`3$a#1UCGm5o>G|tXosRlr&wytAd=3Sbon2Hh zLo~R(*5~N_L>SyRtxq2xcQb!dBGl#UzH5L^p1maR)td$=%hl`_^-dGwYr_&iW3%#H z1YUSaB4CNmaM`t-)g1M#f5iLZ)4E!j$))tIF!|S--*X8>HI8c#Z8s*i7)89}V1}tA zxM9J8n!UQYq>qG|KDRM8mNH0lrgk%3 zfYFPWv;BVXl3UD;>@btUo|!JnKd4FeSG~+_AJ?&{>=&yY$90yBsEW<=u}t+3bGLJ1 zY3wV{m$l_tCT#0(a`%WbstY>f%NR=rc|hWjf#`57%yS+!_{x-c;Lv4s1cvb`g2RAs zhFNJ#3C#F_us2!h4M7!ze>?(JG5i{Y@v+8a+`3M4boyblNbM@?YJl8_f4 zwM9=@)Q06iWQckpGgxD!hm)By6v)3kaYuGprz9jxqW>t3ViiyU#&r<7sYFE06@cxgRjqYL;ES27B?>92_vBTX#pR$yDFhFvtzFC1Rd#yT3tGJhkC zrt`xtnKMsv8;4depA$QB@|>l4bR`=XVorhsA~j5~vnO>G_Cu%U z1~v9f&epu3Z`tH};Rm4C3dylXN{R}35^x|JHml=)0 z8iSv&k|KWWgT(%Bmp)jn)A{{WlOY5&U>SP+FOX;Wd^Hpn* zpfc9S*Ed)kj}xgVVyZ4EGC4ULQ-vHA$ZPhntr@1CpY!!GOxd1130fLtm#Bq2@&Ddk z*z`*H;BQ8>ym%7UsIn%l8GB!`GVAgiB}ejfF=!0w95I_w~@{-6cI^&b;~2Lg=v9z2wB9Dh#KI7VJ1xqcVQ)SV5t{fk>VBz;fepK zDGdKRv9RYaJ?B31DgD&_<%Oqc?M1hpu5quU$kK7Irt95I1YvA@kPC0a*z9D7L=(q( zyn*sAmm$nQFfhPq3^M4$H2#ssfB+K{)PBS&1QL^=2n(;#Xg_o*ZaUByXCfnsMQR|c z@t_@3HW951lAe=&{6B01gNf40Zc1moFoI_kFUxXjdG6K5U8`nycV(E9!YsL=DGd!x z=`Ww!5Ikz;nAC8+?6gl@)zaUre!90QGl1v!%VZ&iT_YJs9G(8F``0X-ozwB?Q@>|? zi~oKlXfbxx&3xHlCC4i`Dj?d1FaFbqUcn3R$ZM3y&HCbTPW7%!bk=}P)3R9};H`k$ zZSgjj`iC#W@bEY+3;n=9%6H$m5X`+W^iBwO-8?jst60UovupEEGV3FYwdEnkl}Nf> zsqvv<;Ze~su_3V`##n!YAN%(WMq?t6Z?GoBd^*_X`;0L-mRGAu@T^2{AP$VibOiRm zC>}cTCYwqT3>L(|e6ck`{7~qa%$X}%{x_dXD&WP(>U40dAj2GflrS!ja=#h9FOXCnH>y~#T;DtCK&tkHZxJiUX%b*@( zg=4-j7G|U857E6pcXx|q3_K;`x{JV599iNjL_i=sEHorK1h25*5wP>astQE$arkP2 zMMSBevCCzV(Y%}({X-35e&LE>Utbd35O*5?$OAD$rdkE&;176X`JP_9iRw6TC2Hgh zb0Xfvw@g5d(oqpN;xmW}LukHKAfeZeB!Qsq%o3FlP41tO=tEuwUYmbAwguS~H^X{sgX_!pr2-F!#U=zsB!qFSDXWxxB5A}qiD@Z8-VL!gFF_a49bj-rqh_P9?fctP>) zKZG6Zv7V4L_Ku?DVCfx2c*aVO9ODakLrV-BV!jQE-~kaJ_yYE6~&1|09}fiTf{|IImop>vRK zSESGYv&@DCwzvt(MBpQf1w>$Wd7pU1*ra#iBm?Wv5d*nayQ`qUUa`M&+uj{_Z>W3l z|Do;81EZ?W{_%V6oqgXj`(!dRne6*yUuTj)5(3!>TiEw~6BG~;5ZMI;6cH5^DYcd& z0&YOvms+LP*X`w1Q2W-Z?W?uYT8rfJ{hV`WvY`FG-`^j<3?$6S;m*D1Jm=Y;&-3Bd zh4(E<8xjnJhiBd2wQ*umbO@=FfbF>*)n15eo>`hXOt5-M(inl{Jd9z;s=m(Y#MDEZL%9!)$)0hqAe zE4Gg2%Y9c@cpn`7pzpzS+vr;lPn~~X(fp*mlDdr2DQ)G(5dZcrv2*WU^4P93v+kc) z+FHKY9&+kZo8~t?3R*%Jq8iy=8Ty;SlIS?cr6D6Tnae{KV5-(Gq?(gc&04KFHCg6X zT3m82r1F@oPL+`Pojt55{0_BkU%mfwKJ}!(Px)OReiP0y1H_{4;dEtsydt$Mz05(q zpMar3MjN#Vlny1TW~9t6svYwDx$McMvxYvo^AnTVmR26xd}Kz>)Y91vXSQE788MK$ z<*=e-SZr!6(AgTB8ciV~yCZyQ{9paUbBh!5+AuI{Quxd9{1c-_4Vkm1 zY}A;c^KN11cmnqDJ>(sr6H&Cr3FuCz&7Nd60r|?NMPY%8leF+aDYZ#v zIj6S~dknx<*4N}Fh4D4yX3=z&y1I)+fNmF*%aXlJ$#wv872RYCoS_WF zUm?V#q?eSwB73j*4Fxi5#q$9aT8ZaPYzsV1Pz zkERhikVZ(e2&F}g7erH1g5we8oBd_Z{blueDC+|sNR&Ls!{0~}loADjF2QU~$_|ow z{_i%=i1m+7jEroKrKAL-^(|R-b6PVSXRVmkm|8!+%<3&%aC}YG%_Y9-+2yxwNso4o z?uw?TM#nvnQJ3SW9>1_KEt>198o%gM+mM{$tGaV!D!uwaBu~g4S=HgpmX?mm%UcSk zk8e-5#fnOojIY$`_L&Wx?J3aKs_WZ&N{gqBYpYKxuBuBZ9yOvl-5RN=O)eSR0bmd$ zqR?Cv9fA zxIBry$8(5bc1!OGxmr+d4P-f;={}>?pG{tMX826jKz3r_>LU4?x*T_QirMU=b;5oN ze%c4V7Z=m}OhFCW8q}ZxArF1ThMjJ0^;aVxRdq7kJmf5?1}E{8s;JdmaICfYSW)Wl zb=AjeqQ@$ozvsC8UcdxmlM_FIUWZW2yI;Ku9e_T7w-h(-TCyArN>v)X0$Ha^;lQO?ejWH+_|IIc8%CDzpKobJ7%4-XYwt# z&HDNe?cIU41&RYFYbzHxKu(Gn;D0&P=bbF#&Z);9erLgfFo!0<7+Q zu05KWp5)BSvsj?VDFvU2WIZZ#kY5T;OP-)h5r{)@2nD{BUzl{$ApyTCVwT)Q90E{0 zg)>72A`O->!9aHxY$BWp{!8&#n%g5l`1QMDp}NoNo#^I|iW{cSDw})H)JT`_hE^HU zm6hvd#)7)xSMB+(f~JyCk~%%?Z;lqJ$gC|ZS1+CYt2IOQK*QLaRM<4LWwoOy)6Ewu zRGzY?)+TRhb#do33QdG_zmMArzw$|B#A!Kng;JB^Q!15G`z%T@(cF&AfdCXp8O6ww zorSlVfbA4Tb(}_{LOT9pFY|IzRK z4DpY5?|Pby;Pb`z9@+i|nJml^Pl+e$$^B$U2U6RfZY28%icAQwuZ|0!@(XcqC=o*7 zN{%eG{{|8SDzGD1&wS*^L8A>b*e7B0qt(6L$-?O4Bz-?R#JqwV9tD1u1o2mdN~I-H zEByN?jT~;FGQ=_e6cEZk5?%=Z*n`Uxv3 zRYGAD20#=rh9;eqzO&6go1yf%ZP_#D?rop!$SCnyoZ9@F?Ce}$W_ev#^M)DKdEGZP zH{Cw7I)Btnu}KGSsO#8tV$$RXmg47e{*^}@&JTE1X~~%sZV+q1yC)s07iF`<_Y;y)AdW9f$UV z{`da^<0q33U3Pc-$i?KgPksA?B8-ywb{mv-I3k`ylmuMEa@gsew zTCM6~%4_eXqKja~B>#DOWCtp9KL#5UaE@gGackQnZQ@oRS<&|M5#**98JGZnOk7Pi z?j^U2w_JT;H+rOABRYPC@P>S!Mak!Lj|f1|z*r_fTX>C)6-Z;!9-wZP=5(?=Mvmn% z@(9!p$YbPp@rQVU|8X9pJXA(x9pdTZ;?-mFjo-3FMs#WY95#SY_#X>TqQ46Rz*AeV-pB3%^Smv;OGx=?~RR$-cF@Vpdm`;e$u`s={$=M$CC?OZyLzxev@P zj#jSED^G2jH&h18o!YW{$!h*%{&mokuOA03{M1^_F3lkglB4rkc3q!{=|4Di*TJFj zue)<899o{ooeMa$x_rQ);rbTw0$+uH30&|T-Zs-EhyP9^2%Ss#bv)0H=L=eOmQ&U;{X z-TV>s=UxZWGO9iQM1q#8adUD40L-l(HwOGoG;!4yki(PD8$~4b=82MJGpJoK$E&J zT55J04R(V~r?={@cD>#pL%p`yq_!KBCbhw!HYsHgA_8x>SPc%F*?CZBG;}JU*vO)4 zjk=rf6a)^$U4%TW^S$Sx_`@evgtQtO3;^ZD8#&;>t4&u%^%}z6aF(zc%!|b_RI|6dAPl zHF9|Tn)SVBdjks>-b}j0gr&&Ts7#a@u+?KyO%43rb&SSPCf^bd><0-4CH=HaFF|>T^S9P z6|suCvhv#U+E{r-ELM?QT_yNAmBr6xR8(Yeev3*`6e%hxj@4E))K}LXEUT)jEGw_* zEbc5R8tM1m7s|EXYmi$kA-P)nfII}54x}vVN|*e5hz0@e?BdG|@w(_`)A>PU0KE+a zfie=dTGV@ypn|JG{*|Pf8x@bQV@N z*QFLuvbUAzDU6;ZSGhaXky|u0H_4KmVfUok)%+Q@%B!xUCYjCO8p}~CR%i=?4(HN* zpTeM5sZ~q0+N5BPBc(XqqmtXbDP$^L{wrS$q&1$K24ip-{7)38rk++2Hk~Fwhf6sN zIg!OBq2HN`q0>b5VfQ&8#u>vMz^I`~_|*3ihXt1~YM7TG!W@M(5R_%vkgj~rQ)FH5*7iq)1h zWVmuB1;6oYP>?h(n=xQ~6D7U)xMRTh<^l6(DmM3BXDb{4M zC}KcwF}#T)f2?vO4wD6=f)Xr4ZlKr=kjV)Q7fCP4q~PnQGfZGfpslv>fv71LA3btH zaPvcb;&a_3;uFtLARS_CpEw-Ywx7#AJH^R6#X{oZKks#cWlj+#a7O~8?l*~FlJxM!r+*TCtTMEE598$}5C>Iv){3b$TVtH|HJlvMov zd$qT)W7z}0yG{IX)g8j7%&yg4dpbY$ecCf`%00`wD^qPs{u@5KA6kX)Ashu)p?D?H zIKE1J{kF05RLIr|2A;G|h-*vdvS{%BTsNLLuU}s`o>)I=oSs+dI$PuG{oQr*x6yM< zX`I^tyZ2ex25C`~65I|i6xPWg90aP#T%;w@kd$%#KVieBGyO$o5%>Rn!?)u8sbnsG zk~zXK@g4E)`6O3-=SH-p&z~p0L#Tl*piVv$b=u#;3WY|<-Eb;gC)Dz}qqxCbeLetD~aGx^jQ->s?DPQS?h47KA;~*^mi@Y7G z^Ub|?flfP`xAmqx8&ei6ZTpF zc6>{m-=fZk7=Z_21|Np71H(;d>oiy!k7&UPflV*Dx~RzyCrV$D|JUiq#~)nY;H{dx z>G8$Ki%!DS%wN0t%+d!tZl+cKa?eZYDDW;^cGIF!5fNVC4`Yx{6|pXAy> z*lZ!_zAb~ zuG;6d-7@MKPIzOH_911k#(fo(8%r#?upojUWe5Q`meS0M`60t8&=Sk^=`Z7^E?WEr z#VC{?*_tDd6*t-5$oTrT!^a}Sdxl5SD;g`(bEe(DB+?W1`#aLAN0++OD(frLA|rZ6 zM8>wQ*yEf1#+*5C&Jw zhMtDv%I4CN>PxJSjupl#WH+kLBVjL+*K}=3oaq8 z2dM;LIQ3*?G&8L`?-KRu_dGN5y( z31UUGJeJcuK0mg!yKrpd*zr#DHk)JIsP3L6v%T$OMm1)2JF={`^=YBIbqgF zPM0OsYt9}vqq3U(BDZ~EoijI<<;n>_RHAMVx(lt_OQGIOj&wzJDY9fmvQNOv;5bW` z!kmSxmW0~!6|Hu|!DoKG3YbZKddp9g z=OKQ{H*VX%X7l!+s?h7)WDq|m>B%SW`cawQUazZ^{o|x1&R40tm9y(h^XJlW z@YOEqIkYiCe`h0w;iRNco|L;F0ADUs z=8?SwDW1bsayOkyi=;p*E{M9+*1`g0=*jKDkN_EmZWm~zk|bB7R(oO8o1(TQ^@Iw7 zg|f8BC955k+{h}I$;YL!^M9R&)dz@QQS^V)vHEdv9H@IpC|S_4;FhSB$(fQBq@f-1 zj$NTJ-!sORBMmFwaA6@ zS8@;DASK@8&DER-mL+oUZ>{d_;91)J@9HM|_{aI|;Yh{D8SX#ZV{`G14-PaX|Z*Q5NGx9NE?oX76qn2-QeK<|pmzDmwMm+x90k$5}bI*;h zE6r*3C^~RnB_G6gpj0IElXQ6-#wEQ4z5&K#>3o-Bec${$sO=ZmX9nsmt#m%t^@zBZ zVZ){%^Kjua==wG)YLB{8YypElsJEq9Lq=235;U0uAqo%cHUu+*mR-g}Mjpj3R!hvL z*PF}@zDI;1%$AUnDl76CG%VFBBFH?H?8O8yESCEH_ubSrP1T0DKL@yN$R)Fe3=$l9 zX&D85ea}_rDjiYi)Q9Sd67a7_9YwVvT_DepZhjj`u;Ny^9A0XltEjReOM->H@>zCW zVTR4-hu{502_crsk_qBkRs&JM4dEs{UVt?C=}}WwWtpKe97y&D{QAnuYG7T>i@NLq zgD2eS@2IXUu6x1LQTzvUm%cOih%kmeA+?05*r575O+cc-i#Yy5godgIJ=S$l;uLOJ zQ1(6Ib!830js1^XJOFQj70XI641JH@d(Tf`FbrCY0U!+h$PE1pGy2E442(vAAIt2U zGH2pf@l&Er zI)4YorTNTU!S&I(Shi-s^|xg9&qwc}zWFRg0%zz1XkYTTAh3e_M=Nc1Cd6xB%U8?K zV1CnUR7Q58p#}Y13!N|bSQO|19M?DGl*fc%UEdn@Wf^nzG9};>)dHG<1q1ncxqNOe z9|}3-JkrgG0CWmNsqV+>SV02r5 z&()8o(9-__=pB?s*cwxQB>B3w(-Arjy+XnGk)%_``B8j67DL$S1iv#4y)gDX#u8cH~` zH8+>b(c@+Hoh6H)6{i7M!ewnelNJh@hCb`;&=;6;U(Ct;$kbIHXIRj!D8HgEI@%c_ zk$_4KJ5V4>zgbn!AhuPefMziWNFp2S&~Ne$U}14<4vGk%kv?|SIZN9Lo!XSr9OQfs z#+W!42cPi8LTp9y4QXca>LB2W!~!|q>ucEtHb0B;i$4BEaT*wZ!A$UrmiTojPUQwBTc!DTB>2~L^W(G} zzdog9Y5tuF{>bK2ekWZ&!S8@gMDdLi^P#!mi#`{dPx+TLADTWLXJ=(UyZ>zI{-v`* ze=EyW9Kn3t3Habbu?L4fe!Lkpm-I>GuR_9sVLzAX35$t9v|>B%#T zk{4&6%qCyp+oELgbhhZs7LHx*=Bs*N5svMW7k1+=RGSYEb01w6LPPoAbVm~Av1`P~Qh)(PK2tXiKI4jy22t@ty zKR)O?sk)jsi+a331P)l9zQ#v8`$1RlhbvSm9qZrt(Ad&Q$`0xzE>2NU}~k@>%K z*#0K`)70ie|5)rhv!h9SoOfQqzV|R4QkuUV1ME7m$KPT+Te7H%G)a!ry38AwibTJSR1>;Ta=nGGgagWY`D8grPydjkpHODi`( zALk`q{5<#+T%usWgSinbL5j}8cyFKbBn%tKcJ~9n`SpQ_&Ogm`_X}S!jORWM0$O zGQGc}P@nf%Zq|$Xu8M&yH=eLcks^>jOZzCj0D?hR3d(c`KVTlx82%kb27EyM>{hrA zVM8!Gp&^6g>ilIn)ty;_<=?_)q;+H$)duwe%67lK9ucCC@fLvb%5%qT==nEb8V8b6 zG<}PwQ2huf0z3)k322S-Jc)Mh!~m7k495hrEyP8lo{-Ds)tHr*&&-C8C=7>8Nb2A| z1I7kmub6WZo=6BTEF4=jLO2z0$tX^=8JQt5iG;LUDUvIQ70B%@?_!C68w^>xyUMtbiwZh~T& zW4^|;y!L3G{^?WC1>ZkbKQrejy!a;{F_}G86Jx@ISDTJMpJWHpH`YwXRe|>{&XsCCO@2t7Hm=O_^l2CAHf+^UsoG06lm>tw~`@tzk>M z`5Y&FY8b6*SGJ>u6L-yS8l_nJV3IkSt<@F@^jytgX zF5$ha-UScMFHQ@}o3Gl?+tPj=g(Waw55XxC zshBJ+fsB1r?!o*#I^HK^haqnP_$C-{8#La5jaY>|HwD?o{y*IqDaM%3uDdCI-LbHF z&M7{_|A*(_4BKh!JkI$faYohZW+y84o=|C&xs^fcqjTI$aTO3ct0|{Ot2U=;u4s*MlJnwXxJmx={^q<(=N{`}W zpoGe~k3DLq2au6+k6`OUOHwMn*}c(Q=)XTH?GX6`=sehGamA~%X*3&w?67p3@#aAB zFh?FbgDKmHX3fcYWS)3qMFMaofx(jbCI8?menIayNAEfi0qvF-9A6^bD*y|HY2=~Jq2xgi-6yCV<)jJpV?Yx80EAElvu%vLq zqnU2K(P%SUoHov{Rx1UI_ETR6r!?BsZa+$s+7(1FTRLT*3TPQ6+2d3?Q|!T4UoJv* zEM?vFg}b7HhTcm*#7aMG2SxD8>3u%1V4rtQKaSVJK@cpe58m6ie+NG=0q2##X`m=9 zbj`1Q4;^g=)5VGy4Q!WmEBuc7>#09a@~1DRTY>pk*nFysN%QZJbhr3?$|u#+HsvU4xZ2VcV%&jr8_K0}@<3cI)o(IS`E=kz)KzC3rbKQGUp?3URx z)vmvu@Di_=bNa)L5H2S_lyp+1cm{Tl2pP897qv(91+zYG90^2kp z5JsY8SF)H%q|cg8gj-BBq$qqW-W9GY%1L_h*S^~6cRaP+SyNqXB&(CfC%Z_ASN!b+ zGEDrykm>4LH>Ezy@|i6amWb@q??(RS^OeX^zyFIDGRsQW2g3u%wKRYlJh=Dk?bHRdTUBSYT( zqNk(&MRP~_i>#S5eN<*jQxPf!O)FT?)bwc)V{}YdBBsq63j-OTKbAkxgvL6A`?`)% zq#RnX7!RD1?@gukt>JYysgr_*_1$x$XkqT=b%n8xoSF$W$!c%OxZB2esZ>LUR7}Y! zy1jP%eDQ~@A>|ofeO^{>OGTzfpU->p+sgdq=|iUZHiybH>^pXc=dN70deO)NDtDcc zEO&b2#8O-JkRj!vtQP;o$#(O0o1?XH=%Ou~ZVYuc0rqP1sTG^?Wez*a_! zG(Sm_wdgnkez8f4_fbNM^RH2Z_*WzN6!7ywqilX3pF)nH3|K1|XY&)|*UsTOQvRHD z{Vf>48DQ6!Vo=ijKK{kd#%f%rRJtEJlj#JB7}w}Q_e0NEE;}32{JwlF*g;HJKo@qZ z6F_6Y7jW?RXIi?{u;jNoHEJEl>HdXExfU?VufPv!Ra+l8KFbiMv9F8xOTo0H*f1RA{|}h= zgxBA(|EBPmssGQg_g!zP#Xk_cLjV5(`TodStjP@V0>hRm1JbDxk=KQ%3Ug9XySpH2 zv=L=-&?UDO;*~6vo<$;j8CODPKgkKDW0U{rr?>?+0L=Jh|;2Q!1^fD-!C2r)@{(6AHjPAQrU`ZI>e%EE`E6eDJXt?)XGnCtLl zhCQ%$`+l><_-`O$?#1yO3@CG3k6HXIPG^$8NusAdc?&v|y+*$ssy~w_q(g(06W+p>W4YN{4?crKlvZ~XU4L9PWWg3 z&gN5}x-@^gL>KHjnM@~O^SOk7Mw(A`0%?9c#uuMYbpq-7%sq9>_BA%V*>jXJ&>&;S{IUo|KXlv}Wo{Lcw;c!5L1Go9#*M?oZ5Jg>lyq z)jnyaC3H;|aFFy)JgO9TV38l{E|7vfQvC2be58TTE3EGKf+1%3Q;12DdivA;Fa!y& zhf(VNl!b!&pPKR;>OZ`|_)sOI=gkTKA;#A-owA3~bBjbj6mN1+p5*2@Ju{3OHlM|~ zNlPMk;JW$od`rqeWLw$i!qHCk4LU;U7UNQ`!9+UFC2|cuXY;A=Nt%Br1`_iPEOx}^ z6Bav)>lFj9ALrlf`et^Y()?lcxv>A);L{Jv_W~y@KWY~^Hw;w zv&JtAP9ej5{A$rQX8Y6tceL6mKbb#X5ei ze3$%?98KROi~zK(fRBd2hH*PDx?$V6P3#8@bVmqIOO)5;zx3#ha8rhRr=b3G)hb#6 z%08FsIRE?S%AVujLMq0(wcIZ55EN$mx$o0X8Abo@bCG3N7SR)aG5cKc!(JZ!Gw``T zr}ip+-VgD)NUoNUb|UzvhD4%9EvGd+m3TS}`;^|_(Zp6Mpa3T(;BzAimbi+>AG(s( ziN!@@s$IIx+Vt|WRxK@Wtr=gPoLoJ=CQ@4)p&dJ<&+C%!K+Md>C7;75?ZC#$iSH@k zYulQQ&`#YXq_tR(10P70nAnIPMh~mO)7n+-b?huBt!?PoHHRU0EmF{PN`-L z=-d2-+~Fn3;p)zu+~H;ZaBXLLQo7e%mXlp&aHaM-MzTPyS5gu=JbBa zIdA{~LMoAylZE59Rkl?Z2Q#C-?nzrJM^u;4e~R_8zwbr@Q(5-tJ;6-;IEjao)?NnR z2qjKV#SJ-!&g`4i6AvMHC$++ zD(;IGaMIdo!6zQSz~3W&^4J$wkzD&Nz9?lSXlsZ33_e%JrPKa073_m%M0HV)HkRSZ zI*t}(zmKa+7sqwso0Rk2kL=z>j|E8=FW^{6bShude=Uy8z-0-k#I^Xf!YZ0~hepnyp59c{1a)Ajo*6|I=bz_J}lh znfKkbcg-^TUokP0zaT6_yu!g*sUEoHl!wRcxSIhen7lT^dhzCu+KQ>@V_5sG5Bo6$Xd>!1@~+Cip=Z z+atejjF2PtG>_5Z&q#96pUD~I;?}#zlAb}M{*0?m`C_v-oCFXUvx)soHb=z2kfg}< zqv9GoR$M;+QGOsUpOyU2xD#}o{8bntRLS;Q5P{$(kl)fVAfRxvE;cuV$VIB}h~s6NTYtEKU`rR!7w)hs%W zj_7PY4YT2MslC=EKa6qsxnP%%rTU)qOf)wVbO4)M_L;}~FeVUg1KzKK-9s@& zbU;$Z`9M%avZ=wP(K3}ey5Wm}R^2hF%{|FiRYLbt zQMR9c#Qvkg+gJ11b0qqaoEs5JHwFU%AUaa z=bfo%$u!)YnM{isol5>PuHpOMP@nm|jFpj5uPC0GB@|IL67)zh6{Wh6_=*2fC)UkA zvIdzCzxhqLb;;dl(T_FUy7=z1WThKjSi3jRX_fl1cHKUAc#(te*|)FvuAgek`s3D} zc&4GaKjb9fnL#sn504^?`zdJLkJdurL9b%%Rp_18*O?GWAq@(;;4F?&0edtbiV+1& zr5njIRDm5cjeqf*V=v6}Y!=Z={mN&;Qo@VkRca)IX5PT(Acl<3EnuH(#d{jVQE-K9 zZJY6)=BrUzqvgs#H$cM^G>Bj+Rp=9>I7I+nif-j6RSi((6pzy;XOhpEQk3MK!P-*I z8-M8<3mbda1o#bGdldox$`9YC2-*NY`XJeQ@XBWoKFIDN-d`8@fO7)6HQ*j-tWU`m zL>+=cCg)ivK=cRXg@2+GpuB-C0L2|*Rw|9=vA7M(Zb6a`pfb0g{G#{s&rcDButv82 zhqZDK)|j3T^ufjFMA+xV=?u?V8?|Z_axI#+X%)cwQ3+ZtQVh{V@02noBOvw1SuBwz zgn*9dvcob-gk_}gVVRmfY=Dqteh)h0`S_6dYmYED(7T0{9zD8?_mM9)9~SfJUVni5 zpnf(5y(7HGD$qWIcn^L+dGNJkvSsqckRz#xz7}wcpW!TIT-1?c%n!S;Iy!eMPxJ;j z&rnL812lwjQI|a5eOYJJrG~$C@h%-+296Z!4F%Yqmyy>25FZxwWXVs=lO`4VfN*Gf zQv&CW=9$Bj8H%U(*O5eSztU=uLuqnEYUKz6q=$LK5yvCPkQA`t(FM*U_J<#Lmi25n zaYNbAC8KN7ER$y)8MbV6WvXfXyk4$*ndSG-x1D(G$cAyc8<&}X|H6=89DiinB)zOb z-?n)3_RZz1?l2g-mv7&`zHs?Y!&9KqV^{?Ah{SqO)Dnt~#KE8Ffn7T}TIfJ84-@qv z>e0c)Xu!%4h0UNvBh93kEv7g9jn`Tn3Yo#6sT{G%i?IcoGei-_L| ztbXG3%iP(t%-N)N8~&MAkI-?E_iMLA;a|Z9K|MTWTgdT30Xz#^V~2nVOE-e5ldO?} z0|{vXh9k#fSG43+84=zSJKQ5ybVhs{cjd%}wp*t+=Q$cWykdu!=ti{#o95m5#Ns*k z&M46NiaVAJySyRu<~#1*x-8tVs69Kn;En_L4Ox7wZu6bhPplpL>n9$%xw&fbLo*|5 zx9!|?Q#!T8_`MsV+bMY9Hxivn_z^cfg} zAPXxPLzpS`(# zMSHrhYTU|u#DAQwy>(_?&xHCpZKVODAo{Dvl>cY%U;KrcVM|(7hE=vpkzCeMv|vSH z&)T-8gZJ&2AMNdat8Lr4MRQh{jk{sRQmm-J&B62fpywK4t2J>ACrw7{SuzVECX>uN zo$B+IG|=pDv%JK3DYe#QD$rc2?Y3VkKyH)7$sx-r$>Ax1cLA}EXRT#Wgfxml;eDC; z)K?Z8W(fH}iDl2t0*kbcmgiUO8sRjivD-q$ztu zRWKzwW!*zly3y#(9Ioh?bKCtB>t{8khU-QOM?AH4bv|p@YpI#DZTQ}qkJawoR?snf z-puqFv(@S)vebr|jYIbBT0ga-c;4Yf^#_0c=mT43R)%W33JS)zQ9eqz+d)4q!ndH# z%9C*4qxH{|Nl7kONCxF)H5zfKk32TAl~<|Ma_4a;0ML1jJ{Su7^k$7O zE$pi)N(-fscbe{*bo-?@U!1x58Yf;8uRP?esxI@~bkmIEdw+TJ9j7L^0SKkgXSc5W z71iy>VlV5km-+A?HJ^hPz6cxaCrhK67*`Mz@Y0r{bwbpfUBI~nRplpXUD{7|#*b7K z$BmWIQXqVZ*;98lHWb@=9pt1GoAQ%E8_7Eu%cu2Rl;!jOaB6z)nW6}#g&s=OH<96mBNQvJp~n$(a*-P z&tNtC!bt|VFV(&yXVmDPg38<0E+1D?GIew3)K`yh?$pduNY$yeH7}vr%1(8oalxd&Bpe$y>%`rZg?>Yu>*mR_He)m|*mzm(Q(|2Up>VL{A*)Y#5w1Iw{HFUuhb^1`fBOMWq;-0eUUYRTgikb-T+h|n!5p`i-z}FrOGej{6FYYF zt`@#y&lJZ)L}goNG>h<|RM-}&X$ZuaffN_^>Vo04_V6ghC;l$4Stl6m&pJAd?FK3p zq5sfiG%6kuGD2-Cf-kT3mlv?o2o|H3`lHb9Z{U@b?pgC@WER69BPZ4xWPzfbjII%l zRoUp2vM|Z02_;+Hrc~Q2dCjM1myfmEs~#gB^j}zCKd#gv6?T!;y`RoqH^jbOkpFG% z%Ivh0;sd(*Q%cd1F7JGVA}x{nIr$i zTg-VYIImvs?Pm$zM8mo ze9rP^V^_BLB_3ZEG+44*E6zMxH0GwpP;tSDn`B&}L?89Zs3s{4^Tl7%X0$4pE(R4)W_?3Q3&- zteC`5EoA|^dr;Tl+?I;qB;UL62YZ>Teq6b;CL&(2mo!gndbx8^YpMNtpG@KXjXTq8 z(xC6JEypN_Wl%mgHii^^x9~sSdH;qRKKPISSh#1;y4UvInCcDNH1K>$wcZ-YG1{*z z8r21Yf-FUrqLeA+Kna@U_Ef-%mEuG#WY;Xlv}B4$TW0;GC1Jwnv%ru`sj+7K91 ze!GHyyNkCuo&o zhvae%=U^8sqOLcx_*5ulMqB75QJd2LKT1Q0%j9`O<{0y5mZXmjad;M{J<&!XbQ>5Y z;vtF|mib_nNkTEF+ri&k`JcCq42GNLZ8ThB-G6Yk2S2rSWL?rorj2C8^}dV)gp(tI77WbzRAMXg zcoY{>HIGVas`v^=0$IWODYx7^?y(mG_0u;zJgw@s&6{fdvBiH?yK~gq3A<(#FPd99 zy`wrSDNC3TURKrl&|T{%m7Y1%cId9#rj{!dihBe-@GvIqIXPyw*> zS;qsJfu^?B-7C6u5V7BgYrbE0>(2UzpZ#Fvs{ed?e?@C!&w0}JckHDH%Gq%2r4eT8 z#HiKCWn~+n&pUH-+&KjXle-{9O;{Rv04#8U!JX{nUd+lZ&CbplRigPQnOf?!r<94d zGr)4O8#TTADvI8vg9bFy_<;-e+p@}Zrjt1aoIVJvct?&hzGB?wo?KH(uGgIw@EPB# zn^GT`w0M!NYQ%(+*xZ&3f9=Fu?w{Ckq~r9Xm5U#mwca>w=c8jHqw7*#)iXzk&gr)w zXj^={n$MiNp*>JE`Hr5B`*y6JSfdh`lRHe$J-qzCPOWuQIwsdvAy=IGC-S+wqOB>8 zoLsx5E7z8vmt^V6OS0$YInlz$`GPANdl40p087>p?+g(JZA}%4%Zq2smwWs(# zH4#^aOqbeWAElCiszWw;TS)jwL)QFd<48Ylvt zbgj5D(8(;w*f6&!YOsY7r?Of&Gy2L9?bD<)s?k`8MneEwj|$`ta0UYasO(C!?OByx z6Xw%28ehA`VnucWYZa6al98we{!ymAGn6Sf+8|jd;UsVb!7o4;5TMLC2vT?8fgxw$ zA*4uRkUHf2k&f1tBXi74jx5uSokWC(MKL;mbTrB4)I6+qxSV4)JhoVzMRv;4TbH)g zb@4^Luf~eTPf*JhSFY%5=RL4s&V@w_Kf2|nmv)bb1Drmq7H4w|&Ss38^bA)3mxVrs zklI+ZHmc3f&oZASbE%6S2g02d4Hux#RtoM%7LLUXm0yIrWM4SDm;x7f>Yz(e1=HcA zBMgF|tc2OV6fjF-cj=J?Tg*egcj7oYmbk z-aN5odbuU5p-|^F){ZTAg@!Hb9QNe1zwRnuziHFT8Nue!o{BM5G;0QaVd~#wI82h? zPx9}@$EElf^ZO;@WB5O`W`V~1pm!i{7WX%@>v)*|P@13cAAUpEn~mQq&Ho155chL- zFh3`RB;t7RQnLH`9y;j2@ogBO`?m4g53(}&s{`DqsF`!Q1A4dH6YzSi9*@rtOv|ZJ zhZXL>fB3n-SrzEv>5t*}4aV>FtXKgQ2@wl&H1)nf)ddNRf$mjbDRLrdKfTOO)AI8G zc?Pycb)EqEpA$D9nix*58GrM=6Z|c!dx{*D(^^k&f8vX8{rqQs@dcr+a@yJm*HVfws z%g>VKNObWMZPeNBBfgZsyTX@|;N_~<{d2Mwu*}~X889jk#_&vdA_k87RS`>`#+gzE zuB$poAU8?~snCH=C->g1RNnpAxV`P;9-a01pF^$7A9{Pkl*6F5J9~QWy=9`0|7KrC zQ_3Uvhe8K}p+9lgdR}?%*oG1QltsTBj7thaM+i~q*EH)NEOlB%l#WOO^ndU%d3Ndn~K#IvI(*QeaT+|!XWV~r4 zX7qHlSj?GkrZjMYH%;c4_OD6}cK8(}St1ZY-K#i*_#-)6DdMv9VvGligBFExG(vl+ zo1pkSvF*SwO~98@{qWn|whnL zsLGu%ZER#jdDyO&-SL$8J5fBpYq(OeN3ohDk#nS`_b*wkv#T@bE}A>CNOngr`o3hj zL}#$PX&9D^zxF?r)#OOPLA62NiN$z9Ngcu3bWbjh*>NuD|Q}zFZ+%*OFAi%qB`uU>G`HliH&FB zXyhuwDxheE0}l7s?>zOCN;Uj+n@TZ%!F$r7{xVrSC$t^i7i`TEH_J)yo= z=7a{G0(L5Z0=oeD_`Znv08yU&g4ch3QoJG_6h9-Vl>A|>Z2gtb==~l+V52YB0s0~$ zr;Hv=0B@^#24VjjqHcx4pyM1)6JC0q1I$}zx~z{mFY6dzrSyfPHrP7^$1l@hsJ$Fw z2rB(2izgdipZCy$vZmc1?iDw{>tprzK1h}($7VIZHR+XEJI7@Rd~VkbLq{Ij&Ez>8SNAFO0FwU#qM&i_cOT<F_WQzsOX zPe^RWgH^qrs)NG}?%43L(-+t9t3H>l|3rNBpZn9qcH+VIJ?`zkA1PwXiSWZ}JOb8a znzU!&!;quUdv_r2EyFXJkt1A+Th|L{RH?^Hs{;^*KwG97)hd%p!I@ErVK%AM3L_^| znPU2wwgLGE4Lo=V`(mygG&1Pp6Hr{Kql&$x$hY){VjD+@HH6|$D^+77Xivr;CC5*Q zZRgG$-*9w;*!BW>>&fHC);_{N%&+X-&^wJk+&hW=J;pEY-NLV=`+*z<_%L+nmsHM8 zd72+1Oi=R~Z>o-+p5Jh$k1n^t?AZzdd_+d5W)Q^6ih#o%=+~YbLirjrJIM z?c??qwNH;Emu5Mf-h1t?J8vynG^5L@N~`KDjx3lxen>{L4-WzNF>3xjoEn}FKd(>{ zw9k+ZzY6{jje^KfaZ9&`6DWhH_#{lzgwqC{CgM#6=w5)(s4tdn19|-={^Li*D<@y* z4L(Yga?jNY;WcEgymqw$AH`?~-@&gaUy}IMV{{upQzaOGl*wrDH_+6R(7op1KPBP* z+=5MJC14gdaAuuCW)-wZAfagj0C!YAp{39;U=7r;rHLJJuWg)cq+F7?3}%X!8_qUi z`pG|umrCc%oH=uLX-00Ee^~pd$#ZM@RkHP8h!2Y5FK1zI@jE z#b+l`7JrLu%?}%Xocn^$okFqBijX>1)oz~`P|>j=(;eJ-JL0QceMI-+5{TC35x z%__CQXi}Qcaztr0ncY^mUahpsG=ifMm51yop2s5QVW-*Ivv7R{PcSVR{0!UoIJhzp zchRXgLM=yDl$t))UKt2?)>cQH;(M<>P|43B`ET?d$nTl4dT3f>#zBSFU(!4xO;{_c zo_m7lugY2%j>y;PS-#BItgodjo-eaJ>WF8`AiAXIQ{6iFUyv#zx11&85Y!1kaC!O? zW-j+7%phaNV9?W)nOBQwT{!xeU7s{Vy>}R+f;yfwLe&u}wg2~|8Nz#xxPNuZQ{1!m z=W{(rJimCxTsUU!@#XfW`CU~3!}KLb+HUGBc54>R=rvn57@s>d_5NKO=Qip#ZZtjn z^NQX3?p)rf7amepPh7I%hVmJ647$e2OXp85T(Ck*)N~JjlsDso%(wx2sO5%6(ZyCN zldH694KENaa*Q-GO-vq>HE6X;j#Kr?Ye)#%BnAQku@g%Lp@OndIVC)-Nijw*VhLP~|qO=1kI{nrt2?--B?;A65=Y%Y! zs_s;sVg9o0p0+CYjVHw)PVsdXUvBr!v6K;EQX{_9Q9N$EN^ey2%}^`|(}NyoYt$GG zs(~zpxDvW%B9ICN2c6MSh4`jM+dyFiWHDIcOx!S0T7*2Xi$wsB0+mR@1;P{e6_Ngu zz~v)f{^iRD@%M=M!_|5yGs|Un(7)_+|A@W$IX>6Obw#rbXrr#xqR$;5>QpjLEf?_V zIxTJD^`@~w-$Kz`q5d%&TIr`@0Aw^kNak(Cb&Wli|8#+Fyiro`SYb1+Eor ziIk3;v}tr^`O4>S6~7?zV-H$$nj`b4^^95B;CY28ekuMSII=qyH+G)dIR-N8`SiMM zhnQ45b)h(cCSI~NEys;tiQjV-nh7T1j_o>wUax~AV0qMFlx|pWh}q+MDlH&oCLuL_ ze)>KK>Zn^@UUYa~aSPQ^HxcRy2?M#RgbzS5Ia%6nI2y z^@K(AY^K2`u3>er4NoeA%Ah-da-3;VwaLw04g|CgL;*BUp!u<*5jKwUv{L?eEz?#Q zI6KXtV7EwJtt^+8_5lh7l}ssN>xp!rG^sMY6pf;Ub&8)Iy8ppZkBzwq&&%Ic_wM%d-xTB>KH739B~Z5JiRGQoy>M##$dr_$p{zfDxsR?R^VtA< z4jJzy+__PE`p$*#Y0oX6{nc^drL@`B%*8iT); zM^cVj>I!xzF;ss1HoCD5W4hm<|Ko<}I)r$1dL5R^DBm zVy#>I_#(h#rJ_%Gn!t@4bCqR`tkCki?o4 zt0jP!*@XRB7WLQxL#tAmoG!uork%J{=v3osurvfNQ|p?AY>_rRhTso^; zXk=%D=3GpWALfFnSS@*+%ckJzCN2cbYKubQGNoNY4Y=O-2gk^~PxO7sX;d=cQD|Zs zBtk+7rsRg`Cb7ULt28L(iHr&s#UaDnURrnHNP5eh_4jlid-DHK_9gI9R_EUDGW$$s zCbRELW|GOwB$*_WeVwcX60#8>5Vnv7!WKwaWD^lVP!RzaM5@$MtCS)FqNue>snu(# zx2?+c6Wpq`wrczJ_UF&5WcdE)yfX=){qDUCnR(wc@4W9h&w0*sp8Zir7CiF$t6zq^ z&MED4_Uv3(&yICXo3elHx@f~+SmlM09INaw zlKiNAGq*naYR^F9#wYq#uFsx2uyT}+E#Y&AJWRJ@Y%KVzFZqkm=|`E0r!!JAQX(dj zDpQOES({?INZ^wU-v?q(YN%8FUW(#U>bPl5NGq1if=LEQ9dpRcOsp;9h(8$b@a~@eO%0Z%t7rbCtLNFln%kDur+4q;^4$43 zc}{D{q1Eg5>$TyA8KLTdjvAXI5-CcH7Fi7k4F+p*Pi<*$R}+SVy@A+pz2dEys}iON z=kbXHb4T~Sws!BF9fiev#;21>Cyl3MOLfz1+wi1x7{RP@q=!I!{XH0J0Ia%6^{HM@>xG(45vF$ zKDMab5D$+@Mn|&MdC5vgzNH)OzK}4~eyrhPCk@SQ|ym zQWA%?fymCP9H%NL2d-^JaJAYH^ZJW|>gQ$A6UvvAoKoqH7X9TB=1JxkaDL?9i*~x+ zqhd7Tj+~ZDUb=d&5u=gaTacs$NRCU=6i!s3RxHTWM3M%CZWN`_nh~h$e!OjHd#iuz zeIMH|wybFln2Y92%53eJnLBi1#`$0RYUfwvw3fJYZ+ZLinNRebv{>!YmOxeA?bh5_ zz#8;w#Y(Mmzgf3&S^lg^xpJvHw>DILsPVP!3{q3vXoOBtY(!;ZEn-H z!FQ)a+#wRO=?Qoy=$f%0!6FL0n)aOboCS$Y5< zo7vk>nPgUKUsUOICPrd1Nf=&@0&%w@e%awsTdk;1CgXVr_ASQjXw$r_9v=plI9cFA z4726DjYKcg1y1}i1)ogvPRCY^#R~r~HBZLI!xu?!B$Cb>#}+#4<9n@&()?+IJNq5J z(xTG3X~myfEC2BhH+p6?-o+l={>vx+@`DERJtBS0x?ixm7vlZH>sT=K1cyq2LnJSR zl0ju)ImasSVw7STvew}RkwgAR4u$xXB>!TYxC5!CuE86siyucBu=f+}gEyfdBsyH= zjqQ#Xb{~K3cqjXM=W+Ihcs-C{F?#^Gex3gh&PjhremDu9LvbHAU4aI}E{pGX!|np# z6nqcU1J(vq(4uMddaMSV(>x7y?aaaWkf;a0Z-q_Ri9E)IunU_tu8>rsm6!@)E;q79 zEvwCyNEBAWU*y7p7zR^CZo0J4bX`%2rRw81gowR-wI7AqODW`}}iiJs= zX_6sN2FyV2Poz!|T&j}>VQNL?cw#aIc1DF=Jiysg>+*hywDFO?Th?O(P$H*ZFDhD6=J?r{_HeRI5!G1aO_C`OL?S8e z5*;2i0*Dn79Q~+Jo-_~(E-4~e|2+khqs|8S-|h%(J-#*Rn%n&3U2l6w?A_I>0u!)%l%eodClZdL7iJ{z|oXYe}iW?JTz;H;LM-X z(zj$No4x0TGa*6Cr_tH`6C5;s$tK2TOuC9O8HW z$$tqr>EU149ZNOHU=0Z?!>%DaCe7B7Co$==K^bwo2v9QU9lLtk4Hu5V|nIW9Q4uXNho zL*+YeGUix0)jl#l9(MIFMQbrW9$NZrraz`MDU_;gl`52-1@r03SOD?XJf9WfSuWFL zy`%`Klp>K&D$PoL$=AtuWMyS%vs@e9q@dV=QB^Re(;<@uLQLsMb3oPxWpprFvP02I zIq(cl#xGqzV!_`9mvOHido{jp^33!}Zl9Pht%aOnkN?A5o6)tgJ)>ag;iXNBYds># z?Z^1fvD8;p*K*ub27hX4EW5FrE#)|PE*IC{y{09tdRDPH-@!dj=M;W+{O{?UW;69M zSnmwkitOwxMaV@)Y-B

    qoMXLXHqru`esTP4khDP9@P+BnF0gOjdp%^GD^S>xVO; zbEC0XR88t|RP{ELEY1TXK5wvXbxV4Dsx>PT^(^_p;%NP?FK(E#ZF(jz-hP~&aGUHVig>QeQ>Woy%&|8+yx)Av5g z3QX1?rny5M>nGol&>eN7zgBp*Ac80jmGV*;t-)$AT8tL6(F9#eC#=UNtKn$|g_7F0GdZdf{S!rqWsyc0XNz2j(9an$pz5Dl6 zo{L{N8*W`un>jNQF9%1}y|=6NaQw5)q3Lb=SnXW|x2}GMJ<M-V=`UUDrH^VAnp_vXS zRV8*HO#cN+YEUQw#Ff-r04^j;DiDGSaRt^PvKteBj)c%j07y$oz|M{w1O8G+>)a)! zv4wSMdV{Ba>a3xgr@C@mVkOZ$o7s@+$#KQDF7MFAk|i19Rxz{trymu}sPej+*3PW? z;o?U=e_~mc<^I&%uI(?}GIF>$uO>q~d-r&ahB&fUBw$K&IkUf~FkMYZGS>8Nz#2JE z86Q)91_3t;n@{{Kq6{rs_U9-llw$kK7CUQqoin#9KT^n0On_LfuUpdJF)ve;QI%7a5xi8VGrlbI zDHt25w7apcz2xPhj+B?(f^w45hUC6Wr3PW^)saB+1j9)yxm`oN1;=y(lEmIC zEGWo=RFQ5X$OH0jGV^;hzS58*&*PB}MVlg~PaN6KsjDXYBlGX+UAt2%8BWd6d-DP= zjkmJ1KW2#P{f>f6uh(xeRLojZU9~O9F1BTRKoKu8ODRbnBRr!cl^s4KAZhzgVAWVSgn~l7w=4QIGk-Jler`O zvY_83^cCSP=2E1*1~~#PBAF%@88yfe!w$9? zO~TO?gLh^*n^#WtC81{jy62Hs4|jgudE|lX!OJb-Id8+-7nREl7a1(gHh*NN07 zhTsms&_DL$2eRTXy>|R4`)@~&zZ!q}DsvT#&2hf-ih=N=B!^51><>E3c4JHQK-iHcZr0z{im*u5hKA2_DjLPk?i#6iQiSix%VHLrlc1zE(`v~y10yXKyACfSP!d)2TtNuWNLY zn_I?l=?x1?r`_3KeDf^=AI%!FF{$|dQOC%2Ue`VA%UpvNA<6vzO37M=n2=zpr>=hz}X;oyeuy5Uv zE$745twZgevGo#}qih!IYPn@_s*~F)65%ZV+gD%pPhPSAIpoaoPhNiS*Ojn7grg& zcyCc#)XMV*Ik_1{HFVG(EPnJcp+AJS9F>^i>*PBvg6|)sh)7Hhx26ALshd-E^?52Q z5Q`KV${f zCb7C)p!Xyci5L*+P2uPG@y5=v|MSxmuX638FXM}^90j=*cnRF|+T60+6SNwn`P$sF zOdX-sgtp&s{f>We{dmpk8?H~}mZkUELhG&4+_mGe4tUIUxmC6&o_j;?TA@Ff^UdsA z{MX!atSI$q$N{9BF~)xZBZoU7|IU>oZhj655To<#)qk>YiMIZ$Yefsc-RBYw(`zx# z#I-sJN0wQVbygVU?|xdx{NAb&GdXX)iAR#!)p%+K2^71AR3goS z$QxZbw<6V$Y7Z6KYNtCZdNx1OE0%HQg7zYt-`C_NkR~DpK{=J%``MSqj>UZ0Tv4&h#?+ zoE6LaTCxdW*&aX2ZDnh*pTkOl{pB^@C%Q+y@f2PWL>*?nEb8okDvnP;tPvln-b?48A_NF!?E zSu^v6#?gCbiM6~buQ_T-^QJp|MSfjpXJqlf+)1IW-``Beu^HpIGck_Br)F5t zKquXua1`=BmB`otHzoY1BPzv^2yTkMX8_+fDE8?0^ndK1S;+~@w^X%b!bbOY|B9ni zP_1{<@D<#J?i@ z!gkOg@=QHb8}q;#q#{1pd8gBktp*53C{iWOw}H2n*xz#o}Yd@rzD2IKqEM`>}8ArG4?wSo1zEM&Tf1{?0Gq zJJF|b#;$#>>3Y_ zOfa8hk}W2_Vgh3UMTmAl%DTga)^d9!S`uuIEs{Z7lR`ZQb<#5Eprja$3i zp5xQ&JVEpS@DoGCt|x+A_7>JPk5T$y32Q*ThDFTHHK~lBJqWEx72dOuJoX@R1v~Jr zVjA(@%`9LK#=@;xs@7Ij7SFRiho<)(YVIsO6b&+O^W2;8)8^UKnnS@(_aV9YP%83V zz#K+3Vl+;YEF{u``JqyV7RSjy;%{Hfrc)RtO17G$*E;8L`tk2b^!WAPP5kb=--*tA z+v*gZ9y{irvZ|$Lsjq2OYtORXf%IUJJ0&AMv&f@X3EybkxpmVEZupKnLBCw4@m%wz zCnZ3C$nS}7+sAHswCa|{wS$9YTb48oF3$}YHMiSyLWRw3S58)K8NwGCUF}W9?!jfb zq2yQXo`3lb*|!4BLVgziJ}9dD6vr{pb#Hx|MY=&G6@W{VHS#M&Eqs9>Zvr&ERL=6V z;`hJ5KmIxU9Dh^%Nc_<`?0J?Lr|r1U)bF_u4^n;oeUKd+c?nrNyX5`-tRdd`8as!b zHz!^ZpG0`6PU0xwFxJi~*7Z%5q802-6laE+k5rs{Ej+Bn_+KD(+IQCSuztFb)>)PI-ZPD?zZlrF})3$=~wqpGDbNYKROY|k$N|SBu33i>RRzmt* zmTY4_XV>wYX+fPLl7JM?4g)k<$Oxt8+=MSpGXN<^jPhf zWx`-xBW_8@y}78nq=;Xj4>k1fm{GH&C8Q5G4(ymwHPjM5VM>oWvzm%CttwLhN0W*J z$m0@-u3fWiS{`&zQwrCuUfP+PHfi~^oJzmiU(wpt<c`Vya!E#T#UVCSK)`PE}&3lZdaYC6T1k2=13X&@a+BJT3o zA?Fu!{uW?o?BDv@#l7r>WxEcIZ6MF+zZ&FyfCXxhJ*{@Cos8*O_6ISwlu1!slp=ho z*ZF@K@&pp;GKTEa32gytR8~{}J!;_yOsJ~znPqH;%JYkt|hX|2J|gM`kr%vQ{e8~rI@rkrK;=t>Hc zZ)W^vhB5nj1w8V85YxL9e(zJEe9LjOSsdzBs-F^LzGxnR2)NNiF9oF#2|t9;pV-GH z$~g(^RERyICYpqKNg{P2qio*dBcsmB;%trGGhANTRcM*k-)XHaZ_71A8;XW$)zQW+jB|9gu7%iQ+mXfp1swQk!5uHb?V*lwHI5nE7P0SdDOLJd>n(? z1AH}bTLCS`Ead4E@k$=nY81KWWDuGkJ|mNH5?-OgVN5Sp$t5WlMXX2(zkSU1JPuPK z_-;V!O8n%3qS%0=)H=!e%q@yUoi|9Pxj8Ju1Y z#dHj#mxWH)Jsz>~q<%pm?IqpEY3cQZQ!RwziEWpI(m9d%PN)#=;u0$xKB%mb6f3CW zczfooy9c-5tqW8H{WBIWE8XJU+R$4YOyM+v>|FEYh2@r0N@re%Asq6n#A2?z(q9#^ zPl+y@Rl03+=CY9$L4Uq0Ww$N6(k}sxg~p&Mrg2%dVyHMxfz+B3gFi~)e*$>16l0Sx zzL=IrcpxJZNf2oEj${FybfPLIIZ^~T6FN?0+}1c)$3Mh}TwE{v_}Er%&4Xm`+XFb& zV-2C+FZiU|7nm&eA+SXS>_cbpVPF)F=h5J2D%gi0N<@mqfuC7>YmSqN6<8li_^4J; zNJ5lcKSbh8q*?+$L-R1XSbm)4F<0?82@nTf!I7Nj%6f0EYaXh1XOuPhOaWJFWaftE zJ8$t6)TKr17ZlceOG4(NYG-M-y|J#sS)S)kapk4ED)rDcL1v!k;(-lUK*;i;)+T8y5y`dtr(&i1>4Njv%VMg2+*4kdcv=MMf z1HTwO3$FOkDZ5<`P0XQ~HZ>{-i8jf7r#!ui({f7U5*RJ`^a_NZpaR{}iDHe|E5?0# z#V`pbqjYE}GvBBSl!t16e(U|tJ16xuWNSEWC_BfPJG(JlKDeWK*B$YT*)<_UX`?19 zE1(pMqwJL6^3mm?+Uh;VP*o5yOk!S}-Bvw$WlQFExiy$)31x|-3W*_{T2e)Er0ePw z{!Y#c`Kc&oy+(GDaq!PmiXlGz2MUBT2uG%}6G4}xn}A?ONKXnaL^CuTf3@^jY5X-O zTZ~V(crgAk>o`EdTM0erVg~s;nK-bC>Q??^T(^==@NW3ZCr;irNK`9;bL8qt$f}2N zEsF6VtG1Ly8g=noQvNOSD^fHgnG=Ehmi+Cx@+SvxUr6u5rtFXF_T!nXEFRQ)2pRgpLPVkVYHb|ISk*bBfQzIHpFChWRg_axd?B*2lYLOFJb={Z z*!2AeEwv=WV-0e$)$pP^U{D?L>4Rn(Z`e<-kw9VMBEB)g``fR4@#YwS;3ZMsEQ#1qW`U0rcQP`C1U+h1&uQ|Pgm2O}C^mcYZe7Bh$eY|;Eiak_5CDJ@S#U3te znH@2v*$pX@Y?PPP@LR7SpZt|A{MIpU<)Yd1(z>R1RA=sxW&_a~QtU;+v@~A=o zDEx%va)XS|t@w9;XYRj~0^QhtgtQT4-yzBH#$G|lrr_yB!|#Z1V1hD-St$wNC%ZPd zIWQPSyr4fBXVy`b2VfAGbmE)9FD;t6y0a*J&$e?Vi|ZT*@+#`WIqh?^S3P-Cby~xc zd!Ajn>iD{<%;pt)p6qPDZAp8fEqlh6&Z*m$b{0FcXWqi6)y%K)U*z_f^Xv2cq5o*% zI;ZXbw{_E=e)5rZlREdky0-7!Q}+*7Z7ZI)W$#1Ha}SMF7xrx0^U&0JhevAwD~{RE zD@4brz0t-jjTNY53W+8~mdcAYGQ5;J2GOfFgV|^{Sq$a}Scyafx~@|~Q^uHdDphKV z6$X_kF?^mU5t781jAOzX07|LR%WuDFeI+V{tr0eXywD74;nOF)uZhi=`d!1ELX+x< z_TFw3d3SF$id0KipLxLS-TT&6C#|&aiDmrbJq)IGGi&D)Q^dBO z<8u5wTk+{%*ua(J+&lN)``7sSJ9odrN-kbxMe%?9iT0xl`mnD-p90_$OHaAo8sfrr zve=wW6AEkGjz9Y{-!~f!3SU?LM?(HQVqPQaU&8q$LwZP}`D9cs4dXGxn0t*w7cn0u zIUP6(gM6FQi<|Q*jxFoFr8}qowwE_IY{=kE)vIPXCVSGRg$9p|R>ixf-qaa3WHsbh zFYjqI`&RG^ve)g{vujgc#iZPs8>S4teD7?3TJOo79a7oD64{hJzl;B&VqnMQ(BuMF z{;E@3(@O#O-+{^dl*F^M0~jyM*ip}n)FD$cr7=XTkjQ$(A1IZKgqNe}1IK?XpC+D# z@bFngdVsV@2i?jy z8+m)%*gqe7K_nr4L2Ya1ebG{WF2%LUu$+|T8jxbJ1>S14Yzyl07B*bgRE96R6dJ1! zy57i3mtP{1^m#3*p#}8};TXQI9a?~E_8maSERW}5RXScTmLz9c$4+p}=oYID{BPY|RK}sM~+?dyv z;Va27_(Q4w!e(xB>wT*y*`lolC2e`;w8|NUt@~HjX|g(M3tOWWdr4PkE>=%Xu&_2= zjd(iC^y%46-Qk=9O`w6`!>^Fz?_sPrHxr3jT&b*DoobSpZSS*cSE`8DcS#lR^FqQt zP%N^>zz!1J!{n3oH7+g|(~W(?n~HP#et#-!VVWy2HzR9uX{O?`@86#e&rfsbv}XjH z%ClA3XI@q+_ABN7s%g2R_Gg|}%I`~&S&C*>leyuLljYAM9Y34yOcgPw%A$grC*q6v z0`?EG0%oOI$!k8489v}W9|XXtzWv1|!4*QA_?+Nd1u{U^enQnjxGy+ali9kVRirCk zj~bHm<`v|2`D%+cPb%u2J1?3$(9oCgfzZzVI={>lM2hoOd6nKw zZ^#|DU)KoqL%8UsXD1lEfeWL*Qn(a} z{5=(qSZt9>B}!3?bTSw>#tRH%DN_9br2^N*<4weV5Z)0lVVi(1fmmBaR%iTo>)9>N zc+<&aC%<;GdF$g_9PIO_kDcbN`(N81f13^AgAK>uBJfAYMQ+hM&T*UWseTC ze_&tV7(aUD>%DY-aew~nvzUdMr(>+tgIpyuV~Uta?vcu+ za!&RZcKeuyinI!IGFSD1?kE@>}2B;J3%L_?%U91eH9h`2LN-2{{QY3bV zmtj#>L1IgBx>6(}gUF$DUh>LPY;w5sLMl8P34=*CJfV^^MFO%M zCs=N6lG~;#C5sjlLxc$*sTo5YI|cqbQFjy?KOl)WoedfqqbCX`86G(4JZaC2e_*R= zsCT(bw^i(T%xdMLr;i_qZ;V$9l~uW~_HO(2OCq^SdVi|nwoU7=tmnRhy-pY_sblA6 zJj~`;h1`pf7^49W9pmG5lkwV8z#gUOc^w_DdwjTt4t9t%Jnk&>?0nd2earFI^`lKm{bsl5ikRQIY5iVe zo1{LQ>#1KV@^e_MDuq;m3LZX(-mkE#r4HV&f7D>Ute(OA5yd=+tG3{lN{T#S{z_Tx zE3Z(E5Ee5brU|h~3bQ7mOUVoc`UKbV&8)M-UCWCl`t8QKTMl-WFPhopT$#1CdH7J; z<`mYN@0-onphHTmi5D?Hc4j&9 zTB&48QvcSZl}Hszu~KYOnov1Ii?DqiZ&ynVmlP&(YN|zT{|LTlmM-l}%9n_h^^5UL z3)B%UNro^kLYIgxla#)NvvD%F4i;_#@Ps7c59A1c6cJ5k(Tg=V<*$3d>SLb;Wg20d z9albbuxTUlpF7z)Z|2{BSGc57^hdR3!=@8^$IkEG%~kAu?AQOMR1vy*6J*&xU_4>w zrdX*_D^vK=+%BizsZb&xkX$SGYyB!GUSW?i?ULFr&va_7NcLw9f5?d6%j_~;HtH@} z$32D<^*6}uP`;e%J+AYSy{4uw78hMwG+KqYo0G8#HBybj)l)Y>|4%wxWyQr8k*`0^cPeuYOw0s;_o=wfY&f<+s_skZY>vGmZ{_-Z_WJ5$M(i= zT{164xlg4`@3?sud&l0#`tR?LBBdc{+#6T_!EYDufe$1+(a)kD;{KRQuac|fI+RTa zWFqOKJ7!3WY4uHMYAG+jtZHN4)oCxmdQGtrDzj45U?mZtkyp+W(S%q`uFc>~M^t=c zeO7*Z=7NHzwJmnTQ2e-)<8~TivpbsNCoP#pE?2(KKtJ>R28BxHo3?9!6YU$D-M7Fj zTIepyG!Y}2r;vUUTNVDu7~o3}P3iT%^#2fiR1@vgA3SkA_~qpFNalxsLOZE{I^N!P zO*{3;Puyo{qMiSc)QC^CW4}n=ht$bUwzJKooxmD3T?wqSm>4s37TIn0pUl?7WwS4) z&&Y6T^lo>S%VLDfv0h(On@=Ebt%zw&O%-~Z+IZR0=2g4jbvfQu2ZaK#5s-saK~pe3 z1#||^lMp(HR>XLXl_TI1v}SB^Q8CE^PSHx37bE=llld0DGe;8@WZm{aQBL^{v!&Nf zRF(bk#)*q33cOqM%hDWae2VUxS-eLg8m~Mn;clE!R=`O(sp-H^nt_W_ImJfx_oE-A z-}B>1oUU0n!T`S?(QZI1%niJi`Msb6a^eeo8pcIAiWuyPr6OvHgcK{pQu!qjI2O20 zxMB(`DL`yxF7w2l5WojT8#lkf8Y9qS%Ebt@il%``dWPk2Tx0wXPWI!UvT4pMA3NFF zL#9B1%bDxb)6cv#JwJQjl^^fjD_Z3)$UteGTzld(fnT(*1iuzvkME$()F(U9K929W zUch&H{RF<_dI8^s_HleiyMXV){m1bg_Yv@2XdlOSvzvWDR3{D4(6bVL29b!3RAw`sJy+jcU zf-@Z?S=7Yy2}8wl##n?M@J1}+d-f8fp|s}eqmaoIpfzUfY*R^G*uI#>VlbJM$TDx# z84TIVECZXR(tIeH0X>~NrU(#96EI#PVYw8g$6kTD89W9!MXGZmB0Qxh*YOun#bYP_ z4(T}hT7t>wp6na}lTo~2YN_@}-05WBF$W7NiBEpQBi}^04P$z~b2O(Uy=7eopSzdU zeuplQG4aHC)lA@)7z%Q^C@luoLPuh}OnVPo@HJg0dB6$WzNg##zwGAsbtAy=Erwj$d@J?~Jiw z=aqMz>=e;gd%jg+Cb1_4u@-krp2b>U#xSX887{GjR>IFE zz34Cx;=**oyYcBpm_rFC$LSz9PMt+T(8mVjr`ILn!0oy7;e+gRd*Yg14<8siN^u)w zL0SvZH^NxXGg?$P=InqI%RB?U-v-bdAIZt*lUfxHX)1VXsluPGzqbBU7t4G?IBLSf zyhoH%&`RKwtTl8QA=YHQK`S}V#$2!WlkyUS3o%X50BmS@aOR{1QL+AB6>A9C>TjzT z8YKGtN__@7ug(Z(&fM9{{jqdvj=nJCU`7^B`^Ma5EjUfb6kt4B=uKBkj$m#^ggMp* z$sD8lyu2whF~^yi6LU=RtNaI@V_Z+?7`YH|pJy<~7{72I;AV1;?*LqWNqOA6WA$OF z%bn))gT+<*r70@8OsY1jkyc8bm2EbCLbzFZn!@{`$F1@xKU6r?R*zq*@ZiN;3EaY& z*T7AnxTJ;;+4Pcc*TM2%m~o)YzZSp*C$0ywh9&s%T|igF-(p*eZ-llNDu>e@*MpqR z-gW;0HuXBVKX%9c2jWB5!XEj0MEd}<05@`VDS;-ljy(nENg|(p4Lob+uOLq-^qh=V zXVzLse3u;21@H1Kt2K7W{Ezw0q+`O9bdY*6O^t=c0rE{Lgjs%k2``Bl_5;r`P`V zo_o08+(YJ?_D$I9Z=yUvVy_qaOxFf&XQz;5POgUz0b>Dfkaj6Mjf@s+rVnE*gAD3o zDq?Q41eIl-E-}k$Ty_|7Rjv+ahxQAN#`qUiy966U^%vkOAyLzGvWcOA)Dqc~FODWh zm;?C}x^v$5x*}Dh5+AYpd)MAEwP;nYv%a>%)VgJ$wbZX@KEcWJwr;;U=g&nwbr~tj zqjHEVSjTr+p?pPw+FFt{o$7FQa3m?a47ReHbIgf-ws1N&G(w8Lt^*%x7w1 zK2DS_6`7@Gl>x7aLuwH5>1K(R`83pSz2s?^U($jsfT4p0P8cPnVssMeG}6htK}sva zoRW%}`P3XGXl>0#m<0B9+`D0>UTLVE5o+>fEGV3^W{Ooe$ynQ>G($IP)K)H;;?lZj zF4<6L%F7LFvIqIzDYrg3Shi%AN_nqb>2KT8!}c!eW4V2ty<&cS*_@g*xEk??l`^v4 zA@?NUbIbMcA>*a+q3z@FIZAPWz(;5o;Dh=!xPF0nG5GB&X4P3HgWV7_m#X#1#AnbN zj9Rl#T58TM#mk6*{tYpkH20&t0yt0V3qH#B>EF|ubvk?YG}T9T3OTF*5@}5c0xWlL zgE1B=bkf?gl(Z&j0ug_Sd@^CB%_J%&P-6?Xq;u2k3ctoy+FjMT zWp0Hg>B}kKa`L(}x>&-89$YUeW5E(KE9?Bo;p)7Uwk03TL67E0Nn6 zv7PUfY=pKS&J`~(97=TXSUYoLx){sH4CW^O7KYWrfr~{N)#|tB&9z+!vk}|aWimQn zI*Rgjo{WESWo(`JvA5!f8T_O76!lNslcD$IgnKgNp3E%_r{y>q_dWMS#5F7GhkfSq zl>?HEZwWUQ?is{ACGf+TdJ+Z2;e~J}CgMEEv-~XE6SKw`5AG=EB;)~-TUew-6~TBT z6?&bIZYOd%*-)7bqCHhr($E%P01B@tD+-<%J@hQAfBedR@neI7^ufY&yZ$H7Wkuw{ zqFWdwFydsA#B=f3{D1Y_C|qg9J~4ZlJU9O3<70!8jf)n&b>}z?hb6ZoM#9GUVmcM0 z;#L6h>Ey>Pprh+0Y#S*EQJ&?1qp{%WCn~vuU!{;;?9o!@I!h5u6ZB!I(7+(aMHi09$cuM|i&R%V$4&eY1VIn$X6Hwx3l8AU8%F2a&_imRz-;NAWT zMM~m?a{&(ryTyvMW3St}!f)RazrH>GdIA^IC0oG*>c~hKHXDZuHYcV=B^32}MywTc zGOn=r1aqSL;;Wp)9Pb>QG+kmu#(^Sz@=B%&deE z5skyklfsUFUGf{)GKiKp`~o!h8GxOJ&4_8t%wB_SFQd@Hs;KZJ9!ZIa0K5qynt;!o zmT>=ow+_pzLXA;JT6s$-@#zm0h0j$_w#m@H%NYF2ZR(_h`5orT`vGb5`wH;g}aMf}fC{Qk;3 z$&+v%OoPGLYRRcc57y^9=H~`H*^z)Jhi!fF#W4~dEWlJ8q4Y!wTW7-y2!+@NG9YFb zo|G`$^Xz==^*nn^%n-w11^R+^N?>FHl*#hP)Y{N*P;J_I;zUY1$uIxQ>DRai;{SN& zy>Fi-1Sdgq60~ufWDnqGU@R{HYwkdI#H{C}#z0V?P=BD^4f+o;x83NE*zMU~oiRNZ zpKjgQMv1}!g`dG2w!p33U`PuiZ9DLp|D1gXag=8G6nLI8BmC)h_$+FeA8j%3F>}ag z0)mSrPBs~EA+V1UM-PG;2?s@=bU09xV^gQ9QygJeeqC(ooz2~%1Lll;huLX!nRS_7 zQ%zmx?F$M^37^DTxlaBb?kk-X*;?()Hrh7z>V14Q{2ZjPD3-Gk(#AZ1Hu!s?Z3|N< zw6W#PtNe0N1L6jqhC$pbg7aQ_?jq;N_w_eXhRA}LyJhrzHS;(A|I+h~^n5Q@E>dxS z!0$EmT!!=e=((1hvp-=!n3d3OAqC`}T@xsqPLDt?Y%M9)?9{B-;`BBMl& z@2lwf#`tgeOY}T8{(T!gpGVJY@cn<%^96=M*kV2bzYv7n0NxNW=kkI=9&Y$ishfl; zr&x^X7HPW0k`7-#cQxw1rJW8jYkQ7K%S3*Efm&qUr=c8Nsd=_Ma9lr?~F6Y;U6 zSwU&uUh3)v10*B}qBWzcf6Sv8Hes6DlkigsH&f!-B;`bYWUEi5uoq0tpS-beij}`m`_PXbdvJSS$*1pEJ@Dvb2Y2?DeHvezKP&UlJuTaw z?A=$lJil{fl6S^EFD}6tNN&L?kp0b|!a=6*EW#ug$4nVgP0(wGLl+W`TRa>oF2JzR z5iO1xr9sO%m8Knq1g3es=geFu_6pRpCVp!WqlFK$67$asGf7<1g&G3Xc?*Og%#E(nwlGz7*>@z+Cp%lU7`2jlVrBtSlL@O+TkvmIN@hrRij6;N%5=#74F--;n)AS-` z(FbXrs=p`&RghpAruLzP=nUxOpy(bSg;E4Uh7@&&N!5C2EQvJ_U%=&+G*Ya9upD6j z%?eGiR9Q9jKp(62#Q(FTqtJ<>8m0`}Ba>%_IF+V#$N3$@_jMrrxPI>Fxr)KI;uMWI z(p?`L?Ws;ta^AW^{s~=PdCXfhw?5;r+U7G^(%r7q#gP?jH&ssk$sMz%ZhvZE`rb9O zD=bsykJhwqnVlP*xu&jo-n7YSkSS5Kj(-F8k2IQ#2KI6x9-K*s?or5vO{7nj)(IiG z@pMdcES8J&{RwL!teD^pWcZOTN7&M2{-!|!*Z3`lAsSYHsR zy~L{xUOKb}GpcQnXa{5y_05idx@C^D_`b(j+50n$lUeCsxzYCz#Y@;T$3GuCi2NpP zTeq@LpvyG;)pvsLCs9u3EbCOvC5Tak9r?#2ae z7K^n5d7zU7jhG(bok^u0D)mR-gvyl4#&i)RmH=j#&fR#eNwP;l3kHWgnyX}uoYSWn zQ}c8Vlh0VwU2Ku1s3q|^LekMANIDwKsmjn2(S&ytwHF%8YwMjf4e21l9H}gboV&yp zMYPMvCpE5{2aXPtIbxBwMN54eDDGlkg^qt1S?Wvz&)0@S1=}3cD48dfzfit`uoUeN z#U`@9j{SD-cX_@9BC;a0s-kCIL;H$qo$`=`vqVGTs<2IR{Mc`3eB!iS^p!{Yv4rOd*Of6^R>YpU|P0Bxjr@Efs%mBMN{^%A}`K&b!KfK+1 zNGx#_;lUw`_{t2)VA-q+*G+325wBkFjX1#Jfd&8$Ye27cNUb+7rmRY3QVj+$iX8>d zvwgq-1@<&gBg4NxwWG?A%6HU-f*l>f5FbhTsIJrUQF*8TT!eZn3yJ}{XTeMPZKCIa z8y88-h&dPBEpy-_Ks-;u786RRrrz45;4X0%!peqsd17OO0Fpt|R}$`@Rv0ekx)s^> z!Z|JFL8IPaZJE^@nP)K~0%B@QQ;9cFzo@ce)4DZ9D;w{B=-`xj4-HpkG%UPn_tf^5 zRBz*?MsLqtSNZI9_s!0jt#c_#g;1o;mxVvop2 zeDSTs!*}h;qH;u}kH*R)`N2@W+hsN}CQhy>j~bBsm^H!3z=~~lmn%AEFxN@s^3ZRQ zNZYN*k4TV5ku|j#hfw$u&WKnySmszb+E>UXq7}TuBE;L7=#9xgVF#h%-{7mM7!XC6 z7SRd>c)?*Gp_fn>5RV5ltz-(Y3zi-;-Bm#>dF{d0=z@HA$&$i5i9>ozr=eGY^?riTX>5ou4~7 zYqTPjy9~x->*-sx{ z-!yUb?N>#!MmKGU#@94;^J@xcjc(i!W$*Nrg^;A|nlVLC8?5Xs1eaZ>s;i~W7ugX| zU@6{%sKtvSer07A)m8rh6SpMCLqh#P=Y)95YgBfWss+9?w(G@o?m+AMZs_dHg#IK{8|0DZ zO7j%k4+Im!eP0#~YzgnLAPXC}n1uU!`7Kvo_pt2`#~({)e|6;7Z2F_T?dXAvSKukg zAju5>F7VpT6h7lZfm4l}MN!i=F{O-^q15eF-69V19iMy-@$Vs&hZ@(3CJ>KF2G@)` z!xA=L!+H6QS5CUwt@F0F`?_a$?ym3dd39y;=ALL)?}251O5?f@v5MGGUtf9Cyh$Z% zrgjBeS2j)EzGX|}{U-EFch$>ZM!zbU>1Ux!8;PYbC7FyplVLLLydp}r8eN(q7|%?< znp5I?zbvHWNdchc3EVr?R>j|yEROKWmd zCPfGN!?wzS`+vKmWJ8g=Vr50&-bFQjb!PLDnx8w5)NPtyvZ%jlS4-N|_Ku8MJLg0) zr!0n>pDNJw7p^`|I$+%z=7A%4G4azVv$|?Z`xR@5%@KAmj@!(Qm z#bP*nU>_w$ZaA8uO1$9SpNIv&*7i!BIbqc#p&u~Wql%RrZ=by1aH?_aPB-^DD?9dn z{F|{C*t1*XkF(te`2|-FbI*=7QT+5?^@?QZqnD{BlFsUw-eI;|EM^7bgB8|6bDTs7 zV>-@Q1~DOGG9jpvf=+z_7G8)ECTu%V1{u^WrGnQ*W0N3ZIsyEl1*fhuhs}kB`9@c0 zdC~f(Ru@0G@9R6bm)v6?w=SLma>r&beQ{40sm^*xEQ<|pu9`E0J8>Ys**k?;!8nH0 z-q|i5L|==LUCowFv|lo~KrD=JWe@yYJlo`-l21(#D56dTyH$e5Q9%W@q~pw^nJ5 zHs`l)oRPyZOJ3SHa@5=(3d zmM|8%|AL~$fIaFN#%Sr#L~JnV!70fgoqxiHekVLw)O6)@R=gs3e5$H4{jmx!6Q(i7ogIoLS`1rYCCsxeGmEQM7Drl|n#St9 zwXtefPJ1ho>9l#PATE(L_z%EX2KwV=%9zr#On~h@WwR-u%b6e3=jnF}qfwFe zhI^4RuE3HbasXL!gw*8IkRbuLB@^Mo5Cg__k?COk$Ag8cXyGG~EX7wjZ~N&#?S05~ z*N!)Tu*f29c&MvyN2jl4_p6(pcs{-!s7ibnuviN0EoKd; ziVF&hp1pcC7G;<@#cW}r1a6LKiblc#e_nPl-LFXVE8R}5!QybLbtaorB4%S8%lNzs z8TnntY?aLI^9M4+PMg()Ov+j6RGrZmHf0%&S*EZL)E6Efh;lxE`Fyf|5u(=KzHr^& zdF}ZyQry@sL@q?$eDlro+ROCqS8#+84oM>u(Z2lh%hVQ>S{lyFJ?mkZ*v_u*6Z{Fr zbBC|@1iQfJ@vtY56B>Wy>;&8EdG_jOzME!^Un@N!`tPpQr}|R8_=DGtH#KUE8hzLO z(Ju>qh0$yOh`s-tTluxOj@^ALJ96{y6aQ|F@4Gd5{HL$lzhblEZwh~+1@Q&!;rsiA zfA_N=#~sO&D({Yk_junO&p_MA&Pkk z&q`riG*AiB=c<_^?Tf%KRQttwKmLSc78ZdA6UQ9#OPx*}^Gu9+EpaTuc(W46V#b0p zBf@VbXy2ANmNHl1VIs84m=K?vIF>UWek5_M5O?xFP8=(l8Ok3ej#W%Z6-XSXa0gUF ziDNahOxw;p!FV8{=i*g}qp8dgygNpq2^z)UAhQ~6bvPPfR?_!gv<%^B1#*yjm>Omo zULK|sZHt)2_->S*4d8PCzgdg-eq6~s;mOaKhpz9UffmsT&?yIXu$4dT4k> zj;ChXGEe8wqQ$F6J)Hxi10!n(`tcj`*cG%J%a~q#GIhf%Mu(U6;%G9S)QcyRzA}?n z_bwa4=XbkamgxMockx{JV{%1u@XUDc%FsRGQ6AyuWwiU>?b7u(8}DG^R}8Xo6DW)( zAm)M2KZin^nK3XjilKY*a&nQ4?Z%O2U)RU++h!+6dgJvTJb(ax0H3Qd4dkI70LX*S z0Za_}b~SA!)8WDK2)^w{%W`~Cq6qnxI8;YKBHV14FA94~%$vJtG5) zhDKKpj12U9R*&@d4=nE;S?U=k-&}J#`2G0j8Cv1NZ9LOg46VlRr>*W?JuvF&UC|%G z4Tk9h`i9r6SUoZ{FpB5@&w((3P{5~gbZ3y0eGTw769XTG=u4{2d z9=d|wgTQ%WJ_tr!I}_x}K6HFJt|7Cx0%yYftxL=oc_zWR<@An(fULwhxr*Q%=^c4O z5?9H$1Ne3b-;lW_WMYD#^x>WaGs*7-%q2Y|ZG@x{3>NTe6fI=@9vE1La1YX+g)>4L zmeTfN91A$Vo<4CM?kZp+S(xNnvIdr-Z3R{e=^g1K8OI{@c$of{^kx{}kg*6OA!~*7 zpWK6tix8tECVOD;C38UTNFYRdPB3`{FLF)@DZ%(849U2=65}DWDa@EaIz7xxdY^v$ zW*xoL1RVSE4Y?bkj_c{u2{9(u2@oe^AZwiTWD%Yyz=%MI^qtJRK+=;0UFavdgFw6q zl!tLGNu@GEp+v?=X5%IbBXTD)2GSVaX$jo>! zezFA)&}+!-v;k7&`Yzm+9FabdJFlia7FGdyMjxKLhTc`cRf16y&n7rS#!D!nfNLvo zHNjp2y=8c6IeoUU+Y$Wkr}#8n63Z=974^e$xP$UTzRkh==2hhQj~8)5Z~ z(wQS8xfY%TUxXelq<13}fWRw>Q)K-Ul10Y94DUjp$O<4ZN@6g%dN{G8kY7*W3;8vH zF1b5tCzL%2e}ZSB?}hYQGQuQ0*5Qaifb{sf zeWZ72IVk@i=>3|}0kR*g92s6T(z|^5(27N#ffZ|qMut}`A6NnYa`8J<^CP$hLfdu^rp8u?dFOOiMG?%=9paWf?Hw5bi*@%zYmr z2}iiY4I$w^6LM!s2)W2bb~j`bl0g1fRjnBx?6BW^GX?X7o+Hhd~`R(pcGWgUPh}c)ne$Rs%1ZuAIf7)qX#;foLBKO=&P!+oVVWB zT~F03`mxb`ww9Y2E96J~SWrMmHdgjWE9jFd)tRxPHzKy=Yrq%|f^0043-TFX+gwJB zpyU;_af9`0zT{V{u}n~@_|XUjkx~>}rcg(h32TP+(-q*BBvh&w{fbwp9u%u$_?YvvGh^AHCY`+$ zr%lR6A4AiF^cAmwOfO+@isGr}yfT!Fot`KfMEYTuQUQRU+W0lz8SZuvl8509#_|eck=%H?iWuvGcGUSkfbq(#~K#Y=R z!N1F27d*$dL-j7*&lC}-rae^v|0BMo3=LzrDt&3#e_#}ytJTT}&YCqkI@(nX2j*QU zhgqn3QvJ)hv00g#R1t~1l?|bmA!r{2hV-@}dZ;-R+haitG2y{SASIMUNgNcxMK_Ke%6Tkf0wX$EYO0icY-#VZ=5?Q=tP zZ>#x#pGOVtjh@hqM9jEa^2hYrlIPo}-JoxuTg^R6^t@=WSZe2u4yK z;QEK9)QmW!CzA3rd1~HfMBmn9QmIYSD&%NJIx?O$V>F{j`i25xG;7;7AJM#42X*+@ zG41DsjB(WC==$Bp znq{2Zj66_keo*t8B@ldO-$k9x6_K_}*0CaWpnb!6qUtYDSZFtXBAR+R6K z3Fj`U3~|x^|$Kx>MXSmjd297paAD}9UA@dx=B}cb6t-P>^CUCjp&w6 zP$#Jq)z{SZSQEPmzh5|2{Z9QsJ%s%rC*T)@QR<*h^$+zvRusx+*;CaASSd1%rqfRN z7URxXF!B-Fg?6Rgu=Hwo+JpAQ0`7H?MwS%`<5BBKOKN=IcL%* zu`R?b^`813#VJ8aa>%6=bz_b)O}%s=9YhDyA#|wvnR<&pMTb!z&89i{_+vlKqxrOe z7SiE#1TCT?!MEN;i)jfhrBBl`T23oyfL78f^*8l*^>g(LI*L})8af(#j2}a5=~z0B zJQ}17Wyz-@8m1iOX&sGFfr^+pDN#UWI$r%!{fa8;7OGN>>a?ClX#gYLv0 z*>}<1bPwH2_tE|I0Dc$ppt_PCqKD}bdXyfc$LR@rlD^mqCPy-y#oVtm<$Tey|m zID)N$Cg9@`QSRVQp2RZGJe8;Mbl!<~=6~Uj@GiV7@5UeH-FXk*ld)uyW4ssd%^%}^ zcwgR+Kh86Fe?EXe!87@j+{LpPA4cRPJM40bySYc*t?uEpx?eq@z5!16KJ`tt8GQ5^ z;HRHfkEzGGS3SZ9;uA>+^C65~VEHia;sH5AaG}#Yd@g)wR4@U8ZhTSAao&N8PM0R9C4ls`J&w>Js%Jui>NlGkgrM1jYh35`Jjxq*j5qQLd?KI3oA_itg-_+p z{8>JYKgXx@8GI(6#b@(5d@g^U&*Ss?3w!~7Om-1p%$M*MzLYQH%lQhvlCR>c`5L~K zujA|a2ELJR;+y#v{vzMXU*a$GSNJx*o$uf~`Kx>v-_7^%y?h_v&kyj|_(6V%ALd8+ zQGSdc=O_3{{yIO!-@vAAPxCYUEI-H3^9%eUzr-)|EBq?I#^2&^^Xrk}!dN+%Fe2Gr zFXiL$*`ajod2f4P(F4(3YWIwc^bJ=0^?n3{QHb`wU^poGBki7%9dk1JRc@%@Z|KN0 z!qGW+N$@fm^wZJIcrY?I<3V*KYs6gOc(wL^3kc#-fFu0|(AOf`KmK&o9}l+oTQhtk zBmG9BuSLiFh9Mopjc|1SxPs{Lcu*%iku({RNID%08c%fO8sXLjc(-WH;SgC^%NMeK z1g~rm?F+5_d29bdV}IUc*g|V*-bh|}IOo@O9Ny5|v91v|wm98Z_H>R6%Ntt(FEsf~ zWRaJt*ZfF93mav&m2)DCOcoThXkCQ#wH9!QEH+P-nx1k~<|(&3ve-OTG8tF$$^k|~ zK{@BQ^p}QP{L*my5}UlhCU1#JUZBOKCAoTO*sIiw1+QM46f~bjmKkKcvBPYGn6gPr z^+c8#WV{GUp)WUat6Fp{Z)8bFwGlQtx@}Q)JCPOU;hGk$E2MyGQa}Tyfa<1z25buI zHU$GF1$8aj1~8}`Zo}(!X?W5=v;C9m&1dZcwjk?9cC2dH)iK%>UfmQPYlI_fObRz@ z5nVH0|DqemgKdSNG+d4Li7h4?MwvF)-q)`$wR^sn9ZU54xCsZ1u+F1oB5h@_mQKRt znUgRjk00cj>piZO_Igi$TTNJ)Nm;NBATp}@EZSEn=R6&p=#_$+U-0u@XMeexhr#;t zgnlsp^5DdUX!knQxkSGhoBN#uO_8HZivF;PG%b(lTVb_ntk&6n&1;(v7RfUP_PXsB zuXPP>wE(XTtW3>o6~MM5ymHw?11Sz>JwCF|i|c$;9#vOf%}cyA-?A(hv@OpM7rmAh zUcKE~)3P*|Z<&MtORISU)7RT6`a+AQ*ofhI)Em!;PQNKd6JUr*zNANocCMTI;iLLu zn|{7^5YgL)C2?(8zfkkqt*+LMxX~gv@s5O~iL$m4owR~ZnlW1(Ov zf(S^bqL*mXqNRp3TKtwW(wD)1n9~+W#&?R^(5xxjnAvG{nj*9}^+{Aetb^JNHy*b# zVWp{KZrt=GZrs!@H*V^d8#i^%jrZ92rjELCQ%Bu+pS^B;nj4>E&*xg%Z{<8A6Ba*V z@ogP<6Ba*V@ok-V6Ba*V@ogJ%6Ba*V@e>w5Veu0dKVk6`7C&k6lNR50A#T#*CoO)` zrr)+XH)-*cHvLJ9pS1W%i=VXkNsFJf_(_ZJSbWFgJ2w9vi|<%`$Ku;=#dR#cWAPn} z?^t}t;yV`KvG|U~cPzeZ@m-7WT73K-O~w}ZJhqh<-?jPgT71{yyB6QI_^!owExv2< zU5oEp{FKE{S^Sj6Pg(qw#ZOs$+xNIBi=VRiDT|-7_$iB@viK>BpR)KVi*NfOx7*_N zSbS{Dpwn-CtJ`Dod#$~_iO4E2gcwte8nH?{@yMta6IX>zbK+HgOKdL___O_(N~OqeDN*)tK04F+7` zp(Vz=*>P3_4kJQ zdqe%bq5j^`^4`$$-q7-1Yq=A*nw_}S?1Y)@#I0s0%xx!bH9K*u*@;`tPTXpC;-Th5 zs5ud8PFS0rgfrPI3h9rbZ&NazM5rwhDoKXVCPQPAA#^fCbV48})bH4=aGcO!C)DhO znq9Xehc_n7n8;+s6n{eoy|6HT`h42)b*Y1xBMPrfAFk&Mm_ZsY6#Su@j#W^V^l9VvL>aQ^a)|1oi765cEt<;4?LnD^qeE?0r}l$R zLPxSEPVGg z-$dw6+Dg!!OiYpqt8MRxb)(yHI`NJ>e!H_a+K4;3mctv__`OL(n3!ui!>rf%S!XrB zVfra_#;LEXLulb7Otm#0oT#nKmV)90u|o&M1_>bh1z&;_HMR)}u8t>|I-bGP zL4&0uY8)L=d&!=J3;q`S6o8$L$`4YoMsM<`@5P1+gE) z`oNaVSf!wL#!KpmT3PUFGHnx8w0zm@7 zr8}_X+2$vqf7y~FW8j8AJkHkILk8ON&l$K=wNje2S}R$hvO}o*YMs<2Rny0f`nVa> ziWPNc0HMwkC#){k$E(oBhtw_A`e0SvR>c?@jF#k$u-zs%*9&`11Y0edD{EV6+#5wO$JjS(YJf((LpW@Xxn^aTd)4 z&e_N8=_^O@xk&9))n-{QFkZOc2EJ3+&Nt{y+&w@|*G$=k;K4$+>>{ve zSAs{o7<`#?O=JNNke_Qrj6k zavfL<#(6a^A5F)}Rm@6E1miperg<+||5bOP>7M7*te=_?WYqbkaKfs@Cgj%GF# zcL*(O58w&*uJ|8fPeoYHq=w@u#2CXLgH4qcrxJTcQ|xIU5>K9N?!PFP||R z^e(vnGr{YFP3L8}`ZQ*vT+AgMhE}!+^FwP;kA_f>DwuKEgc+f;(YCgLOTGcz@>enQ z@&wB39=;n#@Xt67@Ub`+80(6`6raea;A|xyhvO>ta6F0!aa_Y$99MG&$D`TD@iUD5 zKfp*2)l!!|`|?#j(uryWpTt;!`0{00WJ6&S0cB@n+n;pYKB| zc0uYF!u}1{o?^bU3ocn>oy@1yE5W`K6V{wwz)&(JPy?KZ6e?UpA) zlc}Flw}oSV61A&|XU20os=4A@qL_<2bgTa1Uv>X5L-!-%pY0ssI2 literal 0 HcmV?d00001 diff --git a/resources/glyphs/Consolas-Italic.ttf b/resources/glyphs/Consolas-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9966bed49f2f76de4be08bcc778be13b82847788 GIT binary patch literal 100832 zcmd4434ByVwg+5w@9jP5?sPhxq&rJ@I(ujD>h$z<>d|4wXbFl5W4jou7->*Eh{I{+;0ipSc>zo(RH=;886qc z`Y$!W6=EgB?*9>x9kt~nj^&70#_5u(7k?1`?Wi>+Ia z-uXEp;i!)<=$Sulj`eW#SwehKUZtPbv1mRCC3e)8j`I3xvsd)&>Zyt*BsPT5wD+e^ z?dZPb`nVePC*XeObX?Fp&z?n5oi+l(&A(No`QHa-5Zr2ibl8Q<(+8N;sj% zb4}=B*FXw@w3RL)TB7$nL=2ph29wEDAXTJ}xM`l}P0~unl0-7f`OX5hj07N!}ZS? zMv(h?&u934NkBd9nt1jVdN@kF%kJGIS)K!8X73N4uzlnl<=XbKAyw$FC+w*(c$+Mahw?KZS?#W6rvRST23-N2Sm!N04}w-t3(?Q zqUO5YzLevWs8ik%80#|J$+i;apr$nJ|BFAdf z{w4jKI`Fh#PG+^{Bp7&5@nMiA$kSpQ%&hvbV?_r`rk6vNz zzU}i0t~A|t&JLSMljjiog7)J)i||`c)Z}rKXw z8ceInedu8_@-0R$l=K%g5pwWRvI?ol^8uNO5pZ)3G7cjWh0z`1Bmz2(!YEDx7qc&u zq6UnW4ddEFsyx>~ZHIxGW1zjK@%v`RH9`$%Y6U1}eGnx?h#^EFGMeaiPD0kxU<>5aJ|9<#)!@nJV zb@;*IpA6qUyyU&j?^!OBD>tv)xblxH*RTBT%BNS}zw+*tx2~MHa{S7}S9V^BzY=>T z?26@z@p94Sg3Fng!F87%m%}gfmp;Ap=eK$lwIZHpGhB1g?DzlO5B=jm)yG3_bb&{v zf)=KMZ)bpxW|7&Lx%3#_d1O9lY#~_$np;Aaf-{zbqgI0E?f{>xA#2GxaMuR#?IuW^ zJIQ9U1ys~W?jqa3?>j(MJ3*_v!F%_TJ(v&g!@PYz*+(8A`^itp0cZpV$xp%gKLZ8+ z9P_|0K#Ttc3jc3V=QSL6lAh!@FA9r-8C7xFpzf?OkiC0~-ik*~<#$#wD%a)W#g2{TOG z#6tFNPFf?0a7Ok`eh19C27P- z(lO&?V(!U?oXRD6Bp-7?0V#w$DI&$B1k$+-a&t8FiZPH_m6-jiF-O#54j2a+Ru388 zNG6bpkhaZ`(~}_ICPT_kA?>6C{bAI&8I-sSG`AlVc2JyMeg+!*1?cOjNMR3y#u$a2 z0Cf$5zD|O|o&a@$wzweBJ>!b$oEcRAAUI(r=K>GR;Hp67{oEL`fUD+ez%xs@60V#p zqdU2=;G30n1*muke9=kfa23?WRdS`^rd6B;l5-vSWDlbTPRHrF0M5i2IDhguS4+;& zY|hH%bD>-qSI3Qm*0GE;a)De3c&iT_b{F_c;-_sQFA?w*`sn;IMH$B(P4t*Nf696P3Bba`26NpX>@uwYbvUT#iyR%S+W zQer}EbX250B0R|Gr`P#v)GDPyE)#f;l7upQdAoHWx_v;1wpUanvU7U}E_B>>p?$!L zOXc6rGhk)qI&kY-IbF!v^PQY7DW~gJPO7)≪U4Ynk0TaG}(09ilDcnsB_m)ZT0z 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 literal 0 HcmV?d00001 diff --git a/resources/glyphs/Consolas-Italic.woff b/resources/glyphs/Consolas-Italic.woff new file mode 100644 index 0000000000000000000000000000000000000000..9828b0da82d7df9223abe6aa53e9da7cae745917 GIT binary patch literal 63412 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 literal 0 HcmV?d00001 diff --git a/resources/glyphs/Consolas.eot b/resources/glyphs/Consolas.eot new file mode 100644 index 0000000000000000000000000000000000000000..3506f34f1e0a9420eb586de3297a81b2e4a12664 GIT binary patch literal 95274 zcmdSCcYIXU_BOuvInyU)l1zG=WRgj*lSqN&OT+gwbx#I?RAFe z>69#hXuFEbGvJ7S-0gqRq0Iq7F+^v@pyDI}B0 zBr*-(6G$^Ct|ZlDG#Nu?NGVg1;zR0?G8JdXN>`gm5{V~?_#@=MJpswAH17CP5K~D> z?SP68(_SQW+9;$3^erkbAr|31LMPn8aZumNikj531MN5;hGSdbnn49e3w%lmoftrf zN>x#lkUW0Jw4d?N-8kPga$@rozm?JR2vKh#M0s@N%;}~*=l;5ckho>YP}4eP%tX4$ z$23oyLIQ~y`Tc_X4Pz$EX?-NN&*Oxo`w;4q(Kfny)K{PXScK=t;(S^g z67&No?x(E0VQmwq&tAEE&i80<4f5MCVe-i4W%_sQge0xO@tTRvv!}ScEd=5ERk+?X zsd?h)F~ffSfY9PfG(yCb$O~FQmSYNYi~bX*Q0hsDXy!x;!xAu`N$5rU+ca`8x^fR>Yk~J>(Z0h zE%HafQiL~23sIAqfPn5wKvxp|d}<^j&Wegwx{fIEq|Hhz9vRH3c#gCRM5DbYVcUuD ztBsh-bk}Fh0mqWbj$x8ABQesF4nFj%!MMlxh*c+xoVpO7|kgR=ba z4Eh*7NWLXsqGU$t?>(g7{q%f-r~J>KCs2=jpRH(<`#-^`!Gj+rsrNp$f68I|y%T7^ z&wGBu6)pE&MbHYYg$S;@hXN^ukl8^WU3a~?v11s%e^jJ0W=AU^|$ynK(&~i7B$1I%KLi|y-$-O0G za?51Ooh-umuOw-=L7%aa_utQ2A;IBj7adyImpp`) zjY3bcG2jFZvj`mftCoth6n#{Qe);p~5v2ULv{F)v(xsB>9yPuz$@jRby5~9RY7JV8 zeHtMv?DJ0V=xUMrKuY@0FX+Gy@}5-RwP;tg3G@gwByFH3vI82Z1sXD{2igTa%svYA zTK(O>|InQOwPhluJ4Jjm1Q0YQSi3YAuw#YzBoZb;vnw&hFQkj;2HGR|3vUVU2w#fs zqNivPXNU{M)#4iQdGV}bgkq#(hGKot@}Pg23?@&LpDD-`YOJXOA(oK%P$5(m>Kf`E>K7Um8WUO?+7voE{N39^PY?1nK~ry_w*(*IH1hkb zH^1q~ZyEA?T>OiIC|VR#73+c)2mOZpJWM{O0Fz0|FS9ql>88a}eqMjd&n?vVZhoVX zAMNSs`4-w>Ur%2`980@*Ii@-C9664-?u*@@b${IbQTMs-54*Q@Ki9psd&ajbzxBUJ zy6$w{>iVPWX4mgszjS@q^-b3oUF}`_y8hO+t}D7LvMZ>|zsqHajsfRI+mtl0Qz=&T3xn4~kC2KJAJ&sv# z9kkX4%u7#_r^rU~G_=AqWHWgda{C;#z&7ZO=OMFy!R+}0M(T@XH~A~{`b*Fbe}i_} zPwb=}bNm5}^Fz=XFOyDkgd8Qu$Z_%td6m3IPLPwBHQvC;f0LXhZ;`h#hTbLbk@q3h zXUPZTLvoIML_Q|xG4oy^pOVkW=j89?ALI+nx?hp63A&4X1I_Rq#?ANS2l69y#?R!R zL3&_gQz}pqqfbdyRE@Ezr8=so zPSlyYP*-Z8Zq%K6P$TuEUeufVP+#gt{b>LVq(L+oqd0_yQZo&s;nYGSXe5oI(KH6* zIgZBD1e!>bXfn0Z6q-uYXgbZHnY0hhqS-Wu=F&XcmzJ{o$txmS1}&QbP1gtdI~$rd7qgm; zkb$HE`m+jJxCXPyAj~I&q175NgAB!-F&tWS1Zl$J zghlsNXZ6Xmh>wenw1kJ5LxX*cZU$Fp zy-ur9tCUzTQW9HiE@?8^ElqZX#avn%$Bxa-NNBzhIyT#a}%tiL7x!?Jq0Y}?o%|*rb7&8i1R(&@EYFCCE%%;`9 z6BOQj`4^P>zLe(P6jiw4cfwAxD&B1-J8hPEg0i86s9#73E6byuHqwFv`{Js4erzHw z{vE`Y5Yu26n%J2W@)^%T?95{M%-!3Y%xKwSb8%DeznN`5_QfrxxLEY8^e^152*-Jo zU9>c{jBI1HdGu;?5o(CGB(?Q+TM-yrb8ll7w}w0mEg-4=}txO=XbvkQd~z>*lLQ(2!%$9<*ayN9I@obD%|Hm%l} z%W77$oXx!tBqj_W^o+{Vuldmo_Y{oO5@<wc1=$pN)=`2K@s6xh!jUQbx;a3*us-cnaFhbX8TmjjpO0Qh&q%BX?D8eTP7W z!lr_T_As2QKVpJrmr@0m$`aWnCU(G{RE-0TbiMx(8zGCOvkECeIv&|ciIm1KLK-C_ zI|ZI*kgpSvrr>EdDXjq?3@@KHv_F(^v1t_RoB0iGtD73w$Rb|oNBpC9YR)Bg!JOMp z1(lOsXC7T(*P9DidLB#9Wq5yF*O#FQ$$jmMM>ex^kU<#dY*_Xi*?@r{KMP3>R9Hq!(E0bCB&=FuxZYW3*T&<4_{VsCJRXRD>IO6ltGslyviAbyZLsT6BFlYE z*?R+82dDb6vf$rJ6rNprl0BxXlT@2$Loc#IOIfLL+U^|QuNj)3->*lCIYYh~&(egm zB=(F`JXOtV?u2$1!)rTxUNp~P!(aZy#hTg7zyy={AAw+#hSd)y*oVf%#cCc*b(WG> zuhuyK_ZxV7X`Js;_7qbwCK{M`CP6%?^`ZSuoq`}URAxvoLwjV#Av4`Fb6aL^$;=-z zb5myiEi*S{=69L7E;IjJ<_(#7U1m=Ag_RkeLpdX_pzh%WoDkt%#oSdGBZnNX3ETTnVBXtQ)Ond%uJG*i83=mX2#3RIGGtMGi@?6MrK-N zX0*(Vl9`b*(;_p?GSeh8BV?vgW`@hmFqs)DGYv9RFEfK>rcP!C$xN-xRLe}2%v8!u zh0F|;nE^7>UuOErOo_}C$V|S>*kmS8W^!aEOJ*`)bz$PXSH*EVQOT<9`C#`MSu?Ffv-5s?V(;l#m7Hz#*Ex!n;E6WmE} zaqV`|$#@$yPUkk$=jT~#mQ+--sI#ICvR}_IwvPNIl;+m zoV?1(E1VqXrMrp5tT-C(m-SnUiNY*~H1yoNVOe zDNdf`C#9V9<)nm@Vor)UDdePplYCBW zoaAwm%SjF=*_>o?(ub2wPBJ`VgmV(ciJ6m7PC_^_b-1PC_FzteI0@`96d^Hy z6Ms(pIPvAghZAp3yg2dX#K?&UC+?iMabn=al@k|EoH=poa4kc&dQNnlXgSevqUJ=! ziINiqCn6^TCzKOn>%l`A96&d42e=K~0{#GQ0{;eX0KWs*fqwzN0oQ=5z^}j+;4<(F z@K4}p;3wcm;0NG);1cj1@GbBS&;?usz6QPmz68Di{sH_Q_#F5Q_!PJReBx3ad>;51 z_y{-$d%d9C)^h^iuK}+DuK>q^W57}1 z2+#?<3>*dy0SAEtKnKw7+!AaD_5=HXzX5xJmz*nu_kjKt*bTf0>;hf@b^?C^b^y-< z+ktJsR^T~c3-Bzk*=cg{Gr%U`X<#Gp6!0YQ1h4^E53B>$0*~ug2R{a^0Stb0ir->C zVT$i!Um?LK&!@si4E9d&&hr+xdGGfYY~KFCB~y%xjUO6q+KWcTVh_6AKs$R*95BQt zmVnyKhTzbWDX#Q2SGv|^o6CL|algxJF2ZXrAGmz&BHCPX3rfV}EEo@4YLu>PudRuR zDeF}CRF~N`l|$`xl|8(MecP&r*j20G!WlBOzMay?8{jP~thKu_*Q0d2{ISPKU_qHZ zu%^C4+`c`qprOpZnDI87#P=|s;F5-zY13y+n-LQ;ZQ3*%Gi}E7Y15{YnE&lR^nd#j z{=X_9Yk73Ubo2t4>CR z25tbq1J{9n0lxv)fUCf-z!l&!@C)!y;Ah|`;78yG;CtW_@E!0i@D0!fTm-%bz5>1k zz5xCK{2lll_zd_IxBz?toCiJzJ_61G9|9i$XMr=o`@nm^yTCiZ+rV4EY2Z!Z6z~S{ zI&cy=0lWsh3cLaw2aW+pfg?aC@G@{1I0PI74geiMJ75R)1Ahbd0xto3fWHE}ffs>Y zzze`mUUF!SOP2p76J=^`T6X79xw-(4a@>&0@HzMz*Jx|FbS9lOaR6MfGi*r$Nv>YzSt-hIkjAhnR>K0y5kYhwTK?!F;zUG z>d*01>kY@GWA)mEQm}G8fhZUjO?EGmY*%l+|1;K1jx+mn zyWAg*b}ts~uZR({BQl7^p0UU?9xcQog${v#FjDQoI5i$)^uLZ`lVjLUN)c_xABbx^ z)qBjN+)myX-^SwFK0zECi*$RhH@1x(e~u`&D~L?{8Zm6YBDRf1qJNKQHWtYy$Fbc& z)Y={N)g6pn)=zQhDPN4?aP(HDG?M2du8;j4CKqX&;3Vu6>&5BfR`G3xQDIZeRIE{S zD$Xkllrxp5Rk^Cgs`F}_dX)O6rbV+)tI7iylh?*ygu?acn|aL@DY4+eGdBE@J;k>@%_keh~GZHFZ`qZ z3;fsl|2@DMuqNQUz;S`^2L2YbG3b}z>A^Ql8Kzp(jSxdfeaMp`TSIn-d=&C`M0u8n zwuMd&{m5)I&oo~NTNd_n*o|-@+$G#E+!CG~zB>Hp@EaDv;$rc$SS-nwTuXn;X3H*1 zhvikvyOs-+%F~$@V8%bQ_{R8g@iXHej(wAAR#g#CBc@kJuxNGmRO!xpExRU zO5*&)Rf$g~Zcp5s*qL}X$uDVP((0s*NjsAEB^^yVogA9HJ$Y~PXI3BUR_h+?Ve9MG zv(^jNZ>%>{6e)(3fRxCTloVUavXswKzDv29awk=jYD_hy#->hBJ(2o;>Sw9nrCv?F zlcq_Vly)awlWt5mrN^dcq!*=ErVmSROP`*8GhnOigWWFF3ZJ@ahl-}{vG+1sbHDi_W7dEkA1FZku0YypRBN~#H_}w3t8V} zyJY)iTe6e0bF=$r*JVGR{d)GzocNr@Ifrwu=O*UX<}S|NlKXD1BTtnVnYSWuL*ACW z-FXM|PUO9x_gUU|c~|pp+K4U4R&5(%Yq5>9owl8|UC3AEyX1T2*X9q)U!1=x|MmQL z^FJ!6E*MgbrSQX|>Y|R~+~R{JK_%l#PM0|PX7t_E_qWo> z($3No{rvj1^*h@?rT_H)N6Vtij+PtB*OYH6-&t-iKVJTJ`T6pT1Cj^i4%jqc`+(g8 z4h}dzFn-{wf$tCeY~XhTuU1$p;wxrXEUwsEaihXf*;4sgm7yx2Dy(W$)r6|)Rr9O1 zSM9E{S9MmqSFfwyRQ+y^rY5^)Zq4hp#@f1C`yk_>*@G4jx>46sx2*0$-B*J%2ERV| zI)b`2^+okt>d)3+sK3=9H0T=Q8`2tfHtcQqYKZ%gk|8UGd^R*~=(3?-4J#P7d)UEY zCx)jDFBraM_|D;bhj$J?(U{!0sd0Pb-p0)zJGttVRFZ9U)m)tKlpL&ofCGqzQ>ZEpK*tk>90V;$on$7PQj zHg3bXx5p10e{@3Zgo6`ZCVEXAGV$$+A5Oe8DQ!~uq=zTnoSZUw!{l$KcugstGIPqi z5BWS)`Owy>s;RA0r%qisb@kMZQ+G_=H}&YWqG|J|{XFf)bYZ&7bie7A>B-Y`r(c+1 znz3=lH#4rx44s)VvuI}J%zZPD&Qi?sn`N1mJZtx?!?T6iF0=h+TV}V;o;rKs?A5bx z&QZ)U%n6vYdCsXhSLTM!t)07J?pJef%}bm&ci#8&P4g?~ubRJM{+9W>=I@(-c>b&N zPtQL)|HAyQ=6^r`%KRJi9Sc+oTo$ZZNES|7xNhO*MaD&yi&ig=T>SnL+mba)ZaqBp z;a4C2ZE3;MgG;Y3D_FL7*+ypy{cD|zJ_EzD?xIy_5TkkB5E4BKMvQ~}h{--s( z_d0iXlaC9t&jh-+=ZbI;)}DyyV)JoAuoM-E7Q0Fwf})dJ;7>yMZ(d&aS(?x#6A& zR1?c_qQEj)O+@;W4FS~}tx{b>MdW!V?~I!}MU!1%Erx7E0Qz;n&Oo6BU&`f<82SkV zs|S0pN0Ej8^GEOPIIIW=^iYV#V_2$r;>lxep489ROCflAJNrBPdux@!LGB8bTPN** zSfh7xRTxyAxF}YwH3j)P1-|0$<>Bfc;HM9MMWInrMS?qZSGePDS3{mbP#f~qg#!9G zFRyX9)jcyaAz`?i)oL|dPEJm~%TU9G3teA&XLh-HXBtkiiwqaegnZ0 z{ZuMH*G~FoM~YuuCp~3z3G1W$0WYvd3(?w3QGT7&-R9#T6qNLXw=pF%SeF>5@{YnE z^%{s3N?x8+e|PW9WK`d9;j-Z}uj*ygzw6ZbE{Hs?aI?Bu4GUJDl0JrBibk-igQ&M> zJdF^8L?%$K)SQl^RHS;ui%j~{)h^W2=;iH^OW6>K5GvRErHqYBpOThYxNK~G(cHab zLN6}1l! zu_~`zQF*)U@(NJW*M0j`O)Afw)10T;s2|b4xFNiIoo-ZJSY~{rk+~3!J-;ccl}||H zBLb20WuzuwPa?6l&n89qPKNl7KwQ3wj<5x|;N>V$$m>Y&+Ywi7?$G*oc+`qDLKRl_ zY!Kmd`TS)Dnvj4dVIzu3xHq=>c_$#T@E_f#zjR&K3UWvx@3Ftvkg{j`fzl zqjPhbi^FC5ge@~LF4(}og-sFnpKd7*eldFFE`8tmDt{77oXk&_O)9_dB*vjgGSObG$~MSd8r~;Ze}v@GLpGzgM{<%% zX;cpH~w6r6yzAZNqBpK9<~L$lqN+ah$?NA#lu*r2uMap z^tl;OlWKPGVJp<}r~W@4P(6FM$()W8&pQKj}aN4QW~ zzotjTL$q=Q*5phE)if=F8iR10PF+NLw-bVCuBad>iSPbU-*PXX%AOcP3`% zr8TdpDxWpHZ%}aY<3*)&8sW2KmM3 zhNTV9F#9BB;ms&aKPN@IuuIaRK8WU{|2!;;3MJQ@Tx)WjDeMZ|8^Bd1Zo_@Cj$vYp z@*LhT@$PWe2%Yr6L7jRcUhc@tgNDCw3bzqAHt@WOo6+4{_`_jMns{LGXvh3hCmh4* zOM-IFwcmc5HO8^T5&xs3_Eq*w1)kaG{%0OMs8_R>eeZuJYubo3_f!L=6X}`QaR*k7 ze&d8=f%2SVQ1|Z>e*D+1g=0?Ao!`?h=zKhr>=aVPH&8z}()S=SxQUpC>ui2bYOO}$ z4|*RuSo&jght+!il;K)(^7+%Kmpa_T?4gW^qv=Y~ zDu#P2#gj#jAH3IkIlA(w)p4en`unW)rT!(3_r=m#M;&`;-O(9OZ=UfAt#j;oZQdr< zex#PlX0dbb|FvcQT*2&RlyFxrVrz-9l1BP^1ur zyJOZ~$1M8DUit{8t~R=96Mf1tUV82o#~$GvJbb<+@CY#q!XdmH`GGGKb4&#IwW(ihU|66b#A-I@Ck5urIW+r>;1y$wicX15j`obV_ylGT z$*Fl}W(n&r4gElD6sBRcx^;+DJVIg55SGCiR^HSQv9WuPP)9$&yYX~6?1OXgGI|iN z<3tIkxhtL!+peodnIJH@;Xz{{CNw9`&o3?~)SS&|HqG*j&oPJcgHUr$ydP`FX+2Tm zTU>rI?NbqovxW){B+r^>MM12|Nr_O3RD7$udY7bQS_~z^F^~K8C1&3kjzIHaDM8qmKoD%9tyzBLlA}N2AP4ZE!g;V6uYJ zINxP8bRoC@qF^IvkjQjzI3^$(FEVYcaLJx~Xztj(a|?3j9G=6dT`6{d>KdP4lUg)+ zV08B$R3*wa-d1g8hr(kU|J~kPvD2|{<4w>P90x-SO^(qi^?Bxkhfh8loEu1=vDW5< z7A!r9Hd%=ZUQ&jj=Q9tes7~8SU$yDgqNsOLT~@*l!Pf2pO^a6Sq<-yiIOknXmXzRS zX~0{%PJIa}kD`z@Wkfi{O}upHJzD8_%SN|vreQn8qkEqG$L%G^2k)(mSJ8$hl6iz^ z=6k@eQZ!3CE7 zSB&BCxux&-rrzGs>EYMHLas)S)Lhp(U1j~;ec>_#W`l&`?wOa_>~MiuUI_^aNaGU$ zv#~AA*kFBp*8pcMCSH{~H5Z-F<^+{GNEAOUS#$oef%DUHSC_UtS|6D>cuwh4PiJAK zT|B>FO=akS0p;elt>eVl`Xmq29<-hHGSnSSI0aSLmCW^?6|!uWlnzCLmWC*jzV7ILREWFvwPUg)lfFem z9ADFrXT;_^+k_+CY&wBVbfC>YKp8?v{t*&DhadL#!=E0Feazw z*GYX3>Rs-(GGF3I%`2IH(j3c|FZbHk5`*QACzK!>RQ#a!iMOW*78d0BrajuXa`BMF z{X1_8C%V6z+Su{vAiAS@`!t)VR4KMPJHd*{n>$(90>dVB&{~vJAoDQ(iB!&1Vr%z{ z?WF4t>fGIRl2mwx>e6B_`$e1%3{3M?dR%jFNqXzwn03wB&4uAEz9#o2 ze`|{kFCD|$e+IhtC)7(tLSDw3fD}toHqH+!TLd=Ltr+aAo6&+?F)}s%Q}-8d37Orw z;&jEF+bb1wnB6}M{m8~P(~gB?^l_M~Yw;RsC`3u;=N#x~z@HAb)+<;S^r6RXzPh5g z$iUP@=fpmdp`Lx5ot5GL^cQJm_?pK{@FyHSP zQ_{eZ29qaR(L0xVFcaPdram*Tas}~f(cE3lJkOrMCN#`M(2;z(Lrv!p&gv2JGH+57tDZyS?0b69#z z<c$Bp8wS_aMHKYO2uz*U{cLziQBhb(eqm{3Tydh0S6s0qN28?nj>Brv zO0o~AR9+CEbsa7V;hoSo9$qO4zqmx7c5-u5ncngrslKLSYLJ_pT!^~PC$q&3tGu96 zNK5xzArCJh%XTFbA}ZPMyt3)2yZFf_9rh@Q_vLb&==@{YD*BqDIYd58gUO$|PPTShrp<8DZ+17qX!@~Qv73MS* zhPn7KIT{;_b@gV<)n`Np=EewOZddwt3W%mL8(GXzP&LMFbUR+?Wz~QrU1JuJq~7ki z8ay627OcS;(Z0UX8NsIXXdj>GbkhUJqT|7n-R1{VFlKMTw)qm}@?~!&A1A@sa*tv* zP6yG2p;U+bDjzU)SP4%{?uP$r?mSrTTmQxUp_vbq^?zXxDQo|uz>+?Er01%*QBjQ* zpjQV`C_Cw|2Lutjw0Wms^?V64Ib5y93+Cd+iQP@Y_KDqJH%=0ggrvz18*WFPm(ysm zOZ3HvR1+VYPAw{_@}^q6$qcOvtOM9Hn6br-L$T}5PH`B6u;K}a+Y`_S=aB!GJpUs^ zNv|A&2BIR*|J0WlbROwiyuf~Lg0Q{2X@U?UemlAQgYFL|VN0LoPZOXAHp9O1!+YSx z$KeBc397|J|2PmG?Ez6gd%(#<=?@2wO&2bH;G|M{gjc(FQrhkTOZqZa^$D4m4b16t zz6(6}{1{bOpGyYO-N`aY@nBzwn-9LEdCGZ4PIawjxU@Mx*fAg=(QGuD z69fDc!;HqTMDd*+FY0g5MSh9JG5w|vjND?$uWuQ9=k|a4i-^|Iq^MV}$J^;c$>igt z2HW~M#ETBH>CJunm!=OL+K-kdbkbUzS!W*Be<Zv`MTPp zYXf}SO<3r3pU%77tH7Ao0q%ElFCvo(0~eSvX^(dIv$6(g)iaLMJKJ`9N-y{9N`@{ za6v=SqKOT8=GW~Fn`^3f4(a(_pwSf%t=&Ch*8ZvKG-lxBqJia>`rO7w=iuaiqimJ} z*CmIw;Q>L3;l6HapHL?sf0GB@WUEc|$eMS2$+~a0))X(;fByP1$E%JvqN0{qEOa5A z`!w~u`sSkpnqPL@*t7TdE&VEs2K0SwQ~JV1GaAy|wZS``Jfce)8i$58)#?IMq9M0% z!6}Ae9#VrFWpmew3avt^5%4<;#H3bfb(&7e5>@o^Sp4Zo2hJ!B>cXTcoSuF!*3r%k-~Og%P~ZhsPKzsI}bUlbUGCTd#BAU zSQYK;+Z7(J^7&SsZ~0b*5^>cnnSs4NDfnaA>WukZFa%9*+{%NFhNZ%~n@v>`ngs{49!WqOb!cPhs9FpTSOl`;4QhpYoG0YP4!^sNqMAXc~D2P<5t z3tW=U9l8Qc&>pzNm%6kIMY1(JTym-z*cv@tWj0C6bS7qSpfStE(Pb?& z3Uky>n-S|%=sG9Ww^}V#ek(UHsKhGGwwi2IO-mP8*EwQ2#*-rY85wNCg(sZ`TT8u)WzP|JqwwKw6f zWx83iSh08)_jP|FxOD$^Sm;tLb<{uAeH71P&n;{`gGNhzg${fzpbAlD;kLW9QNWz*|b`b*kEB|-P1Ik1j}b$jiTQwDifWL@fs znUQ|(_*istd`#o+{6^yx%QtSkEva(1UJol@m!zelfyIf*t()5-s>%v{a-Z%$ai+r+Drb7_(^Crc9{z>{ zaVg6e2)}Jcep@g;ypf-dlpN5h1$5`m!wS6?o)cYX&(#i{R^8ci5ke&>luC^@pLxa5 zYFL2fS*^?~mEo3|K@+SAR>OsBZ@vj5KEzC=) zh;A&?DSjV^h@T^0C3|aE!RkqD@}0!coTP-YAT zMD$Ft1v)!vGr*~!s`&gOZAqPs`=TvegQ|>;7UWV09Pu1A3iO^HCht|*H8_{UAquKiF)I=(y)&4$BH=f zAk`>!dZy~(2ZnUIs+{vhov72O^HqhE31>2v!Q82?^y7A()*H)KTJ$h{H^w%B%J&Ll)bDsEfAwe&c|v#;9ha)mh_XyrfcTJi4^`OhezV$)qhih&`q<3|DDg%huwsV8ol*3s0@u z(Ltv)cdV>D^1_pccWr%1e6;ep`2!p=O8=OqO*0m*=>C2+YZu2R`1IrOOG9a-KfIH4 z_5ni7*e!bFkdnfTgqQLSo0D0Y?*D?H_xC}@qDahQhjazW-x1zkc?s~vKvX3~$6WI; zDU%YFY>P+IYrUR#W@x3Qd-Eoxm{It&#?>MHxNUQ5vfPSAGq(?Ooir+Ocw$Atn*Nn5 zMrPCJ9&A`A*VY%mUEM(yf2|t&(!VxszqY%{wSK(GGA}JYcJNZ+C)T)?kGwo)*2&dn zm8;RFOa}H~%=km@d`TE7vuOoiN_@Sv1oQH#1Ae+-_~bA{>B7C#r%b*kUtQ3*`g~m> z<|o$fQp;muEZG^DMtF5GGU1YenG-MG?FDJ00lQpc3hxd07yRFO!z(^DFQT@CHcs6+ zHYu#{*rN0Skpq{`+PyAsbl(WNBQY=Bz~-LhA&Um&Ol-(Ec#NnJRAbv5m4TTxY|Ko? zd~^x@luAn4EifTYL6YN8Ze2i{L^YIk|b2MnwM*=b;QKz;x>cSI*B&s+q%# zIr%Qcu#P%~trWIC`IEwwoMb9_dC$!b6$D)+UMgSp+O(80BWgp-j?Jo@Gr*kHG;i^| zrmT#aM;3<+s2C73U~}m+>v|Ju%kVYB<26nO?cM;Be`0s`0fi5b&Q)BpkyTagD zaL}$EyYe6CZPSL!&Z1>YKn!V=Mk~|Be;ET1FKuj-soM zf$vbi%4!O!WV`!!);b(i5FbJwamXVRdAO5R2b~p|Q#BgpFSh*==>r z+dA)XjskY_txNRWQ_njvS>0KA{R4Wb`{v$*qBI66i$GNjM7{K+#1^e5YMrPdYAi#j zN~;j4N++sSlsId#HgVFSNfB?PC=}TUj7MotVsF)Onyu>Cmn%MO@JSo2aE4;C9oXe~ z-Es0OdcZN|WBl}6+!@DY!O(qG*di=)80jzFk4SSTzM%8id|!NosOaY}D`Az2RL~tm zAU<1Q@3I+~XP(6eC`2WbM5(pd%9;0Q!Zieaz?O8&WX6Y<04Il60H1sHaN&G@L>QcRf1T(4om!9z(COw_DuX z*#NkloWa0`?V03L$p(0K;Pr*|B<^gg!Xl`steZq!CC_4 z-HyGS>(X9MfXx}M28er&P=io+Z0F#Ef=T;1N;4+m>$|X_^C{cQq11%@oqO{al$gu* z*t818N2&?pM~I96Q~tev5@+OJ>^O!pw60M_uR-2(ge@X#D-}uY!1h!poqA9qs05g? z&)f9)`4K^l#Vh&;`-91M2ilhqT?6}qz%tQ5xo+%peC@cpl}>tG*g}7I=!HeRZp6`w z{KghHSq)7 zzIYK`OSiEfFsvYXUk&#=GaN1K$J{y89B!q;mVJ&BcN}$!Cw_ClE6kpW{WtL>d}k*3 zbIT5TdTJ^5Y3v=&ArYPQU7M>m(dm6^f_pbK)X!Aq*-2r}8QuQytBkz?=aIC~ki0o; z6GYN&r!Y{N+cMYMgRLjo#G95bhAXQ7I=pUfMR?a2A5NVy=!N=_ zsv#}OLpO|0+y7;LV|Ji>q%A)C*}9cgrLhCra-M#vzG1w@{G5}wyFRjderY52C)Okhngwq@(l0bz+21km=;)f#rAOvgENe{lOsdLu_!_+}4a^6(6>E&W;w}8v zVn&B6@n)}OtAboQsn(`l>V0^bo>FE8wu4oD}8Pms`XX6n&97+lCzf^C34@x zZz#n&czL7wYI=ATMScVp{M)x?q|hbAWukK4aR)o;(px}}3-JI1#ZJW&8@MzULDt3y0mlN;R7?L(~pdvom^gLpnO*$ugpVZJK*Hxi55D1AlNes z>&usIo}SUcXVxm9GNZ%J`Bgf9;-tP|klKwe?M~Q`15Hbg&dSW0v1h{A9g{N;iKfC)1(}US(MlmEJlSjW3u-4< z?OLO6$?}tP$Gx()wqVZRstYGq#>G}nDdgqHH|)RlkWC}WYgfYE)nW5@(d(Vm#LLOk zMR`N-s?oUVJZ=-=hFh(U#iI1?GqSxnERPKr&SDNYC3%-HssfoNX0~@~NU9+u*_$f- z9~t>i+UY25cf3Li>5jIAw=XHa4ZLAr-<>YJvwjy{@8iH~1j>XoI)*8XXv-}89%mDI z>^O13i%wBw0BwIciRfd+q%0_xcAN9yu%y9JNrML`MTsHU|9&4`W(jFFcsNQ* zORC?~gq6gKnlw^EOEe{#=CrKyeJX~%msXi~E&vWuv^_kc(riAQ5w}cuL|HtWBsE=# zvFY75g`Wb^Y<$lr2yt0#>%amasGuze=&YNWN@avaNHD!y2TKmj7x_ItOlah=2`dNIOa-ux~J@uJm z*4Kv@`Oyj8;*-m29x4tRHO6r(B*sUVYU)#wm{OGlCx4BX$UxQwG+q2T8Mqh=WPh~S8 zTub-#7yKqXs7NiX|JBTa?ByxGwQq2hIj3Ntb>BW!R7FK~?8cqKlIhbt5-PL1@p_G8 z%+{pBX!n;pp#zvtMX&c|bOHD~U_i3P)60JL%)xrIVBAQqcO&-7F2WNQ^`G`U>Pk+ga$UQe~vmT8lId1-A-=Q>n%3oUhKb9HX zx2!yD>cbABV12O2zud8m{mYa-{yw`5e(u_JYsAWR^aZ)xay`(uhoJ)^pksBUfW=4r zazKm!;ow!gpvj_C_cPO;Ub7h}+&>ipc6JoX8pNbYJ&obP%pEpUy?K#soJdXxsh^qY z8S$$lcHchw#`lgW!LR#!4Zt*=M3X$e@9$6=8pX~Z)fND1l5h` zN{<_%m4V@Y{uO>z&a&J>7jV^pP^xR0%;@i8ZWXuQr8yJe`&g?t7iISCOT`IlS8q3C zXhec(o7&aK&D|7{7_xo2#xE%)E3B&4CqC5{{fcldA=~WYlh%;mJx_Qwy~yHXOc_$r zJx~1Qwam&S?{Smacy@f*9e!`jp)TGSa{|1r`@!kim~-;>5r}t{zRGZ0UCE>frN(NO z_XI1lhg>#RVkxW=W#9g+I<^`E?b&Vn>Ia*03I-(Z6Mxx}QXK8}a%cBL!jhQ}@xh0@ z1^Df+D0IlX*Ea-X1rtONW?kkR`op!-e3Ka~7+uKp&nw8@*g`p=;I3y#S1`Zhp$RX| z%9CkFRQbfhfs^}&M3qk{Vw5^&yzwYj#U~cbI0?Gs#IoU!4o}EwUD+QiXhu=4?XaP5 zpr9xj_VaisSOlILXN!|xx1rLhLOaO(F)t!ohiQ3Op3E018Nt1poCzwn z>HpKFN)vCDEju_iaYWL-XZ2+;3;twNC62Q`wq4Myczaoa&dD)MxnlR2|FEcDLs=)H ztjw16fF~)XT?74O?{Esh76Y?g%+A^{-&?NLxK`5))8T6!kOJc+a&+bsn`zgC*Po~}xJBG*qw_v} zw{%(O+}ahxlbQMA;qyAiAod~@8mWBA?SeatM`hmpcAG)#VHDgn8sbsqbQ>k(0>)N) z4~Q6Z(W}jAvVgs_P1*3b`Yq3^>|#x&xo=r*qFwyu$oQsr-&!`yH@z;WdmbO7hhZ}v zL%yk`<_L+U2M+m%`G>KHmO}^JbZO?J6#v}~bKSW6mWD;#aEn!&D$#aIn{Jzazh2P0 z+*He^C%U!QgVP(M!Ce`OyMaz=Y`%?PlQW-|@9rHTJ_Aus$HH=}hqolOzEoYmuWrF? zkL0mKi>B2kMl^1powaUY^%H6N4e36fnXT0gbIU`hdunZde2|N~al5ZyQJ#NHM6|!r zTGD8X9#xm=YyJ~#Y zhJQQhoGZv}Z{tmFOkd8ko{==(dA8*v*;JB2ZDFqa5Vv$`RmzQ5@tv%N`L7&x{6Kx7 zU>!NrrX)_yb#Zlmsa36|**Bb-6|Z7w=* zfp}IpRk+?li!y~Ss}YQ7rubkmV`+rk;S^$t6T+G)RuX_74gmTY<<^Lo#iyO875K8btPKb#oDb+1c}Bu*H~Ph;N#n!InXg-EBK|2~XZ(|6dWbq@qB2gLj=R zLa9)xoSgJ(IPtMfp`dPV22mjhuC4}yJ8^MlULY6A3B(*g7nuWyIfHE8dV`azn@*v2 zc1Emig-Tr|&?PI(`I@lxV{_X`zl_;`I_$fIZ0 z@2Pc<*|6ZbIM;#VFL&T|Z=K+1LB;AeEk{olE`Lne&t(U}tBNSpPftE>H)}e3ezXOd z&4S4cXAoRLS{>V;u28F06^Mnd!p;SHkhLWL$2VV2!#$FWU+z69`H@`xZB7!S!?(Gb z8e&$uoWhF=no|uVgIy)LoY++oqs8~1uVU9ozA1K%#9&8AZLTK&@g&KO#jcVV9lrm3 z6_c*}oK#UiIIcOachTjJ$G*mXsQ9U4C0*oLLa|liSV$jsu;ntzLRduXD9s_Hz9MxR~@v*uw_s@LnA(JN^_r>uTw zl9{M!5&M2uX+Y4iKhIowfsQ??53Sff(l6TAD#X}6S*Mb} zJOxiEi!#MX_e4zTnLD2f>DX7i)%~$BaDnjUhE?4Oc=is=DNEo(VxEN1pia8VW^}js zIw{>vPUrM`;`fp71dHs=g2skxKH=1978K6h+tOHuLhKFS%AwGTQXF6)HLATl-HcgO zT+;vKaj*2D#W6AchNT!Ihpr#nKkLVjKlwh((R!$p)+Y@}4S3WisXXO!?aC3>HC;bl z5BBVK?74*gp)D54O(@Oio=;>jqOpf+}*6g@|`;!t>{dAD7l2Xyvgfw z(9fa_3S}NSH+Q|#q(A56#Og8#b(tWU8NGEmeG04P^QemlrXG|!h?gJU8)h;~^-6V1 zNw=VCG4zsDm0_$Z!%>wX8^-p}{^8?KE@dlsAL{(!ZcQ9Jcva|->p|XkE}<$Yn~l~h zEW&SymrG&(^W|V%>GBSPDS%0s-qo%=q5M!ODubPlP!X1Y!sSz64nK9z8uDUm#du6a zDo?Y8%{@T~kx5HcgwxQ{@qN?l%kpxvL$fls3+BFe5A)K5&hjOVso@y~8R3ni%a@Gc z?66kWUI=bO`yR!-+57GqUJgUD2i{%NvRG-nyH+W+_Pux4pg_gm*Xo#!!xpggjNj(q z@%{kNu(9wjj5FFs;U2V}t`z z?SXmB-9yx9oP5L(r*Mw}@EEzh>=WSsmC@*i0y*2<#V~SFrB%7%Pvbs7Q?4#&>qE@i z?9By3_nU}VwkC8ZV=o3q&lxuBC16kW?#omBJr;-#yfvOGGhQL%;g;B475SS~2^hC* zH7+dayDV?T#y;yaUpiE>xMO_GlW~#d;n8)mPh^cLj~O+T<^%?BO~MNucJq~Yr@XwZ zOhL;XdzBu}JBYGjj->l zfFL3uDj*`VsfeH=RRpAn7Lg@kaiOlLwMwm@rTuJ+O08Af+E4uyEhLx!dC#56LT&#q zG27vK@407r&$~R&`~Cy{Ef8)x4< z;ylr?>qGI{LY$2;ZndEET0Hh>os{~QD4*Z2)#Us|BGlfwpzU1VmoV}8Z={ueNEQ*o zd~o!CNNy6$4Eg&4+5pGFmXrocxjGGs6PRv9I4oYp0Nk~XE}x{+IV9!U;l zrk{G`0sJ{{^wx)zKYy}z;pC#0f@pjnnIFxPqur?=PX08wWnAk$W&b<*FGK$nC$3+g zH*#9;`n&Q+PQzF*)O%6l|FOcX6Lz!N2y6RyA%n{pV>GCAIs>?UZ}m_a8;Yp8omQhU zE`3N+cegf^d?09DW-9u5G%3}APw$6&pQh}3@lbm?^&Gp7y$gnLk=#?DE~}` zuS@w19~;H;1Xd)!LtiK4bKu&%&5Gq|=iZxh{W)MAY7#c>-?Virz9opS$lK4nj6^#$ zmxJUdc%sL(aCU7DnW4kz5C(#iUy-G>cj)*ELs}tJCFmR^SV^=>4Q#uV)vItsw_bHi zAHgrt0JiV6GeRlS83lkNJQ*@c7VyoaynsdJ4ZQ%UWQ)+no(?&@6LYWR6`qe48rxDz zHM;CBD6Hxe7UvH-l!W7FryKfqSjZ{s8}TrLM5jjFW6-w4PK%;k9y{$vNDtt)5)nnU zhyjI85ynSLdA{7%+a78Pwhs>_r8W*9nYrfHf{uBM=5-WAXV+yXH`ixtxoJyvVy@TLyma$FVmtH4tQ?)9(wX)9Ob(NJ%m{x)Sw(Hj z#D%qi_L23mF&!oO*H0Q2v(s*g4wU$kDl71hldi8zZy#A#Yt1PPx~gW58J%g(E-b`v zV_P9@5$BLrsn>8@I9NPN9f+Z-vM8&u-z;YiG2uV2&9XI8Hn^zXQC$o2ehncz*( zX}?lU0^W}7_8MXGa{#?h4(YRtYO?X^Nq&}1>{i!$@FNsw4FwMsl|EUJ{!~rXlbHqh zj7t2Op&{yLP~N#4goisZ-?s3aLjrIdsqHLB!C&(l?*14G51flmVD^Yv7cA@#okIka zipA^R**daq$2+T)pKq_4^w7eJ@&yk~nf%bg^0EaxmyMY$|1PWT#>S~5lUvd{CrunX zs!^SF-O2B^K6vT)tm{tjpR?DG-umWkH@&{SbM)3X=;xT;XZ@@hDARoJMrYLGc?)Ng z4@D9#vU$=5bq0R(!6&rpRCKga?UxW!nsUPG4>0(IriI*rKyF@iwA1oHQszOJ4o^#9 z%}J^xO24Pqo0}1hS0U7fzpPqgbCL?`c9N9qJQzD0;{-7XZ*kVtEc^nB)o;MELcodd zP^c=u1@PQ~04V|IAtG>GaV4y1a1e_7A6$_jloBf0&^JXq@TxI)Y3uCe2cMeXIHusX z>fl|Ctv3w!?^&5qk!sJ+PoI#_=55|GCBO2zc{6V=2v)~?Hbl97!^RfLL6%`lNOfgq z=YS?I3fqJ&(%VuuLa0We_9d|0N5d>L;ydE3`XP%UN;)3a;kg#kk-9{%(rTixq$U>jwEt=9Cu8-SV;FY$tnQl>D6h zY%{wbgbd<)9Fg7@7Zbjm@M}+>gznS_%_;8OR?1GoXX9{d?7hEM zf2(gMl?9D4TlER|7j!|keW*~U`Vd1e&&yi+B^Ujqr6~S? z2sz+eNQnh|HVe!dAc|mb!1IhEYaG<#f_5ievjYOx>=1ERi!nJiYVPg{^J@znK6~dq z1qC@7xn-?QYo-<#PhHd0yqbQlZl3n!>gMLvPfnYDkbWK%Uw_IS^E*)&YA+Az)JaK4 z*bQk(J5pIf*^G4~TGq`d!_Srx_&Kk6Rsa7qecF?&nrRGqoK`AVlO$6K_ERvx-%;YR z82K;*l1b%#--@Q*H&t(KkO93Ch4bP!(sn-Xse+F*;JI)b7}(s?iODC`o|9G#8s~K@ z*$TKnZBVc(>?I9{RHf}j)7H0*xnn{O|G92_&i?3}rox=z`5sqpQ{kjps^;c3Q;NYc z8xj85mNlXzF`=j>C#$6-@doP8d~9Y}^%_>NRVY7YR!dgF0QKxp$N{&4&8{{4fFPSr zv|iG|n_F|XkCIOiSxh-8U;i!QRKx%Yho*31`X%f~NtU-fC2wIXpJFQ$I3MA#yozmj zfZZjp?tNuDszF{7S4zv&I~`gv4?RFI_9^k_jofFBcZa}%-3L4tUO8d__#;7u(nLrc1Qn9S zZI8ZpM@j95ckX4ADwph?J^S#8NyS@6+_e5Fv9f;IqvOZ#x~cX`^MV6QDnb?Ob1UxK zs=^px^KNC^#1F&^*l=+J9NCiaNHx#2mHgbR@UTe$3Z<1`$8T!K+DwI{~M5YwI-iu3u>Gm@XyL);6JO$tepRxjzL z^th*;T&~mUH*G4hX8VOXH=?m@;7TIv4lreI*s zHuu2f@i}!l(K9>fJ0v%|ICA~WG9dHDkGgY4N%r_VCoWo;RN0ojc#*%do$uS-klVi- zklTvHR-`wV+mx}Sr<;lD7m#oPZ_PSRh>~7yjL2>1>JZ%#xt;2iK%r5-i=#gHU<(sv zx$n&{zSzJV@;}~XsO{G%FP0a!qUK{$v%FNkk>oFuLLeW+cFczXR&I=_}W z10i*5G@J&6q@EQ@(rbA#2$$XcxG~ylb(lM}do=1ls`S@nggDoC;maEEHz|CK!-eOC zxNxzMQ##3gM%-lX7_!5v2-;b)i_~i;{w1IdhVwr3wHz${pL@jplYSzf|HBuP{&&X? zC6`0ro9xh}iYOkNoLR==s1qq#$p~%;Om#z0K;*Oot&bvnL@T1zOQ7 zp%kZvzd-t)K-wQuGskF>f2wkBb6EEqVpJVNlUn?SlwgF5Yv^6Vukgl2ywgRTWJ>p$ z(_dVw^n1l@9JkX%X0YtyhC>2vE zCVZ@h2WQK`6w6NnB0Ag~igi~d)D-8JOzO%@PR;R_#3qf*nX@1*v#cUKOrQ79z2wdu zUX-BKtW;}Ky-9IPlVja}qh_heFsimFWH;79ZodtisTe+$AK{HTpwyr1F*&Z`jkPPh zF%H;*oDK8FNHtc}{nDZmq*ri8AVu82maFO)!{KG{_Q{{=cA7YCtO~TTnAo{b-ZeAi zW!(|lSoml1<4+kc$?L!x%O0_`HG^1VvlmF}fAQ3i48(_Oie{A~4Za$|rV%$#%a62g z{5S2JhO}?$zYieFV?S53oRaMDG`OxBHD0Vtreu+|Eur{<@WFGl5b=-aO^!wxjMl2c+^%uS$ z`2)^X@(PoQLAVIwPh^kwsMP`trZ+=+9TFSb1)UGR6tI`<%nnItK4Gp?jlfEAc6-%0 zlfwk2a=5f2z$kKSg+bt4(dCY;G*PjQ27#o)bf&%gI(OCm^0zf3LS(#_)7@z<2Yv+GR`=I$B(mrVZhqMoxXX$x<|LlLc|KRq)_aEFo`2K_2`Ta?U z!g?W2mJQCXzW28B`_k8 zehFGSx3!#p(8Un%0a=SDn<5dT@Y`I;_ki(Uz>?#t=WKs%jof?BJ>5$`I$tpFvFkVf z=}(-jbw(j>iIaBr9lCOvm|Nghh1b9Im4@~OAPm@F{riG=6ntEexo7acyQaN=T&`*F z9~a(E_gC)o@9wXR7v;5BC%rGd0-d-zyC5GGtdQT6-(Sdb%eSCyOeg~`f1D!seT(qwMR{aoJw1}u+B z*1!xS&T1I0&Yci;PSdCCH{ncMinAEH8uv>|(GHiwR!IwJlbvZ1N*VCTJi7_ZaEqu0 zHY-ebmDaVcT}5mQZ&`fRj#0O-rr)cJL$X*}!{VHQXri!)>&t57&+O^0u3OvEkVse_ zDWCfzXS1l)?=qp8m43u2EvEN+_wM*pope~O2CHT1jt?7pyVMd9TNF8pc|TVohh0I*1{@$QdV z%%|W$GPBiIYlY~I$X%W{22N1;K4DM5(p5AWZU+->MIqR*_7{m+qp)nm$1^*Yw-Cif z9rs%9?A?po_ubJh!}<9rLS?Ca?>$KJ_5t8xW$2dmLdO}Q3h;wi!4LR>p+p~BQWC3| zvJk88ft8WTj)fdq4SuIO<^6)LtPb5(z2y>NJD`GKePC5{$V6EA2;=p*iXKK#afg%& z+B&`zZ#3az6x|)3I1rW-RgxT-c;9uwE{`!KGuv4_x*(>gbxLbd0A%XG+!-a~s{Kbp zH|?G>cEglB-d5B$rLBm$i|2P1>7_@o?WCxroY=hPP-x`zP+5CbUO{=Fvn8>1UQ@45 z`exzo>k89b7Ss;ws?N_VNlUA)4zH5{7}LhiTOC!~a$;ov*1J&{pUlic(y+tdw6A~~l0 z`N^X)hQAfET7^-=&igu}-*Y+6$92}er)e+f&ChwCQZLZ_@Vqn}fy2c#=h~d*-t@2O zT*QI`T9On$T2x{9E78pn8{v>`VwWSx*RLWVV^{gw>X@p!;l<-yi}Ew4ma?KidBBMr z#b$3%k}IprYD3F^I=iIA`uNi^_InI=ljcb0lpXhuT-lM;HTQ-<^1}r3?t6RAn2{|F z6OOS%xowMUsu#ECVlD51?m*aSpD^cy%BM*XU*NW@#15Sl1h+MT(9|b2evQ%30oBF` zpqfoU5LX*Aa!j=(#a>2M@R~0X1|wuO*C0sACX5vY$KZ;?pBOZCe=H0d^MGfaI}d>z z7N39)apd@qAgvoVT7Ve|q+NOYU(nY3V`H!cNRPmp{EDwhIFPPclY`p_ugO8}L)PS1 za9pQBj?;bm*B%xcuzzU(dg%SHY45-P(Ds4*^LDzwavy$w$hkS_|3Ba-i9#mGttS)x zK3`hGv+PeHOT6HUiBrWpJn^3RLXhcCKrz*^6QR<=3hDd%OOKVlT>9%$RcU*S-{ZUF zjZ5^#e(&*yV&a>Rd0zJX+9P>AUQdGghn!AtLQR600B_4wE`E>^GmG55gd+xoP$8~| z5lMFLLWFU^*Lp5LcOsZLENTh)AJxUBfuK9pg}h57jRrgm(c-zxP(s)8CSS&gxs{FA zH6#`9iOP?aKYxDN2c0bAoe?*VO1GysUbk)fLO5xvZ#jCCHdgA=YpWOBJ!<^M$+^YT zH+I06Dkd~058DygKEG;v#@y3S?QSVvxokm4UeleYmn?qmfpKC)w?Td~j3rzNHqBu9 zp)&CH&Y^P!IWf4sf3AkM56smy?SteK?n85^++UeP33=z-$UBz>iR6aOUf3Ow0PVPZ zDuFqj**VxkY27fXDCiv{^&!Uy8RTS-A}&S{0RlU~PjfA3);Y*$LlGF2qqk0aQT7gw zd6Q)oPU#wng|qX!JaPnL{vLN%@hM z*H6kvO#Kf#I+xyFySn?PoUDfJoj2Vv{GyJN!P73miKNxd0BmP(%ooo)ggqznKuEA)6t?k`dQu zZ#Rw6k4+gsj&kAq_1UoQ6ILn+H~OE+ID{Pi|FLlhJF4~mJ3bD{Vg4TUe#(3FzaO{r z+tIGr`S;M#9hTd#s9Vvl=n_jJ?fm+6{nwK>dEokf-Ghy!c8rrjUaPtuc32`%1-B6! z@aB*w#^E&?eMV!V!*90uELNZ0=JO>bd;OFjCBfuN_u00Y_nJks*>02Bi>lL<>?)T(O)1R| zIDHwSD83p_v;xkVyjHpdou@=-6sGo=8ng1D;GEECqDry|vK!M_McgxsH)^>1ow71` z&x~0eKBAtPz48A{ZLhmz?F=4~#&c_w>pbAN;;^PTIf%tP0+>hV_K;WlPkglg*W>_^ za1^Cfy43%||CXWF|N4I(OjR2+4*!<5HZU;n4xz7MT~$#I7j93a(S8y)Rw?Tz1K8G! ze4h!6;CeP<$o1Q>K9&35KjiwYXy^B5qlR2helF$u&Ee~-h`%=I z{w+%T)ep$8!22KI{ojlRtpBUu;7od5{XFi|^e&xwo!=<^$9M8WI5emgLi=GBt^*|n zR#Un#;&fb`!;qv=k<{q%uq60=dO|KohKgO%#-@GeZued`cN#=}hY0lm78JQCJ_MRX z(c$2)omtlfx9(Dl>(_6;vP~hV-AIfz&yK!Tr@Q-!Y9t$=D;?92%uswogJs57 zTxKXb+8~*M>q%xP*AJE%gLOSFGy3mObEsTTBNve|U#V#h`TfHceSkg3p8E@AOtR3) zCCq7ObV7nD*@}StlAdUj9Spw%A-5?dL5it&d=eF9vVCH%b$x28_k|^j;`t%6Cq@>o zzzY}8Vq^g;BO7%m%z_xj(H_<@$)MQLd+Ts@#86`2KZ>4HnbiH#J9aJ$ho-h?|gy(Q%yl08BWkGdBBtkQR=tB3v!P`zffJu{WI1eMfb>&Cm-E z^k|%Rgp&`1;-G^Vbeb4jt=(Yx1T4*{PgE+6ky68u%Le-a%5WhBP9b^A-Z=Xk!mm*V zBTD}T#f2i^1VRiF91-$I4Y;xNLAM;!eZvji?DOs=;DyIXhkLuk%D&g7!#h8~$Id_e z;ScB+K1dIPjq4Yto%DM3z66vUI0{~Y9vW1JF*YVT$rtl}Y+_=(^@>lEuL)^h)~Gc8 zHn`lZ@!oh*7e6K%tR?E^G-v_kHNTk}ao_T;65*N5T=XxKHSpj0gd$;qgos0HGU@o{Xy@D%^4=8a#etz zQ?OVEQBcE_Q)R4|z*kDS03BEoB}hl4b5dv|3NWq%r*%JT$FgS(@%hi7PFb=nWD@H|R~oPx01kTRt(^jGFuIl|EJ& zvN2Zmf?m`QVbKXiY0=SXMG61rC+33Y9(v{yF5?jdfj->tkH;7-p)*S4zKs4eYBz8^ z$fbt00&EJ-uE-g33he=YrU6$pxE=C8z;Qo!_sgXZ`1A7k^ET3wKtG@kOYdo(#eLiO znX)mXip~^E_)M`_B4-M&{Q~D0@~Yulevke>97ek$^Kg%WK3p~7-XC$@n6B5dPHKV9 z4643rIB%!xsU0o2|Ep+6dNI2H2WS^of-;eTb^?(gR|@sU%&~Kj0hv#`S3mqYY z$;8xZ3zLWgc<_`~vZ%~z6DJTD4ag1tSG5tJlCDgl+ zN!DqL!B(@Uy40*&4D_YG_udJ%Kz?}FE^*Sr$F3KX<+zzYVM3e zzo42bat!-iU7X707P`gx0JPsPGA|cSUi`+X=i<&Cd$zOJx5^s)?Awd7%os;}AofUl zRKbunK<$dpq=VWOUk9#ViFw7D61l#AzR3sjrX-`CU*F$;?fv`pw4wJ$eO7#g`|}5l zo9+{Z=Zf2@9pkwYdw|X|r_dDATP08@oLUE5cenKDkSK@3{!S^%_K9`4zSe2bBE^te zrDU}V!cU4|4(1Y5i6R9=15s>d5(7vigg;4cl_HkQcC9-22e$S4gS6+x#Ve&^CR}+N z(Iwjl(p#6mC(`@Q`|joa*@yn%eW6p89k&zz@`ObYEQseH4cS!|pK!%HPJ1P~-EOwF z5pXbUuyL^oD-Fq>Flz*s8_+`fsnygQv3T*y2${N{UQNA{zq#*TaYckuJ;1YGPo1Oj zgH4&ob&5hhpZz)o_mh%Nq4-0W(6IJjPkOF$J-uC|eUKkk(J8{$D=|Rj`fY=*Z{_=u z-yijN=>s|^>evhiRxTdi)~4fbq)Og&4+EXPh}rDUb(hP}WxBGOol7*?FFaoAgliLS=f zODX=#>7BKgTFV zWr~epfr5pMTVoonSed+Rbn8u{Qq$XRX>Ywg!;x9im|Zu&DGi{!iBqPPU!Re)tZMST zH#t*^e95J0PJ2pO+CA(vi%QCiYpfIZjGbJu=G9HNy|$&LYf?sSj=TDnBTG{AOpiYq zW8WU-8PAShw{22J)|C6^%)M`720Pb!L!)ok7OWTU3yI@$q*#$7iZ6r~0cwAdoE|$w zjvT@ng-mQ{=hyeki?`4Y?s(+-Nc+FsKkRF{_WlEM1oqyiygwyMf9OhO{Yo$J{)`*a zABs&4zP|tLMf;%Zm9rP}XbIwSlq0ZISad>Fs!uI?5orSL!1XM`KT7OzMCbH&J^XxU zyJO2zC~IIUpyHG@Fg5+NEW6~bgQF)pWYFXIGyPR+5Jx~r@c7)|{DBwWK-x17ln)0r zd3;V9CX3D>>8MFGS=DOm_qtBagvfwEb;rIK%Ew0oc|K`qAV?uGkt+`*zDPz|?2Z3G&JT`R zRMGWtRoC|6|0i~S&5Xt@gZwdz79IBg8$&<5&IYfi{&Vk|^%Obx#$nwDA*Q4lq(xd1J1GyiqLM0`K1ESq(qy#1h zLvR|Hw4dtD8bDelQC}-62n~v=L)!oj^Xu11o5mML8b@Z>M&f3lxD|C6n1OOc%4<+X zYIxuC;_!#qQ@eH{zcYVsBu{}?Xzz}RLi*36p%kOmqPIjxTlA92>oEBYu1ngO7@slf zdz;0Mv<2SyA!!RR%Y?(?`KrUzN78=6auSSOnD4w(N*V*41!2oAoXX&N4&Rivq zH0aF5^>pSc*AF^#huj|?ok9H}9HeqR$3ceI*qiDLk^3uHiy>#Ua{a%a(Y!z5GaC4R z_z5mUx5^dTdrV2T#1~-!!Y9pc#%4}t`%W8k@#&0n(s^yOE*cxMB0kgd zJf5ELBBT{JtN{-vN*5N)fNhOr8Ki0@QZhXL8#Zmfl`SeU2%S6KzwGh3xskk}8STqQ zk!_t_M3EOBb>ZGZsebw6vj(RM?L9nk=kg9`zirLe-afonFKp&)%C-y7!Kt*gC{v=~ zBi~XY2#d&>5tWlBnSA~ZUz@AVbJ>oJZyEhQ1Mc+VE(6Nq%0n+h9U!0>a^^eUDxAf_ z-v2^UZ@-)1+PoG6&Vqq3(!b`q=#3h98Jbh<_j119EBKo1KcCQE$ImllD1zLG_%G@D z!C0;S>xafL|Lyg#5S6tD#pe=#E*zi~qZ!nGjz4!QwfEZx_-9;C{tM;$et$jM&vAb} zzh3dzEAKVr{{3>2Ur&02ay^%m;BolzUK;fSumh0C+du~lLUx+|6mlR*pQTB15eHi1 zg1F*#eY8sBZD_2Gdw5rOwy7Rbn~t8SGDUqq^K zb12glWw%+Qtx3rO6aAt;8Uiyq#evUwLsas4A=)1!TGQFy%N)<|IT2( zf}EV(M-o=>a8ltvjwI#fOYsM`0R<#^2pxGcBEO*95?@i_Q>+wNe?JO#_9JmLTFLJc z=hT=pJJ!!jYDo4rq<3~&hRYXNNlNVLLunI}eO;N8r&M5nu7tzIxw zRBdDS7oO9rAJrMJyH5TH5+S^%rfBzIjFoureUZ6^JbOj+5!xx>P*d()%g?KByk+I!{bapPwI5dlblc{R>S~@s*EnVGB7*KxwF6H{&{Q9fM2n)fle^$Bvm~x*bI4hx(q2FBR zi|CHfQRb13f_AR+DedHU2LBd*6{0H&4c!`|@jP=v4b~3q$?i}L^GVXt&`X+kHKOJY zajj~bYOhLCX*tWPF!sXK*e_9v0|;0s?~x>^sE7&t0~1$$dwlEG!{5q?EPQk8tx6Ko zq0g&2F3{t8)RG2IJ06Orr>R~N-$exZjm#Y#6T#|$>P2Thmd&&r++&%Qv+)cS&eq1<*l{qZR)+CNjmtO z^!*BF{7|-CkjDv&nUs}YKPecTRG+??^3?ZDRd1-8JS;JB*yO5Ub#)M_Rr#~TXVqJ= zAET)5v6@{0EMH)OkZG-9ui>cSC4@6k+;|r*eO+kEuQqZRFuj;LX!7>Dz zk*8y__9#@$WK>FoUJ00zT4tb`kqx^@Rg0q${Jbb^OcRv${JPX zTWE^|7q$ZbiiCb~Hu+Y(w@5zsOPB>Wu^WwS9XO=qv$nfU9Z}(vh2{NL8SjC!agh6b zAQ-><#}0~tLqVD6J(y=V_){67Xjl?%tH~(66Qz4+t#zAquT`?fC`13pzTcd){pK8m zBQl&Qph!YHgj2*5M@Ict*5byB_Uh84^s4xA*^3%0+wgnFuz2~R>f7cKN$E+YS>u{V z_%racTbT>wmL^`J7Np-ssy_WCepQ3_tD)TD$CQ-4ABCa>Z8&GIW^I_LsN(;J=ZP#R!ySUVuw%%jvis^*^e>)zcPn~_ zd!NAGri{lnRD$9}L$c$<-4hefrFVeH=f?;tgp$Rl;wPZiA|=BdN%RuRkQEpkD-V?5}_5 zp8PN!ot5$qMz-mduPo>0!%tJS!E9&aC}T>qYO{Yz-y zNbOjv{Qhv?;r{sgM!KNhkM=Fpj%}v2(>tNA73LT^1Fj2W|M7P!E*M;ACHA-)^YP?q zQ3Ln7VpZ}CAB3SKGt$NOoD{#J(gNzZUHE@RvSd*=gle?tgp!a|)bMQoS}n5uO9I>_ zj#FBut``}iP(io^c%68j+HD}^KN4&)mp>Jh6L$Ciu(g25=g4o%Z|$PT>^dmD*PDkv zzlBwGjK2fM8h^tsqul3s2MkvDZ^Xp#H*FwED8!f+!Pi5%FS1bMCml||SGx_Fc)eGV za@CNX`AK@Z+VlCK$|hmY-fu1w zhT=t-E9|atZH2s$Yfxe0vu2O=pNvXbf3U8$dvw zAss#eW2Hci-YR;b5s8%R*4nMB2V}u~T+>g`qT)}Co1BFCw}s)kq&G8yAFSwGBd)LP z(}<2f!a;s6g8A& zjsqV2(%0R6pLa{ERqL*x+z{?Nupff_zTvfn_ZtQ*s1fy8kpe@lLS`)!mFcn}Uxl_- zg_RVkLBJO_c{>Po1k~*tfZi&hr(!+5l%KJ2;=bNn*(pRI=J!3s?mBqzUNM&a?e6_@ zB_4@BK+EO6Bn|B-{EZK)!GL$g-lti=dKmil5}XGqh}z`~H#{eJ+2h?>mWgctJyEF^ zbm!(!UW%Eqg2I&4KZ_nQHt!RY6|iBim?t6`jLqaTNg|weW>#P|*O*1EIUgxEKx+r@ z3m$-}Q+eKN9GsM_8Zd^aGvz6cgU_R$ zY)Tpl_&Of%j<#VS_J*P`4)F=DT1#!5_=(wOW4c-niBTL=H8qidT=?~{G{UY9o*W)z zZ6(5_6}KE-w0myBk(+NjynOYMvg;q2$=2)Z7C$_d{asGlaGzZgd*!Gdcd$Roc{3kd z)(mwHeS-v2RYLY;32kSDSauL$`b?C}5~J~{g*)xF?u0CqFnAUDDm)3Ne-PJvJ`cF46$=-B-2Q$5J z@PGG?o_z3&ZO_YJjt{THAw18!@rM2DQ$xN?!y~x|a zM5cD-tQ0a}=e^C_Hw|f5&N4dVl(Q^yAM*L1=Vu&zm60A~(_yJAsu!;vP%R?Apc2+;uUWc#TKT+jA!o|&8Kc`rOsh&Z7aqQC*7h0s z-K%abnbH{W)l8|Znilf2&sM*2Pm6ch__F`*``+}-LE|=KROy8BMJro2JiqGn>5`du zjdrwdm|0RZlWI^=?hga>%tWD@uogw^_{ms<%S93Lo)ZB>4pnACHfuvob--2=n|no< zlcO&Gr>#prHt9+@?J+^CPzoIGcLgH(4%BxYw2(k+jeG z(o-2~S3Wtn_11zT!RsEKKY8nnf`s7k^s2ed>3t1lYZ@!(G-r4lm$Y|PSB)==ODJhA zEE!kfWrwHlUs0DDD+krPIv%*8wsgiFBZ_9V6>GJ_rmvR&oRsNrTrjeH#_;r=DOH`h zc^wu0)P`9k{%F5roqQ&J1Ha&~=Y%ZwY|*Mzb9nTCFl4qrL;*Foog&v zD-?j>6Uwu+m0v96IeU;Q5V~?O%0Mre06#QIF79~J7L%f`Vwa;+?IUKCM`^qBr{1$b zM1`9puGqAstj6L@6p}L(rX^Jt6;{@<_nRJ`cGrjRygGN&RYATgUw+(NRMU`f^UZUP zJn-~w_aB`S%h&@NOXkR1o*_MnYFB)NIm{A5!n}~fT@%HG>`)D>sR?BZOkD{c`Lm}a zBR&IhFR*7J}j8 zJF*(*-92&16H7`fX5DhSXhv5-+K2@Wp_w&4Yi@Iy^k3!kYx27){0TdgQoT;UKP7ra z^NlN(3=b|_JbhGV%U#E2&OfxFtJP96ZOe?-b(0GV#x3p488dNWKIU~D$p!4&LScHh zSwKp{ccDo70kL;GQazqj2b9R5Ipw>|;I;q@1YAL+ao2|IFL~beh@SX=y4nS~FfUww zWT3JnReYhrIm=@Y+^;<(M5|P2==aefV`x+~&&|LGiL7kt;+!KBHokaUS$gBtl9JXU z&xOCXJT+~`v9V3lD?QsD{P}zPcP6*4nbdG>o+sLsF?vx$MOV3Br_Zfw@NRq18ol2W z8=V?VXdZcgYh!9vc1*N~*6<$rR^VJVK$9Cyr}y36zEl+c`L@TX7h;LsnhGQG^eG|F zCg}5!uTTsbGBWhO@kZ@G^@J6xOV@5}>ibp_ANl&gIDg~yEps=v`<>o2=Rs$3tksb}w*IC)b8^_PIv?4#Y4y^g z2iM>7o7c|Fte-U^-{vZBo?I4atQqF@mX}v0$LFRbnp~~RVf@fLZ6W!M`HL57s8;h` z-5M>{?@|111^CMmpMXq@D%6J5n&La`V_jEF?Hbyf5V-Kbk!pq$`0GN-h zVLcD1bq3X5d-*l_>P2Vpmes3Ddm4Gt`YXRv#YGSnq^A;2UxoeVgw)J{{vQQldAujX zkcd;@II;p~CVubrW_rH2cBOo;$jE-MHQ~nzARE^I8BSNFQXg}K({x&~jd=7xrK-8* z=+Z@pZmx;^?AGLrm|NX2uQ8xeOIa=RYey^`o}v=p+9Q8=_J#?^nX&sZWO= z^!_$gCZAzj;U1xcgsPK;ua^9O56SUU%F`7?qI2snbQd%#!|lW1Dc&Lo}5kTiPLL@{+o-`s_>Ek-== z6zNfT9_l?H2e?kJl~C{j1-DRUjUKL4deF5|ZO=9&mgaa}608I$Mo15eE`@Y$^b2X& zWF;a|&%f4tkp3o5mFAa3^%? z)R#>p?%t?5MsG4GKx>}PW8~t3KeM(cR%b^b{OBoNYEo)SGVC8rSZW|Pkk*zm zR=Vu%w7-W$)XIwIYaYV}Tr;Z7@B)c=gMm1dlE7F|EtD`-z>VQ#tw$0=HBH$^44(8v zhb1|xuqi)5pVi*h)m?(; zKP>vN1hDeHR1Wov=eTUAB|(ZB($J!h5P^zftVnZ`>pvzANn7Se0Dc@1KVPzZ-<<3f z>;C7uJE2A;%5Hu)b1NU~W={rk8L8{1+T&MkHd5 zMpBHUA&i-)U=D}*laTv}AfO_NK7nMu5iipdvbu~ZI(_Uv zZSDH8{@wxoGc4=5WVJwx9IS9E1ws(}Bl0N;nc@7%oZ*kRKf zS8gd-Q*p-*Dz>q)KFK3jNYBkavVP>KjnCeQ>}jP>b;~b%GtOW<>70EV{QEqi@r1)+ zNX>i^YY;{mKVsw9>C=Y%RKc1x9w2FyGjm3xgo*!>8lp|fPtbqBB99Yf8%9&n5fvkA z5W{}R+LAnuk}35|9-5Qgu<_+cO|vzVa((49=g+*W!`pw@?u;+Ui#9k$-*IfgjjwF3 z=S9wPrfzN@y`sI`VJqyMGH}>poN29)T@x!5kmei5-aqAoEo_dFs76N~yr@up&4n?& z?l#{QM|jfBR>?v~ zoXepgN#4NH<_Daz&uLXhKz4M-0m>3S<(|sAwRYOZaT)SQ(hj+5`i=RE@&F>v8h_^m zRKM6kg)b~-<@tA$?TF_mlfQ_n8TmM%z+ga%gSbtK$08j?~8x8c;oLM8S~1E!&eo(^Ww9wXWluY zVn$73aN$pHyzvh=KtF-qOYu|0fs&Amw(B_|0Z|w=OUD7y<486`TiG1)CK(vZ%}Y)? zFS;$n)5}R%ZC@_UG3&8`&0lrEcV%rt|M@7U}HUn_BOj zRjntPRtip32G(t!aBpb1FUy^m9i0;$<;r#C7sR6PF&IEOSocVd2Ua-#+1??4SP;~#iZ~B;6z5=={Kkr2NVLz$vP-9Zy}yLzJc#< zQLG^PlfLLci=nNo2*7Yby*z@TF%%~rq(pc{WRz#pI9ps|swXPb;x3LYYp>3C&nm0< z{5AIQ)3*4;lyG}#M^%Blt+48qSLJP61BD5|r9Y@v#{}C-|9bara*g;8_P{`EVSDMB zHEY&5oI zU+>z9$}E>J8><#RG9N1x>-Y9wJ~#zoVaz3AXlt-Wq3r>OKfRm~|i zi`#NbGtU%NI+BZ$VsewCpJMl{U-R_dZl;?$Oa9+uOS)IrxlwnkdP13}Y~}NJKFJKT zX33YIX})bjVO;t6N)E~*A1sfjfOD_-po9e^MtD2?`}p(CcBq9OJ$=T5gls+ zZ!A`dL)F|jLKb_RH}2B}cG2zjB-9d_qTA?!rx9)hoDP&nkD`i7x<+2RF%qy06h~KM z;CWUjqTG=u`_OB%4&C0|wr+n{a7If`LgS5NPMr8g{HuIMYAu-kz?`*5uFn(qh%xoc zCT2_+k=HjIHK=zf{%nc!R#-Q^tAa^F-zPyPf1=L8J{WNtg21Dp!@)(z?C} z3~#>G^+?lWhqwOsgH7!Ze{lEY-OWwACQaEox5_AfweyR=29o#JwmtE~6HhVW$ONzN zDddz~^w?Zw9>TT8p}&>zdAT-bgR6ejXL<_jX)*U<=im|%ob!JYPlj{JryNg$-Fyq| zFe_{5K>~8n5^gwQG#H#9l|LSeRcAUglFkZIuR0+zVI4a4#`3PzF4Wfmy_)KxuSIEb@}t`%`vRi{ho(TgIiGJT6~eV{rL{ z!^gA-bfqJMJ`?*nk}zbAte7yPs42@KY0Br_+xf<8s(XJa_sC!WVnx1rtNIQW%g(UU zzQ4(9iYC^j$}veHK-}Qm*t^ zt~)qw_U7?v-6!PBmk%Di%+{Ars!8H~lz!d!H+r2H-xg=}?Ez`zwPuMr`?s84RWoA4 z3(MsDBuC#qQAine^O!f!?Rr!a%jaxto%+!HQgHVBgkt1ulLKoo92mcWcwZ_XpD(aq z!G}W(8}VJU=j~p za*tCiEA`tYo&1qDA+IineZuO@d;4PdnyvoOu;Of=Uc5!M?$`1&R}Uu3Z7dG6b38En zDX8Of{&58b_wep-(b>Xf5Y85XUA=_)S%LoBDKoId_V#ErcFPOwaUAB1J$6b&78lWo z2=1HR$QSI?H`$WEmeO7M)@^xSbo^~i&aDnqg0A^Ba_@N@ax0r}jstX_Wm2+KPl?h{w_ z-P1Qo+|xIUf9)4rSV^;<6P)a9$efgFN{TZjC7I%+0t88a5i)BE z3R5i~Yx0anZ(ae_ClaJj@*3hlj|2SiwAIlZ7jw~zau>u}AO{;6e_=;*XDEN_MZC7Y5ebT7$+OwbERC~P(OnBUp>O)P?Aw+&|7p`tJa~l+RQ$W$E4SqO%AQj1oo-$ zsiz*=vo)l5Hy{zGp;pf~7NWyA8p$K-$qU91-6+;}E}Z=|Z1{v1DLh=*QtlwxG^9tT z1@`#LV$&OIDiY+hb4;8j4u9*6yfU$I!m^RcqqFwtbe>^zTBN&W-AgA$v9}l%B}xqz zjz6HdZcab`;QBEBKn&v#teEJVL)j@NQ7kA_kOwKLp8$nGzypyZLI6Dw2VoEJCjSY1 zKztFz21pR9{}12;1}kUQ<10dqx9ppIV8MZpIqJ zYu3De-?rD+)|obJ(2ZFC^6lH+TvuzyZ8m*$y(_#L$iqVl zFzA+e0B3K&T*7G$=O@Lo@LRGpyyT&-+n!!j(34oVU{u>f#ogUvjUyp+^MSc2e)j6< z<*n(SD6yFiafW<*f(n_=@qVSGFWd}4qhZV8v=A}HHbIh1W{r5($W;bPyo`^q`+Msal32_q)_U8g^uPg9D~~3Veg|)JSnq_K zAEZf_B(p8|tbXp+VIwx2xk)}RF7&so9KUwMV>@99_4QWHySshPBlAmC>-$V)lc9z> z*JCUst|v#KKNjq;1R-=rh{uY;(14Yw(Ik3Z*j{@>c5`A}ZEU^kBKU{q+GwQypq`Np z7gmtz$l^nRl8A}0m3hyCgA^uAiadYGW3zKg7w(=be{-0P^OR4noj+&oG8T1Ma#vk< z_vrcC7ee{%U0yM%#xJ|pQF?SfVFS7x^)dIjfGX4nI8^!mfT$kV8pA zbC2Ke0h;F3kO>84J;}l+J|6-*&$6hHJ`~Gh6aEt2fa$ctUi`C(AL|1FV(Qu1-@H!V z2ke+3{-6F#(Zz}l=`WVr^`*0S%pUV-Q`=7`Z2#|VO(P%q;GW5kH8=fa;@fBSecQ#a z<~_EkG?2W{=O6#ncaJ?SUq0O7^&Lvf{QPe_F}I@7Mg9}?nd%DE5?bkz?l?D?)VH9H zHtLlU58j<8eB^SuV`|;?nXgzIe6OgLx?75Af!==oI`YTi+(vh+!{lO8FW?@5VUAfP zAN62AE<3Es>Rdf`?(M^4{B7%I%vjx=z`73}6!(~tLPhyCsWww`sJO5u#Vr2gfe-F$ zoOI~>-G{Co8awXs&+hL#07K~gnRotTW8?5mZ?3!J%}v8c-2FPPCw$|qOs9&9iq6?axTHy@ps zQ91K=wrJMOn$;&)-0=Lyks}}d@~6LGG0$D+*6N`X?o-1jud+6*c#_MReb7y0_+_K9 zgYv-c_ob8PP6vQ?&q)W$e&`@*ei@2N*5x~V=k^iiLLL9UsXZ&41(Sm0sE8GLXW)mF z?ag&vM0DtFm8u<*LUz|^?LnWDihJ6}P>xZtTkwr@eCH+waesojsV1(e4->EW*2c+-@6^Fg=ZtGO^o3RvWwGZZl8u zShZJ-9R{$+C=f?h9M%0GA&leL#*0j0o(EfSl7kF5jr_U6mxT;!Z?bCR-oBK_43@Np zih@9bL0lw_JN(UoS@H5hCR`QR&Y1d@KWCQbdJmzC&_z~Y?P3P8cAL?_uF5#a=A+Mc zVGcrH>=`s_*fSxc7H^@|+st~C?nSm1!(u`b7F0_>=iNxm>2TUQEJjhSLf}{5p>31e zc<=+qD%48F^rv(6{6*CFix5P~@_`x&VKc~LW17RPU>_?uEdQL1d{O>jpZvj#Y!ssX z@$ApaSKkjzmPPs!b?6=DiuT`mtiftw(Q{aMCqw4UJX@yMmYHeuNIx7xS~t@txhYG$z6)-O_(yvvGY-H<(J*5s72OY7r3wKtBx^xdeLWl^4r8BMLv zPtBM&6*LtJG*ZQiYzhmv-1K8~) zu;YHm{X|0%tQz5s(lcHG@P2%^$nfE{Ou60bPhjNchLcnL13 zMG;Ye#X%MlToz&)8F94Gr5BILpPTaPx@Ima*QF01R*;^crv?5i`S-6cg+X$^%2u`P zW#$gY2f6)*=Q@Nrp`ZzGZ8hs^jU!NW$f~x&jUY*a)#{9LwAtItZE7TxkxUwm(bR?= zNXQh9P2mS7)-WtyzKlJ2F=qi9DWsgUsLhUFv+x^iCF(8^B9ihgTwY#Pd^pxvd1md& z8eQDM&}WD9@{avkT=vm!xs07U^7p<+u&&$ItwZ1yA57SX@q82gH3DCxBe+brTF=MH zCaO)wHhrHYsWoi~EQe7uP}BHmAvqiL$FM!gO#$i6BXaSPBMfeiGcrVwJf9K00&{aM zRv_}sFy{Rzx}n0%5azva;8`>)LycDc04X4K^&aMHfpY&`=5k~Vf6gBGrjfHAas@~c z+!SX$a(a*~^gai@|9HqKNjf0lub{#_7pF?Rk)$cNii`a!oxJ{-yk0uQW$HMRspx;W zPCM>91r686J)uSXNZiw-#64qj-ZvWzxu&G#^KMbhHI3mBFr2hJ*f}T+7B&^Ra)dw_ zBmVGzj(J}ALumQ_SqE--Vo8|-QePN~Q$XtTLogWU)KbjT^bHf&y|AXi$nHVRv*y+p z)^B=oRmdRSYN(oXNAtwHrk3ap!)Dzva^~iVxq5n6!q{X`t}bCRAH!WZPfC^bL3Sbm zBv1nY|AdNdAq(upHg%g$hZCxUDJMvnk$-_R>xNj0rG`Cy`XlU@M|RS=fP(XUlrc|0 zP4hKq7qXArtx6P5i|I0}9Oi#AX0>-%+caEcKrV)t1yL>(5l=cIeu`yi-Kf zf3OKB?fU|sy{&kWpGs5t7jdERoKYCFEO5^RJdbw$!0GklR^cfA>~RKc=KhEOR9?#H`LGLtO|#(n7Q`z`k8Awc)hG++ zg_hnEvI}0v_d)_HGkCxEw;8^V9ij%p5XBHHu?fQ4YQqW-*8rgtk-AQp7v%JvvSCx! z)b#X8nf3D;ljMfPtzNv z&K>9@Y(?}D8S3zxLr4`G2+Kg{zKWeZ85frpFn|d03^3?v(ch_(wOU_Fo8>!S2Qpu9 zF~sdfMQsIqk5m;hongu%7W6OO4%?8_@1QjQuv|EMQNl2vuOgwUJ8O!NdTGxJSXQtLD{RQKDka8EI?>3BY;c>s7 zR;(9Aluu$2d6S~Q7rbh9yuZ!#eLNpr^e>`fD~cx9uLq4S4sOyn`OpTX25-ZnQTaCo zgVW0iry|=tg+&!fpI`jP*NcyjJ;KT}YqFxzyW?uLE31KB2gf+u`;!AtU}*o0wYM93 z$*0gul7vymeXuOw30Va{S(d-v=4XDtDH+D4CZxZSjLY-UoiHxh>9snhQ=d2eiv~hl z?JTNLy@I`VjeV&sgaPw1OItem&Ji6u8(Q{u-TC_Zy2iWTSUce-4NVWXJ#)m^_mtSs zbX$wh=i43YX;}Z#>UD2ytc~_Op5)#CC~*V0ZHYd|Aj4JDbAq33?r{h)NC>$ZZdec7 z0{oaOOZdex=gUsf>2yW6xjNE*W$ujsl|p;qn8Z9DKP7$d_4C|?6t*yJLk&!<4h2!i zgKkGOIbFAPOh(uC^-RC5Ii8(5a6sJOJSC$l!{y4XO3$iJcZ#q&t`An-{^|z#o2x?T zwj)dAN*rzzT7P{!ZPJ5_S3We(i=TYojl;rz9+)l4tIci8o{)iTOD5@bahMB$g?x^< z49WR^yqU$IUZ*)WuEXAu@|{_sZvYQM)@8(Bf0j&pC2Ip#H|Wz5+Y%L0!q%mg!MX%c zkf+wRh#&dehShWyIJ0N%eQ?bF)*}xmj~G_q{C~B*34B!5`9FNmxigbwvL};F_DuGD znI#)Z$Rr^otO;Qc>m->hkYqv@VgN<32rj5BBBG_JNZp07t97Z0`m=O-6{)q@+SazV zYHfclZe)1B&$)M!u-Nwh{y(pgx%ZxX?s=ZGJ?A;kd6u5nS;%1j3t0`>3z}^3@IAH8 z&#$nyA+2g)fyLMRQY72rpH!Un5B}nU1*}1%F=kE1IzZ?B6Z~RB;*I~t(#eNEcz5{U z=%jS?A^Z))ZgUHC;H}b>c4dHVX-8U>DO!(4`KSJ_8@S z?h&oD{qVt@>a4JmJO6f+KlANVtBOPS_@+19`SW}E_l|bu3WvLUgu`7ob}G~c=;yb{ z4+PX4sSO4w1#@pd+U!pl$MI4<*imluibo@y90@Q%5#GxlveC$lQRhOXpC5qID)$X- zfN808ve%1wY0vqBQV{1)g0MAX>Hr^EuG9v=j+-T!q^lzBdC5W5P+X0-?Jxk7=)_$RZ}tN>vh} z5X&TR>hc()emEd&x-}*B*@F6KFggXn=A~?+ayeO}*|iL>hu+^sqag zb9(K>bNY>Yy%9$mOr!f1j9#qH&&Z9$%~5}ljsxiog+SU|awHPR@rRBhQNzJJoU!A` zCi1sB9gq5bNVQh_n3Mbt&ZRNIAmLDnijaW|ouCO~@NrS|MWGb*)bPpmlW{)Z=N{qP z>1_VMq5JodN|1Y_=X{=R89$s?`7dn__=7B|?s#TqWV&}2@n9)%>O(wOcw|y`R^pfG zhKpKlLK<8qVvt;n6TCu&^(-Ms>P*6{iS1`9`I^3wRK&gsQVGcx99 z&hH2Rke9KnWbg@BVdeT`{mXth{ki9!VXwN&nKiX6llX`iX!KVwaBI1Zy_K_u8nXnG z#tb>`mxxsZho+u6j@h9k!ye|<5j_p~SK$FgpP2$rn%ekJ0;>#v)M6%H5iV!G;X&=mt&3?7=xR*UZZH z;Wzu_*7sh=4t;cS-HlH)cRn>-t?{nx+qbCs!EU&#BHY!zCvcAepILMy5xAci(Njm9 zu!lzKQa9o!(E|v=pyE%>Jg#-DV$W3UnMPl=VPs9$lS37r zt2?8%y(UvT`hsrX=9;$JEG?fBx{tXBU%g{i_2!pv9DD`WO)tB>EDHuI?>of*Vs2br zvS$b3tmy8n)Fy!+s<1}}1O=M>L&AK0H6bC9Q6auzFmhz~>k27b7IQJs~vAUGSAPq8(8TC4E`4c(> zhZ9*~A~udfCxyfnbO=8Cj?q_-@+CT>yMvTrMB57)tmbo+CC=09QEl>i?t`+vWjdG- zp{Uo!L=%u#sYR_Heg80T(0uOhAYm+uQfTKDzSFi4x@!6a-)ZAC742IhbfqwOuhf;A z=xc4F&$BD?gK|$OpLjIvcs)Ft3U{J!wlRFJ^v$N=gNkoq=j4xgH)c@)0@jPTWFVcy zgh69O{^-&{c_~7P5m0vd5<90^^<8;2tG?Z-=~42+hlrXtM8|{ZTegRPBM)nLKw@n? zBI!?Ye%uf!{VWn@Q=h?CjPPTTs&MTdeOjUYFArN+PRou>jtxq%CMCl~d)e^uVYqTI zWZ5$_t=ZO$u-w#$*s`Xwx%&no)?m`-{2Mr!5pYgR2F_1lar#+gAmF@JuyA99Q6+ zVp&IYw8y6fh@ct&TWDu_c$PhbfMu$6Qh{-)Lry}xTB?&3(vzsy4;2vjU$5~}u9wKCX3eu-iT}Z_(f_?prR0O$kY1jamY12IX2=BoMe`N>ub(XN zcRwe)S%$zr_lPz5O8kL3s$dJuut`wtMMOUuXrcy?Uumz0?EhsAUgw64YdXMHwdwlF zm=nh&w>*M3jE3>Q?u2{Ri-I-)h9qEwN;8l7>Akezn4h-C1qX5|G}I8L zr8AW;bv_|q2Kfc)@Fxe%kpYrU2XMA(1uumE$6>#A2GGxVlov>uQv6d;IVLb5L{xm_ zPXr=6M_=BFVB^M}+%9l}!!C{7Dz||z@IR^z#01SdtB^L2&Vjnxb8CfbZhrn;SA}|& z+a^4SL=-4Xr8s}hl755o(r^Mh=_R-^{Q{d|1AgN`d%hUam*MC&WhAFDi7t_i(8*0X zqOu#x+VrlFcBcp-+c>MC{SVJ9BP2dMDk?iZ#B*hvJa?Y!wJB{8*$JVcrtHW_am|VR z56`Hh7uFVv8@2G%&`k^_Ytm2`-4E9pSiBsk*>RQ5W1(TPPsu~pc`Fhgs`UX^ zO!h(G$Y@jeSys?!)`s_%En5@u*^NQ(S|Uj8AI;|ZN1B*$S94X9c?qGQ;+;42ig)1? zU;Vq!G;QDR)3lAgkyN>)sBq!r#H7lmqQXU!6YnUnSZoDmtF39`o371c>HLE7q9P&5 zj!!3kBH!)4Wf!~g(qAe%XJ#hWbktUK&cd~G#=KceYv#^cBA-$d@M31>Z99-ugIAY+ z^&R=qM~s=IX4zl<4V@vs1kPnPNGzfY;gyDcFv8$ulH9LBHctl)B22pM@BVS_pBFAuKIDBh&0{eV+XWGeyh3J{oAr#isiB@u*yjAp?dt z%r^ za-@7euM@O0@ddQr9yDJZ+hh09N1h6FB%Rc*5SvOd)zQgUOyM}L7~YPHb>;gDR&1L$ zedF>1+e*4FD;$ZnR3@iZ!qq2TtMX$`wC?LH%yH~p-THvI_RZ-iGm>K`y2e*J(S_*4 z-(SvR^`T>4vhEl{3uu}5N+0<>wj8JluNDy7vOMbr3QdN!9WIT6 zz0y)hYU3Jx;PVeAvEn=4I=_Rx({;;(qwAFNFQWW@)H_ZpA+7tDPx=Rh<1ZH0GXH|u zK46Ex{+C`-u!F^XfU`^m40O8D3?kA}u$6eo0%+o>!Nk+X^>hMRLfFOkCr!%?tgf;y zO<4fVNwO%5DVbN)$2Oh6`>`CmH77c^EXA~{urz-1lCr$US<}-R_ffrB2>N3=@G6w1 zo<=|12Xi=Mo-sAmm?x7v{g>>a!M>?Ur?M>(P$|)7JA4C9Q4hhQkz^MoL8I`7?9p2g z-g!JdHo;B)70!4_>0CVof$oC5+~U}|GIo+JJ1Egrwxr9DH+xd!OmkIwaDb^Oy|5=I zqO_tiZuj=8($w;7qp^5-bqYuHj61!(OKcHo(Se)b88Iw0-sm3^u-(hoTf2Hu?cykX zTowi)m)=2}@5NZwLy^R8M>HNkoz_?5L!3{m9X8{BK05zjGJFO9Xx=MjG&`ATLM)F} z2xjVe7r=agC!uIAq|_S*;ZXs0fv~o;%6qpx1Pho&A8&Ji@Q;5?-Lc~y_L)19EpIn$yi#JzPSj|VM~sfLqbe~Hs_R%r zg$5wF6EX(Dh!C_%G{^X94VE4n);1id3TF97Ol`}mmYM=n&33kYdP+)oQe2c@c)9!gk!f*}e!h{Ww1}K6pRh#g`%hHb zlYrH@=%|-h1Uh~1NI)J`I`6dyMP!~1hz$+ZB|aDH@OoZH#79U@1&#Yi=sl`qQk};Y zLNkAyYfVwAO0_G(ai`p0f6HTwZ`@J4qAXU+1JiTz!b(=pwU`$U)z%Iz$af#BnI4{2 zXbQ~AOws!%mVR{C)63)U4$GXJ;jQ)7$zesy>&hKdk`o&5=&dxzWkmXFy!5(^S{pcP zqSJ>iPnTcek=Vl|jrd}Kvt5|jI*pIc_k9^dB+q(g)B=*EZd+0&Zd+b)zxkB=Emrgt zD{gcDfyL}4gCkL<)F!_oxiMOV?BjjJWXHIN>?4JS)hm}imZRJDq8$%i9+6*R2awN8 zim>Z7SRgd#bu!r5yw8YKzji@&4oN#Di&$5WTzrRO{c(9KqwGh?gl~GRVf4YBCe$SDc8yvSU&d6SJ z`*L?i?p;s*XvM;l&+IMQl;PU4r+&eMTe?$~cKRH!0#sPOVIEj6!0NdcmTGLDCKxDq z)v5xllKh^-o9Z36FCtjWpzC_qlP@m6J}fKvy-y$mw@O2He`}^SGc?hwz?_vXA$0tY zAw3Kw@ht5LS@x+ur$5lbj8GKA9|5>k^ z%c@3A%RrCcQ=;y@3&L#q@HObJ;y;{Ni>(4$D~|kdh$Azc{UcaMstrC6ig2UK9&@2U zg5XAB2?2;99}ZH=?l%44w&}N3)a;o3&ey32?%|y8JY;OT`H|*^+bS!zHf%VM%67V& zkjx%qhwi>BE$N^s;n%<37Mf~|G)HXfd~C476#ryu#@?OV!m`36vyI!R-8te-{{t~s zcVK5|mF!0X$?W`iDr0HUFh4tD56w!83cDDWt4g14b<`X3>#>r8y@fno(lnuueXy3sVhve-#2;pNrQm0hS?O`#aHiv4&$Dv8 z=d9cwtIbUNeyHyQ{{EVT??+epRO%`@Bn*`r3iK*0P-(|cWSqE&4^G=w55v^Efgw{k=Z#Go#)hxr3EOx<6cr?$MTH-##gWbPhFq5Tqx)-s>Imc|n4ZV_tc|dKb$!962N;N(DB_ zYLUVcUcwzxe2r{zKU{{C{zysS&II3xr% zP$(k8KpK){gh1zmil&Pqxehl@)YoAl+6h`OaDpqp{pSmg``QaD`u4A=8O{kVUH-kr zF|*@M^_e&QV$WQ6Q{{?^_?StHCs(Yno5Bjps(H8jW%nzS`wld>9&nW=C$=8lI@`;8 zzs|e;f86g3r%!7upVV2OXLcRCB_uHlGJUi;c8law8*NUTDTdgY8Jcn-XUrm9%%gA^ zNb07rP0)G!>SVX~T&7)2MxNvhDi24&7Zj^GalS$)v`O-FBklzww`dHvoY%hkO}X~f z`|n4&gWzMJr-HSA(rNhS`o#&DJbC#t@8{V^n7w*-;Uv>|f2EzvF)Y1~Fn}Ow5)D1b z41}*^;@T2aAICl{LH^>>7WR?5T(*zi$DVcP@F&i1f{*+in|6(EAB9c?VE!MRi;!n; zO%d?(mP+i=@GP!j2vnxi`RKhh-g6OyL!%j&3W#k6i3Xt37759x@~1GfMK~T~nj&}A zdG`a)epGH{Wh5_+al~LN zgG6N{FtZqHCr6XO*n=~A7uq($cLtgR!yUs&-CU1(EK%Gn!QrSval}z#93E0I6~aXP zAx%?4<;ux9(`u$>=FX_8%`9rNC#6)cC`qWOX|&}`ubrAsse+Nw3drmS+FG4+N;mMH6NTXDVFNSX?GwgzT5Ru#;)M&h)634FnqX4oo|{r5k1 z@Sr}yj_RNf)cV;+6)_JM>bmC4zubV1=QZ!s!<&*jByg87qjven$8Qhp$$+YK3zm`o-7@ zOCWX%0Sz!=$(bi&V8|c2-)=zsv#Oy3LvB{o;2vhLtUWL!NA7xSm-{`Ii3`hepF^K= zX^X(`eX#64QtmO05j0GxoDV_n}J{#rgVGaLD85KkUKW$4k-xj?B5tt^?<_pbNr1 zAB3D~0?w<#e7~6ou>b<2X&rQ%-R42oL|_!3A!G|lbxELzG4Fx|=-9cF0K{aEBX3wg z3rTE(@EVZf!}<0n+%C5D3ASTY%aU2J`vbm+jq*{1b(!)aa@QFyj0KaH2kmG0Vh9JM_h&e%C7yW|`dcB|g zj$xi34L9Nd1$G_}o#2;sV)-ybei?x=tE z(x;klwDL_GHti7j4ZZcIfABYV<9Hr6n4lkZxC@igPh%T)*TK)=v0${APzPQoCL3QDjTHPN@V{FR+@*RdIw ze#a7F*i_9P?zl}omoFNyuq^FI}Ww(X|=-o8RJg)pGAF$XP^24?2kt=4NVwt z`q#sO0BX=-q)QF8Jqj``5|m~m)$3bjgBh{7g@*2yuk8jYOs!2}yrRyun zDS{z-8*!}GH#c|Ohj5U0QdWddRsltKEWN7R)Nqg4=8`0yGHZANc^Vd7vzu%9SQsqb zeOGrSwaFE}k59v1@mVN3`-JKZ{@$|wOTT*Q>(C$(i+z5+Kw6)NK1k7FbRRh$9u#CNFj32gW!i&`b(vAdufjtwMm2bSt&=Z` zgEVFK28rR0bA510qOGr*riw{LfqJ+gU$sN!+m|$D&M~P&y{Rd4Zc^&Xg4*Fl=H*v4 zENhrJV^`C|E`K<4-msXTzqT>Q#v_gQj_;TUO8T|)K%`%19w_P8&I6^KYv+NIe(gL^ z%DH+Tgi6z;l}8h&Pk{{+k(?Qve8or)h zdoi~$K6N3sor0d0R3L&Jb^(z7kPjcxH`uQTA>dW*4@xvX0R(9spC(uK+;`I{?+Nqa ziWxJpI~SAp?dwsniPK_@R=cz=d~EKpiM{OJBM6*lj*y_SSyI2M#RA@f;~`U5z2nde|Fe+kH_yGf1qV`;0O9a%aWuLY1#{tja`O7D+GK& zl26nmoQ$N~LpSFAAn)0{m+~}uanWBz8860>0>A#4Pr$cY+H80xlQ2ZVM53vro{r$u zM&TG#%b&5iqzGBFm`Al#uB%-4tZUBR$%(V)&d<1MM?zJzz3c|7Wy6%E5BAwt#9C>A zvBvtvnkn|5B{rnpeyC*fch`v+p#P>e{}%awiz@J<`3}4g7ZdY+70q{L{(?Cxfc*3~*h7a(sYgP| zvjIHF7<@rd=fanKz!*JM$mvI5I>v}8{KsIdKx8ieru4q)%XhaGjtp#wol-L;E+ITJ z*RiKVqq`Jv+g30CP(Q$L1e{NG2LZoJi9FhfA#j96fR#Ru2%`}ZIJ*x)NKxQ=*-PN` zoRU|>yC(gZ%d7x4slZiD*Qqf-1&<}_5G}1d5gZ&E?Hvdu^`j@f_4*h;?-&>d$;W~M z{enXzMe*}NHLaEac#P*hk*sT(D#m4xo ziSsxx5cBwJtTQx^=UuVX{QokKxqEf<7#ny+ANEb`!(_D&lanX(;rCImUW!@Hkr(5w z@%u`BDINr3B95iQx)}eB*Edb-wflsGLXK=#wFia<2Bv33e@#>&C@mNQJo#8k za%c*?b0eN3oEW7@!72FD$6eDT!hHzIgGFc3kB8WS!9Rn3RRW-2wI*u|Qm&+Xu?+ou zCGB&6#NsCsa2Ai0E7st~9s75%fC&xwi=F#-DsAA6wmFS9NaKB%H==c@*MAE&9cl+~ zrDS)FbK}Zm2AI;ImPhOX5fS<*V@!nM9RxF+gPoKwc2fSAbq!HJAKOVmz)zbP7!`u& zfzX%sb3(GDZZUD7L1)@fok%KR@92_eENCsyZ%NPZ$tjpSb9y$*8l8Rqy!`3IS=G4_ zdhaI!L*cHvaP)t%n>lwo(GS8)jMEzgzc9uZT$#?MQ%>YxuG){Ma?0<;=3ZWn7fkpU z{1o+zlyZ*xM|132kDQ2%;$D$+;CI&Vgn!6ouLhW+!hI8Hz7nXrgdAmrDv>|nQeA|B zrrIRE^#g5oQ|myjv9zq%V47N46lCvOF*oh7{ORd}S>_11PVjyv&=8Qh5$Ctu<5Cg&ZP9Bz9XOnZM<}2M!eNA-}ZFKXMZFFV&SQ|Bu^|i>awh{D?k^i6C z4%q5Uk`^CL(gqg5cg*qBoYWkO>TqHtI1rolx9yQ}rAg3M&6)I-AySeuO0zHJr5~R({QS+Xrv}SYs+JTst-SC~?usn4GvB&ka#A{6?(l~bYuamy zR!+@86rbvU?)p*Q=>5wcy>Z&4&RvVE*R8DfPr1GQ@#nSL`?T69^R&FuMjP$>A)nDk z^9Ob7AvxAad8aY*A60_qUW7P|+YQD#ezU}a824rx9Nm1ld|A}lEC{R+I2ScaO0hEj z;8FJr^-Zb9J0-Py&R>PSSpL zV(ZqUe}2rJpj+SGe(nynefI0NLJmZ+lf(Sf*vT&#ydkIv3gq5_5eoL7{a?jS)?QR& zCpW6s+tADCZ|LK#ho2op45aTc$&MBDu;$emupM(my zX^+JTlV^{<%?mD#Xy4xAep_wJa@{7>M~yc=6}Nge)gD0c#vybK(sFNYh|-?_F5Y-~ z)-*>!!OH1bw^dJ`Ts=Bdw~0c&MHeqDx8grHMG~gDa!L0S@SyQHugfBdZXpi60~rFw z8l|0vsGSnHHSVo90o;4bmyO~8TMchM)@I6tHc6k0fNW>2KP)|P*g4{7UxE(*AE0;ls{$Vuu&sfcpR*P*#Dl;>LaD6 zBXy@)GBg46gjLY5$#p@nde*PVM#&186g}dLGK|I;qkm*tbc{JQoa_cQdjn%LqD`sz z3rdPb(1|H|(+B2c^AhYsym=#ZI4>gBGuc?(BK9UDm+$4zX>*X)3}T-rc#XF&-zyi0 zG%z1Z8Z$|6%Kn-f*k{E`Z76Foo~MfEIC_@umi{Kct%1N=>JrZn;`zLIUN4@v@KV@~ zy@T%q>G|>{UJBbVdJdvzww?Wpzb{wg`yuh1?f$XctSiE^uXxtrd6Ibc6VHp?Kar>E z0`PvacsAkLUpylYA@F$^&zr>aa`F5zo?jQwE2TD!B2OGOe`x$wNHuigbL?nosTuK0 zj~tCQ`(yV7H5{{Dn^+j|RkXi2* zox$TNPgTTCQTFw)7#$D1Cc2Tm-PBti)=)d8eOeBp+NMumJ!Mw+)P&nNr&Omhi_KY| zKlz3u16_{~mRD~4(T&sBHo|{GrTl~;zi?7)S#5d8?K5`;#0BkoZ(F6kV{7dLdwhJ> zYR!&a&ebp9Gk?J|%o~B(0^eMdGR0ArG<}NDckLH88}~s*TP)R`mU7w1aRZdAV-j%$ zF=F$>8hXTTfR-bRE%Ev~tuY|(OIvJx)>pz{OtHKm|0;r`vHaaaLRwTl)H+^a=+h{4 zjkrCW%22=#xcp8^*X+64`Oy`tmT&M)t5_%pR6h3G+a}F@;CFYM>(XX))!5qV&A)tSXHsVB5%DS}&>_c(<7OjlZvbPl^4`zov3UJWtY{$dT+) z)|YSCv@hLq|Hze#TNKgG01AahYBh2U}(DLsYv#f(t2 z6Rp_5sa))}kvcIp#&wy}*kDX{d{EAc9WBh;{aNwMTth%iR@|}1?y6`*?zGN37rP$n zu%u0O{M5DHRMk4g-d>j)Zk|&n9}F><+7k*_)TAHqi%1NQNQsXN?#Sy|yP@3HvuZ_c z=FH89o%44LHkFxA-Xdak&L5~QT0VDLBJm8^DYYR6br=OSCR>B=;a1OXu?PBwJ?s0D z@6QmvH0UX9IJS2mj{a0EBjW5&Rf{OFdMd!=wwyVAH4S~!68A$xE;FknF<`Vr>ndGZ znV49)6sHPlV0X}Vyyx5@Kaj8#^I(; z1iLDCZhy^z9s2{5N;0!1B?pcc=-MVNE{hu;h{=OZS3+KlU=ILOgSk((p}{aZnM?fr z(G#`vymX;+t?*o7kB`d;)p^OX{_DB*<@HtR_38Dw@};Uqng~AB9sWFeTxo4Xsu3ku14`Bk3|d0qqG;q0;VD7MgceqR5v|B zMci2Qdo2D>oH1Mq=RW#Ut09bqNh~ZpoJGNq?}d@VC~T#l zvj-%XY5%VC;h8+s=WiKsH4tTwh>nZ1er7bl%tCL;(HS%F$HHeLK96eQ2yHl0!GUpT z&}YKP4Qj(^Uie+YqC+Y`s+NUE&q8QgSiKMgI4qZ?$;wg#(r$vVIos%i;)ky99g@ZycEs5_Y-Rn01=Zhcw^t88-kz{KVaBc2vO5|}?HiiQtg8?9g$%8UF;}Nm zZXJv-oSvyY|5vZDz`ceDt-CX-w9z$d?#6{?%_X-s-0$Jg7#%wtQCi`pw(i7m=Tqyd%7>Nf z;n=DD^QX9GXU5eiSNFN7+{Dn(MDS`ki3USr4&T1#%Oj5ZgYIW=ZLWXReXRAR-A&b- zU)kt+}!*bZiM~jMRc&W9s`(k{>r{|?Vn=WIRB`L!UHA@<= zS>9*M#>Sc8?EXW?Ty0}`o!Z4z_s*l*L|6CDrz*NHTs_RjsyX~irJ9Q#*)SayTfN|+ z4Kq>KZ^iyW8D(N0F*e3TU#8LS9la;U*l97wCRXadA7j^a%+;umcgS~O3VG>PbjHV; zO&CxmM|F%W4N4?v8Ah(G+!dfxARX03>Ea zwPY}tAb))6ZPws^!_E%uW62N8QBUmt;8FtM>;jyRQ2zv}_%zneA03Yjjtow~5%(V; zTuKb`{~{LdJ|#mtH~mvmz<{{P?l6(yk^!s^Rn;Fo4j^l$i62Wy&)aE zoPd4Mc4+g$`&%5_Y4^>K$cac!j>wV8r|J%SWUx;`%IC=mU!>5Xv&6@S-Ykz5q-N(<%7xr_q4;-D{Wk-N#*xLk< z3Elr{d=b+{_G*(m#5$*_b3wINXm%jLBiYbFi5;g^q{pS$&_SOL~ficc-HyY@KB+Gk`1z)9h4$GdV;>e|uX zq+k6B+FK_jp7fa9VICjXw($-|eJhly&w^2zV0HJ&=&#@4C8Lw&0r~xJ8#IR>R_OK6 zI>>hhaOkhZ9yg4Q$rqmo`E)CJ>3!i6Xg+3WvRkXg& z9G~_^;pF6Ojd69UHa;*=7rt8Otp1zQfH;GR%!8M#hzcX^)D`N7sb8K4Em2aY#{Y_W z-o!;$2;LR#W{-g$W|AU*$Dx#BqBpi-k^fP%;YnxLL!Gv;@}5N-Z?tvpU+uiV!*<)o z>h4*Y@2p=_w5U8Gs$_A6bx}ofNMw22%yl=VOj$80y=jJ-H8yNnQBb`6rkbqfj^_Nz zCx`4eJ1eYhGo9{+vm4FeDjMNr+8s(wY2PMu~ZEm zYIQt%!xVR6G4^7ylUG)!%$i;ml`^%B7cG4Jfvtth7u`MMrbidYXILv;_s(0ge{hQZ zu0P=9pthqLBQ29g+O`v2IdtZA7C!y&m*Zh84x4xBypT=(9`xQN8 zaMrKpyM%nJ1Ps{At7)H%;R1VkO2hs<{qlpF$=W-?op^x?6fwimB3nV>smqt`D1T`Y zD=5&hqCzBD^Rm*C%{dt<$@;`(-}u-dLqtq$KuCC0tWL}9oJmOudT;tJwaL#rJ}EgZ zJu5aUGTfSMp!@_KJUSfu99C z!l<)(XY<~D7rtmNh@aQSq?7AGUuN*Ns@iryhVrQ!;_$Sb0RP3Y9#A>uKMSOV$~}YJ`APMjqbFZe?`627rInuk(|`rZR_`e^0l5mJ|6t!S zR;HJ96CYI5y`?bOq~7~TLGmK?Ue6!*{*ij`D`okT%wA5-sT>|lSQ-fIw=JxaaTN)fC;z1Jaqse11vU1CG( zy|lNG7>Zz1M3Scx{%TcG2x@#MtS ziEq~8w-q_1$4!>}`~uUg&Xzt`zpHJ)RPE~Pb@e$0I$b@vrpm4^Q)6d)$3VZS(b@0p zTkCAaHw3mvpwR_~HvLF&_4K>C9Jp&lx$V**U^@UsYIL>_b~$i!E%uXC{3jMfX`UKO zV<=4mPD=Tc1hW4Yfd!&^{c2N~Br_^WO(-pJ_VuGqCUb5+p8ic0u6|`gS=4w=TnEsU z)O;pX&x9*gmfjACRBBQa?)&hz6)D}K5mqCOx=H!w`fuCt_Sz;geMbiqMm+)&VPk?@ zp&o&o0)H+rpus?0O)z_aOP-2>3+bL3v>jxG|=a0b#^=YR-0V(X2N6J^~aE@v&V#DO!Ip>2k`ye0mp!|-{k0N%|ih$ z0ieY-*fY@A>Ffvm|Gp8%$H&AWE5W1EAm&LrD&7xilu2}i3H%ey9IC!FbFd}d(Vt;z zb((7XTrQ-1SC2c;F5PGu>eV*21qV?qwYky*y`tE5{5p`cTcoz*trP9K7Ec7UN0dSB zJl-EPA}00(5w(y>g`tpT4^dX|q!JVYOs(uf-X8q+0WYPF)N(h7 zHlmWK4&(HM+D;+xO5T4z5(wUSKhX#u1RNR*ePS$7n<#W`yk_)`XPi^%)S^o3cB|<$ zHk(l{wFtc>I_K$IT9jzy6aF;*sdbdG;i(g~tqJv~Wy~q^Q18^Ey(s@YluCDm1C=}= zFji&(!Ds>8K~btQR%t|y!zTJl^(9KEjI|!*rqN5|ybDlE1#D%-qVe4-#wYdtfWVk= z^^CDAdu5#{g?f(4@Z_OVm61*(l=?=QJ^iBRs3sHJlg5jJQL`wGC;+vWXH3!jCz3_= z@4~Nw6U_i>BhLt?+%C)~&)4JQg}$cNrP6845M}qYKaI0%=M(j%vU1T^)T3*W(uX@w z3(^>*(yoVhv&c&|^t8ti?x+O_<15w?M`t%Ee;eriV84^rgWf(@d!M7byR)a=qE{3oUIkDVyyLw#xy^a=9T+aY4QvJQomd>_L z@Fjv-Y3XqEIa;t*^_w~zYn@{0>34KHO+%fn10C6>uFlm?ldB60*>JCOs1IvWUw^i# z+p(H(=^Oxzc9*Nw)Y9SVY;g*ldy&J{A=F%-`U;+j0QTWDZp(JvxPFd zTwt+G11?jKtFIdn2b?zypau@CpjdW0DHl?&YJ`5JPSvhKETMgb30g$}BLw=89ZlEb z>c;(mqq%Dst2xR-ZC!09l<(|m1yTYMn4|udKBp6WUwyB$XC7L})aG;y45Fs!wwA6z zESbQjU3}dKxIJ{&GuZ9yb9D8Wn)<;4b~szJO|7m0TI{Ji)vBy>g1syiDUL2QdJh;( zbWeYWqZfq}o(?Lg8TAKV)KYyd4vdyACraw;R~op>gH~G)bWgeTVVk45uX8mODoKk$7X~pKX(ge#?BP>r4l8utQ!33^ znzsr?pn8teKZOo>=6ww~%bD0pjIRowH_9s73Mf=-4>v@-3bh)of!Bkp;6c=7oR%vq z1W~AKS6o^{l+~2JAvmYwPsOB!De^*Tv zj)PVr>OZATX?^tUPbTuDG@58u(67Sj5I?K%Xi7P>HgqA!#L}*AM@+B9u0hb(VL|%` zh&BnT=;)^j+ubSX1m-Hu(VF=)D+MW_2N1^A!4{Bs5Xz3umJa0be2c3}jl{$sBe0m$ zy?vb^xh?py4c&O zXeq=ky%SIdoZYmA?&}0pECfAWF2_WmAXW|qQ=)dBU65;Vpmz|x;fJjm1G?CQO$ zLRS-t|L?@gLk#*sxQJ0BQbW6Tnp8v{dcnqdNX@?`VS<3=wL04zgIxo;j{aVJ2idFm z&)p{7qugEj%U_332>uBm?Z5$m&{Ot?-M=3CY<@T=34nfI5VSBta0+XHx=Fau{v}Po zXhbE5g})@yDNTS}m$W!j5Sca&`@Ib4Y-NF^%RvqDu;DjDl3>NzQURpDMN%>B)F#o1 zIy{U_hE^Lic;I2E8qr{Dq^Z~))=AS~jW`43Zk99~ENKHGXEj0(WFAQ10&Ei&fxtE) z#@kY98SFV$NGs7wk4WEx4~8E}d!#>0w@P(6K@~_WHYAJJ?MeA(Fboz|0DejvZu4sPo+=LBkxE* zmwq8Vi)H2uX@~Tl^saOcr1h`TXVPubDon=JAO&3@UI#Gg*N7?IFQ)5SEMR{@|EXyEQEzZ zq1eE}SU5AX2o}krSTu`au{f-bm%fs|W+s-v5?K;_E~Kzjmd4Uq2FqkwESu%v6gyA) zMmo>(nVDG-M8Sqb#6niYidhMp#7bEiD`%6X-$}n`70k{mSrv@!r?46}mDRF3HjPbZ zGuTWv3o_FqY&NTB4Qvi;WOLa(HlHnE3#D(ROVS^tkJ%!)&uwB$*iyL8UCvgpmCON6 zw-(mQoU9FzzB*VZTg6thF4hh2ggwl~df6K3kJ6u5pLB@zvjH~9*0LdX0~==Rq+d!O zNgqhRmM%!YmEMdl z58J_ZvR!O9yO-@@_p!a~ezuQ2z#e4ZXZzU?*a7wsdzd}K4zfqtA@)N!LwlS(!JcGK zv8UNH>{)i09bqHvC_BcEvlHwjbPP{R53uLh^XvuoBlaTv3%|@>VLxWCveyuX=L~y; zy~*BU|HIy9KVfItPvPj|=j<2k|6%X2ciB1i9y`x|$=+wbVjr+yvkUA)_8azF_7VFX z`#t*u`naOpRv!`-`E%I@9ZD!f7w6Tzt~0gCHsnf&AwsZvP*1~ zx!Gkdkv|dm`hXPyhm$f`BlvJV_vLVU!s#qHhzIi!9?A_ojE8e0kKmC!ibwMp9?Ro+ zJU8(Kp2(AUGEd>DP_0Sl8PNI3;@Lch=kh$B&&}Mzt=z^7cp)#6Mx>*>SUMq{ge3Wx zbX96EUdN~L>3jyC z$!GD|yq-7kIlPh2<@5M_zJM>}i=;cHAMnM}Ug0aqH zZ{kb%Qof8Y=PUS1?%>V5g|~7iZ{zK}gLm>(d^PXl-MojpcrRbW`*=Se;DdZEAL2Lg zVZM&9=NtHq{3gDU-^@4h&HNVrJ-&tC%D3`u{5F0&zk}b&xAVLB-TWTD113AW_-=kL z-^1_Yd-?r*AAf*9$iL6`^B?d7{2~4@e}o_8kMcwOhx{@AIDdja$)Dm+^Jn<8{4hVl zNBB{Gj34JG_(^_>pXSf;=lKi#NBl+p5`US$!hg(P<*)JA`5FEOe-r+h{)fNKf5Ol5 zpX%DXhI>2A%EjU}*wdMxU*)-4)#nng%5Db)#64aP<*KV}?sKkn>cD{F;#KKtcl9_| zdpVS=Z*_~k|W8i3M!P=~E8N1a@X>p)vU8{1L0yvHVuNoB~gpCAP zSEB%Qii^Hx>}$Pq?AEJBMZ>9Fbu~(fPI2+A9Y@5sef(ZuJ63?cee70L+-y-A!fdhn z){TGR+cAExsl$Fk(}5q|w1Lj9R;LczG+exk=0=OO<-9*3DF!pi5jdGf_WH z7k+fJm9Kgxd}S+8zOohSW-DLyC=J)+=yienarJgM<(i&$+1bpsheGP^(r@q-#_lWAjIyK3B=xFD|}w$D72r zfBar4qfqUtLaT0`^6`MUXy#D|4NwQoS2}1=>7e;)4F=U3%vWkKC@$LhU?khM*i!ej z`_G?G@1q||Voxagb4#-D!O*sZq9)zjXu zuOwWQY~U=_Td&F*k>urYimPwE*n=zguJL=(Miz6idMy!Gt66C?t6AxJ*w`rPMK<-S zrWe&{2T1bb-wFu>gctqdNnhF3+u;zmK8_yOfV0cl>F}%R?e7G_BH6nJ(ti5TXBy_c zxbv$Qn_=a>-~0)g_4VD(b|q7AC$e7wODkY$s+_NV$6GNA_<2}5gQ*P{VY3FvfcRS>HjzKRK8@ZvQ zQ?ACphW<_kOl655mFK}IP|fIh);q=;!p}LOM&3X~sXo<5+!+Ry_nuEhqE>vbX-0N! zJ5^5G>g*bDc&TM+*5QRrFR?E|)u=G-YEiW=Q8fk0s%qXd$ZzQMaw+wb`#M}Y2%5Sb zX05o$1E`}4U+zWyTJR6(XkFCsepIXnt-!0$^i#`Jx=?*79`xd4aVyZU<>#wcvwBs= zm@QvfOKkbd*s|p-W6PGWj5%9=k(yr_qqclyjN0-m)pUiX+48H^=PBy7M!il|u4Wa! zS%t5Tahq9%Z&u-}W8P*~;hRKw6|RrqEVzFCEDR^gjf_+}NpS%q&=;agPr>JnnJ zsPHW+e2ZFtbzg2~ARpDDz z`0BD^v#Ri|DtxO7->Sm5s_?BUe5(rIs=~Lb@NFu5n+o5i!ndjLZ7O_qwXxaM_P43< zZ7O`53g4!}x2f=LDtwy?-=@O1sqhO__ysEb0u_FN3co;wU!c}sU3+W=D*OT!et`ti&XfqZzbN!rqWhhkqW;=#ka()TL`Hjc&Z`g zvQQ9l-H^EWEc8rGpCQkkZn2VaSX`)dOTIFyE%{3CSn`WR>9~qo<0_!yDq0;^QEOa9 zt#K8###PiBS5a$RMXhlawZ>J{+LB+amR~Gd8_%M)xQg21s=_T+;TEfKi&ePAD%@ff zZm|ltScO}x!Yx+e7T0KM2K!v1vSwvUTg=K7#?yg}Q*yDVua<1hS4+0$t0i0W)sn6Go|4U; zlFgoy%_`1Tvo*lcO@e-~z7wixHG4`kdkV35zJ&#w=z1_Wo*NHDs|S$PQ@&Mg3aizF zu+>wt)l;&~=G%cC6HZC!fFjU&Lkm`6lJbe?;5CCZ25~OrNeO8m?Cip+QF~XHvu!|R z3LQ)8Bi$O2CbD%oyIliUW)n%{*?e(cgF_baiLbMJ>^>B&(&=iYtrh8Mi1z`#jy`M? zJWvD0<9Ja)&EUaTJICP#s|n*-0w@K1or-s$cpQK2LoAEv2fbiTlr8~lF`;j4Md&jR zcPP;E6LipORz4K$c5auerAz6a0+0xY(jI)me_*)FsbnZLtDRJ6F7T&g zrK{h2I=awOixg)qDAHfv)6nNrRfh95uI3lAM z+(r4t&~}p{LDfTM>IMDKK%xBxcL|W5l025-hqUrw1+PkHk%HVWvbB=jGThe%JAgjt zYH2~2V}OoU;IC+IW0eWARnj)~MP7#QNM1`e=02*V_R4o!@y;W~B}nuhW;5!y6Td|H zjrFrkkl%iHo-0cyZx-W_16n}-qy;nurRCHRQfnK=s5yLB5V!3;8d++Dn|IP$Fdpzf-(Fk6*@qj+$pmAwqVw6H+RV{Aw5ES`R?7wHp#F zl5RoLh5R9690fs+KN%AF<&dhAa>)>+)*D%qkSjud2<#YS|CF0rMG|A1L$W&vSR69o z#YkSlR^lCpmk=MwfZHJZT?J|HAW3%Fdc5&LzAGig480Jw7 zU=i#H{PV!3wx%bV3-}5-hVjQC-%LXaR*#oWPWN(I9z2TG|O2t0MPYg*Np9)tM#z z3zGP4h=ALOlruONvf)(6j=3}wCpAqNKW!L4eK`BL31>66V+QSkRQF*>c@N_Za} zjJM-=Iq$%4H}A)<3%bi365#>-uHi%Y?d71ZkOtq#H{rS-vfx{AT?eV~X1qPjpG7T< zsP#19UyqZfr*JOx3{HGL5vM$Vlm3o#luJ0Bq0_Q3oWw+;-^A=wN3}8wFgtkwN>aup zjcJnnK95l~LHdjDt~X+p8KuX$*yJnLfjF@aq>J@}=&GI$@izy R_4(gNXVUehH#aZ%e*s_!g8~2m literal 0 HcmV?d00001 diff --git a/resources/glyphs/Consolas.ttf b/resources/glyphs/Consolas.ttf new file mode 100644 index 0000000000000000000000000000000000000000..743cbfaf1a8568c54bd4acadf78b578b7590029b GIT binary patch literal 95104 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 literal 0 HcmV?d00001 diff --git a/resources/glyphs/Consolas.woff b/resources/glyphs/Consolas.woff new file mode 100644 index 0000000000000000000000000000000000000000..6078102f800a8af0f1d92a076fa713b19090feed GIT binary patch literal 58472 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%0A5znvFQ6);NOygUTSK8@h|jN`pN- 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 z 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

    O}C>P#nYtX@xi~ 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 a{ZD59lUbT{y8l=Rl!WO&_U~YT#{U6Bhnlkh literal 0 HcmV?d00001 From 297b658e55a60253e5addc73a12d96a1a5e031a7 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Fri, 28 Jul 2017 00:42:36 +0200 Subject: [PATCH 082/112] Cheatsheets finished --- {ba => _ba}/cheatsheets/index.md | 8 +++--- _config.yml | 3 +++ _data/doc-nav-header.yml | 6 ++--- _data/docnames.yml | 3 +++ {fr => _fr}/cheatsheets/index.md | 8 +++--- {ja => _ja}/cheatsheets/index.md | 9 ++++--- _layouts/cheatsheet.html | 25 ++++++++++++++++++- .../cheatsheets}/index.md | 7 +++++- {pl => _pl}/cheatsheets/index.md | 8 +++--- {pt-br => _pt-br}/cheatsheets/index.md | 8 +++--- {zh-cn => _zh-cn}/cheatsheets/index.md | 13 +++++----- 11 files changed, 72 insertions(+), 26 deletions(-) rename {ba => _ba}/cheatsheets/index.md (97%) rename {fr => _fr}/cheatsheets/index.md (97%) rename {ja => _ja}/cheatsheets/index.md (98%) rename {cheatsheets => _overviews/cheatsheets}/index.md (99%) rename {pl => _pl}/cheatsheets/index.md (97%) rename {pt-br => _pt-br}/cheatsheets/index.md (97%) rename {zh-cn => _zh-cn}/cheatsheets/index.md (97%) diff --git a/ba/cheatsheets/index.md b/_ba/cheatsheets/index.md similarity index 97% rename from ba/cheatsheets/index.md rename to _ba/cheatsheets/index.md index e336e31a23..0a7e59790f 100644 --- a/ba/cheatsheets/index.md +++ b/_ba/cheatsheets/index.md @@ -1,16 +1,18 @@ --- layout: cheatsheet -istranslation: true title: Scalacheat + +partof: cheatsheet + by: Brendan O'Connor about: Zahvaljujući Brendan O'Connoru ovaj cheatsheet teži da bude kratki pregled sintakse Scale. Licenca pripada Brendan O'Connor-u, pod CC-BY-SA 3.0 licencom. + language: ba --- ###### Doprinio {{ page.by }} +{{ page.about }} -| | | -| ------ | ------ | | varijable | | | `var x = 5` | varijabla. | | Dobro `val x = 5`
    Loše `x=6` | konstanta. | diff --git a/_config.yml b/_config.yml index 34430c308e..5ac67b2191 100644 --- a/_config.yml +++ b/_config.yml @@ -63,6 +63,9 @@ collections: zh-tw: # Taiwanese translations output: true permalink: /:collection/:path.html + fr: # French translations + output: true + permalink: /:collection/:path.html defaults: - diff --git a/_data/doc-nav-header.yml b/_data/doc-nav-header.yml index 514c8fbc68..086d0a6924 100644 --- a/_data/doc-nav-header.yml +++ b/_data/doc-nav-header.yml @@ -13,9 +13,9 @@ - title: Getting Started url: http://google.com - title: Tour of Scala - url: http://google.com + url: "/tour/tour-of-scala.html" - title: Scala for Java Programmers - url: http://google.com + url: "/tutorials/scala-for-java-programmers.html" - title: Reference url: "#!" submenu: @@ -28,7 +28,7 @@ - title: Style Guide url: "/style/index.html" - title: Cheatsheet - url: https://www.scala-lang.org/contribute/ + url: "/cheatsheets/index.html" - title: Glossary url: "/glossary/index.html" - title: SIPs diff --git a/_data/docnames.yml b/_data/docnames.yml index 3511026f14..7a8ddf97b9 100644 --- a/_data/docnames.yml +++ b/_data/docnames.yml @@ -3,3 +3,6 @@ scala-tour: futures: name: "Futures" + +cheatsheet: + name: "Scala Cheatsheet" diff --git a/fr/cheatsheets/index.md b/_fr/cheatsheets/index.md similarity index 97% rename from fr/cheatsheets/index.md rename to _fr/cheatsheets/index.md index d6a828a6d6..9cb47c6f8d 100644 --- a/fr/cheatsheets/index.md +++ b/_fr/cheatsheets/index.md @@ -1,16 +1,18 @@ --- layout: cheatsheet -istranslation: true title: Scalacheat + +partof: cheatsheet + by: Brendan O'Connor about: Grâce à Brendan O'Connor, ce memento vise à être un guide de référence rapide pour les constructions syntaxiques en Scala. Licencié par Brendan O'Connor sous licence CC-BY-SA 3.0. + language: fr --- ###### Contribué par {{ page.by }} +{{ page.about }} -| | | -| ------ | ------ | | variables | | | `var x = 5` | variable | | Good `val x = 5`
    Bad `x=6` | constante | diff --git a/ja/cheatsheets/index.md b/_ja/cheatsheets/index.md similarity index 98% rename from ja/cheatsheets/index.md rename to _ja/cheatsheets/index.md index 605ea191b1..b133ef48e4 100644 --- a/ja/cheatsheets/index.md +++ b/_ja/cheatsheets/index.md @@ -1,16 +1,19 @@ --- layout: cheatsheet -istranslation: true title: Scalacheat + +partof: cheatsheet + by: Kenji Ohtsuka about: Thanks to Brendan O'Connor. このチートシートは Scala 構文 のクイックリファレンスとして作成されました。 Licensed by Brendan O'Connor under a CC-BY-SA 3.0 license. + language: ja --- ###### Contributed by {{ page.by }} +{{ page.about }} + -| | | -| ------ | ------ | | 変数 | | | `var x = 5` | 変数 | | Good `val x = 5`
    Bad `x=6` | 定数 | diff --git a/_layouts/cheatsheet.html b/_layouts/cheatsheet.html index d3796ae2e6..3e6e9cd3ec 100644 --- a/_layouts/cheatsheet.html +++ b/_layouts/cheatsheet.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent +layout: inner-page-parent-dropdown ---

    @@ -7,6 +7,29 @@
    {{content}} + {% if page.languages %} + + {% elsif page.language %} + {% assign engPath = page.id | remove_first: "/" | remove_first: page.language | append: '.html' %} + {% assign engPg = site.overviews | where: 'partof', page.partof | first %} + {{ engPg.languages }} + + {% endif %}
    diff --git a/cheatsheets/index.md b/_overviews/cheatsheets/index.md similarity index 99% rename from cheatsheets/index.md rename to _overviews/cheatsheets/index.md index 99c2c2380e..f069308031 100644 --- a/cheatsheets/index.md +++ b/_overviews/cheatsheets/index.md @@ -1,9 +1,14 @@ --- layout: cheatsheet title: Scalacheat + +partof: cheatsheet + by: Brendan O'Connor about: Thanks to Brendan O'Connor, this cheatsheet aims to be a quick reference of Scala syntactic constructions. Licensed by Brendan O'Connor under a CC-BY-SA 3.0 license. -languages: [ba, fr, ja, pl, pt-br] + +languages: [ba, fr, ja, pl, pt-br, zh-cn] +permalink: /cheatsheets/index.html --- diff --git a/pl/cheatsheets/index.md b/_pl/cheatsheets/index.md similarity index 97% rename from pl/cheatsheets/index.md rename to _pl/cheatsheets/index.md index 37201bcf85..96b924cc4e 100644 --- a/pl/cheatsheets/index.md +++ b/_pl/cheatsheets/index.md @@ -1,16 +1,18 @@ --- layout: cheatsheet -istranslation: true title: Scalacheat + +partof: cheatsheet + by: Filip Czaplicki about: Podziękowania dla Brendan O'Connor. Ten cheatsheet ma być szybkim podsumowaniem konstrukcji składniowych Scali. Licencjonowany przez Brendan O'Connor pod licencją CC-BY-SA 3.0. + language: pl --- ###### Contributed by {{ page.by }} +{{ page.about }} -| | | -| ------ | ------ | | zmienne | | | `var x = 5` | zmienna | | Dobrze `val x = 5`
    Źle `x=6` | stała | diff --git a/pt-br/cheatsheets/index.md b/_pt-br/cheatsheets/index.md similarity index 97% rename from pt-br/cheatsheets/index.md rename to _pt-br/cheatsheets/index.md index 9ae93150df..5d3176bda0 100644 --- a/pt-br/cheatsheets/index.md +++ b/_pt-br/cheatsheets/index.md @@ -1,16 +1,18 @@ --- layout: cheatsheet -istranslation: true title: Scalacheat + +partof: cheatsheet + by: Reginaldo Russinholi about: Agradecimentos a Brendan O'Connor, este 'cheatsheet' se destina a ser uma referência rápida às construções sintáticas de Scala. Licenciado por Brendan O'Connor sobre a licença CC-BY-SA 3.0. + language: pt-br --- ###### Contribuição de {{ page.by }} +{{ page.about }} -| | | -| ------ | ------ | | variáveis | | | `var x = 5` | variável | | Bom `val x = 5`
    Ruim `x=6` | constante | diff --git a/zh-cn/cheatsheets/index.md b/_zh-cn/cheatsheets/index.md similarity index 97% rename from zh-cn/cheatsheets/index.md rename to _zh-cn/cheatsheets/index.md index 156103e494..0559d37acd 100644 --- a/zh-cn/cheatsheets/index.md +++ b/_zh-cn/cheatsheets/index.md @@ -1,16 +1,19 @@ --- layout: cheatsheet -istranslation: true title: Scalacheat + +partof: cheatsheet + by: Brendan O'Connor about: 感谢 Brendan O'Connor, 本速查表可以用于快速地查找Scala语法结构。Licensed by Brendan O'Connor under a CC-BY-SA 3.0 license. -languages: [zh-cn] + +language: "zh-cn" --- ###### Contributed by {{ page.by }} +{{ page.about }} + -| | | -| ------ | ------ | | 变量 | | |  `var x = 5`                                                                                             | 可变量       | | Good `val x = 5`
    Bad `x=6` | 常量 | @@ -84,5 +87,3 @@ languages: [zh-cn] | `x.isInstanceOf[String]` | 类型检查 (运行时) | | `x.asInstanceOf[String]` | 类型强制转换 (运行时) | | `x: String` | 归属 (编译时) | - - From ce17c3bad1f4e83d0c9a947d9879d4a982ca8e24 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Sat, 29 Jul 2017 16:29:59 +0200 Subject: [PATCH 083/112] Style tweaks to index page of style guide --- _layouts/style-guide.html | 2 +- _sass/layout/style-guide.scss | 23 +++++++++++++++++++++++ resources/css/style.scss | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 _sass/layout/style-guide.scss diff --git a/_layouts/style-guide.html b/_layouts/style-guide.html index ceb81b12f5..8028663a43 100644 --- a/_layouts/style-guide.html +++ b/_layouts/style-guide.html @@ -6,7 +6,7 @@
    -
    +
    {{content}}
    diff --git a/_sass/layout/style-guide.scss b/_sass/layout/style-guide.scss new file mode 100644 index 0000000000..9621d3d267 --- /dev/null +++ b/_sass/layout/style-guide.scss @@ -0,0 +1,23 @@ +// STYLE GUIDE +//------------------------------------------------ +//------------------------------------------------ + +.style-guide { + ul { + li { + font-weight: $font-black; + ul { + margin-top: 6px !important; + margin-bottom: 0; + li { + font-weight: $font-regular; + margin-bottom: 0 !important; + ul { + margin-top: 0 !important; + margin-bottom: 4px; + } + } + } + } + } +} diff --git a/resources/css/style.scss b/resources/css/style.scss index 796ccfcf17..8e4cc393b0 100755 --- a/resources/css/style.scss +++ b/resources/css/style.scss @@ -32,6 +32,7 @@ @import 'layout/overviews'; @import 'layout/toc'; @import 'layout/glossary'; +@import 'layout/style-guide'; @import 'layout/courses'; @import 'layout/documentation'; @import 'layout/upcoming-events'; From b683b9832bcea8dedaf3cf54d6e708a21602472c Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Sat, 29 Jul 2017 16:34:06 +0200 Subject: [PATCH 084/112] Replacing accidentally deleted overview index --- _overviews/index.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 _overviews/index.md diff --git a/_overviews/index.md b/_overviews/index.md new file mode 100644 index 0000000000..335ff94209 --- /dev/null +++ b/_overviews/index.md @@ -0,0 +1,35 @@ +--- +layout: inner-page +title: Guides and Overviews +#languages: [ja, zh-cn, es] +permalink: /overviews/:title.html +--- + +{% for category in site.data.overviews %} +

    {{ category.category }}

    + {% if category.description %}

    {{ category.description }}

    {% endif %} +
    + {% for overview in category.overviews %} +
    +
    + {% if overview.icon %}

    {% endif %} +

    {{ overview.title }}

    + {% if overview.label-text %}
    {{ overview.label-text }}
    {% endif %} + {% if overview.by %}
    By {{ overview.by }}
    {% endif %} + {% if overview.description %}

    {{ overview.description }}

    {% endif %} + {% if overview.subdocs %} + Contents + + {% endif %} +
    + +
    + {% endfor %} +
    +{% endfor %} From be7fe8e34d49cf738fc7ba29ac57d850d78e4553 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Sat, 29 Jul 2017 20:32:33 +0200 Subject: [PATCH 085/112] Fix up the card design on the overview index page --- _layouts/overviews.html | 13 +++++ _overviews/index.md | 39 ++++++++----- _sass/layout/overviews.scss | 108 ++++++++++++++++++++++++------------ resources/js/functions.js | 26 ++++++--- 4 files changed, 128 insertions(+), 58 deletions(-) create mode 100644 _layouts/overviews.html diff --git a/_layouts/overviews.html b/_layouts/overviews.html new file mode 100644 index 0000000000..888ffc2061 --- /dev/null +++ b/_layouts/overviews.html @@ -0,0 +1,13 @@ +--- +layout: inner-page-parent +--- + +
    +
    +
    +
    + {{content}} +
    +
    +
    +
    diff --git a/_overviews/index.md b/_overviews/index.md index 335ff94209..71b659a568 100644 --- a/_overviews/index.md +++ b/_overviews/index.md @@ -1,5 +1,5 @@ --- -layout: inner-page +layout: overviews title: Guides and Overviews #languages: [ja, zh-cn, es] permalink: /overviews/:title.html @@ -11,23 +11,32 @@ permalink: /overviews/:title.html
    {% for overview in category.overviews %}
    -
    - {% if overview.icon %}

    {% endif %} -

    {{ overview.title }}

    - {% if overview.label-text %}
    {{ overview.label-text }}
    {% endif %} - {% if overview.by %}
    By {{ overview.by }}
    {% endif %} - {% if overview.description %}

    {{ overview.description }}

    {% endif %} - {% if overview.subdocs %} - Contents - +
    +
    + {% if overview.icon %} +
    +
    +
    + {% endif %} +

    {{ overview.title }}

    +
    +
    + {% if overview.label-text %}
    {{ overview.label-text }}
    {% endif %} + {% if overview.by %}
    By {{ overview.by }}
    {% endif %} + {% if overview.description %}

    {{ overview.description }}

    {% endif %} + {% if overview.subdocs %} + Contents + {% endif %} +
    {% endfor %} diff --git a/_sass/layout/overviews.scss b/_sass/layout/overviews.scss index ef8c9a4e08..750878c195 100644 --- a/_sass/layout/overviews.scss +++ b/_sass/layout/overviews.scss @@ -2,6 +2,12 @@ //------------------------------------------------ //------------------------------------------------ +.overviews { + .inner-box { + background: #fafafa !important; + } +} + .card-group { // @include span-columns(6); // @include bp(large) { @@ -16,42 +22,73 @@ h3 { font-family: $base-font-family; - // text-transform: uppercase; - border-bottom: $base-border-gray; } .white-card { + display: flex; + flex-direction: column; + // justify-content: flex-end; position: relative; flex: 0 1 calc(50% - 1em); margin-bottom: 2em; + transition: max-height .3s ease-out; + text-decoration: none; + border-radius: 2px; + box-shadow: 0 1px 3px 0 rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.12); + background: #fff; + max-height: 400px; + overflow: hidden; @include bp(medium) { flex: 0 1 calc(100% - 0.5em); } - background: #fff; + .card-wrap { + // align-self: stretch; + position: relative; + // padding: 22px; - max-height: 400px; - overflow: hidden; - &:hover { - transition: max-height .3s ease-in; - max-height: 1000px; - } - transition: max-height .3s ease-out; + .card-header { + display: flex; + padding: 16px; - display: inline-block; - text-decoration: none; - border-radius: $border-radius-base; - box-shadow: $box-shadow-item; + .text { + display: flex; + } + } - .card-content { - padding: 22px; + .card-content { + padding: 20px; + padding-top: 0px; + padding-bottom: 0px; + } h2 { margin-bottom: 0px; } + .card-avatar { + width: 40px; + height: 40px; + margin-right: 12px; + + .icon { + font-size: 26px; + color: #fff; + border-radius: 50%; + margin: auto; + text-align: center; + display: inline-block; + vertical-align: middle; + background-color: $brand-primary; + height: 40px; + width: 40px; + min-height: 40px; + min-width: 40px; + } + } + .by { font-weight: $font-regular; color: $base-font-color-light; @@ -76,14 +113,14 @@ a { h3 { + display: inline-block; + padding: 0; + margin: 0; color: $gray-dark; transition: $base-transition; - margin-top: 0px; - padding-bottom: 8px; - margin-bottom: 6px; font-weight: $font-black; font-size: 26px; - // text-transform: uppercase; + line-height: 1.15; &:hover { text-decoration: none; @@ -108,24 +145,25 @@ position: absolute; bottom: 0; width: 100%; - background: $gray-lighter; - text-align: center; + padding: 20px; + height: 66px; + background: #fff; + border-top: 1px solid lighten($base-font-color-light, 35%); - .expand-btn { - display: inline-block; - margin: 4px 0; - padding: 0 3px; + &:hover { + cursor: pointer; + background: $brand-primary; + .expand-btn, + .go-btn { + color: #fff; + } + } + + .expand-btn, + .go-btn { color: lighten($base-font-color-light, 15%); - text-transform: uppercase; - border: 1px solid lighten($base-font-color-light, 25%); font-weight: $font-regular; - font-size: $font-size-xsmall; - - &:hover { - cursor: pointer; - background: lighten($base-font-color-light, 25%); - color: $gray-lighter; - } + font-size: $font-regular; } } } diff --git a/resources/js/functions.js b/resources/js/functions.js index 3500125667..635d5205ed 100644 --- a/resources/js/functions.js +++ b/resources/js/functions.js @@ -128,7 +128,7 @@ $(document).ready(function() { maxHeight = Math.max(maxHeight, $(this).outerHeight(true)); }); - return maxWidth > $this.width() || maxHeight > $this.height(); + return maxWidth > $this.width() || (maxHeight + 66) > $this.height(); } return false; @@ -136,16 +136,26 @@ $(document).ready(function() { $('.white-card').each(function(index) { if (!$(this).hasOverflow()) { - $(this).find(".card-footer").css("visibility", "hidden"); + $(this).find(".expand-btn").hide(); + $(this).find(".go-btn").show(); } }); - $(".white-card").hover(function() { - $(this).find(".card-footer").hide(); - $(this).find(".expand-btn").hide(); - }, function() { - $(this).find(".card-footer").show(); - $(this).find(".expand-btn").show(); + $(".card-footer").click(function() { + // if we clicked on the expand button, expand the box + if ($(this).find(".expand-btn").is(':visible')) { + + // height mangling becasue flexbox align-self: self-end; doesn't work :( + $(this).parent().css('max-height', 'none'); + var cardWrapHeight = $(this).parent().find(".card-wrap").outerHeight(); + var cardFooterHeight = $(this).outerHeight() + $(this).parent().outerHeight(cardWrapHeight + cardFooterHeight); + + $(this).find(".expand-btn").hide(); + $(this).find(".go-btn").show(); + } else { + window.location.href = $(this).find(".go-btn").attr("href"); + } }); }); From 9472c3564b80752869ccbc9bf1169eed8b511336 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Sat, 29 Jul 2017 23:24:04 +0200 Subject: [PATCH 086/112] Reworked getting started index --- _includes/navbar-inner.html | 4 +-- getting-started.md | 55 ++++++++++++++++++++++++++++++------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/_includes/navbar-inner.html b/_includes/navbar-inner.html index a4d69083eb..b1b391107a 100644 --- a/_includes/navbar-inner.html +++ b/_includes/navbar-inner.html @@ -10,7 +10,7 @@ @@ -20,7 +20,7 @@
    diff --git a/_sass/layout/sips.scss b/_sass/layout/sips.scss new file mode 100644 index 0000000000..110ade73da --- /dev/null +++ b/_sass/layout/sips.scss @@ -0,0 +1,25 @@ +// SIPS INDEX PAGE AND INDIVIDUAL SIP PAGES +//------------------------------------------------ +//------------------------------------------------ + +.sip-toc { + .tag { + // position: absolute; + // right: 0; + position: relative; + display: inline-block; + top: 3px; + color: #fff; + text-transform: uppercase; + font-size: 11px; + font-weight: 700; + padding: 1px 5px; + margin-bottom: 8px; + } + + .vote-text { + line-height: 1.2; + margin-bottom: 24px; + font-style: italic; + } +} diff --git a/resources/css/style.scss b/resources/css/style.scss index 8e4cc393b0..d6ce6e6aa6 100755 --- a/resources/css/style.scss +++ b/resources/css/style.scss @@ -30,6 +30,7 @@ @import 'layout/ides'; @import 'layout/nutshell'; @import 'layout/overviews'; +@import 'layout/sips'; @import 'layout/toc'; @import 'layout/glossary'; @import 'layout/style-guide'; From 81701c04aaed9f594a063842a06580f5157f4f19 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Sun, 30 Jul 2017 17:27:07 +0200 Subject: [PATCH 094/112] More SIP section progress --- _data/sip-data.yml | 12 + _layouts/sip.html | 6 +- ...-05-31-improved-lazy-val-initialization.md | 749 ++++++++++++ _sips/pending/_posts/2013-06-10-spores.md | 459 ++++++++ _sips/pending/_posts/2013-06-30-async.md | 298 +++++ _sips/pending/_posts/2014-06-27-42.type.md | 309 +++++ .../_posts/2015-6-18-repeated-byname.md | 33 + .../_posts/2015-6-18-trait-parameters.md | 70 ++ .../_posts/2016-01-11-static-members.md | 219 ++++ .../pending/_posts/2016-09-09-inline-meta.md | 1024 +++++++++++++++++ ...017-01-11-refer-other-arguments-in-args.md | 105 ++ .../_posts/2017-01-13-binary-compatibility.md | 295 +++++ ...2-07-make-types-behave-like-expressions.md | 203 ++++ .../2017-02-22-comonadic-comprehensions.md | 221 ++++ ...-12-right-associative-by-name-operators.md | 90 ++ 15 files changed, 4090 insertions(+), 3 deletions(-) create mode 100644 _sips/pending/_posts/2013-05-31-improved-lazy-val-initialization.md create mode 100644 _sips/pending/_posts/2013-06-10-spores.md create mode 100644 _sips/pending/_posts/2013-06-30-async.md create mode 100644 _sips/pending/_posts/2014-06-27-42.type.md create mode 100644 _sips/pending/_posts/2015-6-18-repeated-byname.md create mode 100644 _sips/pending/_posts/2015-6-18-trait-parameters.md create mode 100644 _sips/pending/_posts/2016-01-11-static-members.md create mode 100644 _sips/pending/_posts/2016-09-09-inline-meta.md create mode 100644 _sips/pending/_posts/2017-01-11-refer-other-arguments-in-args.md create mode 100644 _sips/pending/_posts/2017-01-13-binary-compatibility.md create mode 100644 _sips/pending/_posts/2017-02-07-make-types-behave-like-expressions.md create mode 100644 _sips/pending/_posts/2017-02-22-comonadic-comprehensions.md create mode 100644 _sips/pending/_posts/2017-07-12-right-associative-by-name-operators.md diff --git a/_data/sip-data.yml b/_data/sip-data.yml index 95e094722c..7e77d7aa43 100644 --- a/_data/sip-data.yml +++ b/_data/sip-data.yml @@ -1,3 +1,15 @@ under-review: color: "#b58900" text: "Under Review" + +pending: + color: "#b58900" + text: "Pending" + +dormant: + color: "#839496" + text: "Dormant" + +under-revision: + color: "#2aa198" + text: "Under Revision" diff --git a/_layouts/sip.html b/_layouts/sip.html index 9e6a7a2ada..f91f543949 100644 --- a/_layouts/sip.html +++ b/_layouts/sip.html @@ -17,9 +17,9 @@ {% if page.vote-status %}
    SIP Committee Decision
    {{ site.data.sip-data[page.vote-status].text }}
    - {% if page.vote-text %} -

    {{ page.vote-text }}

    - {% endif %} +

    + {% if page.vote-text %}{{ page.vote-text }}{% endif %} +

    {% endif %}
    SIP Contents
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - +{% if page.layout == "sips"%} + + // if there indeed are upcoming meetings, display the next one + if (data.items.length > 0) { - - - + // YouTube video ID for the next upcoming SIP meeting + var videoId = data.items[0].id.videoId; - - + $.getJSON(vidQuery + videoId, function(vidData) { + if (vidData.items.length > 0) { + var result = vidData.items[0].liveStreamingDetails.scheduledStartTime; + var startTime = moment(result).format("dddd, MMMM Do YYYY, ha"); - - - - + var meetingTitle = moment(result).format("MMMM YYYY") + " SIP Meeting"; + var href = "https://www.youtube.com/watch?v=" + videoId; + var a = document.createElement('a'); + $(a).attr('href', href).text(meetingTitle); - - - + $("#meeting-title").prepend(a); + $("#watch-meeting").attr('href', href); + $("#meeting-time").text(startTime); + } + }); - - - + } else { + // if there are no upcoming meetings scheduled, hide the upcoming meeting section + $("#upcoming-meeting").hide(); + } - - + }); +}); + +{% endif %} + - - - diff --git a/_layouts/sips.html b/_layouts/sips.html index 2bed1a1807..c5d8452a28 100644 --- a/_layouts/sips.html +++ b/_layouts/sips.html @@ -13,7 +13,33 @@
    diff --git a/_sass/layout/sips.scss b/_sass/layout/sips.scss index ab0e1f4a88..12e351ee5c 100644 --- a/_sass/layout/sips.scss +++ b/_sass/layout/sips.scss @@ -22,6 +22,83 @@ margin-bottom: 24px; font-style: italic; } + + ul { + margin-bottom: 18px; + li { + margin-top: 6px; + a { + color: $headings-font-color; + // font-weight: $font-black; + &:hover { + color: $hover-link-color; + text-decoration: none; + } + } + } + } + #sip-meetings { + margin-bottom: 20px; + } + + #live-on-youtube { + margin-top: 4px; + font-style: italic; + line-height: 1.1; + color: $headings-font-color; + font-size: $font-size-small; + + #watch-meeting { + display: inline-block; + margin-top: 6px; + color: $brand-primary; + } + } + + #meeting-minutes { + a { + color: $headings-font-color; + // font-weight: $font-black; + &:hover { + color: $hover-link-color; + text-decoration: none; + } + } + } + + #meeting-time { + font-size: $font-size-xsmall; + text-transform: uppercase; + font-weight: $font-black; + color: $base-font-color-light; + margin-top: 0px; + } + + #meeting-title { + a { + color: $headings-font-color; + font-weight: $font-black; + &:hover { + color: $hover-link-color; + text-decoration: none; + } + } + } + + #upcoming-meeting { + margin-top: 10px; + padding: 8px; + padding-left: 10px; + padding-bottom: 14px; + background-color: rgba($brand-primary, 0.1); + + h6 { + font-family: $heading-font-family; + color: $brand-primary; + font-size: $font-size-large; + font-weight: $font-regular; + } + } } .sips { diff --git a/_sips/index.md b/_sips/index.md index 61a1a2f34e..13b20ffbf9 100644 --- a/_sips/index.md +++ b/_sips/index.md @@ -1,5 +1,5 @@ --- -layout: singlepage-overview +layout: sips title: Scala Improvement Process --- diff --git a/resources/js/functions.js b/resources/js/functions.js index 635d5205ed..307a48d418 100644 --- a/resources/js/functions.js +++ b/resources/js/functions.js @@ -144,7 +144,7 @@ $(document).ready(function() { $(".card-footer").click(function() { // if we clicked on the expand button, expand the box if ($(this).find(".expand-btn").is(':visible')) { - + // height mangling becasue flexbox align-self: self-end; doesn't work :( $(this).parent().css('max-height', 'none'); var cardWrapHeight = $(this).parent().find(".card-wrap").outerHeight(); diff --git a/resources/js/vendor/moment.min.js b/resources/js/vendor/moment.min.js new file mode 100644 index 0000000000..25fa625cca --- /dev/null +++ b/resources/js/vendor/moment.min.js @@ -0,0 +1,7 @@ +//! moment.js +//! version : 2.18.1 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com +!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return sd.apply(null,arguments)}function b(a){sd=a}function c(a){return a instanceof Array||"[object Array]"===Object.prototype.toString.call(a)}function d(a){return null!=a&&"[object Object]"===Object.prototype.toString.call(a)}function e(a){var b;for(b in a)return!1;return!0}function f(a){return void 0===a}function g(a){return"number"==typeof a||"[object Number]"===Object.prototype.toString.call(a)}function h(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function i(a,b){var c,d=[];for(c=0;c0)for(c=0;c0?"future":"past"];return z(c)?c(b):c.replace(/%s/i,b)}function J(a,b){var c=a.toLowerCase();Hd[c]=Hd[c+"s"]=Hd[b]=a}function K(a){return"string"==typeof a?Hd[a]||Hd[a.toLowerCase()]:void 0}function L(a){var b,c,d={};for(c in a)j(a,c)&&(b=K(c),b&&(d[b]=a[c]));return d}function M(a,b){Id[a]=b}function N(a){var b=[];for(var c in a)b.push({unit:c,priority:Id[c]});return b.sort(function(a,b){return a.priority-b.priority}),b}function O(b,c){return function(d){return null!=d?(Q(this,b,d),a.updateOffset(this,c),this):P(this,b)}}function P(a,b){return a.isValid()?a._d["get"+(a._isUTC?"UTC":"")+b]():NaN}function Q(a,b,c){a.isValid()&&a._d["set"+(a._isUTC?"UTC":"")+b](c)}function R(a){return a=K(a),z(this[a])?this[a]():this}function S(a,b){if("object"==typeof a){a=L(a);for(var c=N(a),d=0;d=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d}function U(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Md[a]=e),b&&(Md[b[0]]=function(){return T(e.apply(this,arguments),b[1],b[2])}),c&&(Md[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function V(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function W(a){var b,c,d=a.match(Jd);for(b=0,c=d.length;b=0&&Kd.test(a);)a=a.replace(Kd,c),Kd.lastIndex=0,d-=1;return a}function Z(a,b,c){ce[a]=z(b)?b:function(a,d){return a&&c?c:b}}function $(a,b){return j(ce,a)?ce[a](b._strict,b._locale):new RegExp(_(a))}function _(a){return aa(a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}))}function aa(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function ba(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),g(b)&&(d=function(a,c){c[b]=u(a)}),c=0;c=0&&isFinite(h.getFullYear())&&h.setFullYear(a),h}function ta(a){var b=new Date(Date.UTC.apply(null,arguments));return a<100&&a>=0&&isFinite(b.getUTCFullYear())&&b.setUTCFullYear(a),b}function ua(a,b,c){var d=7+b-c,e=(7+ta(a,0,d).getUTCDay()-b)%7;return-e+d-1}function va(a,b,c,d,e){var f,g,h=(7+c-d)%7,i=ua(a,d,e),j=1+7*(b-1)+h+i;return j<=0?(f=a-1,g=pa(f)+j):j>pa(a)?(f=a+1,g=j-pa(a)):(f=a,g=j),{year:f,dayOfYear:g}}function wa(a,b,c){var d,e,f=ua(a.year(),b,c),g=Math.floor((a.dayOfYear()-f-1)/7)+1;return g<1?(e=a.year()-1,d=g+xa(e,b,c)):g>xa(a.year(),b,c)?(d=g-xa(a.year(),b,c),e=a.year()+1):(e=a.year(),d=g),{week:d,year:e}}function xa(a,b,c){var d=ua(a,b,c),e=ua(a+1,b,c);return(pa(a)-d+e)/7}function ya(a){return wa(a,this._week.dow,this._week.doy).week}function za(){return this._week.dow}function Aa(){return this._week.doy}function Ba(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function Ca(a){var b=wa(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")}function Da(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function Ea(a,b){return"string"==typeof a?b.weekdaysParse(a)%7||7:isNaN(a)?null:a}function Fa(a,b){return a?c(this._weekdays)?this._weekdays[a.day()]:this._weekdays[this._weekdays.isFormat.test(b)?"format":"standalone"][a.day()]:c(this._weekdays)?this._weekdays:this._weekdays.standalone}function Ga(a){return a?this._weekdaysShort[a.day()]:this._weekdaysShort}function Ha(a){return a?this._weekdaysMin[a.day()]:this._weekdaysMin}function Ia(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],d=0;d<7;++d)f=l([2e3,1]).day(d),this._minWeekdaysParse[d]=this.weekdaysMin(f,"").toLocaleLowerCase(),this._shortWeekdaysParse[d]=this.weekdaysShort(f,"").toLocaleLowerCase(),this._weekdaysParse[d]=this.weekdays(f,"").toLocaleLowerCase();return c?"dddd"===b?(e=ne.call(this._weekdaysParse,g),e!==-1?e:null):"ddd"===b?(e=ne.call(this._shortWeekdaysParse,g),e!==-1?e:null):(e=ne.call(this._minWeekdaysParse,g),e!==-1?e:null):"dddd"===b?(e=ne.call(this._weekdaysParse,g),e!==-1?e:(e=ne.call(this._shortWeekdaysParse,g),e!==-1?e:(e=ne.call(this._minWeekdaysParse,g),e!==-1?e:null))):"ddd"===b?(e=ne.call(this._shortWeekdaysParse,g),e!==-1?e:(e=ne.call(this._weekdaysParse,g),e!==-1?e:(e=ne.call(this._minWeekdaysParse,g),e!==-1?e:null))):(e=ne.call(this._minWeekdaysParse,g),e!==-1?e:(e=ne.call(this._weekdaysParse,g),e!==-1?e:(e=ne.call(this._shortWeekdaysParse,g),e!==-1?e:null)))}function Ja(a,b,c){var d,e,f;if(this._weekdaysParseExact)return Ia.call(this,a,b,c);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),d=0;d<7;d++){if(e=l([2e3,1]).day(d),c&&!this._fullWeekdaysParse[d]&&(this._fullWeekdaysParse[d]=new RegExp("^"+this.weekdays(e,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[d]=new RegExp("^"+this.weekdaysShort(e,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[d]=new RegExp("^"+this.weekdaysMin(e,"").replace(".",".?")+"$","i")),this._weekdaysParse[d]||(f="^"+this.weekdays(e,"")+"|^"+this.weekdaysShort(e,"")+"|^"+this.weekdaysMin(e,""),this._weekdaysParse[d]=new RegExp(f.replace(".",""),"i")),c&&"dddd"===b&&this._fullWeekdaysParse[d].test(a))return d;if(c&&"ddd"===b&&this._shortWeekdaysParse[d].test(a))return d;if(c&&"dd"===b&&this._minWeekdaysParse[d].test(a))return d;if(!c&&this._weekdaysParse[d].test(a))return d}}function Ka(a){if(!this.isValid())return null!=a?this:NaN;var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Da(a,this.localeData()),this.add(a-b,"d")):b}function La(a){if(!this.isValid())return null!=a?this:NaN;var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Ma(a){if(!this.isValid())return null!=a?this:NaN;if(null!=a){var b=Ea(a,this.localeData());return this.day(this.day()%7?b:b-7)}return this.day()||7}function Na(a){return this._weekdaysParseExact?(j(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysStrictRegex:this._weekdaysRegex):(j(this,"_weekdaysRegex")||(this._weekdaysRegex=ye),this._weekdaysStrictRegex&&a?this._weekdaysStrictRegex:this._weekdaysRegex)}function Oa(a){return this._weekdaysParseExact?(j(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(j(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=ze),this._weekdaysShortStrictRegex&&a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function Pa(a){return this._weekdaysParseExact?(j(this,"_weekdaysRegex")||Qa.call(this),a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(j(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=Ae),this._weekdaysMinStrictRegex&&a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Qa(){function a(a,b){return b.length-a.length}var b,c,d,e,f,g=[],h=[],i=[],j=[];for(b=0;b<7;b++)c=l([2e3,1]).day(b),d=this.weekdaysMin(c,""),e=this.weekdaysShort(c,""),f=this.weekdays(c,""),g.push(d),h.push(e),i.push(f),j.push(d),j.push(e),j.push(f);for(g.sort(a),h.sort(a),i.sort(a),j.sort(a),b=0;b<7;b++)h[b]=aa(h[b]),i[b]=aa(i[b]),j[b]=aa(j[b]);this._weekdaysRegex=new RegExp("^("+j.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+h.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+g.join("|")+")","i")}function Ra(){return this.hours()%12||12}function Sa(){return this.hours()||24}function Ta(a,b){U(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})}function Ua(a,b){return b._meridiemParse}function Va(a){return"p"===(a+"").toLowerCase().charAt(0)}function Wa(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Xa(a){return a?a.toLowerCase().replace("_","-"):a}function Ya(a){for(var b,c,d,e,f=0;f0;){if(d=Za(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&v(e,c,!0)>=b-1)break;b--}f++}return null}function Za(a){var b=null;if(!Fe[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=Be._abbr,require("./locale/"+a),$a(b)}catch(a){}return Fe[a]}function $a(a,b){var c;return a&&(c=f(b)?bb(a):_a(a,b),c&&(Be=c)),Be._abbr}function _a(a,b){if(null!==b){var c=Ee;if(b.abbr=a,null!=Fe[a])y("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),c=Fe[a]._config;else if(null!=b.parentLocale){if(null==Fe[b.parentLocale])return Ge[b.parentLocale]||(Ge[b.parentLocale]=[]),Ge[b.parentLocale].push({name:a,config:b}),null;c=Fe[b.parentLocale]._config}return Fe[a]=new C(B(c,b)),Ge[a]&&Ge[a].forEach(function(a){_a(a.name,a.config)}),$a(a),Fe[a]}return delete Fe[a],null}function ab(a,b){if(null!=b){var c,d=Ee;null!=Fe[a]&&(d=Fe[a]._config),b=B(d,b),c=new C(b),c.parentLocale=Fe[a],Fe[a]=c,$a(a)}else null!=Fe[a]&&(null!=Fe[a].parentLocale?Fe[a]=Fe[a].parentLocale:null!=Fe[a]&&delete Fe[a]);return Fe[a]}function bb(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return Be;if(!c(a)){if(b=Za(a))return b;a=[a]}return Ya(a)}function cb(){return Ad(Fe)}function db(a){var b,c=a._a;return c&&n(a).overflow===-2&&(b=c[fe]<0||c[fe]>11?fe:c[ge]<1||c[ge]>ea(c[ee],c[fe])?ge:c[he]<0||c[he]>24||24===c[he]&&(0!==c[ie]||0!==c[je]||0!==c[ke])?he:c[ie]<0||c[ie]>59?ie:c[je]<0||c[je]>59?je:c[ke]<0||c[ke]>999?ke:-1,n(a)._overflowDayOfYear&&(bge)&&(b=ge),n(a)._overflowWeeks&&b===-1&&(b=le),n(a)._overflowWeekday&&b===-1&&(b=me),n(a).overflow=b),a}function eb(a){var b,c,d,e,f,g,h=a._i,i=He.exec(h)||Ie.exec(h);if(i){for(n(a).iso=!0,b=0,c=Ke.length;b10?"YYYY ":"YY "),f="HH:mm"+(c[4]?":ss":""),c[1]){var l=new Date(c[2]),m=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][l.getDay()];if(c[1].substr(0,3)!==m)return n(a).weekdayMismatch=!0,void(a._isValid=!1)}switch(c[5].length){case 2:0===i?h=" +0000":(i=k.indexOf(c[5][1].toUpperCase())-12,h=(i<0?" -":" +")+(""+i).replace(/^-?/,"0").match(/..$/)[0]+"00");break;case 4:h=j[c[5]];break;default:h=j[" GMT"]}c[5]=h,a._i=c.splice(1).join(""),g=" ZZ",a._f=d+e+f+g,lb(a),n(a).rfc2822=!0}else a._isValid=!1}function gb(b){var c=Me.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(eb(b),void(b._isValid===!1&&(delete b._isValid,fb(b),b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b)))))}function hb(a,b,c){return null!=a?a:null!=b?b:c}function ib(b){var c=new Date(a.now());return b._useUTC?[c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate()]:[c.getFullYear(),c.getMonth(),c.getDate()]}function jb(a){var b,c,d,e,f=[];if(!a._d){for(d=ib(a),a._w&&null==a._a[ge]&&null==a._a[fe]&&kb(a),null!=a._dayOfYear&&(e=hb(a._a[ee],d[ee]),(a._dayOfYear>pa(e)||0===a._dayOfYear)&&(n(a)._overflowDayOfYear=!0),c=ta(e,0,a._dayOfYear),a._a[fe]=c.getUTCMonth(),a._a[ge]=c.getUTCDate()),b=0;b<3&&null==a._a[b];++b)a._a[b]=f[b]=d[b];for(;b<7;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b];24===a._a[he]&&0===a._a[ie]&&0===a._a[je]&&0===a._a[ke]&&(a._nextDay=!0,a._a[he]=0),a._d=(a._useUTC?ta:sa).apply(null,f),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[he]=24)}}function kb(a){var b,c,d,e,f,g,h,i;if(b=a._w,null!=b.GG||null!=b.W||null!=b.E)f=1,g=4,c=hb(b.GG,a._a[ee],wa(tb(),1,4).year),d=hb(b.W,1),e=hb(b.E,1),(e<1||e>7)&&(i=!0);else{f=a._locale._week.dow,g=a._locale._week.doy;var j=wa(tb(),f,g);c=hb(b.gg,a._a[ee],j.year),d=hb(b.w,j.week),null!=b.d?(e=b.d,(e<0||e>6)&&(i=!0)):null!=b.e?(e=b.e+f,(b.e<0||b.e>6)&&(i=!0)):e=f}d<1||d>xa(c,f,g)?n(a)._overflowWeeks=!0:null!=i?n(a)._overflowWeekday=!0:(h=va(c,d,e,f,g),a._a[ee]=h.year,a._dayOfYear=h.dayOfYear)}function lb(b){if(b._f===a.ISO_8601)return void eb(b);if(b._f===a.RFC_2822)return void fb(b);b._a=[],n(b).empty=!0;var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=Y(b._f,b._locale).match(Jd)||[],c=0;c0&&n(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length),Md[f]?(d?n(b).empty=!1:n(b).unusedTokens.push(f),da(f,d,b)):b._strict&&!d&&n(b).unusedTokens.push(f);n(b).charsLeftOver=i-j,h.length>0&&n(b).unusedInput.push(h),b._a[he]<=12&&n(b).bigHour===!0&&b._a[he]>0&&(n(b).bigHour=void 0),n(b).parsedDateParts=b._a.slice(0),n(b).meridiem=b._meridiem,b._a[he]=mb(b._locale,b._a[he],b._meridiem),jb(b),db(b)}function mb(a,b,c){var d;return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&b<12&&(b+=12),d||12!==b||(b=0),b):b}function nb(a){var b,c,d,e,f;if(0===a._f.length)return n(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;ethis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ob(){if(!f(this._isDSTShifted))return this._isDSTShifted;var a={};if(q(a,this),a=qb(a),a._a){var b=a._isUTC?l(a._a):tb(a._a);this._isDSTShifted=this.isValid()&&v(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Pb(){return!!this.isValid()&&!this._isUTC}function Qb(){return!!this.isValid()&&this._isUTC}function Rb(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}function Sb(a,b){var c,d,e,f=a,h=null;return Bb(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:g(a)?(f={},b?f[b]=a:f.milliseconds=a):(h=Te.exec(a))?(c="-"===h[1]?-1:1,f={y:0,d:u(h[ge])*c,h:u(h[he])*c,m:u(h[ie])*c,s:u(h[je])*c,ms:u(Cb(1e3*h[ke]))*c}):(h=Ue.exec(a))?(c="-"===h[1]?-1:1,f={y:Tb(h[2],c),M:Tb(h[3],c),w:Tb(h[4],c),d:Tb(h[5],c),h:Tb(h[6],c),m:Tb(h[7],c),s:Tb(h[8],c)}):null==f?f={}:"object"==typeof f&&("from"in f||"to"in f)&&(e=Vb(tb(f.from),tb(f.to)),f={},f.ms=e.milliseconds,f.M=e.months),d=new Ab(f),Bb(a)&&j(a,"_locale")&&(d._locale=a._locale),d}function Tb(a,b){var c=a&&parseFloat(a.replace(",","."));return(isNaN(c)?0:c)*b}function Ub(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Vb(a,b){var c;return a.isValid()&&b.isValid()?(b=Fb(b,a),a.isBefore(b)?c=Ub(a,b):(c=Ub(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c):{milliseconds:0,months:0}}function Wb(a,b){return function(c,d){var e,f;return null===d||isNaN(+d)||(y(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Sb(c,d),Xb(this,e,a),this}}function Xb(b,c,d,e){var f=c._milliseconds,g=Cb(c._days),h=Cb(c._months);b.isValid()&&(e=null==e||e,f&&b._d.setTime(b._d.valueOf()+f*d),g&&Q(b,"Date",P(b,"Date")+g*d),h&&ja(b,P(b,"Month")+h*d),e&&a.updateOffset(b,g||h))}function Yb(a,b){var c=a.diff(b,"days",!0);return c<-6?"sameElse":c<-1?"lastWeek":c<0?"lastDay":c<1?"sameDay":c<2?"nextDay":c<7?"nextWeek":"sameElse"}function Zb(b,c){var d=b||tb(),e=Fb(d,this).startOf("day"),f=a.calendarFormat(this,e)||"sameElse",g=c&&(z(c[f])?c[f].call(this,d):c[f]);return this.format(g||this.localeData().calendar(f,this,tb(d)))}function $b(){return new r(this)}function _b(a,b){var c=s(a)?a:tb(a);return!(!this.isValid()||!c.isValid())&&(b=K(f(b)?"millisecond":b),"millisecond"===b?this.valueOf()>c.valueOf():c.valueOf()9999?X(a,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):z(Date.prototype.toISOString)?this.toDate().toISOString():X(a,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")}function jc(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var a="moment",b="";this.isLocal()||(a=0===this.utcOffset()?"moment.utc":"moment.parseZone",b="Z");var c="["+a+'("]',d=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",e="-MM-DD[T]HH:mm:ss.SSS",f=b+'[")]';return this.format(c+d+e+f)}function kc(b){b||(b=this.isUtc()?a.defaultFormatUtc:a.defaultFormat);var c=X(this,b);return this.localeData().postformat(c)}function lc(a,b){return this.isValid()&&(s(a)&&a.isValid()||tb(a).isValid())?Sb({to:this,from:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function mc(a){return this.from(tb(),a)}function nc(a,b){return this.isValid()&&(s(a)&&a.isValid()||tb(a).isValid())?Sb({from:this,to:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function oc(a){return this.to(tb(),a)}function pc(a){var b;return void 0===a?this._locale._abbr:(b=bb(a),null!=b&&(this._locale=b),this)}function qc(){return this._locale}function rc(a){switch(a=K(a)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":case"date":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===a&&this.weekday(0),"isoWeek"===a&&this.isoWeekday(1),"quarter"===a&&this.month(3*Math.floor(this.month()/3)),this}function sc(a){return a=K(a),void 0===a||"millisecond"===a?this:("date"===a&&(a="day"),this.startOf(a).add(1,"isoWeek"===a?"week":a).subtract(1,"ms"))}function tc(){return this._d.valueOf()-6e4*(this._offset||0)}function uc(){return Math.floor(this.valueOf()/1e3)}function vc(){return new Date(this.valueOf())}function wc(){var a=this;return[a.year(),a.month(),a.date(),a.hour(),a.minute(),a.second(),a.millisecond()]}function xc(){var a=this;return{years:a.year(),months:a.month(),date:a.date(),hours:a.hours(),minutes:a.minutes(),seconds:a.seconds(),milliseconds:a.milliseconds()}}function yc(){return this.isValid()?this.toISOString():null}function zc(){return o(this)}function Ac(){ +return k({},n(this))}function Bc(){return n(this).overflow}function Cc(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}function Dc(a,b){U(0,[a,a.length],0,b)}function Ec(a){return Ic.call(this,a,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)}function Fc(a){return Ic.call(this,a,this.isoWeek(),this.isoWeekday(),1,4)}function Gc(){return xa(this.year(),1,4)}function Hc(){var a=this.localeData()._week;return xa(this.year(),a.dow,a.doy)}function Ic(a,b,c,d,e){var f;return null==a?wa(this,d,e).year:(f=xa(a,d,e),b>f&&(b=f),Jc.call(this,a,b,c,d,e))}function Jc(a,b,c,d,e){var f=va(a,b,c,d,e),g=ta(f.year,0,f.dayOfYear);return this.year(g.getUTCFullYear()),this.month(g.getUTCMonth()),this.date(g.getUTCDate()),this}function Kc(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)}function Lc(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function Mc(a,b){b[ke]=u(1e3*("0."+a))}function Nc(){return this._isUTC?"UTC":""}function Oc(){return this._isUTC?"Coordinated Universal Time":""}function Pc(a){return tb(1e3*a)}function Qc(){return tb.apply(null,arguments).parseZone()}function Rc(a){return a}function Sc(a,b,c,d){var e=bb(),f=l().set(d,b);return e[c](f,a)}function Tc(a,b,c){if(g(a)&&(b=a,a=void 0),a=a||"",null!=b)return Sc(a,b,c,"month");var d,e=[];for(d=0;d<12;d++)e[d]=Sc(a,d,c,"month");return e}function Uc(a,b,c,d){"boolean"==typeof a?(g(b)&&(c=b,b=void 0),b=b||""):(b=a,c=b,a=!1,g(b)&&(c=b,b=void 0),b=b||"");var e=bb(),f=a?e._week.dow:0;if(null!=c)return Sc(b,(c+f)%7,d,"day");var h,i=[];for(h=0;h<7;h++)i[h]=Sc(b,(h+f)%7,d,"day");return i}function Vc(a,b){return Tc(a,b,"months")}function Wc(a,b){return Tc(a,b,"monthsShort")}function Xc(a,b,c){return Uc(a,b,c,"weekdays")}function Yc(a,b,c){return Uc(a,b,c,"weekdaysShort")}function Zc(a,b,c){return Uc(a,b,c,"weekdaysMin")}function $c(){var a=this._data;return this._milliseconds=df(this._milliseconds),this._days=df(this._days),this._months=df(this._months),a.milliseconds=df(a.milliseconds),a.seconds=df(a.seconds),a.minutes=df(a.minutes),a.hours=df(a.hours),a.months=df(a.months),a.years=df(a.years),this}function _c(a,b,c,d){var e=Sb(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()}function ad(a,b){return _c(this,a,b,1)}function bd(a,b){return _c(this,a,b,-1)}function cd(a){return a<0?Math.floor(a):Math.ceil(a)}function dd(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data;return f>=0&&g>=0&&h>=0||f<=0&&g<=0&&h<=0||(f+=864e5*cd(fd(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=t(f/1e3),i.seconds=a%60,b=t(a/60),i.minutes=b%60,c=t(b/60),i.hours=c%24,g+=t(c/24),e=t(ed(g)),h+=e,g-=cd(fd(e)),d=t(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function ed(a){return 4800*a/146097}function fd(a){return 146097*a/4800}function gd(a){if(!this.isValid())return NaN;var b,c,d=this._milliseconds;if(a=K(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+ed(b),"month"===a?c:c/12;switch(b=this._days+Math.round(fd(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3;case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}}function hd(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*u(this._months/12):NaN}function id(a){return function(){return this.as(a)}}function jd(a){return a=K(a),this.isValid()?this[a+"s"]():NaN}function kd(a){return function(){return this.isValid()?this._data[a]:NaN}}function ld(){return t(this.days()/7)}function md(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function nd(a,b,c){var d=Sb(a).abs(),e=uf(d.as("s")),f=uf(d.as("m")),g=uf(d.as("h")),h=uf(d.as("d")),i=uf(d.as("M")),j=uf(d.as("y")),k=e<=vf.ss&&["s",e]||e0,k[4]=c,md.apply(null,k)}function od(a){return void 0===a?uf:"function"==typeof a&&(uf=a,!0)}function pd(a,b){return void 0!==vf[a]&&(void 0===b?vf[a]:(vf[a]=b,"s"===a&&(vf.ss=b-1),!0))}function qd(a){if(!this.isValid())return this.localeData().invalidDate();var b=this.localeData(),c=nd(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function rd(){if(!this.isValid())return this.localeData().invalidDate();var a,b,c,d=wf(this._milliseconds)/1e3,e=wf(this._days),f=wf(this._months);a=t(d/60),b=t(a/60),d%=60,a%=60,c=t(f/12),f%=12;var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(m<0?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var sd,td;td=Array.prototype.some?Array.prototype.some:function(a){for(var b=Object(this),c=b.length>>>0,d=0;d68?1900:2e3)};var te=O("FullYear",!0);U("w",["ww",2],"wo","week"),U("W",["WW",2],"Wo","isoWeek"),J("week","w"),J("isoWeek","W"),M("week",5),M("isoWeek",5),Z("w",Sd),Z("ww",Sd,Od),Z("W",Sd),Z("WW",Sd,Od),ca(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=u(a)});var ue={dow:0,doy:6};U("d",0,"do","day"),U("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),U("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),U("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),U("e",0,0,"weekday"),U("E",0,0,"isoWeekday"),J("day","d"),J("weekday","e"),J("isoWeekday","E"),M("day",11),M("weekday",11),M("isoWeekday",11),Z("d",Sd),Z("e",Sd),Z("E",Sd),Z("dd",function(a,b){return b.weekdaysMinRegex(a)}),Z("ddd",function(a,b){return b.weekdaysShortRegex(a)}),Z("dddd",function(a,b){return b.weekdaysRegex(a)}),ca(["dd","ddd","dddd"],function(a,b,c,d){var e=c._locale.weekdaysParse(a,d,c._strict);null!=e?b.d=e:n(c).invalidWeekday=a}),ca(["d","e","E"],function(a,b,c,d){b[d]=u(a)});var ve="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),we="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),xe="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),ye=be,ze=be,Ae=be;U("H",["HH",2],0,"hour"),U("h",["hh",2],0,Ra),U("k",["kk",2],0,Sa),U("hmm",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)}),U("hmmss",0,0,function(){return""+Ra.apply(this)+T(this.minutes(),2)+T(this.seconds(),2)}),U("Hmm",0,0,function(){return""+this.hours()+T(this.minutes(),2)}),U("Hmmss",0,0,function(){return""+this.hours()+T(this.minutes(),2)+T(this.seconds(),2)}),Ta("a",!0),Ta("A",!1),J("hour","h"),M("hour",13),Z("a",Ua),Z("A",Ua),Z("H",Sd),Z("h",Sd),Z("k",Sd),Z("HH",Sd,Od),Z("hh",Sd,Od),Z("kk",Sd,Od),Z("hmm",Td),Z("hmmss",Ud),Z("Hmm",Td),Z("Hmmss",Ud),ba(["H","HH"],he),ba(["k","kk"],function(a,b,c){var d=u(a);b[he]=24===d?0:d}),ba(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),ba(["h","hh"],function(a,b,c){b[he]=u(a),n(c).bigHour=!0}),ba("hmm",function(a,b,c){var d=a.length-2;b[he]=u(a.substr(0,d)),b[ie]=u(a.substr(d)),n(c).bigHour=!0}),ba("hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[he]=u(a.substr(0,d)),b[ie]=u(a.substr(d,2)),b[je]=u(a.substr(e)),n(c).bigHour=!0}),ba("Hmm",function(a,b,c){var d=a.length-2;b[he]=u(a.substr(0,d)),b[ie]=u(a.substr(d))}),ba("Hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[he]=u(a.substr(0,d)),b[ie]=u(a.substr(d,2)),b[je]=u(a.substr(e))});var Be,Ce=/[ap]\.?m?\.?/i,De=O("Hours",!0),Ee={calendar:Bd,longDateFormat:Cd,invalidDate:Dd,ordinal:Ed,dayOfMonthOrdinalParse:Fd,relativeTime:Gd,months:pe,monthsShort:qe,week:ue,weekdays:ve,weekdaysMin:xe,weekdaysShort:we,meridiemParse:Ce},Fe={},Ge={},He=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ie=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Je=/Z|[+-]\d\d(?::?\d\d)?/,Ke=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Le=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Me=/^\/?Date\((\-?\d+)/i,Ne=/^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/;a.createFromInputFallback=x("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}),a.ISO_8601=function(){},a.RFC_2822=function(){};var Oe=x("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=tb.apply(null,arguments);return this.isValid()&&a.isValid()?athis?this:a:p()}),Qe=function(){return Date.now?Date.now():+new Date},Re=["year","quarter","month","week","day","hour","minute","second","millisecond"];Db("Z",":"),Db("ZZ",""),Z("Z",_d),Z("ZZ",_d),ba(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Eb(_d,a)});var Se=/([\+\-]|\d\d)/gi;a.updateOffset=function(){};var Te=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Ue=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Sb.fn=Ab.prototype,Sb.invalid=zb;var Ve=Wb(1,"add"),We=Wb(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Xe=x("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)});U(0,["gg",2],0,function(){return this.weekYear()%100}),U(0,["GG",2],0,function(){return this.isoWeekYear()%100}),Dc("gggg","weekYear"),Dc("ggggg","weekYear"),Dc("GGGG","isoWeekYear"),Dc("GGGGG","isoWeekYear"),J("weekYear","gg"),J("isoWeekYear","GG"),M("weekYear",1),M("isoWeekYear",1),Z("G",Zd),Z("g",Zd),Z("GG",Sd,Od),Z("gg",Sd,Od),Z("GGGG",Wd,Qd),Z("gggg",Wd,Qd),Z("GGGGG",Xd,Rd),Z("ggggg",Xd,Rd),ca(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=u(a)}),ca(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}),U("Q",0,"Qo","quarter"),J("quarter","Q"),M("quarter",7),Z("Q",Nd),ba("Q",function(a,b){b[fe]=3*(u(a)-1)}),U("D",["DD",2],"Do","date"),J("date","D"),M("date",9),Z("D",Sd),Z("DD",Sd,Od),Z("Do",function(a,b){return a?b._dayOfMonthOrdinalParse||b._ordinalParse:b._dayOfMonthOrdinalParseLenient}),ba(["D","DD"],ge),ba("Do",function(a,b){b[ge]=u(a.match(Sd)[0],10)});var Ye=O("Date",!0);U("DDD",["DDDD",3],"DDDo","dayOfYear"),J("dayOfYear","DDD"),M("dayOfYear",4),Z("DDD",Vd),Z("DDDD",Pd),ba(["DDD","DDDD"],function(a,b,c){c._dayOfYear=u(a)}),U("m",["mm",2],0,"minute"),J("minute","m"),M("minute",14),Z("m",Sd),Z("mm",Sd,Od),ba(["m","mm"],ie);var Ze=O("Minutes",!1);U("s",["ss",2],0,"second"),J("second","s"),M("second",15),Z("s",Sd),Z("ss",Sd,Od),ba(["s","ss"],je);var $e=O("Seconds",!1);U("S",0,0,function(){return~~(this.millisecond()/100)}),U(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),U(0,["SSS",3],0,"millisecond"),U(0,["SSSS",4],0,function(){return 10*this.millisecond()}),U(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),U(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),U(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),U(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),U(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),J("millisecond","ms"),M("millisecond",16),Z("S",Vd,Nd),Z("SS",Vd,Od),Z("SSS",Vd,Pd);var _e;for(_e="SSSS";_e.length<=9;_e+="S")Z(_e,Yd);for(_e="S";_e.length<=9;_e+="S")ba(_e,Mc);var af=O("Milliseconds",!1);U("z",0,0,"zoneAbbr"),U("zz",0,0,"zoneName");var bf=r.prototype;bf.add=Ve,bf.calendar=Zb,bf.clone=$b,bf.diff=fc,bf.endOf=sc,bf.format=kc,bf.from=lc,bf.fromNow=mc,bf.to=nc,bf.toNow=oc,bf.get=R,bf.invalidAt=Bc,bf.isAfter=_b,bf.isBefore=ac,bf.isBetween=bc,bf.isSame=cc,bf.isSameOrAfter=dc,bf.isSameOrBefore=ec,bf.isValid=zc,bf.lang=Xe,bf.locale=pc,bf.localeData=qc,bf.max=Pe,bf.min=Oe,bf.parsingFlags=Ac,bf.set=S,bf.startOf=rc,bf.subtract=We,bf.toArray=wc,bf.toObject=xc,bf.toDate=vc,bf.toISOString=ic,bf.inspect=jc,bf.toJSON=yc,bf.toString=hc,bf.unix=uc,bf.valueOf=tc,bf.creationData=Cc,bf.year=te,bf.isLeapYear=ra,bf.weekYear=Ec,bf.isoWeekYear=Fc,bf.quarter=bf.quarters=Kc,bf.month=ka,bf.daysInMonth=la,bf.week=bf.weeks=Ba,bf.isoWeek=bf.isoWeeks=Ca,bf.weeksInYear=Hc,bf.isoWeeksInYear=Gc,bf.date=Ye,bf.day=bf.days=Ka,bf.weekday=La,bf.isoWeekday=Ma,bf.dayOfYear=Lc,bf.hour=bf.hours=De,bf.minute=bf.minutes=Ze,bf.second=bf.seconds=$e,bf.millisecond=bf.milliseconds=af,bf.utcOffset=Hb,bf.utc=Jb,bf.local=Kb,bf.parseZone=Lb,bf.hasAlignedHourOffset=Mb,bf.isDST=Nb,bf.isLocal=Pb,bf.isUtcOffset=Qb,bf.isUtc=Rb,bf.isUTC=Rb,bf.zoneAbbr=Nc,bf.zoneName=Oc,bf.dates=x("dates accessor is deprecated. Use date instead.",Ye),bf.months=x("months accessor is deprecated. Use month instead",ka),bf.years=x("years accessor is deprecated. Use year instead",te),bf.zone=x("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",Ib),bf.isDSTShifted=x("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",Ob);var cf=C.prototype;cf.calendar=D,cf.longDateFormat=E,cf.invalidDate=F,cf.ordinal=G,cf.preparse=Rc,cf.postformat=Rc,cf.relativeTime=H,cf.pastFuture=I,cf.set=A,cf.months=fa,cf.monthsShort=ga,cf.monthsParse=ia,cf.monthsRegex=na,cf.monthsShortRegex=ma,cf.week=ya,cf.firstDayOfYear=Aa,cf.firstDayOfWeek=za,cf.weekdays=Fa,cf.weekdaysMin=Ha,cf.weekdaysShort=Ga,cf.weekdaysParse=Ja,cf.weekdaysRegex=Na,cf.weekdaysShortRegex=Oa,cf.weekdaysMinRegex=Pa,cf.isPM=Va,cf.meridiem=Wa,$a("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===u(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),a.lang=x("moment.lang is deprecated. Use moment.locale instead.",$a),a.langData=x("moment.langData is deprecated. Use moment.localeData instead.",bb);var df=Math.abs,ef=id("ms"),ff=id("s"),gf=id("m"),hf=id("h"),jf=id("d"),kf=id("w"),lf=id("M"),mf=id("y"),nf=kd("milliseconds"),of=kd("seconds"),pf=kd("minutes"),qf=kd("hours"),rf=kd("days"),sf=kd("months"),tf=kd("years"),uf=Math.round,vf={ss:44,s:45,m:45,h:22,d:26,M:11},wf=Math.abs,xf=Ab.prototype;return xf.isValid=yb,xf.abs=$c,xf.add=ad,xf.subtract=bd,xf.as=gd,xf.asMilliseconds=ef,xf.asSeconds=ff,xf.asMinutes=gf,xf.asHours=hf,xf.asDays=jf,xf.asWeeks=kf,xf.asMonths=lf,xf.asYears=mf,xf.valueOf=hd,xf._bubble=dd,xf.get=jd,xf.milliseconds=nf,xf.seconds=of,xf.minutes=pf,xf.hours=qf,xf.days=rf,xf.weeks=ld,xf.months=sf,xf.years=tf,xf.humanize=qd,xf.toISOString=rd,xf.toString=rd,xf.toJSON=rd,xf.locale=pc,xf.localeData=qc,xf.toIsoString=x("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",rd),xf.lang=Xe,U("X",0,0,"unix"),U("x",0,0,"valueOf"),Z("x",Zd),Z("X",ae),ba("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),ba("x",function(a,b,c){c._d=new Date(u(a))}),a.version="2.18.1",b(tb),a.fn=bf,a.min=vb,a.max=wb,a.now=Qe,a.utc=l,a.unix=Pc,a.months=Vc,a.isDate=h,a.locale=$a,a.invalid=p,a.duration=Sb,a.isMoment=s,a.weekdays=Xc,a.parseZone=Qc,a.localeData=bb,a.isDuration=Bb,a.monthsShort=Wc,a.weekdaysMin=Zc,a.defineLocale=_a,a.updateLocale=ab,a.locales=cb,a.weekdaysShort=Yc,a.normalizeUnits=K,a.relativeTimeRounding=od,a.relativeTimeThreshold=pd,a.calendarFormat=Yb,a.prototype=bf,a}); From ed73f4eb856aba6222dfeee997c19df91b64ab3a Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Mon, 31 Jul 2017 11:37:05 +0200 Subject: [PATCH 101/112] SIPs site getting close... --- _layouts/sips.html | 2 +- _sips/minutes-list.md | 16 ++++++ .../minutes}/2016-07-15-sip-minutes.md | 4 +- .../2016-08-16-sip-10th-august-minutes.md | 4 +- .../2016-09-20-sip-20th-september-minutes.md | 6 ++- .../minutes}/2016-10-25-sip-minutes.md | 6 ++- .../minutes}/2016-11-29-sip-minutes.md | 6 ++- .../minutes}/2017-02-14-sip-minutes.md | 4 +- {sips => _sips}/sip-submission.md | 2 +- {sips => _sips}/sip-template.md | 2 +- {sips => _sips}/sip-tutorial.md | 2 +- sips/.jekyll-metadata | Bin 6730 -> 0 bytes sips/README.md | 15 ------ sips/index.md | 49 ------------------ sips/minutes-list.md | 16 ------ sips/sip-list.md | 23 -------- sips/slip-submission.md | 26 ---------- 17 files changed, 41 insertions(+), 142 deletions(-) create mode 100644 _sips/minutes-list.md rename {sips/minutes/_posts => _sips/minutes}/2016-07-15-sip-minutes.md (99%) rename {sips/minutes/_posts => _sips/minutes}/2016-08-16-sip-10th-august-minutes.md (99%) rename {sips/minutes/_posts => _sips/minutes}/2016-09-20-sip-20th-september-minutes.md (99%) rename {sips/minutes/_posts => _sips/minutes}/2016-10-25-sip-minutes.md (99%) rename {sips/minutes/_posts => _sips/minutes}/2016-11-29-sip-minutes.md (99%) rename {sips/minutes/_posts => _sips/minutes}/2017-02-14-sip-minutes.md (99%) rename {sips => _sips}/sip-submission.md (99%) rename {sips => _sips}/sip-template.md (99%) rename {sips => _sips}/sip-tutorial.md (98%) delete mode 100644 sips/.jekyll-metadata delete mode 100644 sips/README.md delete mode 100644 sips/index.md delete mode 100644 sips/minutes-list.md delete mode 100644 sips/sip-list.md delete mode 100644 sips/slip-submission.md diff --git a/_layouts/sips.html b/_layouts/sips.html index c5d8452a28..f54fb89149 100644 --- a/_layouts/sips.html +++ b/_layouts/sips.html @@ -30,7 +30,7 @@
    Next meeting:
    diff --git a/_sips/minutes-list.md b/_sips/minutes-list.md new file mode 100644 index 0000000000..ce481c5c56 --- /dev/null +++ b/_sips/minutes-list.md @@ -0,0 +1,16 @@ +--- +layout: sips +title: SIP Meeting Minutes +--- + +This page hosts all the meeting notes (minutes) of the SIP meetings, starting +from July 2016. + +### Minutes ### +
      + {% for pg in site.sips %} + {% if pg.partof == 'minutes' %} +
    • {{ pg.title }}
    • + {% endif %} + {% endfor %} +
    diff --git a/sips/minutes/_posts/2016-07-15-sip-minutes.md b/_sips/minutes/2016-07-15-sip-minutes.md similarity index 99% rename from sips/minutes/_posts/2016-07-15-sip-minutes.md rename to _sips/minutes/2016-07-15-sip-minutes.md index c925ca73b4..88e49e338d 100644 --- a/sips/minutes/_posts/2016-07-15-sip-minutes.md +++ b/_sips/minutes/2016-07-15-sip-minutes.md @@ -1,6 +1,8 @@ --- -layout: inner-page-no-masthead +layout: sips title: SIP Meeting Minutes - 13th July 2016 + +partof: minutes --- # Minutes diff --git a/sips/minutes/_posts/2016-08-16-sip-10th-august-minutes.md b/_sips/minutes/2016-08-16-sip-10th-august-minutes.md similarity index 99% rename from sips/minutes/_posts/2016-08-16-sip-10th-august-minutes.md rename to _sips/minutes/2016-08-16-sip-10th-august-minutes.md index c9a9b1a8c4..35f3b61ac6 100644 --- a/sips/minutes/_posts/2016-08-16-sip-10th-august-minutes.md +++ b/_sips/minutes/2016-08-16-sip-10th-august-minutes.md @@ -1,6 +1,8 @@ --- -layout: inner-page-no-masthead +layout: sips title: SIP Meeting Minutes - 10th August 2016 + +partof: minutes --- # Minutes diff --git a/sips/minutes/_posts/2016-09-20-sip-20th-september-minutes.md b/_sips/minutes/2016-09-20-sip-20th-september-minutes.md similarity index 99% rename from sips/minutes/_posts/2016-09-20-sip-20th-september-minutes.md rename to _sips/minutes/2016-09-20-sip-20th-september-minutes.md index c957c40f77..c6ce822242 100644 --- a/sips/minutes/_posts/2016-09-20-sip-20th-september-minutes.md +++ b/_sips/minutes/2016-09-20-sip-20th-september-minutes.md @@ -1,6 +1,8 @@ --- -layout: inner-page-no-masthead +layout: sips title: SIP Meeting Minutes - 20th September 2016 + +partof: minutes --- # Minutes @@ -144,7 +146,7 @@ Sébastien introduces his updates on the proposal. He's tried hard to remove the performance regression in his implementation, but he hasn't found a way. He describes that the implementation is still 6% slower because of performance hits in the hashCode and equals implementation of a case class. To work around this -issue, two things are required: +issue, two things are required: * Changing the underlying implementation of byte and short ints. * Making the super class of unsigned integer extend *java.lang.Number* diff --git a/sips/minutes/_posts/2016-10-25-sip-minutes.md b/_sips/minutes/2016-10-25-sip-minutes.md similarity index 99% rename from sips/minutes/_posts/2016-10-25-sip-minutes.md rename to _sips/minutes/2016-10-25-sip-minutes.md index 5af070e2f8..7a1100cbb0 100644 --- a/sips/minutes/_posts/2016-10-25-sip-minutes.md +++ b/_sips/minutes/2016-10-25-sip-minutes.md @@ -1,6 +1,8 @@ --- -layout: inner-page-no-masthead +layout: sips title: SIP Meeting Minutes - 25th October 2016 + +partof: minutes --- # Minutes @@ -103,7 +105,7 @@ Committee finally decides to not consider abstentions as no's. Adriaan, however, suggests that we should penalize people that abstain too often. Martin proposes a new system that helps survive the abstention issue mentioned -before. For a proposal to pass, at least 50% of all the Committee +before. For a proposal to pass, at least 50% of all the Committee and two-thirds of all the people voting (not counting abstentions) must accept it. The quorum rule is kept. After some discussion, the Committee unanimously votes in favor of Martin's new system, along with not considering abstentions as diff --git a/sips/minutes/_posts/2016-11-29-sip-minutes.md b/_sips/minutes/2016-11-29-sip-minutes.md similarity index 99% rename from sips/minutes/_posts/2016-11-29-sip-minutes.md rename to _sips/minutes/2016-11-29-sip-minutes.md index 5ae124dff6..ef5111ab2c 100644 --- a/sips/minutes/_posts/2016-11-29-sip-minutes.md +++ b/_sips/minutes/2016-11-29-sip-minutes.md @@ -1,6 +1,8 @@ -SIP-25:Static--- -layout: inner-page-no-masthead +--- +layout: sips title: SIP Meeting Minutes - 29th November 2016 + +partof: minutes --- # Minutes diff --git a/sips/minutes/_posts/2017-02-14-sip-minutes.md b/_sips/minutes/2017-02-14-sip-minutes.md similarity index 99% rename from sips/minutes/_posts/2017-02-14-sip-minutes.md rename to _sips/minutes/2017-02-14-sip-minutes.md index 0058ebda87..1e67d9a433 100644 --- a/sips/minutes/_posts/2017-02-14-sip-minutes.md +++ b/_sips/minutes/2017-02-14-sip-minutes.md @@ -1,6 +1,8 @@ --- -layout: sip-landing +layout: sips title: SIP Meeting Minutes - 14th February 2017 + +partof: minutes --- # Minutes diff --git a/sips/sip-submission.md b/_sips/sip-submission.md similarity index 99% rename from sips/sip-submission.md rename to _sips/sip-submission.md index ada4dbf939..63774e880c 100644 --- a/sips/sip-submission.md +++ b/_sips/sip-submission.md @@ -1,5 +1,5 @@ --- -layout: inner-page-no-masthead +layout: sips title: SIP Specification and Submission --- diff --git a/sips/sip-template.md b/_sips/sip-template.md similarity index 99% rename from sips/sip-template.md rename to _sips/sip-template.md index 2e2a650982..0781e987b3 100644 --- a/sips/sip-template.md +++ b/_sips/sip-template.md @@ -1,5 +1,5 @@ --- -layout: inner-page-no-masthead +layout: sips discourse: true title: SIP-NN - SIP Title --- diff --git a/sips/sip-tutorial.md b/_sips/sip-tutorial.md similarity index 98% rename from sips/sip-tutorial.md rename to _sips/sip-tutorial.md index 7919ffcdd2..9467cc3e59 100644 --- a/sips/sip-tutorial.md +++ b/_sips/sip-tutorial.md @@ -1,5 +1,5 @@ --- -layout: inner-page-no-masthead +layout: sips title: Writing a new SIP --- diff --git a/sips/.jekyll-metadata b/sips/.jekyll-metadata deleted file mode 100644 index 7851d3cff2f33bc46b1c98a34f50632f46f082e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6730 zcmd6rJ!~9B7>45jceWEpF8C9&k!7NQWX899{yF;&kVCMQiy%sD6Cfd&x!t+*!0zm_ zGi!6U5GSQUf&wIpARz*XiiU;)q*N&|1p--Abcg~3MXD4j`DT23aqf0uH@iqQ)>;0U zXWsAq-uY(k?%$rOj#~v23taFv3x;Eo0ynUQ_r6Xxs95)FlZIm#ICVMvg6&#F5VNr6 zI$ZEVQPbv;HiL?DNc|m>zdbqphXoA3q-~4>8P~HRjv&;Lm;S?@YpIt~|v3noSMQ8P6}dM&Q0b?+%7A393`*L{p?#zahb3SwEugMTz`Y9aP0AX`*&yBq zC7bEtXeu~eLz<3?MZ~a8OoW*!rNh1@5cV3rO_<0)z*n+i+!L^f=77z^%?!ZO^;TQF z67-KC9>W$#PQ6`b%$Ota|H6P0a|UU%fdO4&P;AjHMVH=yYpIJ1?W-e~8n1%`Ic+d3 ze2-*MT{AUV2^BTO$R^~bWf4P`6B!ofJPW*(U7;f=tX@PEGM^e$M1nm&+WPqD6>m`H z-QiB&9c#(F8$KfQuK&cM3g#^04zf;iO(8BknRKc*T*nG`8?d>P!@goWc`(T07Z;+~ zaQu>b0G#n;8$Lel`oui|z%Qtpig*MzlTUN4#9uq0+SyV}^ zqq?u`k6h2OX_(PKvnyK2k;m4=9Gpuu;r6~ve;GU-M5ui zvmLF@y&46Tu8hDmg?slr`4M$O4 zjIUfsb1F!18h3opAjooztd7qF>bZoOAkMA%87c{f=aepE)`O|d1l_ipvK=7VV8Dz% zNI|{LcP@XT)Vr^v-ij8b-g12PLMdlu?sC@&J34{ov9)WTN|wHFgUopFOccu5)F@C# z%$qFK`7ihX^qErSo{sa6MS5x_4a&5fdWt19U7VEd7}kcDuAluREzF7iN35bu#p!ox5=xYz?AI23y`6zr3~ z_|kU@>WiRgxu^dVozyT0CpDMTv(diKg(5xtz!N_xq~GoeEd5cWP6@Ip>=*E^7a@$Kt`oJ@#cc z!4wk`;tC0L@|hgu!RB)OYlL|!F_@83syU>?6$qr#HL;Gc-|)G}PPPPv?CnEP4W4P{ zK`ihV0nZEEQ%LB+i5sVOm9wq~!I1N#dN9GGN~p2s<;_Q`Rg`LR;)=b8l?b0DpO#DU z%So9@-s~3S-bY7UwUP1Xl_1wTp|mIk2_4U$ioalclgOf6bg$ca3X9!Q<@gIr!LwO( zA=#qp?T&M}-*)e}3dZ~8{LQOMXD!e - {% for post in site.categories.minutes %} - {% if post.layout == 'sip-landing' %} -
  • {{ post.title }} ( {{ post.date | date: "%b %Y" }} )
  • - {% endif %} - {% endfor %} - diff --git a/sips/sip-list.md b/sips/sip-list.md deleted file mode 100644 index edbfeff33c..0000000000 --- a/sips/sip-list.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -layout: inner-page-no-masthead -title: List of SIPs ---- - -### Completed SIPs ### -
      - {% for post in site.categories.completed %} - {% if post.layout == 'sip' %} -
    • {{ post.title }} ( {{ post.date | date: "%b %Y" }} )
    • - {% endif %} - {% endfor %} -
    - -### Rejected SIPs ### -
      - {% for post in site.categories.rejected %} - {% if post.layout == 'sip' %} -
    • {{ post.title }} ( {{ post.date | date: "%b %Y" }} )
    • - {% endif %} - {% endfor %} -
    - diff --git a/sips/slip-submission.md b/sips/slip-submission.md deleted file mode 100644 index 95fb52976c..0000000000 --- a/sips/slip-submission.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -layout: inner-page-no-masthead -title: SLIP Submission Process ---- - -## How do I get started? ## - -Before proposing a new SLIP or new library functionality, it's a good idea -to get a feel for the core libraries first, and there's no better way than -by helping fix some of the -[core library issues in community tickets](http://scala-lang.org/contribute/#community_tickets). - -## How do I submit? ## - -Follow the instructions on the -[Scala SLIP github project page](https://www.github.com/scala/slip) - -Some more helpful resources: - -* [SLIP Gitter Channel](https://gitter.im/scala/slip) to chat or ask for help. -* [Markdown Syntax](http://daringfireball.net/projects/markdown/syntax) - for help with the markdown syntax used for SLIP documentation. -* [scala-internals](https://groups.google.com/scala-internals) for on-the-record - discussions about SLIP proposals. -* [Scala Process on Google+](https://plus.google.com/scalaslip) for details on the - next SLIP/SIP meeting and other information. \ No newline at end of file From 11e336a36193252a4d4ee3d5290d18c4f0ac2603 Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Mon, 31 Jul 2017 11:40:31 +0200 Subject: [PATCH 102/112] SIP site finished, I think --- _sips/README.md | 3 +++ _sips/all.md | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 _sips/README.md diff --git a/_sips/README.md b/_sips/README.md new file mode 100644 index 0000000000..72244f693c --- /dev/null +++ b/_sips/README.md @@ -0,0 +1,3 @@ +# Scala Improvement Process Documents + +This directory contains the source of the SIP website, including all pending/finished/rejected SIPs. diff --git a/_sips/all.md b/_sips/all.md index 318fd589bb..d04931bd6d 100644 --- a/_sips/all.md +++ b/_sips/all.md @@ -1,6 +1,8 @@ --- layout: sips title: List of All SIPs + +redirect_from: "/sips/sip-list.html" --- From 565703c9e2ff8ce53f6ec64f755ffb9017287f5f Mon Sep 17 00:00:00 2001 From: Heather Miller Date: Mon, 31 Jul 2017 12:55:02 +0200 Subject: [PATCH 103/112] SIP minutes list: most recent meetings first --- _sips/minutes-list.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_sips/minutes-list.md b/_sips/minutes-list.md index ce481c5c56..ff651a8a46 100644 --- a/_sips/minutes-list.md +++ b/_sips/minutes-list.md @@ -8,7 +8,8 @@ from July 2016. ### Minutes ###