Scala is a modern, multi-paradigm programming language that runs on the JVM. It combines object-oriented and functional programming features, offering full compatibility with Java while providing more concise syntax and powerful abstractions.
'val' declares immutable variables (cannot be reassigned), while 'var' declares mutable variables (can be reassigned). Example: val x = 1 (immutable), var y = 2 (mutable)
Array is mutable and fixed-size, while List is immutable and variable-length. Arrays are better for random access, Lists are better for sequential access and recursive operations.
Functions are defined using the 'def' keyword, followed by name, parameters, return type, and body. Example: def add(x: Int, y: Int): Int = x + y
Case classes are special classes that are immutable by default and come with built-in methods for comparison, hashing, and copying. They're ideal for modeling immutable data.
Option is a container type that represents optional values. It has two subtypes: Some(value) for existing values and None for absent values, helping to avoid null pointer exceptions.
'object' creates a singleton instance, while 'class' is a blueprint for creating multiple instances. Objects are used for storing static members and utility functions.
Pattern matching is a feature that allows matching values against patterns. It's similar to switch statements but more powerful, supporting type matching, deconstruction, and guards.
Traits are similar to interfaces but can contain both abstract and concrete members. They support multiple inheritance and are used for sharing interfaces and implementations between classes.
Scala discourages use of null by using Option type. Instead of returning null, functions return Option[T] which can be either Some(value) or None.
The apply method allows objects to be called like functions. It's commonly used in companion objects to create new instances without using the 'new' keyword.
Implicit parameters are automatically passed to functions if they're marked with 'implicit' keyword and a matching type is in scope. They reduce boilerplate and enable context passing.
Higher-order functions are functions that take other functions as parameters or return functions. Common examples include map, filter, and reduce.
String interpolation allows embedding expressions in string literals using s'', f'', or raw'' prefixes. Example: s'Hello ${name}' will replace ${name} with the value of name variable.
fold requires an initial value and works on empty collections, while reduce uses the first element as initial value and requires non-empty collections. Both combine elements using a function.
Type bounds restrict the types that can be used with a generic class or method. Upper bounds (T <: A) mean T must be subtype of A, lower bounds (T >: A) mean T must be supertype of A, and mixed bounds combine both.
Variance determines subtyping relationships: covariant [+T] preserves subtyping, contravariant [-T] inverts it, and invariant [T] allows no subtyping relationship. Used in generic type parameters.
Implicit conversions automatically transform one type to another. They're defined using 'implicit def' and are used for extending existing classes, domain-specific languages, or type adaptation. Use cautiously to maintain code clarity.
Lazy evaluation delays computation until needed using 'lazy val'. It's useful for expensive computations, avoiding unnecessary work, and handling infinite sequences. Values are computed once and cached.
Partial functions are functions defined for a subset of possible inputs using case statements. They include isDefinedAt method to check if input is valid. Useful for pattern matching and handling specific cases.
Type classes are interfaces that define behavior for types. Implemented using traits and implicit parameters, they enable ad-hoc polymorphism and extension methods without modifying original classes.
Futures represent asynchronous computations, while Promises are writable, single-assignment containers for future results. Used for concurrent programming with methods like map, flatMap, and recover.
The cake pattern is a dependency injection technique using traits and self-type annotations. It enables component-based programming and dependency management without external frameworks.
Sealed traits restrict class hierarchy to current file, enabling exhaustive pattern matching. Case objects are singleton case classes, useful for enumeration and pattern matching.
For-comprehension is syntactic sugar for combining map, flatMap, and filter operations. It makes working with monadic types (Option, List, Future) more readable and composable.
Currying transforms functions with multiple parameters into chain of single-parameter functions. Enables partial application and function composition. Example: def add(x: Int)(y: Int) = x + y
Abstract type members are type declarations within traits/classes that can be defined by implementing classes. They provide type abstraction and are useful for dependency injection.
Tail recursion optimizes recursive calls by reusing stack frame, marked with @tailrec annotation. Last operation must be recursive call. Compiler transforms it into loop for efficiency.
Companion objects are singleton objects sharing name with class/trait. They hold static members, factory methods, and implicit definitions. Can access private members of associated class.
Path-dependent types are types that depend on object instances. They ensure type safety when dealing with nested classes and enable more precise type relationships. Example: outer.Inner refers to Inner class in specific outer instance.
Shapeless is a type-level programming library that enables generic programming, providing tools for type manipulation, heterogeneous lists (HLists), and type-level computations. Used for deriving type class instances and type-safe data transformations.
Free Monads separate program description from interpretation, enabling pure functional programming. They allow building domain-specific languages and deferring execution details. Implemented using case classes and pattern matching.
Phantom Types are type parameters that don't appear in the type's definition but provide compile-time type safety. Used for encoding constraints, ensuring correct API usage, and preventing invalid operations.
Cats and Scalaz are functional programming libraries. Cats focuses on accessibility and documentation, while Scalaz prioritizes mathematical purity. Both provide type classes, data types, and functional patterns.
Type-Level Programming uses Scala's type system to perform computations at compile time. Involves implicit resolution, type classes, and type members to encode logic that's verified by compiler.
Existential Types represent unknown types meeting certain bounds. Written as T forSome { type T }, they're used when exact type isn't important but type constraints must be maintained.
Higher-Kinded Types are type constructors that abstract over types with type parameters. Enable writing generic code for containers like List[_], Option[_]. Used in functional programming abstractions.
Akka implements the Actor model for concurrent computation. Actors are isolated units communicating via message passing, with supervision hierarchies for fault tolerance. Enables distributed systems.
Kleisli composition combines functions returning monadic values (A => F[B]). Useful for composing operations with effects, database queries, or validation chains. Implemented using for-comprehension or compose.
Lenses and Prisms are functional abstractions for accessing and modifying nested data structures. Lenses handle product types (case classes), Prisms handle sum types (sealed traits). Part of Monocle library.
Custom Typeclass Derivation automatically generates typeclass instances using macros or shapeless. Reduces boilerplate by deriving implementations based on type structure.
Effect Systems track and manage side effects in type signatures. IO Monad encapsulates side effects, enabling pure functional programming. Examples include cats.effect.IO and ZIO.
Magnolia is a generic derivation library for type class derivation. Simpler alternative to Shapeless, using macros for automatic instance generation with less boilerplate.
Dependent Types are types that depend on values. Implemented using type members, singleton types, and path-dependent types. Enable encoding invariants in type system.
Traversal patterns process container types uniformly. Include Functor (map), Applicative (independent effects), and Traverse (sequencing effects). Enable generic container operations.
Category Theory concepts like Monads, Functors, and Applicatives provide abstractions for functional programming. Enable composition of effects and generic programming patterns.
Custom Monads implement flatMap and pure methods following monad laws. Used for custom effect types or domain-specific abstractions. Requires careful consideration of laws and composition.
Refined Types add compile-time predicates to basic types. Example: type PositiveInt = Int Refined Positive. Ensure constraints at compile time rather than runtime.
Tagless Final is a pattern for encoding DSLs using interfaces instead of ADTs. Enables multiple interpretations, better type inference, and separation of concerns.
Effect composition combines different effect types (IO, Future, Either) using monad transformers or effect types like ZIO. Manages multiple effects while maintaining type safety.