Object, Methods, and Fields in Scala

March 12, 2017


All values in Scala are objects. Conversley, an object is just a value. A value we can add other values to and reference via a name of our choosing. Objects can be constructed in one of two ways:

  • Using object literal syntax
  • Creating an instance object of a class using the new operator.

Since we haven't covered classes yet, I won't be going over that now, and you won't need to understand what a class is for this blog.

So what are objects in Scala anyways?

First, why should you know what an object is? We've seen how expressions can be used to bid our will in a program, and though you can get very far with just this, it is far from ideal. When trying to do complex task, it is very useful to be able to refer to our expressions to evaluate when and how we want, to group them. This is done with an object.

You might be surprised to know we've already covered many different objects. That is, objects we created using Scala's built in types (e.g. Int, Boolean, Long, String, etc.). I mentioned we'll be covering how to create objects using object literals. Object literals are created via declarations which essentially map a name of our choosing to a value. Here is a very simple object.

object Nick {}

scala> Nick
res0: Nick.type = Nick$@5c748168

object Nick is not an expression. It does not evaluate to a value. It does however, bind the name nick to the expression following the name Nick that I have chosen for my object. If you truely understood the previous blog, this shouldn't be very surprising to you. We are just binding the Name Nick to an expression block, which in turn is just a value! Believe it or not, this is a connection many experienced developers pass over.

Check out the type for our object Nick. It is Nick.type. This is new, we haven't seen this before. It is known as a singleton type. No other objects of this type can be created.

So, the object is just an expression block... now what? First off, it means that the object has it's own scope within the object. Good to know. You can even import code inside of your object. It also means that before we can do anything with the object, it needs to be evaluated. Scala does not evaluate objects until they are referenced in the code after their original definition. This is to say the object is lazily loaded. Lets thow in some stuff similar to expression blocks we've created before.

scala> object Nick {
     |         println("evaluating Nicks object!")
     |         "Hello World!"
     | }
<console>:13: warning: a pure expression does nothing in statement position; multiline expressions may require enclosing parentheses
            "Hello World!"
                ^
defined object Nick

scala> Nick
evaluating Nicks object!
res0: Nick.type = Nick$@76d05cc9

Very, very interesting right!? Okay, maybe it is just me. There are a few things to take away from this.

First. Heed the warning! The value this expression evaluates to does not do anything in statement position. This is in reference to the value of the expression referenced via the declaration. This is not incorrect code however, so the code compiles, and just gives us a warning letting us know that our little program will never actually get to say "Hello" to the world. Rude Scala, just rude.

Secondly, the print statement did output text when Nick was referenced. This should be expected, as when I referenced the object, the expression was merely being evaluated, so the print statement was run.

Now, you should be asking a very important question. If I can't reference the value of the object with the object's name, what are objects good for? This is where methods and fields come into play, and start offereing some very powerful tools to throw in your belt.

Fields

Fields allow you to reference other values within your object's value. There are two kinds of fields. val and var. val, or values in Scala are immutable. You can't reassign a value. This is best practice for assigning values to names, as it helps you as a developer avoid unintended consequences in your code. var, or variables are mutable, and can be reassigned. Here is a quick example to illustrate what I mean:

scala> val name = "Nick"
name: String = Nick

scala> name = "John"
<console>:12: error: reassignment to val
       name = "John"

Notice how I can't reassign name to a different value, because I declared name to be immutable, and can't be changed.

NOTE: In the scala shell - values can be reassigned like so:

scala> val name = "Nick"
name: String = Nick

scala> val name = "Nathan"
name: String = Nathan

BUT don't let this trick you! The shell is a little odd because each time you enter a line it has it's own scope... try doing this as a :paste (so it all gets evaluated in the same scope) and you'll see it doesn't work.

scala> :paste
// Entering paste mode (ctrl-D to finish)
val name = "Nick"
val name = "Nathan"
// Exiting paste mode, now interpreting.
<pastie>:12: error: name is already defined as value name
       val name = "Nathan"

As I mentioned, vars can be reassigned

scala> var name = "Nick"
name: String = Nick

scala> var name = "David"
name: String = David

Here is a quick example to see what that looks like to add fields to an object.

scala> object Nick {
     |   val firstName = "Nick"
     |   val lastName = "Brady"
     | }
defined object Nick

scala> Nick.firstName
res0: String = Nick

scala> Nick.lastName
res1: String = Brady

Hopefully you can already see how useful it is to start grouping values within an object. The last thing you must know about fields is the syntax for declaring them. It is as follows (note that ??? in Scala can be used as a placeholder for code in Scala.. handy!):

val valueName: type = { ??? }

or

var variableName: type = { ??? }

You might notice that I haven't used types to restrict my objects yet in an effort to keep thigns as simple as possible. In all future blogs I will likely define them for now on. As said previously, it is a way to provide extra checks on our program by telling it explicitly what we expect. Adding types to this object might look like this:

scala> object Nick {
         |   val firstName: String = "Nick"
         |   val lastName: String = "Brady"
         | }

Methods

Methods allow you to reference an expression within your object. This is a very subtle and important distinction. This means that expressions for methods are not evaluated until they are called. This should look very similar to fields:

scala> object Nick {
     |   def firstName = "Nick"
     |   def lastName = "Brady"
     | }
defined object Nick

scala> Nick.firstName
res0: String = Nick

scala> Nick.lastName
res1: String = Brady

Methods can also take parameters or arguments (terms I will use interchangably) which can be used in the scope, or context, of the expression they are assigned to.

The full syntax then, for declaring a method is:

def methodName(param: paramType, ...): type = { ??? }

Note that parameters are optional, and if none are provided, you may omit the parenthesis when referencing the expression making them behaving very similar to fields. IF parameters are provided however, types must be specified.

One thing I want to make clear. This behavior of methods behaving similar to fields is not an accident. This allows developers to interchange and swap the implementation of a method or a field without affecting other code. This is a feature in programming languages called the uniform access principle.

Here I'll create a method which helps Nick introduce himself. He's a very shy beast.

scala> object Nick {
     |   def firstName = "Nick"
     |   def lastName = "Brady"
     |   def introduce(name: String) = s"Hi $name, I am $firstName $lastName"
     | }
defined object Nick

scala>

scala> Nick
res4: Nick.type = Nick$@f9cab00

scala> Nick.introduce("Ryan")
res5: String = Hi Ryan, I am Nick Brady

Recap, difference between fields and methods

One question I found myself asking myself when I learned Scala is

"How are fields different than a method with no parameters?"

Let me explain. Say for this object:

object nick {
    def firstName = "Nick"
    val lastName = "Brady"
}
// defined object nick
nick.firstName
// Nick
nick.lastName
// Brady

Both the method (firstName, defined using a def) and the value (lastName, defined using val) both return the value of the expression assigned within the object on creation, so how are they different?

The difference is subtle. Here is another example which will make the differences more clear.

object Foo {
    val myField = {
        println("evaluating field expression")
        "my field woot woot"
    }
    def myMethod = {
        println("evaluating method expression")
        "my method woot woot"
    }
}

The expression assigned to myField is evaluated when the object is evaluated. The expression assigned to myMethod via a declaration is evaluated every time the method is called. So, you might ask, when is the object evaluated? As we've mentioned before, the object is evaluated when it is first referenced! When an object is loaded, all fields (val/var) are evaluated, to which the field references that value. So, lets reference our object then to see what happens.

scala> Foo
evaluating field expression
res0: Foo.type = Foo$@66e8997c

scala> Foo
res1: Foo.type = Foo$@66e8997c

Notice that after running Foo the first time, the output "evaluating field expression" was thrown into the console. This is because the expression for myField was evaluated. Also notice that the expression for myMethod was NOT evaluated. Only fields expressions are evaluated when an object is loaded.

If you were astute, you might have noticed that when I called Foo the second time, the print statement did not output anything to the shell. This is because the expression for myField was already evaluated. When the object is run again. the expression does not need to be reevaluated.

Lets actually interact with out object to illustrate the differences a little more.

scala> Foo.myField
res2: String = my field woot woot

scala> Foo.myMethod
evaluating method expression
res3: String = my method woot woot

scala> Foo.myMethod
evaluating method expression
res4: String = my method woot woot

There are a couple things of import here. First, when we referenced myField, the expression did not print. Again, this is because the expression has already been evaluated, so the field references and returns the value that the expression had already evaluated. Second, when we ran the expression for the method myMethod it did print the output. This is because when we referenced the method, the entire expression is run. When we rerun the method again, we again see the output because our method is just rerunning the same expression. That is, it is reevaluating the same expression.

Comment Enter a new comment: