3

I am really new to scala, and I am currently making my way through the tour (https://docs.scala-lang.org/tour/variances.html).

Now, looking at some library (akka-http), I stumbled across some code like this:

def fetchItem(itemId: Long): Future[Option[Item]] = Future {
    orders.find(o => o.id == itemId)
  }

And I don't quite understand the syntax, or more precisely, the = Future { part. As I learned, the syntax for methods is def [methodName]([Arguments])(:[returnType]) = [codeblock].

However the above seems to differ in that its having the Future in front of the "codeblock". Is this some kind of object instantiation? Because I could not find documentation about this syntax, I tried in my play code stuff like this:

{
  val myCat:Cat      = new Cat("first cat")
  val myOtherCat:Cat = Cat { "second cat" }
  val myThirdCat:Cat = MyObject.getSomeCat
}

...

object MyObject
{
   def getSomeCat: Cat = Cat 
   {
     "blabla"
   }
}

And all of this works, in that it creates a new Cat object. So it seems like new Cat(args) is equivalent to Cat { args }.

But shouldn't def getSomeCat: Cat = Cat define a method with a code block, not the instantiate a new Cat object? I am confused.

user826955
  • 3,137
  • 2
  • 30
  • 71
  • In Scala, a single function parameter can be passed using `(..)` or `{..}`, the former one being used more when passing a lambda as parameter (high order function). So `Future { .. }` is similar to `Future(..)`, and both refer to `Future.apply`. – cchantep Jul 27 '18 at 10:15

3 Answers3

4

I think there are a couple of things here:

1. The [codeblock] in method syntax doesn't have to be enclosed in {}. If there's only one expression, it's allowed to omit them.

E.g.

def add(x: Int, y: Int) = x + y

or

def add(x: Int, y: Int) = Future { x + y }

2. Each class can have its companion object define an apply() method, which can be invoked without explicitly saying "apply" (this is special Scala syntactic sugar). This allows us to construct instances of the class by going through the companion object, and since "apply" can be omitted, at first glance it looks like going through the class itself, just without the "new" keyword.

Without the object:

class Cat(s: String)

val myFirstCat: Cat = new Cat("first cat") // OK
val mySecondCat: Cat = Cat("second cat") // error

And now with the object:

class Cat(s: String)

object Cat {
  def apply(s: String) = new Cat(s)
}

val myFirstCat: Cat = new Cat("first cat") // OK
val mySecondCat: Cat = Cat.apply("second cat") // OK 
val myThirdCat: Cat = Cat("third cat") // OK (uses apply under the hood)
val myFourthCat: Cat = Cat { "fourth cat" } // OK as well

Note how fourth cat invocation works just fine with curly braces, because methods can be passed codeblocks (last evaluated value in the block will be passed, just like in functions).

3. Case classes are another slightly "special" Scala construct in a sense that they give you convenience by automatically providing some stuff for you "behind the curtain", including an associated companion object with apply().

case class Cat(s: String)

val myFirstCat: Cat = new Cat("first cat") // OK
val mySecondCat: Cat = Cat.apply("second cat") // OK
val myThirdCat: Cat = Cat("third cat") // OK

What happens in your case with Future is number 2, identical to "fourth cat". Regarding your question about new Cat(args) being equivalent to Cat { args }, it's most likely situation number 3 - Cat is a case class. Either that, or its companion object explicitly defines the apply() method.

slouc
  • 9,508
  • 3
  • 16
  • 41
  • I'd replace "statement" by "expression" in the third sentence. See [FunDef](https://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html#function-declarations-and-definitions), it's an "Expr" on the right hand side: `FunDef ::= FunSig [‘:’ Type] ‘=’ Expr`. – Andrey Tyukin Jul 27 '18 at 10:47
  • This distinction makes much more sense in `C`. If it evaluates to a value, it's an expression. If it occurs in a list of statements inside a code block, it's a statement. But in Scala, everything is an example of everything else... even `if (1 < 2) { println(42) }`, which I would rather consider to be a "statement", with another "statement" inside of the block, it still "evaluates" to `()` in scala, so you can pass it as argument to functions, for example: `println(if (3 < 2) { println("blah") })` is valid... It's very blurry in Scala. – Andrey Tyukin Jul 27 '18 at 13:23
  • [Most upvoted answer by Joel Spolsky](https://stackoverflow.com/a/19224/2707792). – Andrey Tyukin Jul 28 '18 at 22:27
1

The short answer is Yes, that Future code is an object instanciation.

Your Cat class has a single String argument and can be created using Cat(<string>). If you want to compute a value for the string you can put it in a block using {} as you did in your example. This block can contain arbitrary code, and the value of the block will be the value of the last expression in the block which must be type String.

The Future[T] class has a single argument of type T and can be created using Future(T). You can pass an arbitrary block of code as before, as long as it returns a value of type T.

So creating a Future is just like creating any other object. The fetchItem code is just creating a Future object and returning it.

However there is a subtlety with Future in that the parameter is defined as a "call-by-name" parameter not the default "call-by-value" parameter. This means that it is not evaluated until it is used, and it is evaluated every time it is used. In the case of a Future the parameter is evaluated once at a later time and potentially on a different thread. If you use a block to compute the parameter then the whole block will be executed each time the parameter is used.

Scala has very powerful syntax and some useful shortcuts, but it can take a while to get used to it!

Tim
  • 26,753
  • 2
  • 16
  • 29
  • Thanks all for the responses, it helped me alot! I can only accept one answer, though you all explained it very well, so I am upvoting all of you :) – user826955 Jul 27 '18 at 12:45
1

A typical method structure will look like:

case class Bar(name: String)
def foo(param: String): Bar = {
  // code block.
}    

However, Scala is quite flexible when its comes to method definition. One of flexibility is that if your method block contains single expression, then you can ignore curly braces { }.

def foo(param: String): Bar = {
  Bar("This is Bar")  //Block only contains single expression.
} 
// Can be written as:
def foo(param: String): Bar = Bar("This is Bar")

// In case of multiple expressions:
def foo(param: String): Bar = {
  println("Returning Bar...")
  Bar("This is Bar")
} 
def foo(param: String): Bar = println("Returning Bar...") Bar("This is Bar") //Fails
def foo(param: String): Bar = println("Returning Bar..."); Bar("This is Bar") //Fails
def foo(param: String): Bar = {println("Returning Bar..."); Bar("This is Bar")} // Works

Similarly, in your code, fetchItem contains only single expression - Future {orders.find(o => o.id == itemId)} that return a new Future (instance of Future) of Option[Item], therefore braces { } is optional. However, if you want you can write it inside braces as below:

def fetchItem(itemId: Long): Future[Option[Item]] = {
    Future {
        orders.find(o => o.id == itemId)
    }
}

Similarly, if a method take only single parameter, you can use curly braces. i.e. you can invoke fetchItems as:

fetchItem(10)
fetchItem{10}
fetchItem{
    10
}

So, why use curly braces { } instead of brackets ( )?

Because, you can provide multiple code blocks inside braces, and this situation is required when need to perform multiple computation and return a value as result of that computation. For example:

fetchItem{
    val x = 2
    val y = 3
    val z = 2
    (x + y)*z  //final result is 10 which is passed as parameter.
}

// In addition, using curly braces will make your code more cleaner e.g. in case of higher ordered functions. 
def test(id: String => Unit) = ???
test {
    id => {
        val result: List[String] = getDataById(x)
        val updated = result.map(_.toUpperCase)
        updated.mkString(",")
    }
}

Now, coming to your case, when you invoke Future{...}, you are invoking apply(...) method of Scala future companion object that take function literal body: =>T as parameter and return a new Future.

//The below code can also be written as:
Future {
    orders.find(o => o.id == itemId)
}
//Can be written as:
Future(orders.find(o => o.id == itemId))
Future.apply(orders.find(o => o.id == itemId))

// However, braces are not allowed with multiple parameter.
def foo(a:String, b:String) = ???
foo("1","2") //work
foo{"1", "2"} //won't work.
Ra Ka
  • 2,995
  • 3
  • 23
  • 31