Important

Variable and functions should be defined into object, class, function. You can’t define them on the root of a Scala file.

Basics

Types

In Scala, there is 5 major types

Type

Literal

Description

Boolean

true, false

Int

3, 0x32

32 bits integer

Float

3.14f

32 bits floating point

Double

3.14

64 bits floating point

String

“Hello world”

UTF-16 string

Variable

In scala, you can define variable by using the var keyword :

var number : Int = 0
number = 6
number += 4
println(number) // 10

Scala is able to infer the type automatically. You don’t need to specify it if the variable is assigned at declaration:

var number = 0   //The type of 'number' is inferred as a Int during the compilation.

But in fact, it’s not very common to use var in Scala. In place of var, the val is very often used. val allow you to define a constant value :

val two   = 2
val three = 3
val six   = two * three

Function

For example, if you want to define a function which return true if the sum of its two arguments is bigger than zero, you can do as following :

def sumBiggerThanZero(a: Float, b: Float): Boolean = {
  return (a + b) > 0
}

Then to call this function you can do as following :

sumBiggerThanZero(2.3f, 5.4f)

You can also specify arguements by name, which is useful if you have many arguements :

sumBiggerThanZero(
  a = 2.3f,
  b = 5.4f
)

Return

The return keyword is not necessary. In absence of it, Scala take the last statement of your function as returned value.

def sumBiggerThanZero(a: Float, b: Float): Boolean = {
  (a + b) > 0
}

Return type inferation

Scala is able to automatically infer the return type. You don’t need to specify it :

def sumBiggerThanZero(a: Float, b: Float) = {
  (a + b) > 0
}

Curly braces

Scala function doesn’t require to have curly braces if your function contain only one statement :

def sumBiggerThanZero(a: Float, b: Float) = (a + b) > 0

Function that return nothing

If you want a function to return nothing, the return type should be set to Unit. It’s equivalent to the C/C++ void.

def printer(): Unit = {
  println("1234")
  println("5678")
}

Arguements default value

You can specify a default value to each arguements of a function :

def sumBiggerThanZero(a: Float, b: Float = 0.0f) = {
  (a + b) > 0
}

Apply

Functions named apply are special because you can call them without having to type their name :

class Array(){
  def apply(index: Int): Int = index + 3
}

val array = new Array()
val value = array(4)   //array(4) is interpreted as array.apply(4) and will return 7

This concept is also applicable for scala object (static)

object MajorityVote{
  def apply(value: Int): Int = ...
}

val value = MajorityVote(4) // Will call MajorityVote.apply(4)

Object

In scala, there is no static keyword. In place of that, there is object. Everything defined into an object is static.

The following example define a static function named pow2 which take as parameter an floating point value and return a floating point as well.

object MathUtils{
  def pow2(value: Float): Float = value*value
}

Then you can call it by writing :

MathUtils.pow2(42.0f)

Entry point (main)

The entry point of a Scala program (the main function) should be defined into an object as a function named main.

object MyTopLevelMain{
  def main(args: Array[String]) {
    println("Hello world")
  }
}

Class

The class syntax is very similar to the Java one. Imagine you want to define an Color class which take as construction parameter three Float value (r,g,b) :

class Color(r: Float, g: Float, b: Float){
  def getGrayLevel(): Float = r * 0.3f + g * 0.4f + b *0.4f
}

Then to instantiate a the class from the previous example and use its gray function :

val blue = new Color(0, 0, 1)
val grayLevelOfBlue = blue.getGrayLevel()

Be careful, if you want to access a construction parameter of the class from the outside, this construction parameter should be defined as a val :

class Color(valr : Float, val g: Float, val b: Float){ ... }
...
val blue = new Color(0, 0, 1)
val redLevelOfBlue = blue.r

Inheritance

As an example, imagine you want to define an class Rectangle and a class Square which extends the class Shape :

class Shape{
  def getArea(): Float
}

class Square(sideLength: Float) extends Shape {
  override def getArea() = sideLength * sideLength
}

class Rectangle(width: Float, height: Float) extends Shape {
  override def getArea() = width * height
}

Case class

Case class is an alternative way of declaring classes.

case class Rectangle(width: Float, height: Float) extends Shape {
  override def getArea() = width * height
}

Then there is some differences between case class and class :

  • case class doesn’t need the new keyword to be instantiated

  • construction parameters are accessible from the outside, you don’t need to define them as val.

In SpinalHDL, for some reason explains into the coding conventions, it’s in general recommended to use case class instead of class to have less typing and more coherency.

Templates / Type parameterization

Imagine you want to design a class which is a queue of a given datatype, in that case you need to provide a type parameter to the class :

class  Queue[T](){
  def push(that: T) : Unit = ...
  def pop(): T = ...
}

If you want to restrict the T type to be a sub class of a given type (for example Shape), you can use the <: Shape syntax :

class Shape() {
    def getArea(): Float
}
class Rectangle() extends Shape { ... }

class  Queue[T <: Shape](){
  def push(that: T): Unit = ...
  def pop(): T = ...
}

The same is possible for functions :

def doSomething[T <: Shape](shape: T): Something = { shape.getArea() }