
- Scala - Home
- Scala - Overview
- Scala - Features
- Scala - Environment Setup
- Scala - Build Tool (SBT)
- Scala - REPL
- Scala - Dot & Dotty
- Scala - Basic Syntax
- Scala - Hello World Program
- Scala - Identifiers
- Scala - Keywords
- Scala - Comments
- Scala - Code Blocks
- Scala - Semicolon
- Scala - Constructs
- Scala - Expressions
- Scala - Input and Output
- Scala - Optional Braces
- Scala - Underscore (_)
- Data Types and Variables
- Scala - Data Types
- Scala - Type Bounds
- Scala - Context Bound
- Scala - Variances
- Scala - Type Hierarchy
- Scala - Variables
- Scala - Variable Scopes
- Scala - Literals
- Scala - Numeric Types
- Scala - Boolean Types
- Scala - Char Type
- Scala - Unit Types
- Scala - Strings
- Scala - Arrays
- Scala - Null Type
- Scala - Nothing
- Scala - Any Type
- Scala - AnyRef Type
- Scala - Unified Types
- Scala - Dates and Times
- Scala - Ranges
- Scala - Multidimensional Arrays
- Scala - WrappedArray
- Scala - StringBuilder
- Scala - String Interpolation
- Scala - StringContext
- Scala - Type Casting
- Scala var vs val
- Scala Operators
- Scala - Operators
- Scala - Rules for Operators
- Scala - Arithmetic Operators
- Scala - Relational Operators
- Scala - Logical Operators
- Scala - Bitwise Operators
- Scala - Assignment Operators
- Scala - Operators Precedence
- Scala - Symbolic Operators
- Scala - Range Operator
- Scala - String Concatenation Operator
- Scala Conditional Statements
- Scala - IF ELSE
- Scala - IF-ELSE-IF-ELSE Statement
- Scala - Nested IF-ELSE Statement
- Scala Loop Statements
- Scala - Loop Statements
- Scala - while Loop
- Scala - do-while Loop
- Scala - Nested Loops
- Scala - for Loop
- Scala - break Statement
- Scala - yield Keyword
- Scala Classes & Objects
- Scala - Classes & Objects
- Scala - Constructors
- Scala - Auxiliary Constructor
- Scala - Primary Constructor
- Scala - This Keyword
- Scala - Nested Classes
- Scala - Getters and Setters
- Scala - Object Private Fields
- Scala - Singleton Object
- Scala - Companion Objects
- Scala - Creating Executable Programs
- Scala - Stateful Object
- Scala - Enumerations
- Scala - Polymorphism
- Scala - Access Modifiers
- Scala - Apply Method
- Scala - Update Methods
- Scala - UnapplySeq Method
- Scala - Inheritance
- Scala - Extending a Class
- Scala - Method Overloading
- Scala - Method Overriding
- Scala - Generic Classes
- Scala - Generic Functions
- Scala - Superclass Construction
- Scala Methods & Functions
- Scala - Methods
- Scala - Functions
- Scala - Methods vs Functions
- Scala - Main Methods
- Scala - Functions Call-by-Name
- Scala - Functions with Named Arguments
- Scala - Function with Variable Arguments
- Scala - Recursion Functions
- Scala - Default Parameter Values
- Scala - Functions without Parameters
- Scala - Implicit Parameters
- Scala - Higher-Order Functions
- Scala - Nested Functions
- Scala - Extension Methods
- Scala - Anonymous Functions
- Partially Applied Functions
- Scala - Lazy Val
- Scala - Pure Function
- Scala - Currying Functions
- Scala - Control Abstractions
- Scala - Corecursion
- Scala - Unfold
- Scala - Tail Recursion
- Scala - Infinite Sequences
- Scala - Dynamic Invocation
- Scala - Lambda Expressions
- Scala - Polymorphic Functions
- Scala Collections
- Scala - Collections
- Mutable and Immutable Collections
- Scala - Lists
- Scala - Sets
- Scala - Maps
- Scala - TreeMap
- Scala - SortedMap
- Scala - Tuples
- Scala - Iterators
- Scala - Options
- Scala - NumericRange
- Scala - Infinite Streams
- Scala - Parallel Collections
- Scala Advanced Types
- Scala - Union Types
- Scala - Intersection Types
- Scala - Type Aliases
- Scala - Structural Types
- Scala - Match Expression
- Scala - Singleton Type Operator
- Scala - Abstract Types
- Scala - Dependent Types
- Scala - Abstract Type Bounds
- Scala - Higher-Kinded Types
- Scala - Opaque Type Alias
- Scala - Path-Dependent Types
- Scala - Type Lambdas
- Scala - Type Inference
- Scala - Algebraic Data Types
- Scala Pattern Matching
- Scala - Pattern Matching
- Scala - Guards
- Scala - Variables in Patterns
- Scala - Type Patterns
- Scala - The Matchable Trait
- Scala - Matching Arrays
- Scala - Matching Lists
- Scala - Matching Tuples
- Scala - Exception Handling
- Scala - Extractors
- Scala - Pattern Bindings
- Scala - Regular Expressions
- Scala - Case Classes
- Scala - Partial Functions
- Scala - Packaging and Imports
- Scala - Implicit Imports
- Scala - Export Clauses
- Scala - Nested Packages
- Scala - Chained Packages
- Scala - Package Objects
- Scala Files I/O
- Scala - Files I/O
- Scala - Writing Files
- Scala - Listing Files
- Scala - Deleting Directories
- Scala - Check File Exists
- Scala Advanced Concepts
- Scala - Closures
- Scala - Futures
- Scala - Promises
- Scala - Traits
- Scala - Trait Mixins
- Scala - Layered Traits
- Scala - Trait Linearization
- Scala - Sealed Traits
- Scala - Transparent Traits
- Scala - Process Management
- Scala - Scaladoc
- Scala - Literal Type Arithmetic
- Scala - Inline keyword
- Scala - Def, Var & Val
- Scala - Dropped Features
- Scala Unit Testing
- Scala - Unit Testing
- Scala - uTest
- Scala - MUnit
- Scala - ScalaTest Runner
- Scala - ScalaMock
- Scala - JUnit
- Scala - Mocking
- Scala - BDD Testing
Scala - Structural Types
Scala is primarily a nominally typed language. Types are equal only if these have the same name. But, sometimes, types with different names share common behaviors. And so, modifying the type hierarchy to create relationships between these types may not be possible. In such cases, Scala structural types provide solutions for writing polymorphic code that is checked at runtime for type safety with flexibility.
Using Structural Types for Type-Safe Database Access
One significant use case for structural types is modeling database access. In dynamically typed languages, it is easy to represent a row as a record (or object) and select entries using simple dot notation (e.g., row.columnName). But, achieving the same in statically typed languages like Scala requires defining a class for every possible row configuration and setting up a mapping scheme. But it leads to a lot of boilerplate code. It drives developers to use simpler but less type-safe schemes, like passing column names as strings (e.g., row.select("columnName")).
Structural types use dot notation in dynamic contexts without sacrificing the advantages of static typing. So, developers can use dot notation and configure how fields and methods should be resolved at runtime.
Structural Types in Action
Consider a scenario where you want to define a type Flyer for any object that has a fly method -
type Flyer = { def fly(): Unit } def callFly(thing: Flyer): Unit = thing.fly() def callFly2(thing: { def fly(): Unit }): Unit = thing.fly() def callFly3[T <: { def fly(): Unit }](thing: T): Unit = thing.fly()
In this example, we can declare a structural type in various contexts, like type aliases, method parameter types, and type bounds. The Scala compiler ensures that the required methods exist. So, it provides compile-time type safety while using reflection under the hood to call the methods.
Preventing Resource Leaks
A practical example of structural types is preventing resource leaks by ensuring resources are always closed. Instead of depending on try-catch blocks and conventions. You can use structural types to create a flexible control structure -
type Closable = { def close(): Unit } def using(resource: Closable)(fn: () => Unit): Unit = { try { fn() } finally { resource.close() } } using(new java.io.FileInputStream("file.txt")) { () => // Code using the file }
The using function closes resources after use. Structural types call the close method. The reflection-based call to close is efficient. Rest of the code inside the function remains statically typed.
Extending Structural Types
Scala structural types are implemented using the Selectable trait. So dynamic member selection through the selectDynamic method -
trait Selectable extends Any { def selectDynamic(name: String): Any def selectDynamicMethod(name: String, paramClasses: ClassTag[_]*): Any = new UnsupportedOperationException("selectDynamicMethod") }
The Selectable trait uses dynamic access to fields and methods. For instance, a record class can use Selectable to map field names to their values dynamically -
case class Record(elems: (String, Any)*) extends Selectable { private val fields = elems.toMap def selectDynamic(name: String): Any = fields(name) } type Person = Record { val name: String; val age: Int } val person = Record("name" -> "Emma", "age" -> 42).asInstanceOf[Person] println(s"${person.name} is ${person.age} years old.") // Emma is 42 years old.
Local and Anonymous Classes
Local and anonymous classes extending Selectable can have more refined types. So, its structural dispatch -
trait Vehicle extends reflect.Selectable { val wheels: Int } val i3 = new Vehicle { val wheels = 4 val range = 240 } println(i3.range) // Works because Vehicle extends Selectable
If Vehicle does not extend Selectable. Its accessing range would result in a compilation error.
Comparison with scala.Dynamic
While both structural types and scala.Dynamic give dynamic member selection. Structural types provide type safety with correspondence between structural type and underlying value. scala.Dynamic is used for more flexible reflective access operations but without the same level of type safety. Structural types, with the selectDynamic and applyDynamic methods. These provide more structured approach to dynamic member access.