By Developers, For Developers

Historical errata for Programming Kotlin

PDF PgPaper PgTypeDescriptionFixed onComments
46ERROR

listktsfiles.kts doesn’t work with Kotlin 1.3.0.
Getting the following errors:
D:\\dev>kotlin -version
Kotlin version 1.3.0-release-212 (JRE 1.8.0_181-b13)

D:\\dev>kotlinc-jvm -script listktsfiles.kts
listktsfiles.kts:1:6: error: unresolved reference: io
java.io.File(“.”)
^
listktsfiles.kts:3:11: error: cannot infer a type for this parameter. Please specify it explicitly.
.filter { file -> file.extension == “kts” }
^
listktsfiles.kts:4:2: error: cannot choose among the following candidates without completing type inference:
HidesMembers public inline fun <T> Iterable<???>.forEach(action: (???) -> Unit): Unit defined in kotlin.collections HidesMembers public inline fun <K, V> Map<out ?,?>.forEach(action: (Map.Entry<?,?>) -> Unit): Unit defined in kotlin.collections
.forEach { println(it) }
^
listktsfiles.kts:4:20: error: unresolved reference: it
.forEach { println(it) }
^

Turning this into a kotlin file and trying to compile it (wrapping it in a fun main()) yields compiler errors about not knowing about java.io.File as well.

Running the same inside a scratch in IntelliJ works fine. So it’s something to do with the runtime environment that’s not mentioned in the book.

2019-02-21Hi Jeroen, \n \nThank you. \n \nI am unable to reproduce this error on two different machines. Not sure if some environment variable is not set properly is causing this. Could you please try with final 1.3 release for Kotlin? \n \nAlso, if you could email me at venkats@agiledeveloper.com may be we can try to figure out the setting this may be casing this. \n \nRegards, \n \nVenkat
3TYPO

…it offers choices and [let’s] us pick… -> lets

2018-11-03
33TYPO

…it may have endured for most of your programming [carrier]. -> career

Sorry, in the previous errata I reported the page was 18. And the same mistake (improper use of let’s) can be found at the page 109

2018-11-03
46-47ERROR

The status in the first canVote function can be a val already, so the argument in favor of expressions for that specific case is not very valid. We’d need another example, or state as an advantage that we can infer the type of status from the if/else expression without having to specify it.

2018-11-04Hi Pere, \n \nThe code example shown is written in Kotlin but like the way it is written in Java and C#. The intent of the example was to show how it is done in those languages where if is not an expression. \n \nGood point about stating about type inference. Will do that. Thank you. \n \nRegards, \n \nVenkat
47SUGGEST

“If a finally part is present it should not end
with an expression, Kotlin will raise a warning if it does, saying the result of
that expression within finally is ignored.”
Careful with the word “expression” here, as we’re in the exact section that exposes how almost everything is an expression in kotlin. The warning happens only when placing constant expressions in there (for example, I tried UUID.randomUUID() and there’s no warning). So I would rather omit that part or be more specific about when that warning triggers, otherwise the readers may become afraid of placing trivial code in the finally block.

2018-11-04Good catch, Pere. Thank you. \n \nVenkat
57TYPO

“Once again, when two arguments are passed to greet(), the default argument
is ignored, [its] not computed.” -> it’s

2018-11-06
AnySUGGEST

Hello,

Almost all programming languages use english prepositions (“to”) or pronouns (“what”) and any documentation explaining their usage also uses them as well. If their respective scope is not well defined, it becomes harder to understand a sentence, especially for readers from another mother language (french is my case).

A sentence like:
The only change was to the to parameter, from to: Array to to:Array.

Would be much easier to read as:
The only change was to the “to” parameter, from “to”: Array to to Fruit>.

I used double quotes here but italic or any character enhancement dedicated for this would be welcomed.

Otherwise, M. Subramanian remains an authority and great professor of programming languages in general.

Thanks

Jacques Ledoux

2018-12-15
94TYPO

In the sentence “Kotlin/Native can be used to compile your source code to different native targets like iOs, Linux, MacOS, Windows”, “iOs” should be “iOS”.

2018-12-15
90TYPO

“To implement system level or backend tasks using Kotlin, create a single Kotlin file and run it as script using the —script option. ”

“…—script…” should be “..-script…”

2018-12-15
57SUGGEST

The example iteration/iterate.kts uses arrayOf(1, 2, 3) and the accompanying text says “Since all the values given are of type Int, the array created in this example is
an array of Integer values.”.

I think a remark (or footnote) to point out the difference with intArrayOf (which will create a primitive int array instead of an object Integer array) would be helpful.

2018-12-15
68TYPO

Second paragraph, “Both Pair and Tuple are immutable”: Tuple should be Triple

2019-01-16Thank you Mark. \n \nIf you could let me know your full name (here or over email at venkats at agiledeveloper dot com) it will help me to include a thank you in the book's acknowledgement. \n \nThanks \n \nVenkat
143TYPO

Top of page:
Car -> Card

2019-01-30
137ERROR

The statement “Each call to remote will result in a different instance…” at the bottom of the page is incorrect. This is a field and will always return the same instance.

2019-01-30
46TYPO

Current sentence: “The spread operator is useful to explode or spread
values in a collection as discreate arguments.”

I think what is meant here is: “The spread operator is useful to explode or spread
values in a collection as discrete arguments.”

2019-02-21
188ERROR

“you may create instances of Set using setOf() or mutableSetOf(). ”

setOf() creates an instance of Set, but mutablesetOf() creates an instance of MutableSet

2019-02-21
58SUGGEST

It might be useful to also include the withIndex() option here, which allows you to iterate over the collection accessing both index and value at the same time e.g

for ((index, value) in array.withIndex()) {
println(“the element at $index is $value”)
}

2019-02-21
4SUGGEST

Minor textual suggestion: I think it would be nice to use one way of writing “boilerplate code” (see for example en.wikipedia.org/wiki/Boilerplate_code). These are the variations you can find by searching for “boiler” (in the pdf version):

- page 4: boiler plated code

- page 5: boiler plated code and boiler plate code

- page 6: boiler plated code

- page 58: boiler plated code

- page 103: boiler-plated code

- page 105: boiler-plate code

- (page 111: boilerplate code)
- page 128: boiler-plate code

2019-02-21
70TYPO

Small typo (I think an uppercase ‘L’ should be used instead of a lowercase ‘l’). I think on page 70 the function name for creating a mutable list should be mutableListOf (instead of mutablelistOf).

In the rest of the book, the mutableListOf function name is used (page numbers 71, 72, 75, 175, and 203).

2019-02-21
161TYPO

Some nice message
This is stupid
comment is of length: 14

“is”, in the output has been highlighted as a keyword by accident

2019-02-21
72ERROR

“Sets are unordered collection of elements. …

Since sets often guarantee uniqueness of elements, ….”

The actual idea and definition for sets is to always guarantee uniqueness of elements (that is, if its contained objects have proper equals and hashCode implementations). Hash sets are unordered, but you can keep insertion order with a linked hash set.

2019-03-03
xiERROR

Introduction

When you write object-oriented code, you will see that the compiler works for you instead of
you working for the compiler.

should be or something like that :
When you write functional-oriented code, you will see that the compiler works for you instead of
you working for the compiler.

2019-04-14Hi, \n \nThank you for the note. I did mean "object-oriented code" in that sentence. In that paragraph we are talking about different styles. I don't think there is an error. Please let me know if you still think otherwise. \n \nAlso, if you could let me know your full name, I will be delighted to thank you in the acknowledgement in the book. \n \nThank you \n \nVenkat
47TYPO

I think this sentence could be improved by using a lowercase ‘w’ (instead of an uppercase ‘W’) and by adding the word “any” (after “pass”):
“That worked nicely, we can pass any number of arguments and […]”

Original text:
“That worked nicely, We can pass number of arguments and […]”

2019-03-22
83TYPO

I think this sentence could be improved by removing the “’s” after the word “Here”:
“Here are the murmurs from the compiler:”

Original text:
“Here’s are the murmurs from the compiler:”

2019-03-22
98TYPO

A minor textual suggestion: would it be clearer to use “[…] we’re interested in here.” (instead of “[…] we’re interested here.”)? (It might also be nice to add a comma after “equal”?)

Suggested text:
“It assumes that the size of the two arrays are equal, since that detail is not relevant to what we’re interested in here.”

Original text:
“It assumes that the size of the two arrays are equal since that detail is not relevant to what we’re interested here.”

2019-03-22
81TYPO

“The class Nothing has no instances and it represents a value or result that may never exists.”

s/exists/exist/

2019-03-22
73SUGGEST

This is about the passage “The - operator is useful to create a new list without the specified element, like this:…”. It seems to remove only the first occurrence of an element in the list, so it may be better to rephrase the passage to something like this:

The - operator is useful to create a new list without the first occurrence of the specified element, like this:

collections/lists.kts
————————————-
val oneMoreBanana = fruits + “Banana”
println(oneMoreBanana) //[Apple, Banana, Grape, Banana]
val oneLessBanana = oneMoreBanana - “Banana”
println(oneLessBanana) //[Apple, Grape, Banana]

2019-03-22
86SUGGEST

It’d be good if the link to the code file <types/safecallreturnstring.kts> is provided. The links to code files are missing in some other places too.

2019-04-14Hi Raghavendra, \n \nThank you for the suggestion. I do not provide links to files when doing so will break the flow for the reader. The files internally I use may vary occasionally compared to what the reader is expected to be using if they were practicing along. \n \nThank you. \n \nVenkat
90SUGGEST

The book says, “All the references in this example were of type Any . If the reference types were of the specific class types—for example, if the greet reference was defined as type String—then the Kotlin compiler will barf at the equality check due to a type mismatch that can be detected from the source code. We kept the references types as Any to get around that check, in order to illustrate type checking.” However, here is what I get:

types/equals.kts
———————————
class Animal {
override operator fun equals(other: Any?) = other is Animal
}

val greet: String = “hello”
val odie: Any = Animal()
val toto: Any = Animal()

println(odie greet) //false println(odie toto) //true

$ kotlinc-jvm -Werror -script equals.kts
false
true

greet is defined as type String, and kotlinc is not protesting at the equaiity check odie == greet, which seems contrary to the quoted passage. Either I am missing the point, or some explanation may be helpful.

2019-03-22
91TYPO

“Suppose the Animal class has a age property and we need to use that property
when comparing objects.”

s/a age/an age

2019-03-22
124TYPO

Here’s a way to given an explicit name for the companion object of MachineOperator:

2019-04-14Hi, \n \nIf you mention your full name, if you like, I will be delighted to acknowledge in the book. \n \nThanks \n \nVenkat
158TYPO

Kotlin nicely takes care of resolving method collisions and also makes it easy to override select [ED] methods of the delegate interface in the delegating class.

I also added the typo of page 124.

2019-04-14Hi Adekunle, \n \nThank you for reporting. The word should be select and selected (select as in any methods you choose to). In any case, the copy editor will take care of correcting this. \n \nRegards, \n \nVenkat
159SUGGEST

Do not know if you want to be strict about optional semicolon :

assistant.fileTimeSheet();

2019-04-14
222TYPO

May be
“For example, does today + mean two days or two months from now?”

Should be
“For example, does today + 2 mean two days or two months from now?”
i.e adding a “2” character after the “+”

2019-04-14
185TYPO

A few years ago a conference organizer asked if I’d like to talk about Lamb*a*da expressions

2019-04-14Hi Adekunle, \n \nThank you for reporting. The word Lambada is correct in that sentence, I am referring to the dance and not lambda here. \n \nThanks \n \nVenkat
212TYPO

Let’s replace the sort[ed]By() call in the above example with sortByDescending() to see this in action:

There is only sortedBy() and no sortBy()

2019-04-14
250TYPO

That can be a lot of effort, and you have to balance that agains[*] the flexibility you get.

2019-04-14
315TYPO

The reason for this behavior is that coroutines started using launch() don’t propagate exceptions to their caller.

2019-04-15Hi Adekunle, \n \nMy apologies, I am not able to detect the error in that sentence. Could you please point out what's incorrect. \n \nThanks \n \nVenkat
318TYPO

Don’t know what is intended here :

“Both Job, which is returned by launch(), and Deferred, which is returned by async(), have a cancel() method and a cancelAndJoin() method.

2019-04-14
102TYPO

In the “Star Projection” section, just before the types/star.kts example - use “piece” instead of “pice”:

“Here’s a piece of code to use star projection:” (instead of “Here’s a pice of code to use star projection:”)

2019-04-14
119ERROR

Hello! In the access modifiers section of the book, it lists the four possible access modifiers: public, private, protected and internal. The next sentence following the listing states that “the first three have the same meaning as in Java”. This isn’t exactly true for protected. Kotlin’s protected is equivalent to private + visible in subclass, whereas Java’s protected is equivalent to package private + visible in subclass.

Thanks!

2019-04-14
5SUGGEST

The terminology infix annotation is not strictly correct. I would suggest replacing it with infix notation as used later in the chapter on this subject.

2019-05-15
34TYPO

Second paragraph, third sentence: “Let’s rework the about code”, should be “above code”

2019-05-15
51TYPO

“You may pass a variable number of arguments to functions without loosing compile-time safety”

Did you mean “losing compile-time safety”?

2019-05-15
ixERROR

About #84653 : Hi Venkat Thx for tour reply.
For me it’s confusing: yes, the compiler work for us, but when we write imperative code, we help the compiler with sequences of commands/statements to be performed… This is not the case for the declarative code !

2019-05-15Thank you for the response. I will leave it to the copy editor to make necessary changes where needed. \n \nVenkat
193ERROR

In the ePub version of the book the line numbers printed are out of sync with the lines printed in the non-local return example. The line 5 reference is therefore confusing. In the PDF the line number reference is correct.

A screenshot is available if reproduction is hard or my description is poorly described.
Note: this happens on my iPad. When using the default font size the line number sync issue occurs on line 10. I was reading at two font sizes smaller. Then the issue occurs at line 5.

2019-08-15
326TYPO

The last sentence just before Wrapping Up: “The coroutines that took its sweet time got canned” should be “got canceled”

2019-05-15
60ERROR

Hi,
in the introduction, in the subpart “Run as script”, you give an example of a script with a she-bang, for Unix-like systems.
The first line is like so :
#! /usr/bin/env kotlinc-jvm -script
Unfortunately, under LINUX this doesn’t seem to work, for a problem of number of arguments, discussed in stackexchange and stackoverflow articles.

I had to use the full path of the kotlinc-jvm executable, like so :

#! /opt/idea-IC-191.6707.61/plugins/Kotlin/kotlinc/bin/kotlinc-jvm -script

2019-05-15
234 SUGGEST

Note : I use the epub version, and it is not possible here to give the epub version page number !! Very bad…

In Part 1.6 “Generics: Variance and Constraints on Parametric Types”, p. 234 epub version, the paragraph about “Type Invariance” begins as follows :
“When a method receives an object of a class T, you may pass an object of any derived class of T. However, if a method receives a generic object of type T, for example, List, then you may not pass an object of a derived type of T.”
These sentences are very strange indeed. In the first sentence, I am not completely sure I can make sense out of of the phrase a “derived class of T”. And in the second sentence, well, it seems “a generic object of type T” is never List: the type of a List object is not T but rather its type is … List. The whole cited text seems to me rather confusing…

2019-06-11
68ERROR

The paragraph starting with “A word of caution…”, has got the collection and reference the wrong way around - the collection can be changed but the reference not.

There’s also a typo: “there’s no guaranteed…” should not have the ‘d’.

2019-08-12
326ERROR

Is it the correct approach to have a SupervisorJob declared as the context of the coroutine obtained with the launch builder when discussing default coroutine ex. handling behavior? The line of code I’m referring to is this one:
“val job = launch(Dispatchers.IO + SupervisorJob() + handler) { ”
It seems to me that the behavior of the supervisorJob is also ignored and it is there only to let the exception handler handle the exception.

2019-08-12
425TYPO

“On the receiving end, the parameters
for the lambda expressions are backed my functional interfaces in Java” -> backed by

2019-08-12
373TYPO

“getAirportStatus calls getAirportData asynchrously” { -> asynchronously

2019-08-12
24TYPO

in an fateful termination -> a fateful

2019-08-12
26TYPO

preemptive strike agains potential errors -> against potential

2019-08-12
67TYPO

The withIndex() is but one

2019-08-12
67TYPO

The withIndex() is but one

2019-08-12
67TYPO

The withIndex() is but one

2019-08-12
70SUGGEST

“When used on the left-hand side, the index operator []
will invoke the set() method of Array”. -> to me (non-native speaker) this sentence in not so clear. Does the author intend this syntax array[index] = value ?

2019-08-12
84TYPO

String? will becomeS String

2019-08-12
96TYPO

Kotlin permits contravariance
both at declarationS-site and use-site. -> declaration-site

2019-08-12
96TYPO

an object of a GENETIC object of derived type

2019-08-12
104TYPO

fun findFirst(books: List, ofClass: Class): T {

The first left angular bracket has a different color than right one (fun ). Pretty sure I’ve spotted another in a previous page. Cannot remember now.

105SUGGEST

Maybe this signature inline fun findFirst(books: List): T
could be more restrictive with a constraint on the type parameter given the example is using Book and its derived classes.

2019-08-12
105SUGGEST

It seems to that this sentence is no complete

“Keep your eyes open for functions that use reified type parameters as you
work along with Kotlin code—for example, functions like listOf() and mutableListOf<
T>() of the Kotlin standard library you saw in Chapter 5, Using Collections,
on page 65, and parse of the Klaxon library as you’ll see in Chapter
16, Asynchronous Programming, on page 311.”

2019-08-12
114SUGGEST

fun precision(): Int = throw RuntimeException(“Not implemented yet”)

Maybe is more appropriate in this case to use the TODO top level function declared in Standard.kt (the reader can also see another usage of the Nothing type).
The exception thrown is also self-documenting (NotImplementedError()).

The example would became:
fun precision(): Int = TODO()

2019-08-12
147TYPO

for any of the derived class —-> classes

2019-08-12
158SUGGEST

I’d also mention that one additional pro of the approach presented in “Delegating to a Parameter” is also testability. The Manager class can be easily provided with a convenient implementation of Worker. This is not possible in the example presented in the paragraph “Delegation using Kotlin’s by”.

2019-08-12
164TYPO

a delegate that filter out —-> filters out

2019-08-12
192TYPO

if (it == 2) { return } //ERROR, return is not allowed here // —-> the final “//” in the comment is not necessary

2019-08-12
193TYPO

line in the the overall context —-> “the” is repeated

2019-08-12
198SUGGEST

Paragraph “Inline Optimization”.
When dealing with the inline keyword, I was expecting also a reference to the anonymous classes that are created when using lambdas for compatibility with Java versions < 8. Not sure if it will be treated in the following chapters, but IMHO a few words in this chapter will be useful.

2019-08-12
202TYPO

The reason is by the time —-> …is THAT by ?

2019-08-12
202TYPO

will be not be permitted

2019-08-12
210SUGGEST

Example: val totalAge2 = people.map { person -> person.age }.sum(). I was wondering if a reference to the sumBy function would be useful at this stage to remove the map function and reduce the amount of code.

2019-08-12
210SUGGEST

In the diagram that represents the filter -> map -> map -> reduce pipeline I’d put in emphasis that the operations are executed on the whole list for each operator (filter all the list, then the resulting list is passed to the first map and so on). At a glance I read it from right to left following the horizontal arrows for every element in the input collection.

2019-08-12
217SUGGEST

When speaking about the pros and cons of eager vs lazy evaluation related to collections and sequences there is no reference to the fact lambdas in sequences cannot be inlined so anonymous class are used under the covers.

2019-08-12
218SUGGEST

(2..n - 1) —-> (2 until n)

2019-08-12
218TYPO

sequence of prime numbers start with 5 —-> … starting with …

2019-08-12
219ERROR

“The call to take() triggers the evaluation of the elements in the sequence, but
only until the given number of values are produced by the sequence.”

Shouldn’t it be the call to toList() to trigger the evaluation since take() is an intermediate operation?

2019-08-12
229SUGGEST

Maybe it’s worth mentioning that by adding the operator keyword to the extension function signature the code “circle.contains(point2)” could be written as “point2 in circle”. Sounds good :)

2019-08-12
229SUGGEST

#85622 is wrong. The same suggested example is reported on page 230. Sorry

2019-08-12
230SUGGEST

“method in Circle with operator” the operator keyword should use a different style

2019-08-12
236SUGGEST

Function andThen signature:

in fun <T, R, U> the color of “<T” should be black.

238SUGGEST

“Specifically Kotlin’s Any class has four significant methods that can make code
fluent: also(), apply(), let() and run().”

I don’t get why those methods are specifically associated with the Any class in the book. They are top level functions declared in Standard.kt and available to any type given they operate on generic types and are not specifically declared for Any.

2019-08-12
239ERROR

val result1 = str.let { arg ->
print(String.format(format, “let”, arg, this, result))
result
}

This code does not compile: “this is not defined in this context”. “this” inside the lambda does not exist, the only argument is “arg” given that the function type of the let function param is defined as (T) -> R and not as T.() -> R.

The same goes for “also”:

val result2 = str.also { arg ->
print(String.format(format, “also”, arg, this, result))
result
}

2019-08-12
246SUGGEST

IMHO the section entitled “Passing a Receiver” should be treated before the section that presents some of the most used Scope functions. It would help readers to grasp the notation used by the function type parameters that have a receiver.

2019-08-12
663ERROR

Hi, I am working on a Linux Ubuntu machine.
I started reading the chapter about coroutines, but found now to have it work, because I don’t know how to import the kotlinx.coroutines (with Gradle).
I think you should explain such things if you want all that stuff about coroutines be really usable… Thanks.

102TYPO

“Thankfully, we have a much better alternatively in Kotlin—reified type parameters.”

Should be “alternative”

185168ERROR

There’s a part on that page that says “Once the expression within the lambda is evaluated, the delegate will memoize the result and future requests for the value will receive the saved value. The lambda expression is not reevaluated.”

Unless I’m misunderstanding precisely what you mean by memoization here, I do not think this is correct. I wrote some code locally expanding on the “lazyevaluation.kts” example to prove this to myself. When referring to the same ‘temperature’ value, the ‘getTemperature’ function is not executed again. But, when actually calling ‘getTemperature’ again (and passing it the same arg as before) it does get executed again. Based on the statement in the book, I expected this function to not be executed again (within some reasonable scope). Maybe it’s not technically incorrect, but some rephrasing or clarification of what precisely is getting memoized, or what you should be using in subsequent code (the value, or another call to the function) in order to take advantage of the memoization would be helpful.

Re-reading the original book text after laying it out here makes me believe it is not strictly an error in the text, perhaps just something that could be communicated with more precision.

75SUGGEST

In the section called “Wrapping up” it says “Kotlin’s Pair and Triple are useful to create finite small-sized collections. For a larger fixed-sized collection, reach for the Array class.” Here you should repeate the recommendation given on page 68: “If you need to group more than three immutable values, then consider creating a data class”. So the wrapping up on page 75 should be something like “Kotlin’s Pair and Triple are useful to create finite small-sized collections. If you need to group more than three immutable values, then consider creating a data class. For a larger fixed-sized and mutable collection, reach for the Array class.”
Note that I inserted “and mutable” in the last sentence about the Array class. Mutability is a very noteworthy property of Array when comparing with the other alternatives mentioned.

58SUGGEST

Just above the code iteration/withIndex.kts, the paragraph starts “Alternatively, we can get both the index and the position in one shot”. I think it should be “Alternatively, we can get both the index and the value in one shot” or “Alternatively, we can get both the index and the value at the position in one shot”

This code is also an example of destructuring, but noting that probably takes focus away from iteration.

259SUGGEST

In the example dsl/meetingdsl.kts I was trying to complete the example before reading the solution. I came up to a different solution that achieves the same result (readability and “type guidance”) by defining “start” and “end” as constants of two different enums (nested in the Meeting class) and by declaring at() an by() as extension functions respectively on “start” and “end”. Maybe it’s a bit less longer than the proposed solution…

261ERROR

fun main() {
val result =
html {
body {
h1 { +“Methods’ Behavior” }
p { “This is a sample” }
}
}
println(result)
}

p { “This is a sample” } is missing a “+” in front of the String (the same holds for the subsequent example).

12ERROR

(On Linux) the example using “#!/usr/bin/env kotlinc-jvm -script” doesn’t work:

/usr/bin/env: ‘kotlinc-jvm -script’: No such file or directory
/usr/bin/env: use -[v]S to pass options in shebang lines

It needs to be “#!/usr/bin/env -S kotlinc-jvm -script”. (But “#!/usr/bin/env kotlin” also seems to work fine.)

88TYPO

running/Hello.kt <<— what is that?
fun main() = println(“Hello World”)

The above does not compile… If I remove the “running/Hello.kt” it does run.

32ERROR

Original: With no arguments the trimmargin() method removes the spaces until the leading | character.

Proposed correction: With no arguments the trimmargin() method removes the spaces until AND INCLUDING the leading | character

Categories: