You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: source/20-imperative.rst
+84-2Lines changed: 84 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -517,6 +517,27 @@ We can read the standard input as lines using this iterator:
517
517
518
518
This gives you an iterator of strings with each item representing one line. When the iterator has no more items, you are done reading all the input. (See also this `concise reference <https://alvinalexander.com/scala/how-to-open-read-text-files-in-scala-cookbook-examples>`_.)
519
519
520
+
521
+
The role of EOF (end of file)
522
+
`````````````````````````````
523
+
524
+
When reading from a file, the iterator naturally exhausts itself when the end of the file is reached.
525
+
When reading from an interactive terminal, the user signals end-of-input by pressing **Ctrl-D** (Unix/macOS) or **Ctrl-Z** (Windows), which sends an *EOF* (end-of-file) marker to the standard input stream.
526
+
This is the standard Unix convention for signaling "I am done providing input."
527
+
528
+
In batch pipelines, EOF is sent automatically when the upstream data source closes its output.
529
+
For example, when you run:
530
+
531
+
.. code-block:: bash
532
+
533
+
$ echo -e "hello\nworld"| myapp
534
+
535
+
the shell closes the pipe—and thus sends EOF—as soon as ``echo`` finishes writing.
536
+
Your Scala app's ``getLines()`` iterator will then return ``false`` from ``hasNext`` and the reading loop terminates cleanly.
537
+
538
+
A common mistake is to assume that reading "nothing" means EOF; in fact, an empty line is still a valid line (an empty string), and only the exhaustion of the iterator signals EOF.
539
+
540
+
520
541
To break this iterator of lines down into an iterator of words, we can use this recipe:
521
542
522
543
.. code-block:: scala
@@ -664,9 +685,33 @@ Many REPLs, including the Python and Scala ones, allow the user to edit their in
664
685
665
686
To add this capability to a Java- or Scala-based command-line app, we can use the `JLine library <https://github.com/jline/jline3>`_, which is the Java equivalent of the `GNU Readline library <https://en.wikipedia.org/wiki/GNU_Readline>`_.
666
687
If you want to make your command-line app convenient to use and give it a professional touch, consider using JLine instead of basic console input.
667
-
JLine has excellent documentation; please look there for examples.
Here is a minimal example that uses JLine to read lines with an editable prompt in Scala:
694
+
695
+
.. code-block:: scala
696
+
697
+
import org.jline.reader.LineReaderBuilder
698
+
import org.jline.terminal.TerminalBuilder
699
+
700
+
import scala.util.Success
701
+
import scala.util.Try
702
+
703
+
val terminal = TerminalBuilder.builder().system(true).build()
704
+
val reader = LineReaderBuilder.builder().terminal(terminal).build()
705
+
706
+
Iterator.continually:
707
+
Try(reader.readLine("Whatcha got? "))
708
+
.takeWhile:
709
+
line => line.isSuccess
710
+
.foreach:
711
+
case Success(line) => println(s"You said: $line")
712
+
713
+
The ``Try`` wrapper around ``readLine`` ensures that an ``EndOfFileException`` (thrown when the user presses Ctrl-D / EOF) causes the iterator to stop cleanly via ``takeWhile``.
714
+
JLine automatically suppresses the prompt when stdin is redirected from a file or pipe, so this code works correctly in both interactive and batch modes.
670
715
671
716
672
717
Finding good third-party libraries
@@ -752,6 +797,43 @@ To use log4s minimally, the following steps are required:
For long-running console applications—such as those processing large files or performing iterative computations—it is useful to report progress to the user so they know the application is alive and can estimate how much longer it will take.
804
+
805
+
The simplest form of progress reporting is to print periodic updates to ``stderr``:
806
+
807
+
.. code-block:: scala
808
+
809
+
var count = 0
810
+
for item <- items do
811
+
count += 1
812
+
if count % 1000 == 0 then System.err.println(s"Processed $count items so far...")
813
+
// process item ...
814
+
815
+
For a more polished, terminal-aware progress bar, the `progressbar <https://github.com/ctongfei/progressbar>`_ library provides a convenient solution.
816
+
It displays a live-updating progress bar on the terminal but suppresses it automatically when output is redirected (non-interactive mode).
817
+
818
+
Add the dependency to your ``build.sbt``::
819
+
820
+
"me.tongfei" % "progressbar" % "0.10.1"
821
+
822
+
Then use it as follows:
823
+
824
+
.. code-block:: scala
825
+
826
+
import me.tongfei.progressbar.ProgressBar
827
+
828
+
val pb = ProgressBar("Processing", items.size.toLong)
829
+
for item <- items do
830
+
pb.step()
831
+
// process item ...
832
+
pb.close()
833
+
834
+
An example of progress reporting for a Monte Carlo simulation (where the total number of steps is known) can be found `here <https://github.com/LoyolaChicagoBooks/introds-scala-examples/blob/main/montecarlo-scala/GenerateDarts.scala#L28>`_.
Copy file name to clipboardExpand all lines: source/30-objectoriented.rst
+33Lines changed: 33 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -395,3 +395,36 @@ The Thin Cake idiom provides basic DI in Scala without the need for a DI framewo
395
395
To recap, ``common.Main`` cannot run on its own but declares by extending ``TreeBuilder`` that it requires an implementation of the ``buildTree`` method.
396
396
One of the ``TreeBuilder`` implementation traits, such as ``FoldTreeBuilder`` can satisfy this dependency.
397
397
The actual "injection" takes place when we inject, say, ``FoldTreeBuilder`` into ``common.Main`` in the definition of the concrete main object ``fold.Main``.
398
+
399
+
400
+
The Dependency Inversion Principle
401
+
````````````````````````````````````
402
+
403
+
The `Dependency Inversion Principle <https://en.wikipedia.org/wiki/Dependency_inversion_principle>`_ (DIP), one of the SOLID design principles, states:
404
+
405
+
.. note::
406
+
407
+
- High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces/traits).
408
+
- Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
409
+
410
+
Applying these ideas to the process tree and iterators examples, we can map the roles as follows:
411
+
412
+
- **High-level modules**: ``common.Main``, ``TreeBuilderSpec``—these orchestrate the overall application logic.
413
+
- **Low-level modules**: ``MutableTreeBuilder``, ``FoldTreeBuilder``—these are specific implementations of a building-block behavior.
414
+
- **Abstractions**: the ``TreeBuilder`` trait—this is the shared contract that both the high-level and low-level modules depend on.
415
+
- **Details**: the concrete implementations such as ``MutableTreeBuilder``—these know *how* to build a tree but are not imported by high-level modules directly.
416
+
417
+
Rather than thinking in a strict binary high/low-level classification, it can be more illuminating to arrange the building blocks along a *continuum* from high-level (closer to application intent) to low-level (closer to implementation mechanics):
0 commit comments