Hide the entire implementation behind a BasicQueueImplementation private class.

Private constructors and private members are one way to hide the
initial- ization and representation of a class. Another, more radical
way is to hide the class itself and only export a trait that reveals the
public interface of the class.

The code here implements this design. All three methods of the
FunctionalQueue trait are implemented in a subclass
BasicQueueImplementation, which is itself a private inner class of
object BasicQueue. This exposes to clients the same information
as before, but using a different technique.

Instead of hiding individual constructors and methods, this version
hides the whole implementation class.
Add a simple example of a Scala class using nonvariant generic subtyping.

Nonvariant is Scala's default for generic type variance.
Generalize FunctionalQueue.enqueue by making it polymorphic.

We make it polymorphic by giving the enqueue method itself a type parameter
and using a lower bound for its type parameter.

The new definition gives enqueue a type parameter U, and with the
syntax, "U >: T", defines T as the lower bound for U. As a result, U is
required to be a supertype of T. The parameter to enqueue is now of
type U instead of type T, and the return value of the method is now
Queue[U] instead of Queue[T].

As an example, suppose there is a class Fruit with two subclasses, Apple
and Orange. With the new definition of class Queue, it is possible to
append an Orange to a Queue[Apple]. The result will be a Queue[Fruit].
This revised definition of enqueue is type correct. Intuitively, if T is
a more specific type than expected (for example, Apple instead of
Fruit), a call to enqueue will still work, because U (Fruit) will still
be a supertype of T (Apple).
Add an example of a contravariant generic type.

Here, OutputChannel is defined to be contravariant in T. So an output
channel of AnyRefs, say, is a subtype of an output channel of Strings.

Although it may seem non-intuitive, it actually makes sense. To see
why, consider what you can do with an OutputChannel[String].

The only supported operation is writing a String to it. The same operation
can also be done on an OutputChannel[AnyRef]. So it is safe to substitute
an OutputChannel[AnyRef] for an OutputChannel[String].

By contrast, it would not be safe to substitute an OutputChannel[String]
where an OutputChannel[AnyRef] is required. After all, you can send any
object to an OutputChannel[AnyRef], whereas an OutputChannel[String]
requires that the written values are all strings.
Add an example of covariance and contravariance together in one type.

Sometimes covariance and contravariance are mixed in the same type. A
prominent example is Scala’s function traits. For instance, whenever you
write the function type A => B, Scala expands this to Function1[A, B].

The definition of Function1 in the standard library uses both covariance
and contravariance: the Function1 trait is contravariant in the function
argument type S and covariant in the result type T, as shown here.

It is safe to assume that a type T is a subtype of a type U if you can
substitute a value of type T wherever a value of type U is required.

This is called the Liskov Substitution Principle. The principle holds if
T supports the same operations as U and all of T’s operations require
less and provide more than the corresponding operations in U.

Function1 satisfies the Liskov substitution principle, because arguments
are something that’s required, whereas results are something that’s
provided.

So it follows, that a Function1 of S, T is able to accept an S or any
supertype in its apply method, and it is able to return a T
or any subtype.
Add an example of covariance and contravariance in function type parameters.

Even though the type of the function passed to Library.printBookList is
Book => AnyRef, the actual function passed to it in Customer, getTitle,
Publication => String, type checks.

If you think about it, this makes sense. The Liskov Substitution
Principle says that every subtype should be suitable as a drop-in
replacement for any of its supertypes. Every Book is also a Publication,
so a function that takes Publications type checks for a function that
takes Books.

Similarly, on the return side, printBookList expects a function that
returns AnyRef. String is an AnyRef, so that also type checks, because
every String is also an AnyRef.

This example of type covariance and contravariance also lines up with
how Java does generics as per the PECS acronym, Producer Extends,
Consumer Super.
Improve the implementation of BasicQueue's implementation class.

This new implementation of BasicQueue performs at most one
trailing to leading adjustment for any sequence of head operations.

What's different with respect to the previous version is that now
leading and trailing are reassignable variables, and mirror performs
the reverse copy from trailing to leading as a side-effect on
the current queue instead of returning a new queue. This side-effect
is purely internal to the implementation of the BasicQueue operation;
since leading and trailing are private variables, the effect is not
visible to clients of BasicQueue.

This new version of BasicQueue still defines purely functional
objects, in spite of the fact that they now contain reassignable fields.
All of the state is hidden away and is purely an implementation detail.
Add an example of using type upper bounds to design more flexible algorithms.

Because Person mixes in the Ordered trait, you can run orderedMergeSort
on Lists of Person, because they are subtypes of Ordered.

Until we learn about how to use implicit parameters and view bounds,
this is the most general solution we can use.
Add an example of abstract members in use.

Like Java, Scala lets you define abstract methods. But Scala goes
farther and lets you also define abstract vals, vars, and types as well,
not just methods!

Scala is even more OOP than Java.