tags:

views:

582

answers:

5

What are the precise rules for when you can omit (omit) parentheses, dots, braces, = (functions) etc?

For example (service.findAllPresentations.get.first.votes.size) must be equalTo(2)

  • service is my object
  • def findAllPresentations:Option[List[Presentation]]
  • votes returns List[Vote]
  • must and be are both functions of specs

Why can't I go:

(service findAllPresentations get first votes size) must be equalTo(2) ?

The compiler error is:

"RestServicesSpecTest.this.service.findAllPresentations of type Option[List[com.sharca.Presentation]] does not take parameters"

Why does it think I'm trying to pass in a parameter? Why must use dots for every method call?

Why does (service.findAllPresentations get first votes size) must be equalTo(2) result in:

"not found: value first"

Yet the "must be equalTo 2" of (service.findAllPresentations.get.first.votes.size) must be equalTo 2 i.e method chaining works fine? - object chain chain chain param

I've looked through the Scala book and website and can't really find a comprehensive explanation.

Is it in fact, as Rob H explains on http://stackoverflow.com/questions/1006967/scala-which-characters-can-i-omit , that the only valid use-case for omitting the '.' is for "operand operator operand" style operations, and not for method chaining?

+5  A: 

So, of course I've looked in a lot of places, before simply asking the question here.

This isn't exactly an explicit answer, but a collection of quotes giving insight into the various conditions...

Personally I thought there'd be more in the spec. I'm sure there must be, I'm just not searching for the right words...

There are a couple of sources however, and I've collected them together, but nothing really complete / comprehensive / understandable / that explains the above problems to me...:

"If a method body has more than one expression, you must surround it with curly braces {…}. You can omit the braces if the method body has just one expression."

http://programming-scala.labs.oreilly.com/ch02.html

"The body of the upper method comes after the equals sign ‘=’. Why an equals sign? Why not just curly braces {…}, like in Java? Because semicolons, function return types, method arguments lists, and even the curly braces are sometimes omitted, using an equals sign prevents several possible parsing ambiguities. Using an equals sign also reminds us that even functions are values in Scala, which is consistent with Scala’s support of functional programming, described in more detail in Chapter 8, Functional Programming in Scala."

http://programming-scala.labs.oreilly.com/ch01.html

"A function with no parameters can be declared without parentheses, in which case it must be called with no parentheses. This provides support for the Uniform Access Principle, such that the caller does not know if the symbol is a variable or a function with no parameters.

The function body is preceded by "=" if it returns a value (i.e. the return type is something other than Unit), but the return type and the "=" can be omitted when the type is Unit (i.e. it looks like a procedure as opposed to a function).

Braces around the body are not required (if the body is a single expression); more precisely, the body of a function is just an expression, and any expression with multiple parts must be enclosed in braces (an expression with one part may optionally be enclosed in braces)."

"Functions with zero or one argument can be called without the dot and parentheses. But any expression can have parentheses around it, so you can omit the dot and still use parentheses.

And since you can use braces anywhere you can use parentheses, you can omit the dot and put in braces, which can contain multiple statements.

Functions with no arguments can be called without the parentheses. For example, the length() function on String can be invoked as "abc".length rather than "abc".length(). If the function is a Scala function defined without parentheses, then the function must be called without parentheses.

By convention, functions with no arguments that have side effects, such as println, are called with parentheses; those without side effects are called without parentheses."

http://jim-mcbeath.blogspot.com/2008/09/scala-syntax-primer.html

"A procedure definition is a function definition where the result type and the equals sign are omitted; its defining expression must be a block. E.g., def f (ps) {stats} is equivalent to def f (ps): Unit = {stats}.

Example 4.6.3 Here is a declaration and a definition of a procedure named write:

trait Writer { 
def write(str: String) 
} 
object Terminal extends Writer { 
def write(str: String) { System.out.println(str) } 
}

The code above is implicitly completed to the following code:

trait Writer { 
def write(str: String): Unit 
} 
object Terminal extends Writer { 
def write(str: String): Unit = { System.out.println(str) } 
}"

From the language spec.

"With methods which only take a single parameter, Scala allows the developer to replace the . with a space and omit the parentheses, enabling the operator syntax shown in our insertion operator example. This syntax is used in other places in the Scala API, such as constructing Range instances:

val firstTen:Range = 0 to 9

Here again, to(Int) is a vanilla method declared inside a class (there’s actually some more implicit type conversions here, but you get the drift)."

http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6

"Now, when you try "m 0", Scala discards it being a unary operator, on the grounds of not being a valid one (~, !, - and +). It finds that "m" is a valid object -- it is a function, not a method, and all functions are objects.

As "0" is not a valid Scala identifier, it cannot be neither an infix nor a postfix operator. Therefore, Scala complains that it expected ";" -- which would separate two (almost) valid expressions: "m" and "0". If you inserted it, then it would complain that m requires either an argument, or, failing that, a "_" to turn it into a partially applied function."

"I believe the operator syntax style works only when you've got an explicit object on the left-hand side. The syntax is intended to let you express "operand operator operand" style operations in a natural way."

http://stackoverflow.com/questions/1006967/scala-which-characters-can-i-omit

But what also confuses me is this quote:

"There needs to be an object to receive a method call. For instance, you cannot do “println “Hello World!”" as the println needs an object recipient. You can do “Console println “Hello World!”" which satisfies the need."

Because as far as I can see, there is an object to receive the call...

http://www.scalaprogrammer.com/general/operator-notation-in-scala/

Antony Stubbs
Ok, so tried reading the Specs source to get some clues and woah. That's a great example of the problems with magic code - too many mixins, type inference and implicit conversions and implicit parameters. It's so hard to understand from the outside in! For big libraries like that, better tooling could do some wonders... one day...
Antony Stubbs
+1  A: 

Actually, on second reading, maybe this is the key:

With methods which only take a single parameter, Scala allows the developer to replace the . with a space and omit the parentheses

As mentioned on the blog post: http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6 .

So perhaps this is actually a very strict "syntax sugar" which only works where you are effectively calling a method, on an object, which takes one parameter. e.g.

1 + 2
1.+(2)

And nothing else.

This would explain my examples in the question.

But as I said, if someone could point out to be exactly where in the language spec this is specified, would be great appreciated.

Ok, some nice fellow (paulp_ from #scala) has pointed out where in the language spec this information is:

6.12.3: Precedence and associativity of operators determine the grouping of parts of an expression as follows.

  • If there are several infix operations in an expression, then operators with higher precedence bind more closely than operators with lower precedence.
  • If there are consecutive infix operations e0 op1 e1 op2 . . .opn en with operators op1, . . . , opn of the same precedence, then all these operators must have the same associativity. If all operators are left-associative, the sequence is interpreted as (. . . (e0 op1 e1) op2 . . .) opn en. Otherwise, if all operators are rightassociative, the sequence is interpreted as e0 op1 (e1 op2 (. . .opn en) . . .).
  • Postfix operators always have lower precedence than infix operators. E.g. e1 op1 e2 op2 is always equivalent to (e1 op1 e2) op2.

The right-hand operand of a left-associative operator may consist of several arguments enclosed in parentheses, e.g. e op (e1, . . . ,en). This expression is then interpreted as e.op(e1, . . . ,en).

A left-associative binary operation e1 op e2 is interpreted as e1.op(e2). If op is rightassociative, the same operation is interpreted as { val x=e1; e2.op(x ) }, where x is a fresh name.

Hmm - to me it doesn't mesh with what I'm seeing or I just don't understand it ;)

Antony Stubbs
hmm, to further add to the confusion, this is also valid: (((((realService findAllPresentations) get) first) votes) size) must be equalTo 2, but not if i remove any of those parenthesis pairs...
Antony Stubbs
+17  A: 

You seem to have stumbled upon the answer. Anyway, I'll try to make it clear.

You can omit dot when using the prefix, infix and postfix notations -- the so called operator notation. While using the operator notation, and only then, you can omit the parenthesis if there is less than two parameters passed to the method.

Now, the operator notation is a notation for method-call, which means it can't be used in the absence of the object which is being called.

I'll briefly detail the notations.

Prefix:

Only ~, !, + and - can be used in prefix notation. This is the notation you are using when you write !flag or val liability = -debt.

Infix:

That's the notation where the method appears between an object and it's parameters. The arithmetic operators all fit here.

Postfix:

That notation is used when the method follows an object and receives no parameters. For example, you can write list tail, and that's postfix notation.

You can chain infix notation calls without problem, as long as no method is curried. For example, I like to use the following style:

(list
 filter (...)
 map (...)
 mkString ", "
)

That's the same thing as:

list filter (...) map (...) mkString ", "

Now, why am I using parenthesis here, if filter and map take a single parameter? It's because I'm passing anonymous functions to them. I can't mix anonymous functions definitions with infix style because I need a boundary for the end of my anonymous function. Also, the parameter definition of the anonymous function might be interpreted as the last parameter to the infix method.

You can use infix with multiple parameters:

string substring (start, end) map (_ toInt) mkString ("<", ", ", ">")

Curried functions are hard to use with infix notation. The folding functions are a clear example of that:

(0 /: list) ((cnt, string) => cnt + string.size)
(list foldLeft 0) ((cnt, string) => cnt + string.size)

You need to use parenthesis outside the infix call. I'm not sure the exact rules at play here.

Now, let's talk about postfix. Postfix can be hard to use, because it can never be used anywhere except the end of an expression. For example, you can't do the following:

 list tail map (...)

Because tail does not appear at the end of the expression. You can't do this either:

 list tail length

You could use infix notation by using parenthesis to mark end of expressions:

 (list tail) map (...)
 (list tail) length

I hope this has cleared all the doubts. If not, just drop a comment and I'll see what I can do to improve it.

Daniel
ahh, so you're saying that in my statement: (((((realService findAllPresentations) get) first) votes) size) must be equalTo 2 - get, first, votes and size are all postfix operators, because they take no parameters? so I wonder what must, be and equalTo are...
Antony Stubbs
I'm saying nothing of the sort, though I'm pretty sure that must be the case. :-) "be" is probably a helper object, to make syntax prettier. Or, more specifically, to enable use of the infix notation with "must".
Daniel
Well get is Option.get, First is list.first, votes is a case class property and size is list.size. What do you think now?
Antony Stubbs
Now I think you got it right on. :-)
Daniel
Ah yes - this is all reinforced by the fact that "(realService findPresentation 1).get.id must be equalTo 1" works - as Service#findPresentations(id:Int) is an infix operator it seems. Cool - I think I get it now. :)
Antony Stubbs
+3  A: 

Ok, in the quest to compile a more complete, concise answer (if I've missed anything, or gotten something wrong/inaccurate please comment). Please note this isn't a language spec, so I'm not trying to make it exactly academically correct - just more like a reference card:


Class definitions:

Val or Var can be omitted from class parameters which will make the parameter private.

Adding var or val will cause it to be public (i.e. method accessors and mutators are generated).

{} can be omitted if the class has no body i.e.

class EmptyClass


Class instantiation:

Generic parameters can be omitted if they can be inferred by the compiler. However note, if you're types don't match, than the type parameter is always infered so that it matches. So without specifying the type, you may not get what you expect - i.e. given

class D[T](val x:T, val y:T);

This will give you a type error (Int found, expected String)

var zz = new D[String]("Hi1", 1) // type error

Where as this works fine:

var z = new D("Hi1", 1)
== D{def x: Any; def y: Any}

Because the type parameter T is inferred as the least common supertype of the two - Any.


Function definitions:

= can be dropped if the function returns Unit (nothing)

{} for the function body can be dropped if the function is a single statement, but only if the statement returns a value (you need the = sign)i.e.

def returnAString = "Hi!"

but this doesn't work:

def returnAString "Hi!" // compile error - '=' expected but string literal found."

The return type of the function can be omitted if it can be inferred (a recursive method must have it's return type specified)

() can be dropped if the function doesn't take any arguments i.e.

def endOfString {
  return "myDog".substring(2,1)
}

which by convention is reserved for methods which have no side effects - more on that later.

() isn't actually dropped per se when defining a pass by name parementer, but is actually quite symantically different notation i.e.

def myOp(passByNameString: => String)

Says myOp takes a pass by name parameter, which results in a String (i.e. it can be a code block which returns a string) as opposed to function parameters

def myOp(functionParam: () => String)

which says myOp takes a function which has zero parameters and returns a String.

(Mind you, pass by name parameters get compiled into functions, it just makes the syntax nicer.)

() can be dropped in the function parameter definition if the function only takes one argument e.g.:

def myOp2(passByNameString:(Int) => String) { .. } // - you can drop the ()
def myOp2(passByNameString:Int => String) { .. }

But if it takes more than one argument, you must include the ()

def myOp2(passByNameString:(Int, String) => String) { .. }


Statements:

. can be dropped to use Operator Notation, which can only be used for infix operators (operators of methods that take arguments). See Daniel's answer for more information.

. can also be dropped for postfix functions list tail

() can be dropped for postfix operators list.tail

() cannot be used with methods defined as:

def aMethod = "hi!" // missing () on method definition
aMethod // works
aMethod() // compile error when calling method

Because this notation is reserved by convention for methods that have no side effects, like List#tail (i.e. the invocation of a function with no side effects means that the function has no observable effect, except for its return value).

() can be dropped for Operator Notation when passing in a single argument

() may be required to use postfix operators which aren't at the end of a statement

() may be required to designate nested statements, ends of anonymous functions or for operators which take more than one parameter

When calling a function which takes a function, you cannot omit the () from the inner function definition e.g.:

def myOp3(paramFunc0:() => String) {
    println(paramFunc0)
  }
myOp3(() => "myop3") // works 
myOp3(=> "myop3") // doesn't work

When calling a function that takes a by-name parameter, you cannot specify the argument as a parameter-less anonymous function. E.g. given:

def myOp2(passByNameString:Int => String) {
  println(passByNameString)
}

You must call it as:

myOp("myop3")

or

myOp({
  val source = sourceProvider.source
  val p = myObject.findNameFromSource(source)
  p
})

but not:

myOp(() => "myop3") // does't work

IMO, over use of dropping return types can be harmful for code to be re-used. Just look at Specs for a good example of reduced readability due to lack of explicit information in the code. The number of levels of indirection to actually figure out what the type of a variable is can be nuts. Hopefully better tools can avert this problem and keep our code concise.

[1] http://hestia.typepad.com/flatlander/2009/01/scala-for-c-programmers-part-3-pass-by-name.html

Antony Stubbs
+2  A: 

There aren't any. You will likely receive advice around whether or not the function side-effects. This is bogus. The correction is to not use side-effects to the reasonable extent permitted by Scala. To the extent that it cannot, then all bets are off. All bets. Using parentheses is an element of the set "all" and is superfluous. It does not provide any value once all bets are off.

This advice is essentially an attempt at an effect system that fails (not to be confused with is less useful than other effect systems).

Try not to side-effect. After that, accept that all bets are off. Hiding behind a de facto syntactic notation for an effect system can and does, only cause harm.

Well, but that's the problem when you're working with a hybrid OO/ Functional language right? In any practical example you're going to want to have side effect functions... Can you point us to some information about "effect systems"? I think the more more to point quote is the "A function with no parameters can be declared without parentheses, in which case it must be called with no parentheses This provides support for the Uniform Access Principle, such that the caller does not know if the symbol is a variable or a function with no parameters.".
Antony Stubbs