
- 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 - 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 Collections
- Scala - Collections
- Mutable and Immutable Collections
- Scala - Lists
- Scala - Sets
- Scala - Maps
- Scala - TreeMap
- Scala - SortedMap
- Scala - Tuples
- Scala - Iterators
- Scala - Options
- Scala - Infinite Streams
- Scala - Parallel Collections
- Scala - Algebraic Data Types
- Scala Pattern Matching
- Scala - Pattern Matching
- Scala - Type Patterns
- Scala - Exception Handling
- Scala - Extractors
- Scala - Regular Expressions
- Scala Files I/O
- Scala - Files I/O
- 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 - Literal Type Arithmetic
- Scala - Inline keyword
- Scala - Def, Var & Val
- Scala - Dropped Features
- Scala - BDD Testing
Scala - Optional Braces
Scala 3 has rules on indentation and also has braces { … } as optional. There are some codes which can give warnings if they are not indented properly. Braces { … } are optional in Scala 3. Braces do not affect the meaning of a well-indented program in Scala 3. You can use `-no-indent` to turn off indentation mode.
Indentation Rules
There are two rules for indentation. If the program is not well-indented then there will be a warning by the compiler.
1. In a section enclosed by braces, no statement can begin before the first statement on a new line after the opening brace. This rule is used to find missing closing braces and can prevent errors.
For example,
if condition { print("This is indented correctly") print("This is indented too far to the left") # Error: IndentationError
2. When indentation is off or in Scala 2 mode, and a sub-part ends with a newline, the next statement should start with an indentation less than the sub-part to avoid missing an opening brace.
For example,
if condition print("This is indented correctly") print("This is also indented correctly") // error: missing `{`
These rules provide flexibility in indentation. You are not restricting expression indentation or demanding exact alignment within a block. These rules help to identify errors with missing braces. This can be challenging to spot in large programs.
Optional Braces
Compiler adds <indent> and <outdent> tokens at line breaks. These tokens work like { and }. Algorithm uses stack (IW) to track previous indentation widths. It starts with one element having zero width and current width is from the top of the stack.
These are two rules:
1. `<indent>` is added at a line break if
- Source can start indentation region at the current position and
- First token on the next line has greater indentation width.
Indentation starts:
- After an extension's leading parameters.
- After 'with' in an instance.
- After a ": at end of line" token.
- After certain tokens like '=', '=>', 'for', etc.
If <indent> is added, the next line token width is pushed onto IW.
2. `<outdent>` is added at a line break if
- Next line's token width is less than the current width.
- Previous line's last token is not 'then', 'else', 'do', etc.
- Next line first token is not a leading infix operator.
If `<outdent>` is added, top of stack IW element is removed. If the next line token width is still less than the new current width, then more <outdent> tokens can be added.
`<outdent>` is added if the next token after <indent> and statement sequence closes the indentation, like 'then', 'else', 'do', etc.
<outdent> is also inserted before a comma after an <indent> if the indented part is within parentheses.
An error occurs if the token after an <outdent> does not match the previous line indentation. For example, the following will be rejected.
if condition1 then result1 else // error: `else` does not align correctly result2
Indentation tokens are used where newline separates statements: top-level, within braces {...}, not inside parentheses (...), patterns, and types.
Optional Braces around Template bodies
In Scala grammar, class, trait, and object definitions are in 'template body' enclosed in braces. These braces can be optional according to rules.
If a template body can start and there is a ':' at the end of line and followed by indented statement, ':' is changed to ': at end of line.' The grammar can have optional ': at end of line' before a template body. Similar rules apply to enum bodies and local packages with nested definitions.
For example, these construct are valid:
trait Animal: def sound: String class Dog extends Animal: def sound = "Woof" object Cat: def sound = "Meow" enum Food: case Pizza, Burger, Salad new Animal: def sound = "Moo" package fruits: def apple = "Red" package vegetables: def carrot = "Orange"
In all cases, ': at end of line' can be replaced by braces without changing the meaning.
The syntax changes to allow this are as follows:
TraitTemplate ::= ExtendingTraits [colonEol] [TemplateContents] CaseClassDef ::= case id ClassParams ExtendingTraits [colonEol] CaseClassBody ObjectDefinition ::= 'object' Identifier [nl | colonEol] '{' Members '}' MethodDefinition ::= 'def' MethodName ParameterClauses [colonEol] '=' MethodBody
`colonEol` means ': at end of line,' as explained earlier. The lexer now reports ': at end of line' if it is valid next token for the parser.
Spaces Vs Tabs
Indentation uses spaces and/or tabs. Indentation widths are sorted by their prefixes. For example, '2 tabs, then 4 spaces' is shorter than '2 tabs, then 5 spaces'. But '2 tabs, then 4 spaces' cannot be compared with '6 tabs' and '4 spaces, then 2 tabs'. An error occurs if the indentation of a line cannot be compared to the width of the current area. To avoid errors, it is best not to mix spaces and tabs in the same file.
Indentation and Braces
Indentation can be mixed with braces {...}, brackets [...], and parentheses (...). Here are the rules:
1. For braces {...}, the assumed indentation is from the first new line's token after the opening brace.
2. For brackets [...] or parentheses (...):
- If the opening symbol is at the line's end, use the token after it.
- Otherwise, use the enclosing region's indentation.
3. When a closing brace }, bracket ], or parenthesis ) is found, <outdent> tokens are added to close all open nested indentation regions.
For example,
{ val a = calculate( a: Double, b => a * ( b + 2 ) + (a + a) ) }
Special Treatment of Case Clauses
`match` expressions and `catch` clauses have refined indentation rules:
- An indentation region starts after a 'match' or 'catch' when a 'case' follows at the current indentation width.
- It ends at the first token at the same width that's not 'case' or any token with a smaller width.
These rules permit match expressions with cases that are not indented. For example,
grade match case "A" => print("Excellent") case "B" => print("Good") case "C" => print("Satisfactory") case "D" => print("Needs improvement") case "F" => print("Fail") println(".")
The End Marker
Indentation-based syntax is useful, but it is challenging to know when a large region ends. Braces have the same issue. Scala 3 introduces an optional end marker to address this.
For example,
def complicatedFunction(...) = ... if someCondition then ... else ... // a lengthy block of code end if ... // additional code end complicatedFunction
End marker has 'end' and specifier (e.g., 'if', 'while'). Specifiers include keywords and identifiers. End markers can be used in statement sequences and the specifier should match the preceding statement.