16
votes

Is it possible to use something similar to where-clauses in Scala? Maybe there is some trick I didn't think of?

Edit:

Thanks for all your answers, they are very much appreciated. To sum up: Local vars, vals and defs can be used to achieve almost the same thing. For lazy evaluation, one can use lazy val (with implicit caching) or function definitions. Ensuring functional purity is left to the programmer.

Now just one question remains: Is there a way of putting the value or function definitions after the expressions they are used in? Sometimes that seems a lot clearer. This is possible with fields/methods of a class or object, but it doesn't seem to work within methods.

One other thing that wasn't mentioned in the answers so far. where-clauses also limit the scope of the expressions defined in them. I haven't found a way to achieve that in Scala either.

4
What's a Haskell where-clause?Daniel C. Sobral
It's a keyword that allows you to define "variables" (for lack of a better term) that are local to the function in which the where keyword is used.mipadi
Edited my answer, discussing scopes and definitions of functions after they are used. It can be done (...ish), but some of it isn't pretty.Flaviu Cipcigan
Edited my answer in response to your comment. As I have said, because of side effects, it's a bit awkward defining functions after the expression they are used, especially when those expressions must be evaluated in place!Flaviu Cipcigan
Somebody could implement the <smth> where <smth> construct using macros; also the Haskell do notation could be implemented like that—for-comprehensions are somewhat ugly for monadic code...Erik Kaplun

4 Answers

25
votes

In Hakell, where clauses hold local definitions to a function. Scala does not have explicit where clauses, but the same functionality can be achieved by having local var, val and def.

Local `var` and `val`

In Scala:

def foo(x: Int, y: Int): Int = {
  val a = x + y 
  var b = x * y
  a - b
}

In Haskell:

foo :: Integer -> Integer -> Integer 
foo x y = a - b
        where 
          a = x + y
          b = x * y

Local `def`

In Scala

def foo(x: Int, y: Int): Int = {
  def bar(x: Int) = x * x
  y + bar(x)
}

In Haskell

foo :: Integer -> Integer -> Integer 
foo x y = y + bar x
         where 
           bar x = x * x

Please correct me if I have made any syntax errors in the Haskell example, as I currently have no Haskell compiler installed on this computer :).

More complicated examples can be achieved in similar ways (for example using pattern matching, which both languages have support for). Local functions have exactly the same syntax as any other function, just that their scope is the block they are in.

EDIT: Also see Daniel's answer for such an example and some elaboration on the subject.

EDIT 2: Added a discussion about lazy vars and vals.

Lazy `var` and `val`

Edward Kmett's answer rightly pointed out that Haskell's where clause has laziness and purity. You can do something very similar in Scala using lazy variables. These are only instantiated when needed. Consider the following example:

def foo(x: Int, y: Int) = { 
  print("--- Line 1: ");
  lazy val lazy1: Int = { print("-- lazy1 evaluated "); x^2}
  println();

  print("--- Line 2: ");
  lazy val lazy2: Int = { print("-- lazy2 evaluated "); y^2}
  println();

  print("--- Line 3: ");
  lazy val lazy3: Int = { print("-- lazy3 evaluated ")
    while(true) {} // infinite loop! 
    x^2 + y^2 }
  println();

  print("--- Line 4 (if clause): ");
  if (x < y) lazy1 + lazy2
  else lazy2 + lazy1
}

Here lazy1, lazy2 and lazy3 are all lazy variables. lazy3 is never instantiated (therefore this code never enters in an infinite loop) and the order of instantiation of lazy1 and lazy2 depends on the arguments of the function. For example when you call foo(1,2) you will get lazy1 instantiated before lazy2 and when you call foo(2,1) you will get the reverse. Try the code out in the scala interpreter and see the printout! (I won't put it here as this answer is already quite long).

You could achieve similar results if instead of lazy variables you used no-argument functions. In the example above, you could replace every lazy val with a def and achieve similar results. The difference is that lazy variables are cached (aka only evaluated once) but a def is evaluated every time it is invoked.

EDIT 3: Added a discussion about scoping, see question.

Scope of local definitions

Local definitions have the scope of the block they are declared in, as expected (well, most of the time, in rare situations they can escape the block, like when using mid-stream variable binding in for loops) . Therefore local var, val and def can be used to limit the scope of an expression. Take the following example:

object Obj {
  def bar = "outer scope"

  def innerFun() {
    def bar = "inner scope"
    println(bar) // prints inner scope
  }

  def outerFun() {
    println(bar) // prints outer scope
  }

  def smthDifferent() {
    println(bar) // prints inner scope ! :)
    def bar = "inner scope"
    println(bar) // prints inner scope
  }

  def doesNotCompile() {
    { 
      def fun = "fun" // local to this block
      42 // blocks must not end with a definition... 
    }
    println(fun)
  }

}

Both innerFun() and outerFun() behave as expected. The definition of bar in innerFun() hides the bar defined in the enclosing scope. Also, the function fun is local to its enclosing block, so it cannot be used otherwise. The method doesNotCompile() ... does not compile. It is interesting to note that both println() calls from the smthDifferent() method print inner scope. Therefore, yes, you can put definitions after they are used inside methods! I wouldn't recommend though, as I think it is bad practice (at least in my opinion). In class files, you can arrange method definitions as you like, but I would keep all the defs inside a function before they are used. And vals and vars ... well ... I find it awkward to put them after they are used.

Also note that each block must end with an expression not with a definition, therefore you cannot have all the definitions at an end of a block. I would probably put all the definitions at the start of a block, and then write all my logic producing a result at the end of that block. It does seem more natural that way, rather then:

{
// some logic

// some defs

// some other logic, returning the result
}    

As I previously said, you cannot end a block with just // some defs. This is where Scala slightly differs from Haskell :).

EDIT 4: Elaborated on defining stuff after using them, prompted by Kim's comment.

Defining 'stuff' after using them

This is a tricky thing to implement in a language that has side effects. In a pure-no-side-effect world, the order would not be important (methods would not depend on any side-effects). But, as Scala allows side effects, the place where you define a function does matter. Also, when you define a val or var, the right hand side must be evaluated in place in order to instantiate that val. Consider the following example:

// does not compile :)
def foo(x: Int) = {

  // println *has* to execute now, but
  // cannot call f(10) as the closure 
  // that you call has not been created yet!
  // it's similar to calling a variable that is null
  println(f(10))

  var aVar = 1

  // the closure has to be created here, 
  // as it cannot capture aVar otherwise
  def f(i: Int) = i + aVar

  aVar = aVar + 1

  f(10)
}

The example you give does work though if the vals are lazy or they are defs.

def foo(): Int = {
  println(1)
  lazy val a = { println("a"); b }
  println(2)
  lazy val b = { println("b"); 1 }
  println(3)
  a + a
}

This example also nicely shows caching at work (try changing the lazy val to def and see what happens :)

I still thing in a world with side effects it's better to stick with having definitions before you use them. It's easier to read source code that way.

-- Flaviu Cipcigan

5
votes

Similar, yes. I won't go into details, as Flaviu has already, but I'll give an example from the Wikipedia.

Haskell:

calc :: String -> [Float]
calc = foldl f [] . words
  where 
    f (x:y:zs) "+" = (y + x):zs
    f (x:y:zs) "-" = (y - x):zs
    f (x:y:zs) "*" = (y * x):zs
    f (x:y:zs) "/" = (y / x):zs
    f xs y = read y : xs

Those definitions are just definitions local to calc. So, in Scala, we would do this:

def calc(s: String): List[Float] = {
  def f(s: List[Float], op: String) = (s, op) match {
    case (x :: y :: zs, "+") => (y + x) :: zs
    case (x :: y :: zs, "-") => (y - x) :: zs
    case (x :: y :: zs, "*") => (y * x) :: zs
    case (x :: y :: zs, "/") => (y / x) :: zs
    case (xs, y) => read(y) :: xs
  }

  s.words.foldLeft(List[Float]())(f)
}

Since Scala has no equivalent of read, you might define it as below, for the purpose of running this particular example:

def read(s: String) = s.toFloat

Scala doesn't have words either, much to my chagrin, though it's easily defined:

implicit toWords(s: String) = new AnyRef { def words = s.split("\\s") }

Now, Haskell's definition is more compact for various reasons:

  • It has a more powerful type inference, so nothing beyond the type of calc itself needed to be declared. Scala can't do that because of a conscious design decision to be object-oriented with the class model.

  • It has an implicit pattern matching definition, whereas, in Scala, you have to declare the function and then declare the pattern match.

  • Its handling of currying is just plain superior to Scala's, as far as conciseness goes. This is a result of various decisions, about the class model and the operator notation, in which handling currying was considered not as important.

  • Haskell has special treatment for lists, making it possible to have a more concise syntax for them. In Scala, lists are treated like any other class, the effort being made, instead, to ensure that any class can be as compact as List can be in Scala.

So, there are various reasons for why Scala does what it does, though I'd love an implicit pattern matching definition. :-)

4
votes

You can use var and val to provide local variables, but that is different than Haskell's where clause in two fairly important aspects: laziness and purity.

Haskell's where clause is useful because laziness and purity alows the compiler to only instantiate the variables in the where clause that are actually used.

This means you can write a big long local definition, and drop a where clause below it and there is no consideration needed for the order of effects (because of purity) and no consideration needed for if each individual code branch needs all of the definitions in the where clause, because laziness allows unused terms in the where clause to exist just as thunks, which purity allows the compiler to choose to elide from the resulting code when they aren't used.

Scala, unfortunately, has neither of these properties, and so cannot provide a full equivalent to Haskell's where clause.

You need to manually factor out the vars and vals that you use and put them in before the statements that use them, much like ML let statements.

3
votes

Haskell binds values to names with let and where expressions. I'm pretty sure that any where expressions can be standardized into a let expressions (regardless of evaluation order) before evaluation or code generation.

Scala encodes bindings with val statements inside a scope. The compiler assures that the value assigned to that name does not change. These appear to be let-like because they are executed in order first to last. This is contrary to what we want our code to read: the main idea shown first, and the supporting details expressed afterwards. This is the cause of our aesthetic burden.

In the spirit of standardizing where -> let, one way we might encode a where in Scala could be with macros (I didn't try it, just hypothesizing) EXPN1 where { EXPN2 } such that EXPN1 is any valid expression, and EXPN2 could be anything valid inside of an object declaration expanding to:

object $genObjectname { EXPN2 }
{ import $genObjectName._; EXPN1 }

Example use:

sausageStuffer compose meatGrinder where {
  val sausageStuffer = ... // you really don't want to know
  val meatGrinder = ... // not that pretty
}

I feel your pain. I'll get back to you if I ever make a working macro.