admittedly this example is confusing, since it combines several language features
- type parameters (generics)
- currying (=two argument lists)
- anonymous inline function (function block)
- implicit arguments (which are picked up from the context)
As always, when matters start to become complicated, lets' sort it out one by one
(1) A is a type parameter. This is replaced by a suitable type on each invocation of the function. Since A
is mentioned at two locations in the argument list and in the return type, this means that whatever type you use, the result type of the passed-in function block will be the same than the overall return type. You could explicitly define A
when using the function, but typically you can leave it out, since the compiler can infer the actual type used.
(2) currying is easy to understand. This function here just has two parameter lists. Which means you can apply this function in several steps. First apply the left parameter list:
def myBlock(connection:Connection):SQL =
SQL("select ......" .....
val pf = DB.withConnection(myBlock)
Question: what kind of Object is pf
? what is ists type?
Answer: it is a function, taking one argument, an Application object
Thus the type of pf would be Application => SQL
since in the first, partial application of the function, we just passed in another function with return type SQL, thus type parameter A
is inferred to be SQL
(3) but in the code above, we've defined the function myBlock
in a conventional fashion, and we gave it explicitly the name "myBlock". This isn't necessary. We can define the same function just inline, using the block syntax.
(4) and now the confusing, "magic" part, the implicits. This is a very special feature of Scala, where the compiler allows you to omit some values, or arguments (in our case). You may not omit arbitrary arguments, but only arguments marked as implicit
. When you do so, the compiler doesn't immediately generate an error; rather it looks in the current scope, if he can find some suitable other object with the same name.
Effectively this means, that in your example, there must somehow be an value "connection" of type Connection
, and there must be a value "application" of type Application
. Both values must be visible somehow in the current scope -- that is, either as parameter, as value in an enclosing scope, or as value in the companion object, or you might have brought them into scope explicitly with an import statement. The purpose of this language feature is just to save you typing those obvious arguments (application and connection9 again and again