From 4daeccbd8a4bf6ad4e28a9befef40b1c68291d7e Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 15 Nov 2022 17:27:27 +0100 Subject: [PATCH 1/7] make outdated notice more intrusive --- _getting-started/index.md | 1 - _includes/outdated-notice.html | 7 + _ja/scala3/contribute-to-docs.md | 2 +- _layouts/blog-detail.html | 6 - _layouts/blog-list.html | 9 - _layouts/cheatsheet.html | 3 + _layouts/contribute.html | 17 - _layouts/documentation.html | 1 + _layouts/events.html | 13 - _layouts/frontpage.html | 386 -------- _layouts/glossary.html | 3 + _layouts/inner-page-community.html | 9 - _layouts/inner-page-parent-dropdown.html | 6 - _layouts/multipage-overview.html | 3 + _layouts/overview.html | 27 - _layouts/singlepage-overview.html | 3 + _layouts/sip.html | 3 + _layouts/sips.html | 3 + _layouts/style-guide.html | 3 + _layouts/tour.html | 3 + _layouts/training.html | 9 - ...scala-for-csharp-programmers.disabled.html | 859 +++++++++--------- _sass/base/helper.scss | 6 +- _sass/components/wip-notice.scss | 3 +- _sips/sips/fewer-braces.md | 1 - _sips/sips/trailing-commas.md | 2 +- _tour/basics.md | 4 +- guides.md | 4 +- index.md | 1 + scala3/contribute-to-docs.md | 2 +- 30 files changed, 476 insertions(+), 923 deletions(-) create mode 100644 _includes/outdated-notice.html delete mode 100644 _layouts/blog-detail.html delete mode 100644 _layouts/blog-list.html delete mode 100644 _layouts/contribute.html delete mode 100644 _layouts/events.html delete mode 100644 _layouts/frontpage.html delete mode 100644 _layouts/inner-page-community.html delete mode 100644 _layouts/overview.html delete mode 100644 _layouts/training.html diff --git a/_getting-started/index.md b/_getting-started/index.md index 27396701c4..7058dff991 100644 --- a/_getting-started/index.md +++ b/_getting-started/index.md @@ -4,7 +4,6 @@ title: Getting Started partof: getting-started languages: [fr, ja, ru, uk] includeTOC: true - newcomer_resources: - title: Are You Coming From Java? description: What you should know to get to speed with Scala after your initial setup. diff --git a/_includes/outdated-notice.html b/_includes/outdated-notice.html new file mode 100644 index 0000000000..6248ee33ef --- /dev/null +++ b/_includes/outdated-notice.html @@ -0,0 +1,7 @@ +
+
+

Outdated Notice

+

   + This page has a new version.

+
+
diff --git a/_ja/scala3/contribute-to-docs.md b/_ja/scala3/contribute-to-docs.md index 74023f6f7d..e95cc31b5f 100644 --- a/_ja/scala3/contribute-to-docs.md +++ b/_ja/scala3/contribute-to-docs.md @@ -1,5 +1,5 @@ --- -layout: inner-page-documentation +layout: singlepage-overview overview-name: "Scala 3 Documentation" title: Contributing to the Docs language: ja diff --git a/_layouts/blog-detail.html b/_layouts/blog-detail.html deleted file mode 100644 index 8e6419da3b..0000000000 --- a/_layouts/blog-detail.html +++ /dev/null @@ -1,6 +0,0 @@ ---- -layout: inner-page-parent ---- - - -{% include inner-page-blog-detail-main-content.html %} \ No newline at end of file diff --git a/_layouts/blog-list.html b/_layouts/blog-list.html deleted file mode 100644 index d06c97051a..0000000000 --- a/_layouts/blog-list.html +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: inner-page-parent-dropdown ---- - -{% if page.category %} - {% include blog-list.html category=page.category %} -{% else %} - {% include blog-list.html %} -{% endif %} diff --git a/_layouts/cheatsheet.html b/_layouts/cheatsheet.html index 9ee9695c01..319eca87fe 100644 --- a/_layouts/cheatsheet.html +++ b/_layouts/cheatsheet.html @@ -7,6 +7,9 @@
+ {% if page.new-version %} + {% include outdated-notice.html new-version=page.new-version %} + {% endif %} {{content}}
diff --git a/_layouts/contribute.html b/_layouts/contribute.html deleted file mode 100644 index 8cd5f886e5..0000000000 --- a/_layouts/contribute.html +++ /dev/null @@ -1,17 +0,0 @@ ---- -layout: inner-page-no-masthead -includeTOC: true ---- - -
-
-
-
-
- {{ content }} -
-
-
-
-
-
diff --git a/_layouts/documentation.html b/_layouts/documentation.html index f578b644d1..2b9f374060 100644 --- a/_layouts/documentation.html +++ b/_layouts/documentation.html @@ -1,5 +1,6 @@ --- --- + {% include headertop.html %} {% include headerbottom.html %} {% if page.new-version %}This page has a new version.{% endif %} diff --git a/_layouts/events.html b/_layouts/events.html deleted file mode 100644 index 4959010ad3..0000000000 --- a/_layouts/events.html +++ /dev/null @@ -1,13 +0,0 @@ ---- -layout: inner-page-parent ---- - -{% include events-training-list-top.html collection=paginator.events %} -
- {{content}} -
- -
- - {% include paginator.html urlPath="events" %} -
\ No newline at end of file diff --git a/_layouts/frontpage.html b/_layouts/frontpage.html deleted file mode 100644 index 29fa5825fe..0000000000 --- a/_layouts/frontpage.html +++ /dev/null @@ -1,386 +0,0 @@ ---- ---- - -{% include headertop.html %} -{% include headerbottom.html %} - - - - - - -
- -
-
-

{{site.data.common.texts.scalaBackendsTitle}}

-
    - {% for backend in page.scalaBackends %} -
  • - - {{backend.description}} - -
  • - {% unless forloop.last %}
  • {% endunless %} - {% endfor %} -
-

- {{site.data.common.texts.scalaBackendsMore}} -

-
-
- - -
-
-
-

IDEs for Scala

-
- -
-
- - -
-
-
-

Scala in a Nutshell

-
-

click the boxes below to see Scala in action!

-
-
-
-
-
- {% for scalaItem in site.scala_items %} - {% assign loopIndexMod = forloop.index | minus: 1 | modulo: 3 %} - - {% if loopIndexMod == 0 %} - {% assign codeSnippets = '' | split: ',' %} -
- {% endif %} - {% assign codeSnippets = codeSnippets | push: scalaItem.content %} -
-

{{scalaItem.shortTitle}}

-

{{scalaItem.shortDescription}}

-
- {% if loopIndexMod == 2 or forloop.last %} -
-
- {% for snippet in codeSnippets %} -
{{snippet}}
- {% endfor %} -
- {% endif %} - {% endfor %} -
- -
-
- - - - - -
-
- {% include online-courses.html %} - {% include upcoming-training.html %} -
-
- - -
-
-
-

Upcoming Events

-
-
- {% assign upcomingEvents = '' | split: ',' %} - {% capture now %}{{site.time | date: '%s' | plus: 0}}{% endcapture %} - {% for event in site.events %} - {% capture date %}{{event.date|date: '%s'|plus: 86400}}{% endcapture %} - {% if now <= date %} - {% assign upcomingEvents = upcomingEvents | push: event %} - {% endif %} - {% endfor %} - {% for event in upcomingEvents limit: 6 %} - - {% endfor %} -
- -
-
- -
-
-
-

{{page.ecosystemTitle}}

-

{{page.ecosystemDescription}}

-
-
-
- - -
-
-

The Scala Library Index

-
- - -
-
-
-
-
-
-
-
-
- -
-
-
-
-

What’s New

-
- {% assign firstPost = site.posts | first %} -
-

{{firstPost.post-type|upcase}}

-

{{firstPost.title}}

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

{{firstPost.content}}

-
-
- -
- -
- -
-
-
-

Talk to us!

-
-
-

Mailing lists / forums

- {% for forum in site.data.chats-forums.discourseForums %} - - {{forum.title}} -

{{forum.title}}

-

{{forum.subtitle}}

-
- {% endfor %} -
-
-

Real-time chat

- {% assign modLimit = site.data.chats-forums.discordServers.size | modulo: 2 %} - {% capture channelLimit %} - {% if modLimit != 0 %} - {{site.data.chats-forums.discordServers.size | minus: 1}} - {% else %} - {{site.data.chats-forums.discordServers.size}} - {% endif %} - {% endcapture %} - {% for server in site.data.chats-forums.discordServers limit: channelLimit %} - {% if forloop.first %} -
    - {% endif %} -
  • - - - {{server.name}} - -
  • - - {% assign halfLength = forloop.length | divided_by: 2 | floor %} - {% if forloop.index == halfLength %} -
-
    - {% endif %} - - {% if forloop.last %} -
- {% endif %} - {% endfor %} -
- {% if page.communities %} -
-

Communities

-
    - {% for community in page.communities %} -
  • - {{community.name}} -
  • - {% endfor %} -
-
- {% endif %} - -
-
- - -{% include twitter-feed.html %} - - -
- - -
-
-
-

{{site.data.common.texts.scalaMaintainersTitle}}

-
-
    - {% for maintainer in site.data.scala-supporters.maintainers %} -
  • {{maintainer.name}}
  • - {% endfor %} -
-

{{site.data.common.texts.scalaSupportersTitle}}

-
- {% for supporter in site.data.scala-supporters.supporters %} - {{supporter.name}} - {% endfor %} -
-
-
-
- -{% include footer.html %} diff --git a/_layouts/glossary.html b/_layouts/glossary.html index 59a14bf8f9..004e2b59f3 100644 --- a/_layouts/glossary.html +++ b/_layouts/glossary.html @@ -8,6 +8,9 @@
+ {% if page.new-version %} + {% include outdated-notice.html new-version=page.new-version %} + {% endif %} {{content}}
{% if page.languages %} diff --git a/_layouts/inner-page-community.html b/_layouts/inner-page-community.html deleted file mode 100644 index f5cdb3afdb..0000000000 --- a/_layouts/inner-page-community.html +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: inner-page-parent ---- - - -{% include masthead-community.html %} - - -{% include inner-page-main-content.html %} \ No newline at end of file diff --git a/_layouts/inner-page-parent-dropdown.html b/_layouts/inner-page-parent-dropdown.html index bd5b2c4e24..50ce6bec67 100644 --- a/_layouts/inner-page-parent-dropdown.html +++ b/_layouts/inner-page-parent-dropdown.html @@ -12,12 +12,6 @@
- {% if page.new-version %} -
-

Outdated Notice

-

  This page has a new version.

-
- {% endif %}
{% if page.overview-name %}
{{ page.overview-name }}
diff --git a/_layouts/multipage-overview.html b/_layouts/multipage-overview.html index ca65e57101..ba111000c6 100644 --- a/_layouts/multipage-overview.html +++ b/_layouts/multipage-overview.html @@ -9,6 +9,9 @@
+ {% if page.new-version %} + {% include outdated-notice.html new-version=page.new-version %} + {% endif %} {% if page.versionSpecific %} {% if page.scala3 %} {% assign versionSpecificLang = 'scala3' %} diff --git a/_layouts/overview.html b/_layouts/overview.html deleted file mode 100644 index 2d255c52bf..0000000000 --- a/_layouts/overview.html +++ /dev/null @@ -1,27 +0,0 @@ ---- -layout: inner-page-parent -includeTOC: true -includeCollectionTOC: true ---- - -{% for pg in site.posts %} - {% if pg.overview == page.overview and pg.languages %} - {% assign languages = pg.languages %} - {% endif %} -{% endfor %} - -{% for pg in site.pages %} - {% if pg.overview == page.overview 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 %} - - -{% include inner-page-main-content.html %} diff --git a/_layouts/singlepage-overview.html b/_layouts/singlepage-overview.html index b39eafc05e..179ac2fdd5 100644 --- a/_layouts/singlepage-overview.html +++ b/_layouts/singlepage-overview.html @@ -8,6 +8,9 @@
+ {% if page.new-version %} + {% include outdated-notice.html new-version=page.new-version %} + {% endif %} {% if page.versionSpecific %} {% if page.scala3 %} {% assign versionSpecificLang = 'scala3' %} diff --git a/_layouts/sip.html b/_layouts/sip.html index 688adf4d46..ebce160573 100644 --- a/_layouts/sip.html +++ b/_layouts/sip.html @@ -7,6 +7,9 @@
+ {% if page.new-version %} + {% include outdated-notice.html new-version=page.new-version %} + {% endif %} {{content}}
diff --git a/_layouts/sips.html b/_layouts/sips.html index 328980a408..7cc4bde97d 100644 --- a/_layouts/sips.html +++ b/_layouts/sips.html @@ -6,6 +6,9 @@
+ {% if page.new-version %} + {% include outdated-notice.html new-version=page.new-version %} + {% endif %} {{content}}
diff --git a/_layouts/style-guide.html b/_layouts/style-guide.html index b5e7741522..b45c9ef0b5 100644 --- a/_layouts/style-guide.html +++ b/_layouts/style-guide.html @@ -9,6 +9,9 @@
+ {% if page.new-version %} + {% include outdated-notice.html new-version=page.new-version %} + {% endif %} {{content}}
diff --git a/_layouts/tour.html b/_layouts/tour.html index ce65fd5098..69792acecb 100644 --- a/_layouts/tour.html +++ b/_layouts/tour.html @@ -9,6 +9,9 @@
+ {% if page.new-version %} + {% include outdated-notice.html new-version=page.new-version %} + {% endif %} {{content}}
diff --git a/_layouts/training.html b/_layouts/training.html deleted file mode 100644 index 3274549b11..0000000000 --- a/_layouts/training.html +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: inner-page-parent ---- - -{% include events-training-list-top.html collection=paginator.trainings %} -
- - {% include paginator.html urlPath="training" %} -
\ No newline at end of file diff --git a/_overviews/tutorials/scala-for-csharp-programmers.disabled.html b/_overviews/tutorials/scala-for-csharp-programmers.disabled.html index 7641a4ec65..6dfc4969ec 100644 --- a/_overviews/tutorials/scala-for-csharp-programmers.disabled.html +++ b/_overviews/tutorials/scala-for-csharp-programmers.disabled.html @@ -1,5 +1,5 @@ --- -layout: overview +layout: singlepage-overview title: A Scala Tutorial for C# Programmers --- @@ -7,10 +7,10 @@ ## 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 +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 @@ -109,9 +109,9 @@ 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, +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 @@ -139,7 +139,7 @@ ### 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 +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`. @@ -154,8 +154,8 @@ 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, +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 @@ -220,7 +220,7 @@ ### Tuples -Everybody hates out parameters. We all know that code like this just +Everybody hates out parameters. We all know that code like this just isn't nice: Widget widget; @@ -229,21 +229,21 @@ 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*, +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 +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, +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) @@ -253,27 +253,27 @@ 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 +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 +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, +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) = { @@ -281,23 +281,23 @@ 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 +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 +(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? @@ -306,60 +306,60 @@ 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 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 +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. +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 +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, +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 +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 @@ -373,24 +373,24 @@ } } -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. +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 +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 +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 +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 +Here's a Scala trait that represents objects that can be compared and ordered: trait Ord { @@ -400,14 +400,14 @@ 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 +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 @@ -415,13 +415,13 @@ #### 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 +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 +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: @@ -437,12 +437,12 @@ } } -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. +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: @@ -452,68 +452,68 @@ override def count: Int = _size } -The `count` member of the `Enumerable` trait works like a virtual method: +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. +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 +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 +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. +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 +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. +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 +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 +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 +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 +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: @@ -521,99 +521,99 @@ 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). +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 +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 +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. +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 +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 +> 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 +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 +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, +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 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 +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 +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. +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) @@ -630,30 +630,30 @@ 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 +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 +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 +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. +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#: @@ -675,24 +675,24 @@ 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 +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 +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 +Let's see the custom while implementation again, this time with Scala *pass by name* parameters: def myWhile(condition: => Boolean)(body: => Unit): Unit = @@ -709,26 +709,26 @@ i += 1 } -Unlike the C# attempt, this prints out the numbers from 0 to 9 and then +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 +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'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 +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: @@ -739,9 +739,9 @@ 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, +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 } @@ -750,46 +750,46 @@ 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 +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 +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 +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 +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. +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; } @@ -804,9 +804,9 @@ 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 +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 = @@ -815,18 +815,18 @@ 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` +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 +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 +In fact, armed with this understanding of the Scala "syntax," we can easily map it back to C#: void While(Func condition, Action body) @@ -837,7 +837,7 @@ While(condition, body); } } - + int i = 0; While(() => i < 10, () => { @@ -845,67 +845,67 @@ ++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 +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 +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 +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 +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))` +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 +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 +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) { + 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` +(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 +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 +Hardly readable. To make it look nice, Scala requires us to define an *implicit conversion* from `Double` to `RicherDouble`: object Implicits { @@ -920,31 +920,31 @@ val twoToTheSeven = 2.0.toThe(7.0) -and all will be well. The `Double` type has apparently been successfully +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 +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 +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 +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 { @@ -953,69 +953,69 @@ } } -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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 @@ -1028,7 +1028,7 @@ 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) @@ -1036,21 +1036,21 @@ } } -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 +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 +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 +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 { @@ -1060,16 +1060,16 @@ } } -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 +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")) @@ -1079,10 +1079,10 @@ 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, +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 { @@ -1093,14 +1093,14 @@ 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 +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 +However, let’s not worry about the implementation, and see how this is used at the calling end: val result = concatenate(List( @@ -1108,109 +1108,109 @@ 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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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` +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 +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? +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 +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) { + class DoubleExtensions(d : Double) { def **(exp: Double): Double = Pow(d, exp) } - + val raised = 2.0 ** 9.0 // it still just works Much nicer. @@ -1220,16 +1220,15 @@ 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 +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/_sass/base/helper.scss b/_sass/base/helper.scss index 5a53ee033a..8b6f9c46d2 100755 --- a/_sass/base/helper.scss +++ b/_sass/base/helper.scss @@ -20,10 +20,14 @@ background: #fff; -webkit-box-shadow: 0 0 18px 20px #fff; -moz-box-shadow: 0 0 18px 20px #fff; - box-shadow: 0 7px 15px 5px #fff; + box-shadow: 0 0 18px 20px #fff; z-index: 5; } +.inline-sticky-top.inline-sticky-top-higher { + z-index: 7; +} + .wrap-inline { // add vertical padding @include outer-container; diff --git a/_sass/components/wip-notice.scss b/_sass/components/wip-notice.scss index b4a011e9fa..3c84c75ac0 100644 --- a/_sass/components/wip-notice.scss +++ b/_sass/components/wip-notice.scss @@ -2,7 +2,8 @@ position: relative; top: 1em; padding: 1em; - background: rgba(255,255,255,0.7); + border-radius: 5px; + background: $gray-light; a { color: $brand-primary; diff --git a/_sips/sips/fewer-braces.md b/_sips/sips/fewer-braces.md index 4d1f9c1091..d3aac00417 100644 --- a/_sips/sips/fewer-braces.md +++ b/_sips/sips/fewer-braces.md @@ -293,4 +293,3 @@ This is a tradeoff between conciseness and consistency. In the interest of minim - Latest discussion on contributors (there were several before when we discussed indentation in general): https://contributors.scala-lang.org/t/make-fewerbraces-available-outside-snapshot-releases/5024/166 ## FAQ - diff --git a/_sips/sips/trailing-commas.md b/_sips/sips/trailing-commas.md index 9bacf33264..7eef93dd03 100644 --- a/_sips/sips/trailing-commas.md +++ b/_sips/sips/trailing-commas.md @@ -1,5 +1,5 @@ --- -layout: inner-page-no-masthead +layout: sip title: SIP-27 - Trailing Commas stage: completed status: shipped diff --git a/_tour/basics.md b/_tour/basics.md index 8118507637..5230a0e482 100644 --- a/_tour/basics.md +++ b/_tour/basics.md @@ -83,7 +83,7 @@ val x: Int = 1 + 1 {% endtab %} {% endtabs %} -Notice how the type declaration `Int` comes after the identifier `x`. You also need a `:`. +Notice how the type declaration `Int` comes after the identifier `x`. You also need a `:`. ### Variables @@ -236,7 +236,7 @@ println(getSquareString(2.5)) // 6.25 def getSquareString(input: Double): String = val square = input * input square.toString - + println(getSquareString(2.5)) // 6.25 ``` {% endtab %} diff --git a/guides.md b/guides.md index 98700b64b3..69ad998fc9 100644 --- a/guides.md +++ b/guides.md @@ -1,6 +1,6 @@ --- layout: inner-page-no-masthead title: Guides and Overviews -languages: [ja, zh-cn, es, ru] - +permalink: /guides.html +redirect_to: /overviews/index.html --- diff --git a/index.md b/index.md index bf1dda539c..40f616e01e 100644 --- a/index.md +++ b/index.md @@ -1,6 +1,7 @@ --- layout: inner-page-documentation languages: [ja, zh-cn, ru, uk] + title: Learn Scala namespace: root discourse: true diff --git a/scala3/contribute-to-docs.md b/scala3/contribute-to-docs.md index 2a9795cada..ca9201c5b4 100644 --- a/scala3/contribute-to-docs.md +++ b/scala3/contribute-to-docs.md @@ -2,7 +2,7 @@ layout: singlepage-overview overview-name: "Scala 3 Documentation" title: Contributing to the Docs -languages: ["ru"] +languages: ["ja", "ru"] --- ## Overview There are several ongoing efforts to produce high quality documentation for From 9b8c791cc1af6957ad03eeff6f7970049a040984 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 16 Nov 2022 11:22:27 +0100 Subject: [PATCH 2/7] remove inner-page-no-masthead --- _includes/inner-page-main-content.html | 16 ------- _layouts/inner-page-no-masthead.html | 6 --- _overviews/index.md | 1 + _overviews/scaladoc/basics.md | 6 --- _overviews/scaladoc/for-library-authors.md | 14 +++--- _overviews/scaladoc/interface.md | 2 + _overviews/scaladoc/usage.md | 6 --- _overviews/tutorials/partest-guide.md | 2 +- _zh-cn/overviews/index.md | 2 + _zh-cn/overviews/thanks.md | 2 +- _zh-cn/scala3/guides.md | 47 ------------------- _zh-cn/thanks.md | 2 +- api/all.md | 2 + guides.md | 6 --- ...les-in-scala-impressions-and-statistics.md | 2 +- reference.md | 6 --- 16 files changed, 19 insertions(+), 103 deletions(-) delete mode 100644 _includes/inner-page-main-content.html delete mode 100644 _layouts/inner-page-no-masthead.html delete mode 100644 _overviews/scaladoc/basics.md delete mode 100644 _overviews/scaladoc/usage.md delete mode 100644 _zh-cn/scala3/guides.md delete mode 100644 guides.md delete mode 100644 reference.md diff --git a/_includes/inner-page-main-content.html b/_includes/inner-page-main-content.html deleted file mode 100644 index ba5f15e916..0000000000 --- a/_includes/inner-page-main-content.html +++ /dev/null @@ -1,16 +0,0 @@ -
-
-
-
-
- {{content}} -
- - {% include contributors-list.html %} -
-
- - - {% include sidebar-toc.html %} -
-
diff --git a/_layouts/inner-page-no-masthead.html b/_layouts/inner-page-no-masthead.html deleted file mode 100644 index 28c8fe1663..0000000000 --- a/_layouts/inner-page-no-masthead.html +++ /dev/null @@ -1,6 +0,0 @@ ---- -layout: inner-page-parent ---- - - -{% include inner-page-main-content.html %} \ No newline at end of file diff --git a/_overviews/index.md b/_overviews/index.md index 572296a85d..53ad207975 100644 --- a/_overviews/index.md +++ b/_overviews/index.md @@ -6,6 +6,7 @@ languages: [ja, zh-cn, ru, uk] permalink: /overviews/:title.html redirect_from: - /scala3/guides.html + - /guides.html --- diff --git a/_overviews/scaladoc/basics.md b/_overviews/scaladoc/basics.md deleted file mode 100644 index 4dc91dad79..0000000000 --- a/_overviews/scaladoc/basics.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -layout: inner-page-no-masthead -sitemap: false -permalink: /overviews/scaladoc/basics.html -redirect_to: /overviews/scaladoc/for-library-authors.html ---- diff --git a/_overviews/scaladoc/for-library-authors.md b/_overviews/scaladoc/for-library-authors.md index 5d0d29a146..332cbffd17 100644 --- a/_overviews/scaladoc/for-library-authors.md +++ b/_overviews/scaladoc/for-library-authors.md @@ -7,6 +7,8 @@ overview-name: Scaladoc num: 3 permalink: /overviews/scaladoc/:title.html +redirect_from: + - /overviews/scaladoc/basics.html --- Scaladoc is a documentation system that lives in the comments of Scala source code @@ -106,13 +108,13 @@ scalacOptions in (Compile, doc) ++= Seq( ``` Each section should have a single-word identifier that is used in all of -these tags, shown as `` below. By default, that identifier is -shown as the title of that documentation section, but you can use +these tags, shown as `` below. By default, that identifier is +shown as the title of that documentation section, but you can use `@groupname` to provide a longer title. Typically, you should put `@groupprio` (and optionally `@groupname` and `@groupdesc`) in the Scaladoc for the package/trait/class/object itself, -describing what all the groups are, and their order. Then put `@group` +describing what all the groups are, and their order. Then put `@group` in the Scaladoc for each member, saying which group it is in. Members that do not have a `@group` tag will be listed as "Ungrouped" in @@ -207,12 +209,12 @@ Some types of markup available: with no interruptions from other block styles. Unordered lists can be bulleted using `-`; numbered lists can be denoted using `1.`, `i.`, `I.`, or `a.` for the various numbering styles. In both cases, you must have extra space in front, and - more space makes a sub-level. - + more space makes a sub-level. + The markup for list blocks looks like: /** Here is an unordered list: - * + * * - First item * - Second item * - Sub-item to the second diff --git a/_overviews/scaladoc/interface.md b/_overviews/scaladoc/interface.md index 3f2908182e..e985de9c3c 100644 --- a/_overviews/scaladoc/interface.md +++ b/_overviews/scaladoc/interface.md @@ -7,6 +7,8 @@ overview-name: Scaladoc num: 2 permalink: /overviews/scaladoc/:title.html +redirect_from: + - /overviews/scaladoc/usage.html --- Many Scala developers, including those with a great deal of experience, are diff --git a/_overviews/scaladoc/usage.md b/_overviews/scaladoc/usage.md deleted file mode 100644 index e1efd956f5..0000000000 --- a/_overviews/scaladoc/usage.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -layout: inner-page-no-masthead -sitemap: false -permalink: /overviews/scaladoc/usage.html -redirect_to: /overviews/scaladoc/interface.html ---- diff --git a/_overviews/tutorials/partest-guide.md b/_overviews/tutorials/partest-guide.md index 36fa8c74c7..ddb3dc14ef 100644 --- a/_overviews/tutorials/partest-guide.md +++ b/_overviews/tutorials/partest-guide.md @@ -1,5 +1,5 @@ --- -layout: inner-page-no-masthead +layout: redirect sitemap: false permalink: /tutorials/partest-guide.html redirect_to: https://github.com/scala/scala-partest diff --git a/_zh-cn/overviews/index.md b/_zh-cn/overviews/index.md index 38b8dc4ba2..a051fef550 100644 --- a/_zh-cn/overviews/index.md +++ b/_zh-cn/overviews/index.md @@ -3,6 +3,8 @@ layout: overviews language: zh-cn partof: overviews title: 目录 +redirect_from: + - /zh-cn/scala3/guides.html --- diff --git a/_zh-cn/overviews/thanks.md b/_zh-cn/overviews/thanks.md index e1429b74f0..6c2935710d 100644 --- a/_zh-cn/overviews/thanks.md +++ b/_zh-cn/overviews/thanks.md @@ -1,5 +1,5 @@ --- -layout: inner-page-no-masthead +layout: singlepage-overview language: zh-cn title: 致谢名单 --- diff --git a/_zh-cn/scala3/guides.md b/_zh-cn/scala3/guides.md deleted file mode 100644 index 5a656b5a4d..0000000000 --- a/_zh-cn/scala3/guides.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: inner-page-parent -title: Guides on Scala 3 -language: zh-cn -scala3: true - -guides: - - title: "从 Scala 2 到 Scala 3 的迁移" - icon: suitcase - url: "/scala3/guides/migration/compatibility-intro.html" - description: "迁移至 Scala 3 的兼容性须知" - - title: 宏 - by: Nicolas Stucki - icon: magic - url: "/scala3/guides/macros" - description: "覆盖 Scala 3 中涉及到编写宏的所有特性的详细导引" - label-text: 特性 - - title: TASTy 概览 - by: Alvin Alexander - icon: birthday-cake - url: "/scala3/guides/tasty-overview.html" - description: "针对 Scala 语言最终用户的 TASTy 格式概览" - - title: "贡献指南" - by: Jamie Thompson, Anatolii Kmetiuk - icon: cogs - url: "/scala3/guides/contribution/contribution-intro.html" - description: "Scala 3 编译器指南及如何贡献代码" - - title: Scaladoc - by: Krzysztof Romanowski, Aleksander Boruch-Gruszecki, Andrzej Ratajczak, Kacper Korban, Filip Zybała - icon: book - url: "/scala3/guides/scaladoc" - description: "Scala 的 API 文档生成工具" ---- - -
-
-
-
-

概览与导引

-

- Scala 3 语言及其特性的详细导引 -

-{% include scala3-guides-card-group.html %} -
-
-
-
diff --git a/_zh-cn/thanks.md b/_zh-cn/thanks.md index e1429b74f0..6c2935710d 100644 --- a/_zh-cn/thanks.md +++ b/_zh-cn/thanks.md @@ -1,5 +1,5 @@ --- -layout: inner-page-no-masthead +layout: singlepage-overview language: zh-cn title: 致谢名单 --- diff --git a/api/all.md b/api/all.md index df590222cb..b9ced56bf8 100644 --- a/api/all.md +++ b/api/all.md @@ -2,6 +2,8 @@ layout: singlepage-overview title: Scala API Docs includeTOC: true +redirect_from: + - /reference.html --- ## Latest releases diff --git a/guides.md b/guides.md deleted file mode 100644 index 69ad998fc9..0000000000 --- a/guides.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -layout: inner-page-no-masthead -title: Guides and Overviews -permalink: /guides.html -redirect_to: /overviews/index.html ---- diff --git a/news/_posts/2012-12-12-functional-programming-principles-in-scala-impressions-and-statistics.md b/news/_posts/2012-12-12-functional-programming-principles-in-scala-impressions-and-statistics.md index c52618024d..c6c40c21ca 100644 --- a/news/_posts/2012-12-12-functional-programming-principles-in-scala-impressions-and-statistics.md +++ b/news/_posts/2012-12-12-functional-programming-principles-in-scala-impressions-and-statistics.md @@ -1,5 +1,5 @@ --- -layout: inner-page-no-masthead +layout: singlepage-overview title: "Functional Programming Principles in Scala: Impressions and Statistics" --- ###### By Heather Miller and Martin Odersky diff --git a/reference.md b/reference.md deleted file mode 100644 index e7a837825e..0000000000 --- a/reference.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -layout: inner-page-no-masthead -sitemap: false -permalink: /reference.html -redirect_to: /api/all.html ---- From 65bcac707947bace7afbc8f7bf8f9f531b609c2a Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 16 Nov 2022 11:56:04 +0100 Subject: [PATCH 3/7] rename layouts --- _ja/index.md | 2 +- _ja/scala3/guides.md | 4 +--- _ja/scala3/index.md | 4 ++-- _layouts/{inner-page.html => basic-index.html} | 2 +- _layouts/cheatsheet.html | 2 +- _layouts/glossary.html | 2 +- _layouts/{documentation.html => landing-page-deprecated.html} | 2 +- _layouts/{inner-page-documentation.html => landing-page.html} | 2 +- _layouts/multipage-overview.html | 2 +- _layouts/overviews.html | 2 +- ...ner-page-parent-dropdown.html => root-content-layout.html} | 2 -- _layouts/{inner-page-parent.html => root-index-layout.html} | 3 +-- _layouts/singlepage-overview.html | 2 +- _layouts/sip.html | 2 +- _layouts/sips.html | 2 +- _layouts/style-guide.html | 2 +- _layouts/tour.html | 2 +- _overviews/contribute/add-guides.md | 2 +- _ru/index.md | 4 ++-- _uk/index.md | 2 +- _zh-cn/index.md | 2 +- books.md | 2 +- index.md | 2 +- tutorials.md | 2 +- 24 files changed, 25 insertions(+), 30 deletions(-) rename _layouts/{inner-page.html => basic-index.html} (87%) rename _layouts/{documentation.html => landing-page-deprecated.html} (98%) rename _layouts/{inner-page-documentation.html => landing-page.html} (72%) rename _layouts/{inner-page-parent-dropdown.html => root-content-layout.html} (99%) rename _layouts/{inner-page-parent.html => root-index-layout.html} (78%) diff --git a/_ja/index.md b/_ja/index.md index 9fa0201200..fccb29750e 100644 --- a/_ja/index.md +++ b/_ja/index.md @@ -1,5 +1,5 @@ --- -layout: documentation +layout: landing-page-deprecated title: ドキュメント language: ja partof: documentation diff --git a/_ja/scala3/guides.md b/_ja/scala3/guides.md index 9f5d0964fe..0bffcc5146 100644 --- a/_ja/scala3/guides.md +++ b/_ja/scala3/guides.md @@ -1,5 +1,5 @@ --- -layout: inner-page-parent +layout: root-index-layout title: Scala 3 のガイド language: ja scala3: true @@ -36,5 +36,3 @@ guides:
- - diff --git a/_ja/scala3/index.md b/_ja/scala3/index.md index 5940a8babb..31d285d6a5 100644 --- a/_ja/scala3/index.md +++ b/_ja/scala3/index.md @@ -1,5 +1,5 @@ --- -layout: inner-page-documentation +layout: landing-page title: Documentation for Scala 3 language: ja namespace: root @@ -41,4 +41,4 @@ sections: description: "Scala 3 の言語仕様" icon: "fa fa-book" link: https://docs.scala-lang.org/scala3/reference ---- \ No newline at end of file +--- diff --git a/_layouts/inner-page.html b/_layouts/basic-index.html similarity index 87% rename from _layouts/inner-page.html rename to _layouts/basic-index.html index e0cc813293..901f1a4453 100644 --- a/_layouts/inner-page.html +++ b/_layouts/basic-index.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent +layout: root-index-layout ---
diff --git a/_layouts/cheatsheet.html b/_layouts/cheatsheet.html index 319eca87fe..8809f9cc3e 100644 --- a/_layouts/cheatsheet.html +++ b/_layouts/cheatsheet.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent-dropdown +layout: root-content-layout ---
diff --git a/_layouts/glossary.html b/_layouts/glossary.html index 004e2b59f3..bd15a6274e 100644 --- a/_layouts/glossary.html +++ b/_layouts/glossary.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent-dropdown +layout: root-content-layout includeTOC: true --- diff --git a/_layouts/documentation.html b/_layouts/landing-page-deprecated.html similarity index 98% rename from _layouts/documentation.html rename to _layouts/landing-page-deprecated.html index 2b9f374060..929cb01207 100644 --- a/_layouts/documentation.html +++ b/_layouts/landing-page-deprecated.html @@ -1,6 +1,6 @@ --- --- - + {% include headertop.html %} {% include headerbottom.html %} {% if page.new-version %}This page has a new version.{% endif %} diff --git a/_layouts/inner-page-documentation.html b/_layouts/landing-page.html similarity index 72% rename from _layouts/inner-page-documentation.html rename to _layouts/landing-page.html index 5c420b46c3..5dfce6e343 100644 --- a/_layouts/inner-page-documentation.html +++ b/_layouts/landing-page.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent +layout: root-index-layout --- diff --git a/_layouts/multipage-overview.html b/_layouts/multipage-overview.html index ba111000c6..a9162e4d3d 100644 --- a/_layouts/multipage-overview.html +++ b/_layouts/multipage-overview.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent-dropdown +layout: root-content-layout includeTOC: true includeCollectionTOC: true --- diff --git a/_layouts/overviews.html b/_layouts/overviews.html index 73b4afb478..2568640141 100644 --- a/_layouts/overviews.html +++ b/_layouts/overviews.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent +layout: root-index-layout --- {% if page.language %} diff --git a/_layouts/inner-page-parent-dropdown.html b/_layouts/root-content-layout.html similarity index 99% rename from _layouts/inner-page-parent-dropdown.html rename to _layouts/root-content-layout.html index 50ce6bec67..1828666a99 100644 --- a/_layouts/inner-page-parent-dropdown.html +++ b/_layouts/root-content-layout.html @@ -1,8 +1,6 @@ {% include headertop.html %} {% include headerbottom.html %} - - {% include navbar-inner.html %} diff --git a/_layouts/inner-page-parent.html b/_layouts/root-index-layout.html similarity index 78% rename from _layouts/inner-page-parent.html rename to _layouts/root-index-layout.html index 2dc28bb539..97304053e1 100644 --- a/_layouts/inner-page-parent.html +++ b/_layouts/root-index-layout.html @@ -1,5 +1,4 @@ - {% include headertop.html %} {% include headerbottom.html %} - {% if page.new-version %}This page has a new version.{% endif %} +{% include headertop.html %} {% include headerbottom.html %} diff --git a/_layouts/singlepage-overview.html b/_layouts/singlepage-overview.html index 179ac2fdd5..867e4af164 100644 --- a/_layouts/singlepage-overview.html +++ b/_layouts/singlepage-overview.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent-dropdown +layout: root-content-layout includeTOC: true --- diff --git a/_layouts/sip.html b/_layouts/sip.html index ebce160573..9566acb26f 100644 --- a/_layouts/sip.html +++ b/_layouts/sip.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent-dropdown +layout: root-content-layout includeTOC: true --- diff --git a/_layouts/sips.html b/_layouts/sips.html index 7cc4bde97d..369fa1d22d 100644 --- a/_layouts/sips.html +++ b/_layouts/sips.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent-dropdown +layout: root-content-layout ---
diff --git a/_layouts/style-guide.html b/_layouts/style-guide.html index b45c9ef0b5..41e39d8709 100644 --- a/_layouts/style-guide.html +++ b/_layouts/style-guide.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent-dropdown +layout: root-content-layout includeCollectionTOC: true includeTOC: true --- diff --git a/_layouts/tour.html b/_layouts/tour.html index 69792acecb..b7fbba1989 100644 --- a/_layouts/tour.html +++ b/_layouts/tour.html @@ -1,5 +1,5 @@ --- -layout: inner-page-parent-dropdown +layout: root-content-layout includeTOC: true includeCollectionTOC: true --- diff --git a/_overviews/contribute/add-guides.md b/_overviews/contribute/add-guides.md index ea2de8d132..7598ad5f63 100644 --- a/_overviews/contribute/add-guides.md +++ b/_overviews/contribute/add-guides.md @@ -273,7 +273,7 @@ Once the tutorial is written, to aid user navigation their link must be added to the metadata of `/tutorials.md`. e.g. it could look like --- - layout: inner-page-parent + layout: root-index-layout title: Tutorials tutorials: diff --git a/_ru/index.md b/_ru/index.md index 9a3ba61e88..3a3cf3a918 100644 --- a/_ru/index.md +++ b/_ru/index.md @@ -1,5 +1,5 @@ --- -layout: inner-page-documentation +layout: landing-page title: Изучаем Scala language: ru partof: documentation @@ -93,4 +93,4 @@ sections: description: "От начала до конца: узнайте, как вы можете помочь открытой экосистеме Scala." icon: "fa fa-code-branch" link: /contribute/ ---- \ No newline at end of file +--- diff --git a/_uk/index.md b/_uk/index.md index 81ea875030..375daaba5d 100644 --- a/_uk/index.md +++ b/_uk/index.md @@ -1,5 +1,5 @@ --- -layout: inner-page-documentation +layout: landing-page title: Документація language: uk partof: documentation diff --git a/_zh-cn/index.md b/_zh-cn/index.md index 4b0b3d0e34..fedb5b7c5b 100644 --- a/_zh-cn/index.md +++ b/_zh-cn/index.md @@ -1,5 +1,5 @@ --- -layout: inner-page-documentation +layout: landing-page language: zh-cn title: 学习 Scala namespace: root diff --git a/books.md b/books.md index 0bdfff3358..8e38462300 100644 --- a/books.md +++ b/books.md @@ -1,6 +1,6 @@ --- title: Books -layout: inner-page +layout: basic-index redirect_from: - /documentation/books.html --- diff --git a/index.md b/index.md index 40f616e01e..4f97d4d051 100644 --- a/index.md +++ b/index.md @@ -1,5 +1,5 @@ --- -layout: inner-page-documentation +layout: landing-page languages: [ja, zh-cn, ru, uk] title: Learn Scala diff --git a/tutorials.md b/tutorials.md index 7cffa28385..17ade963ba 100644 --- a/tutorials.md +++ b/tutorials.md @@ -1,5 +1,5 @@ --- -layout: inner-page-parent +layout: root-index-layout title: Tutorials tutorials: From a65880e8b60e7e0a5755cc26872e8344b853ec1f Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 16 Nov 2022 11:59:57 +0100 Subject: [PATCH 4/7] add code tab notice to tour layout --- _layouts/tour.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/_layouts/tour.html b/_layouts/tour.html index b7fbba1989..33e67984de 100644 --- a/_layouts/tour.html +++ b/_layouts/tour.html @@ -12,6 +12,12 @@ {% if page.new-version %} {% include outdated-notice.html new-version=page.new-version %} {% endif %} + {{content}}
From de024e8ad0356e31eeb1e1d3c63d9455c23a71d4 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 17 Nov 2022 13:56:15 +0100 Subject: [PATCH 5/7] no outdated notice on sips --- _layouts/sip.html | 3 --- _layouts/sips.html | 3 --- 2 files changed, 6 deletions(-) diff --git a/_layouts/sip.html b/_layouts/sip.html index 9566acb26f..90174048d9 100644 --- a/_layouts/sip.html +++ b/_layouts/sip.html @@ -7,9 +7,6 @@
- {% if page.new-version %} - {% include outdated-notice.html new-version=page.new-version %} - {% endif %} {{content}}
diff --git a/_layouts/sips.html b/_layouts/sips.html index 369fa1d22d..b7da7039b5 100644 --- a/_layouts/sips.html +++ b/_layouts/sips.html @@ -6,9 +6,6 @@
- {% if page.new-version %} - {% include outdated-notice.html new-version=page.new-version %} - {% endif %} {{content}}
From b6f198bf8d33bb3369efd17327bed0d4106e2b06 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 17 Nov 2022 14:26:41 +0100 Subject: [PATCH 6/7] do not emit language options for orphan translations --- _includes/sidebar-toc-multipage-overview.html | 7 ++++--- _includes/sidebar-toc-singlepage-overview.html | 11 ++++++----- _zh-cn/overviews/thanks.md | 1 + _zh-cn/thanks.md | 1 + 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/_includes/sidebar-toc-multipage-overview.html b/_includes/sidebar-toc-multipage-overview.html index c792533ad9..9f1feb5636 100644 --- a/_includes/sidebar-toc-multipage-overview.html +++ b/_includes/sidebar-toc-multipage-overview.html @@ -50,9 +50,10 @@
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 %} + {% elsif page.language and page.orphanTranslation == false %} + + {% assign engPath = page.id | remove_first: "/" | remove_first: page.language | append: '.html' %} + {% assign engPg = site.overviews | where: 'partof', page.partof | first %} - {% elsif page.language %} - {% assign engPath = page.id | remove_first: "/" | remove_first: page.language | append: '.html' %} - {% assign engPg = site.overviews | where: 'partof', page.partof | first %} + {% elsif page.language and page.orphanTranslation == false %} + + {% assign engPath = page.id | remove_first: "/" | remove_first: page.language | append: '.html' %} + {% assign engPg = site.overviews | where: 'partof', page.partof | first %}