
Prepare for the Scala advanced course by using the setup module to download the materials and the support materials, follow the course, and try the examples and exercises.
Set up a recent JDK (prefer Oracle JDK for compatibility), install SBT, and use IntelliJ IDEA Community Edition or another IDE, then import the course exercises from the zip.
Set up the Java JDK by ensuring Java 8 or 9 is installed, verify with the Java compiler, and use Oracle Technical Network downloads or Linux PPA if needed.
Install Skarner from the official Scala Langberg page, download intelligence and download SBT, install the standalone scanner, and add the Mac bin path after unzip.
Install sbt across Linux, Mac, or Windows by using the download page for the Scala language, then verify with sbt in the terminal and check the sbt version.
Download and install the IntelliJ IDEA Community Edition, follow the setup wizard, and install the Scala scanner plugin while avoiding the Vim plugin to prevent confusion.
Learn to configure IntelliJ to use the correct JDK by setting the project SDK in project defaults and structure, then import the Scala exercises from the provided zip.
Please download the zip file attached to this lecture, then follow the video to unpack it and import it into IntelliJ IDEA
Explore Scala's approach to properties and state, contrast immutable structures with mutable state, and learn safe, performance-conscious usage in this module.
Learn the uniform access principle, Scala modifiers, and implementing them; write custom properties, use scope modifiers for safety, and apply caching best practices for mutable state.
Explains the principle of uniform access, where all module services are accessible through a uniform notation regardless of storage or computation, contrasting with Java's distinction between fields and methods.
Compare def and val in Scala: def runs code on every use, while val is evaluated once and cached when a class is initialized.
Explore mutable state in Scala using the rewriting convention, demonstrated with a weight example that uses an accessor and a setter to convert between pounds and kilos.
Explore how the Scala compiler rewrites kilo weights to a call for kilos, using underscores and a modifier; a var automatically generates a modifier, with rules next.
Explore how to manually implement a Scala class with private this backing fields, accessors, and weight conversion between pounds and kilos using explicit getters and modifiers.
See how the code Scalia generates uses private fields with access modifiers and modifier methods; use the scripts and worksheets to experiment and restore an older version if needed.
Explore how Scala rewrites value and var fields into private backing fields with unique names, generates accessors, and how abstract rules influence disambiguation in unified access syntax.
Recap how private and this private limit access to the field only, while companion objects and other instances cannot access private this; generated modifiers and distinct names prevent conflicts.
Explore how abstract properties in traits and abstract classes generate only accesses and modifiers, forming contract to fulfill in concrete instances, and override height and height_ equals with proper types.
Override height and weight to require positive values using a precondition with require, backed by private fields with safe initialization, and demonstrate exceptions for invalid inputs.
Back properties with a mutable map, limited to string and double, preserving the same access and modifier API while defaulting to 0.0 and enabling external storage options.
Explore how mutable state in Scala challenges referential transparency, showing how heaviest person can change after weight updates, and outline best practices to prevent unintended consequences.
Examine caching slow operations through a simulated weather lookup, and why thread safety is hard; use proven tools like concurrent hash map, Guava cache loader/builder, and futures.
Demonstrate how futures enable non-blocking cache lookups with a Guava cache, using a cache loader to fetch values and resolve futures concurrently.
Learn caching best practices in Scala: minimize mutable state, document it when used, favor immutable results, use Guava for in-memory caching, and complete exercises to implement custom accesses and modifiers.
Delve into the Scala type system, exploring the regular and implicit type systems as the course moves from mutability to generics and variants in module two.
Explore generics and the Scala type system, covering generic type parameters, upper bounds, and the concepts of covariance and contravariance, with practical exercises on bounds.
Explore Scala simple types by modeling an abstract food class with a string name and concrete case classes like banana and apple (fruit), granola and muesli (cereal).
Explore how inheritance and subtyping let you pass apples and muesli to the eat method that accepts food, then store them in a bowl.
Use generics to preserve exact contents through bowls, so apple and muesli remain distinct and the compiler infers the specific type F.
Learn how upper bounds constrain a generic type to be a kind of food, enabling name calls on F. See how bounds prevent invalid assignments and compile errors.
Explore how the Scala type system uses an upper bound to restrict a food bowl to food only, preventing Dotty from being treated as food.
Explore how Scala's type system enforces generic type parameters through a fruit bowl example, illustrating subtypes, type variance, and the rule that a fruit bowl must match a fruit eater.
This lecture explains invariance in generic types and introduces covariance with a food bowl example, showing how plus F makes a fruit bowl usable as a food bowl.
Understand how the food bowl relates to its type parameter, with apple as a subtype of fruit, and how plus and minus express covariance and invariant behavior in Scala.
Explore variance in Scala's type system, including invariant, covariant, and contravariant generics. See how lists are covariant and sets are invariant, and how contravariance flips type arrows for input parameters.
Explore covariance in the Scala type system through a food bowl example, showing how a subtype of food preserves the upper bound, and how fruit, cereal, and muesli bowls fit.
Discover how the Scala type system links subtypes and super types in an inheritance hierarchy, using food bowls of fruit or apples to illustrate their relationships.
Explore covariance and contravariance in Scala generics through the sink analogy, showing how fruit, apples, and foods relate in transportable containers.
Explore how covariance and contravariance affect type safety in sinks and fruit handling, illustrating upper and lower bounds and why variance matters in scala's type system.
explains contravariance in the Scala type system, showing how a sink for a supertype can accept specific items like apple or orange, via minus T and generic sender patterns.
Explore how Scala treats type parameters as invariant, covariant, or contravariant, including upper and lower bounds, where only class or trait definitions specify variance and bounds.
Examine control variance and covariance in Scala function definitions, showing how input types stay at least as general and outputs at least as specific through a fruit and description example.
Explore how functions behave with variance, passing an apple into a function that handles fruits and producing descriptions like taste and texture, illustrating contravariance on inputs and covariance on outputs.
Explore variance in the Scala type system, comparing covariance and contravariance with examples like fruit, orange, texture, and description to understand function input and output types.
Explore scala function variance: a function with multiple type parameters has contravariant input parameters and a covariate return type. Learn when to keep inputs invariant and what comes next.
Examine the costs of adding covariance and contravariance to tight type parameters, using semigroup to model combining two T values, and see how invariance stays simple while covariance challenges it.
Examine covariance in Scala's type system, illustrate how covariant types can be treated as super types and why this leads to invalid assignments, and preview how the compiler prevents it.
Explore introducing extra freedom into types using inclusive upper and lower bounds, and understand least upper bounds and type inference in Scala's lists as items change.
Explore covariance in a food bowl to mix foods by introducing a mixed type M with lower and upper bounds, a super type of F and a subtype of food.
Master lower and upper bounds in scala to define new types, use least upper bounds for inferred types, and apply variance rules for covariant and contravariant positions.
Master the Scala type system by mastering variance and bounds, including covariant, contravariant, and invariant relationships. Apply upper and lower bounds to method generics to design simpler, safer APIs.
Explore advanced Scala type system features beyond generics, including type members as an alternative to type parameters, the bounce, path dependent types, and F bounded polymorphism.
Explore type members and how they relate to type parameters within the Scala type system, distinguishing their features from type parameters.
Recap the generic type parameters and a food-based type hierarchy. Explore a covariance container upper-bounded by food that stores any food type.
Explore how type parameters become part of Scala type and how variance governs subtypes and supertypes, enabling the compiler to infer relationships from generic containers such as a food bowl.
Explore Scala's advanced type system and how outside access differs from inside a class, using variable parameters, fields, and type members.
Explore how types can be members of a class, using abstract types, upper bounds, and a type projection to define and access internal and external types like food and apple.
Introduce a proper type alias to expose the food type from outside the class, making it interchangeable with Apple and accessible through the projection.
Understand singleton types in Scala, where each instance has a unique type. See how string literals like 'hello' acquire distinct singleton types and constrain method inputs.
Explore building a tiny Scala DSL with singleton types and infix notation to create a type-safe to be or not to be style API that links type system concepts.
Explore how type parameters initialize type members and enable path dependent types in Scala, using generic type parameters in apply methods to infer and set types automatically.
Explore refinement types that combine easy initialization with embedding a specific type inside a type member, illustrated by a food bowl where the food equals apple.
Demonstrates path dependent types in Scala, where same name yields distinct types when accessed via different instances or paths, and shows compilation requires proving type equivalence.
Note that type members are available inside and outside class, while type parameters are only accessible inside; apply creates an anonymous class and a new type in Scala type system.
Explain how type parameters define variance and bounds in trait or class definitions, using upper and lower bounds and layered generic constraints, with F as a covariance bound of food.
Explore recursive type self references in a distance class that uses itself, and extend to generics to compare distances and return the larger or smaller one.
Explore how to implement insertion sort in Scala using generic types and type members, defining a distance type, and recursively sorting lists to enable sorting on other types.
We introduce recursive types by defining a generic compare trait for type T with two abstract methods, greater than and less than, to avoid confusion with Scala's Compar trait.
Create a generic insert and sort for any type T that defines compare T, using an upper bound to enable less than calls and ensure type safety.
This lecture explains a type class pattern using compare T of itself, defines a distance case class extending the trait, and shows sorting engine sizes and people by name.
Explore F-bounded polymorphism and recursive types in Scala, where a trait or class defines itself through its own type parameter, then apply with Module 3 exercises and tests.
Explore existential types, structural typing, refinement types, and self types with constraints, plus infix type notation, within the regular Scala type system in module four.
Explore existential types, underscore notation, structural types for static duck typing with compile-time checks; reflexion, refinement types, self types and self aliases, infix type notation, and Scala enumerations.
Explore how Scala enforces type parameters, contrasts with Java generics, and uses existential types to flexibly handle lists, enabling generic length functions while avoiding unnecessary type commitments.
Learn how existential types express a type parameter without naming it, using for some type T or an underscore, and import language existentialism to silence warnings.
Explore existential types in the Scala type system, showing generics with upper bounds by fruit enabling mapping names, and existential forms and underscore notation that manage type parameters.
Learn how existential types serve as bounds shortcuts, compare them to Java wildcards, and explore structural types with static duck typing, including reflection considerations.
Explore how structural types enable safe reflection shortcuts in Scala, preserving runtime knowledge while the compiler may lose track when upcasting to any, and prevent length calls on unknown types.
Explore how structural types enable casting to bring external data into the Scala type system, showing compile-time guarantees versus runtime exceptions and reflection risks, and when to use structural subtyping.
Explore refinement types and type parameters in Scala with a food bowl example (apple vs muesli) to enforce correct feeding by a fruit eater, via type members and upper bounds.
Refinement types offer safer structural typing by constraining a bowl to fruit, allowing apple bowl but rejecting a muesli bowl; they enforce compile-time rules without reflection, boosting type-member expressiveness.
Explore self aliases in the Scala type system by aliasing the outer type inside a case class to correctly reference the life partner name across nested classes.
Explore self types with type requirements in scala to express dependencies like lazy logging without inheritance. Learn renaming and referencing this to refine and constrain trait types.
Learn self typing in Scala by creating a concrete instance that requires lazy logging, using a trait with a self type to ensure a logger is available.
Explore infix notation in Scala by recalling standard infix usage for values, such as five plus one. Apply the same syntax sugar to types, translating infix form into type-level expressions.
Explains infix type notation for two-parameter types in Scala, showing the equivalence between infix and square-bracket forms and how parentheses affect scope.
Explore Scala enumerations within the advanced type system by defining an object that extends enumeration, listing colors such as red, green, blue, and yellow, and using value to generate entries.
Illustrates how path dependent types give color and size enumerations distinct types, enabling type safety even when underlying integers coincide and the compiler catches swaps.
Explore Scala type system basics beyond enumerations, using sealed trait and case objects, retrieving items by name with Excel, and handling color values, paving the way toward implicits.
Explore the Scala implicit system, using implicit parameters to build type classes, apply context bounds, and leverage the implicit mechanisms to enforce constraints; future parts cover implicit conversions.
Explore simple implicit parameters, explicit overrides, and the power of type classes in Scala. Learn how the implicitly function, composition, and class tags enable practical, understandable runtime behavior.
Explore the Scala type system's implicit instances and type classes, then a simple retry pattern using nonfatal, by-name bindings and an explicit retry type.
Learn a Scala retry pattern with a checkIt method that throws when X is not big enough and returns X after five retries, using currying and implicit parameters.
Register and use implicit parameters in Scala by marking values as implicit, enabling compiler lookup by type for features like retry parameters and execution context in futures.
Learn how Scala's implicit parameters work, how explicit definitions override implicits, how the compiler fills implicit arguments, and best practices for implicit parameter lists and the implicitly keyword.
Learn how implicit parameters in Scala use type matches, not names, and how to override, hide, or disable implicits with scoped definitions, avoiding ambiguous defaults.
Explore how a type class defines behavior for a generic type with a single parameter and a compare method, using implicit instances to enable insert sort without changing inheritance.
Learn how to implement an insertion sort in Scala using a type class and implicit parameters, with a compare type class to sort type T via recursion and implicit resolution.
Discover how to enable sorting of custom types in Scala by providing implicit comparators as type classes, using implicit vals or objects for is smaller and is larger.
Explore ad hoc polymorphism through implicit type class instances in Scala, enabling new behavior for any type without changing inheritance. See how implicits in scope control which comparator is used.
Explore how Scala's implicit mechanism applies to objects, classes, and defs. See how implicit objects, implicit vals, and implicit defs enable compositional patterns and the powerful capabilities of implicit defs.
Explore using context bounds as sugar for implicit type class patterns in Scala. Learn to replace implicit parameters with a bound like T: type class and retrieve evidence with implicitly.
Explore how implicit parameters in Scala provide a simple mechanism to summon an implicit value for a type T from the compiler and return it to the caller.
Build a simple json serializer using a scala type class, implicit values, and context bounds to demonstrate how type-class instances compose.
Implement a json writer type class in Scala by adding an implicit string writer that quotes strings via interpolation, then extend with a double writer using toString without quotes.
Learn to derive a JSON writer for lists of any type using implicit type classes and context bounds, reducing boilerplate and enabling seamless composition when the items have a writer.
Explore composing json encoders with implicit type class instances in Scala, building maps, dictionaries, and nested structures using string keys and type-safe json writers.
Explore composing strings, lists of strings, and maps from string to list of string. Benefit from compile-time verification and performance by using libraries such as Sprey and Jason.
Explore the rules and restrictions of Scala's implicit parameters, including when to use or avoid them, how scope and imports govern implicit values, and how to prevent ambiguity.
Explore the powerful class tag and type class concepts in Scala, using implicit resolution, reflection, and ClassTag to create typed arrays and support runtime type information.
Explore how the Scala type system handles type erasure during runtime and why unchecked pattern match warnings occur, then learn how implicit context and class tags enable safe runtime checks.
Explore class tags and type tags in scala to reveal runtime type information for maps of string to int, using reflect runtime universe to guard pattern matches.
Explore implicit constraints and conversions as the final part of the implicit section, completing the tour of the Scala type system.
Explore multi-parameter type constraints with implicit parameters and type classes in Scala, including =:= and <:<, the safe casting pattern, and moving from implicit parameters to implicit conversions, with best practices.
Explore how implicit constraints extend Scala's type system by using implicit parameters to enforce rules across type parameters via a compiler lookup of implicits, ensuring availability without altering the type.
Explore a Scala type-system based rules approach that matches eaters to foods using implicit evidence and upper-bounded type parameters, implementing vegan, vegetarian, and paleo rules for fruit, cereals, and meat.
Explore how compile-time enforced behavior uses implicits to import and alter rules, enabling safe, configurable constraints. See how databases with long transactions are required and unsupported ones trigger compiler errors.
The module explains infix type notation to express implicit evidence that an eater eats food. It presents the infix form as a concise way to write and define the rules.
Experiment with constraint rules using a script that combines vegetarian and paleo examples, and compare implicit objects with implicit vowels and defs for cleaner code in Scala.
This module explains the Scala numeric type class, how to define numerics for custom types (rational, complex), and how implicit constraints enable safe operations.
Explore a generic cell container for spreadsheet cells, enabling numeric multiplication only when the cell holds a numeric type, while allowing strings and formulas without forcing numeric constraints.
Discover type equivalence with the =:= evidence and enforce that a numeric type and its concrete type match, enabling sum on numeric lists via implicit evidence but not on strings.
Explore how Scala uses a sealed, implicit equals type class to enforce type equality, enabling numeric operations only when two types match, with implicit conversions simplifying the evidence.
Explore flattening futures in Scala by using safe casting and evidence that a type equals a future of another type. Apply flatMap to implement the flatten operation.
Explain a flaw in Sakala futures or flatten where type inference needs an explicit parameter; use <: bounds to defer inference and let Nothing yield the correct type.
Explore implicit conversions that automatically promote integers to complex numbers to enable addition, illustrating compiler-driven wrapping and companion-object resolution.
Explore how implicit conversions create extension methods in Scala, showing a Time class and syntax, and why safe practice requires owning a type and using implicit classes inside an object.
Explore how implicit classes wrap values and incur minimal overhead, and learn the rules for extension methods: a single parametric implicit parameter, no additional state, and top-level placement.
Learn to debug implicit rules in Scala by applying implicits explicitly to reveal type incompatibilities, resolve ambiguous implicits, and use scalac options to inspect the AST and code expansion.
Place implicit definitions in the companion object or a package object. Use a convenient utility object to import implicits, or import from the package object that shares the package name.
Use implicits judiciously, prefer companion object implicits for ubiquitous defaults, and rely on type classes to reduce macro usage and slow compiler lookups.
Delve into Scala implicits, mastering non-ambiguity rules, compilation behavior, and the lookup process via imports and companion objects for implicit parameters and conversions.
Scala Advanced, Part 1
The Escalate Software Scala Advanced course is intended for experienced Scala developers looking to improve their skills, particularly for library and API design and development. It covers topics needed to be effective in producing high quality, correct, powerful and flexible Scala libraries that are still easy to use by others.
Part 1 provides in depth and thorough knowledge of the Scala type system, an important precursor to any library development. We start off with a look at the dangers, and safe use, of mutable shared state in Scala, covering properties and caching. From there we delve into the Scala type system including:
Generics
Co- and Contra-Variance
Upper and Lower Bounds
Type Inference
Type Parameters and Type Members
Path Dependent Types
Refinement Types
Structural Types
Recursive Types
F-bounded Polymorphism
The final two modules then move on to the secondary Implicits type system that augments the regular Scala type system, covering:
Simple Implicit Parameters
Type Classes
Implicit classes, objects and methods
Type Class Composition
Implicit Constraints
=:= and <:<
Implicit Class and Type Tags
Implicit Conversions
extends AnyVal
And more.
After completing this course you will understand the Scala type system in a way that lets you construct your own well designed APIs, reason about type abstraction and calculus, apply implicit constraints and augment the language rules, and much more.
Parts 2 and 3 (available separately) cover topics like best practices, idioms, patterns, advanced functional programming, asynchronous programming, parser-combinators, macros, performance profiling and optimization, and much more. These parts rely on information presented during this advanced part 1 course.