Code block is not an object. There's no "thunk" in Scala -- you either
have a function, or you don't. A code of block is simply a syntactical
scope whose value is that of the last statement. So you can't
"convert" a code block into something.
>
> ()=>Unit implicitly converted to Runnable - does not work as expected
> - prints hello 1 once
"() => Unit" is a function with one parameter list of arity 0. A
Function0, in other words.
> implicit def funcToRunnable(x: => Unit): Runnable = new Runnable {
A "=> Unit" is not a function at all. "=> Unit" is the same thing as
"Unit", but the parameter is passed by name instead of by value. In
practice, it means whatever is passed in place of the parameter is not
first evaluated and then have its value passed but, instead, is passed
"as is", and evaluated it time it is used.
Anyway, "=> Unit" is different than "() => Unit".
> println("converting...")
> def run = {
> x
> }
> }
>
> def playRunnableImplicit() {
> println("playRunnableImplicit")
> playRunnable({
> println("hello 1")
> println("hello 2")
> })
So, the parameter is { println("hello 1"); println("hello 2") }, which
does not conform to Runnable. However, there's a conversion from Unit
to Runnable, and the type of that parameter is Unit. The value of a
block is the value of it's last statement, but since Unit is being
passed by name, println("hello 2") is passed instead of just ()
(return value of println, not to be confused with empty parameter
list).
> }
At any rate, even if what you want were doable, it wouldn't be a good
idea. That's a gratuitous implicit conversion, saving a single token.
If, instead, you named funcToRunnable just "runnable", you could then
write
playRunnable(runnable { print("hello 1"); print("hello 2") })
No implicits required, and very little syntactic overhead.
--
Daniel C. Sobral
I travel to the future all the time.
def block2fun0[T](block: => T): () => T = () => block
Functions and methods are different things. Do not confuse them.
Functions must ALWAYS get at list a 0-arity parameter list, because
that's how they "apply" syntactic sugar gets invoked.
> The case with RunnableString clearly works as scala considers the
> block
> { s: String =>
> println("hello 1 " + s)
> println("hello 2 " + s)
> }
> as a function, while it does not consider the block
> {
> println("hello 1")
> println("hello 2")
> }
> as a function
Because it has no parameter list. This would be a function:
{
() =>
println("hello 1")
println("hello 2")
}
>
I meant "at _least_ a 0-arity parameter least" here.
This is pretty much what he had, but he is trying to make it apply
implicitly, which causes problem as I described. Specifically:
scala> implicit def block2fun0[T](block: => T): () => T = () => block
block2fun0: [T](block: => T)() => T
scala> val f: () => Unit = {
| println("hello 1")
| println("hello 2")
| }
hello 1
f: () => Unit = <function0>
scala> f()
hello 2
You will *not* understand as long as you persist in thinking of "code
block". While there's something in Scala code block, it is not what
you think it is. So, first, make it clear to yourself: THERE IS NO
SUCH THING AS A CODE BLOCK. Convince yourself of that. If you start
reading the code and think "code block", stop and say that again:
there is no such thing as a code block. Once you take that completely
out of your mind, you can go back to the code:
{ s: String =>
println("hello 1 " + s)
println("hello 2 " + s)
}
This is a function literal. Function literals are either simple
expressions or delimited by () or {}, and they either have "_" in them
as placeholder parameters, or they start with "parameters =>", where
"parameters" may be a single identifier, with or without type, or a
list of identifiers inside parenthesis. Again, this is a function --
learn to recognize what marks a function.
{
println("hello 1")
println("hello 2")
}
This is nothing. It is just a couple of statements inside a scope delimiter.
After you have that fully understood, then you can start thinking of
"code block" as a delimited list of declarations and statements with a
value.
{
println("hello 1")
println("hello 2")
}This is nothing. It is just a couple of statements inside a scope delimiter.
I have explained it in two different ways already, but I'll try again.
Remember, first, that "=> Unit" is *not* a function. It is a parameter
passed by-name -- go look on wikipedia about that.
So, the first thing to notice is that there is no function anywhere,
and "doFunction" is incorrectly named. So, since this code has no
function in it, we can move ahead and understand how it works. If you
can't let go of the fact it has no function, then go back, re-read the
spec, re-read stuff I wrote... hell, read stack overflow about it.
There's no function signature anywhere, therefore there is no
function.
NEXT, let's see how it works:
def doFunction(f: => Unit) = { println("got " + (f _)) ; f ; f }
doFunction { println("1"); println("2") }
The grossly misnamed "doFunction" method takes a by-name Unit
parameter "f". That means that everywhere "f" appears, it is replaced
with what was passed. That means it will do this:
{ println("got " + ({ println("1"); println("2") } _)) ; {
println("1"); println("2") } ; { println("1"); println("2") } }
You might now try to take this opportunity to point out that it
printed "got <function0>", so it must be a function. Well, I've got
two words for you: eta expansion. When you wrote "f _", Scala created
a function for you. That's what it does when you write "f _". You'd
have a stronger argument if you decompiled doFunction and showed that
it expected a function, but that's implementation, and, in fact, it is
just an interface name.
So, let's proceed:
doSam(body2sam({ println("1"); println("2") }))
becomes, because of body2sam's by-name parameter,
doSam(new SAM {
override def onSam = { println("1"); println("2") }
})
Finally, this:
doSam { println("1"); println("2") }
So, doSam does not receive a parameter by name, which means "{
println("1"); println("2") }" gets evaluated before being passed. It's
value is defined to be the value of the last statement, println("2").
Since it is not a SAM, Scala checks if it conforms to SAM through an
implicit conversion (or other means), which, in fact, it does. So it
becomes:
doSam { println("1"); body2sam(println("2")) }
All of which makes perfect sense and, while might be surprising the
first time you see it, can be easily deduced from the specification.
But only if you do pay attention to the specification, and stop
thinking of "block of code" as some sort of equivalent of a function,
never mind confusing by-name parameters with functions.
>
> AFAICT, the "couple of statements inside a scope delimiter" can indeed be a
> function, as the call to doFunction shows.
> It's quite odd that the same syntax (doFunction { ... } vs. doSam { ... })
> results in very different behavior depending on whether an implicit
> conversion is involved.
There's no oddity here -- it only looks odd because of your
presumption that there's a function here.
Actually for those cases the reader can't expect to much as long as
implicits and by-name parameters are concerned because at the
call-site those are completely transparent. To understand a program
completely the reader has to know at least the signatures of the
callees and which implicits are in scope. With his knowledge of how
implicit conversions work he can then infer what is happening.
But those are just artifacts of a powerful, generic language where
libraries can (re-)define semantics to a certain degree.
I agree that implicits taking a by-name parameter are a potential
pitfall and seldom useful and if something can be done against this it
would probably be putting a warning at the definition site of an
implicit conversion with a by-name parameter.
--
Johannes
-----------------------------------------------
Johannes Rudolph
http://virtual-void.net
Because { println("1"); println("2") } is not a function!
Let me try this in a different way...
scala> { println("1"); println("2") } == println("2")
1
2
2
res0: Boolean = true
Do you consider this result (true) to be correct or incorrect?
So, finally we arrive at the crucial point: as I tried to explain
earlier, this special exception is in place so that libraries can
provide methods which "feel like" control constructs. It is a conscious
design decision which is immensely important if you want to write nice
DSLs, one of Scala's central strengths.
Scala is a hammer which fits many a nail. But if your special nail
absolutely requires that { ... } never is a function, then by all means
choose a different tool.
Regards,
Roland
On 25/07/11 23:28, Yoav Abrahami wrote:
> My point is that in a programming language, a code fragment (by
> whatever name) should be understood by the compiler and developers
> as the same thing, always.
Scala is not a lazy, pure functional programming language. Just sayin'
- --
Tony Morris
http://tmorris.net/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk4tfOEACgkQmnpgrYe6r6052wCfe8aJOzvN56pBltyQTid3UZsU
Yp4An24ZfWsNNBvOFHiqXa86B/aVbBpd
=fnZG
-----END PGP SIGNATURE-----
Thanks for taking the time to do so.
> Remember, first, that "=> Unit" is *not* a function.
Hm...okay, I guess that makes sense.
I still find myself tempted to think of them that way, especially for
DSLs and custom control structures where they're used (abused?) to get
syntactically-cheap functions, e.g.:
def main(args: Array[String]): Unit = {
bar { println("1"); println("2") }
bar { _ => println("1"); println("2") }
bar { s => println(s); println(s) }
}
def bar(f: String => Unit): Unit = f("hi")
def bar(f: => Unit): Unit = bar(_ => f)
Where I want the first bar call to be considered, effectively, as a
function that ignores its parameter. Here it works as expected, and is
what I was attempting to emulate against an API that takes a SAM.
Pimping the API seems to be the better way to go.
- Stephen
But body2sam receives a parameter by name -- you can't use by name
parameters and ignore how they work. You are being explicit in that
println("1") is part of it. Implicitly, it becomes:
{ println("1"); body2sam(println("2")) }
This is all according to the rules. The problem is not with the rules,
the problem is with your expectation that body2sam would apply over
the whole block, instead of its value. And that expectation only
exists because you insist of thinking of the block as a kind of "block
of code" object.
Don't go overboard with implicit conversions. It costs very little to
put a "bar" in front of the block, so do that.
Yes, there must have been some misunderstanding on my part along the
way: allowing by-name parameters on implicit conversions is
questionable. So, as long as by-name parameters in general remain
supported, we are in agreement ;-)
BTW: can someone think of a non-confusing use-case for conversions
taking by-name arguments?
Regards,
Roland
https://issues.scala-lang.org/browse/SI-3237
-Mark
Watch out, you almost said it was "complex". I new here, but even I
know better than that ;)
I'm not saying they are the same, I'm saying they are different. When
you use implicits, it is the latter version that is used. When you do
it explicitly, you can do it any way you want, and the example
happened to use the former.
> My observation is that if we apply body2sam as an implicit conversion
> on {println("1"); println("2")}, the compiler sees code written as the
> second example, not the first.
> I understand that the compiler has a hard life in this case, because
> when it looks were the body2sam conversion fits the code, it finds two
> places.
> adding body2sam to the code {println("1"); println("2")} can be done
> as
> body2sam({println("1"); println("2")})
> or
> {println("1"); body2sam(println("2"))}
> And those two ways are different!
> In such a case, we have ambiguity and the compiler must issue a
> compilation error. The fact that it does not means there is something
> broken with the Scala compiler.
There are many ways things can be written in different manners with
different meanings and still be valid, why would this cause a warning?
There's no ambiguity here -- there's need of a Unit, and the last
statement of the block provides one. Since the parameter is by name,
it passes that statement by name.
The only "ambiguity" here is "I'd like it to do something else." Well,
it doesn't, but it is not ambiguous.
IO is associative but does not commute.
Yoav, I suggest trying your examples in a language where the
type-checker makes some of your observations more apparent. You are
right of course, but it can be made more obvious with a type checker
that delineates between these differents type of value.
- --
Tony Morris
http://tmorris.net/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk4t32AACgkQmnpgrYe6r60amwCglMj/LSpkS249MilmVv1wlvs3
Y+IAn1bvKPgMASJLz/YvXbGeoDTVQkpz
=RVW0
-----END PGP SIGNATURE-----