DelayedInit
DelayedInit was introduced in 2.9 to fix Application and generally to
be able to inject some bindings into the scope of a block. The idea
was that the block would be really a template of a class or object,
that the binding would be introduced via inheritance, and that we
could get out of the straightjacket of running the template code in a
constructor through DelayedInit. I believe we will have with macros a
better way to do the binding injection, which has the advantage that
we can use real blocks of code and not use templates. Here's an
example of what I mean:
One use case of DelayedInit was that we should be able to write:
new Transaction { << code >> }.start
where the idea of defining <<code>> in the Transaction template was
that, that way, <<code>> could automatically inherit an (implicit)
transaction context. But we really want to run <<code>> outside of the
constructor, hence Transaction would inherit from DelayedInit. I think
with macros we can achieve the same effect in a much simpler way:
atomic { << code >> }
The transaction context would be injected by AST manipulation.
That leaves us with the problem how to simulate App going forward. I
hope that in the future that can also be done with a (type) macro. But
details would need to be worked out. If this would not work out I'd
prefer to special case App in the compiler rather than support the
DelayedInit feature. An alternative, which I believe we should have
done from the start, but which might introduce too much change now,
would be to write an application as follows:
object Test extends App {
def main() {
...
}
}
That means, the only thing App would buy us would be the ability to
define a zero-parameter main method. That could be called from the
standard main that has an Array[String] parameter, and no delayed init
tricks would be needed (My fingers always stumble when I write the
(args: Array[String]) bit.). In retrospect, I believe this would have
been the best scheme, but I am not sure we can do it now. Maybe that's
too much code to change for something too trivial.
But in summary, I believe we should get rid of DelayedInit if macros
are accepted into the language.
View Bounds
It seems perverse that we would have syntactic sugar for implicit
conversions that we propose to put under a feature flag because they
are so easily misused. So I propose to deprecate view bounds.
Your thoughts?
Cheers
- Martin
Speaking of syntactic sugar. Will we need a flag to declare an implicit class?
I don't think we should need a flag for that (provided SIP 13 is accepted)
Cheers
- Martin
--
Martin Odersky
Prof., EPFL and Chairman, Typesafe
PSED, 1015 Lausanne, Switzerland
Tel. EPFL: +41 21 693 6863
Tel. Typesafe: +41 21 691 4967
martin odersky wrote :
> But in summary, I believe we should get rid of DelayedInit if macros
> are accepted into the language.
I agree.
> View Bounds
>
> It seems perverse that we would have syntactic sugar for implicit
> conversions that we propose to put under a feature flag because they
> are so easily misused. So I propose to deprecate view bounds.
>
> Your thoughts?
Are they problematic in any way? Deprecating them seems drastic. If we must have feature flags, why not hide them behind the same flag as implicits?
What about context bounds? Are implicit values perceived as similarly "easily misused" as implicit conversions? I guess not, but just asking anyway. :)
Kind regards
Andreas
Getting rid of them would simplify the language, which is something
I'm very keen to do.
> What about context bounds? Are implicit values perceived as similarly "easily misused" as implicit conversions? I guess not, but just asking anyway. :)
>
No context bounds are essentially the replacement of view bounds. It's
what we should have done from the start, but we did not know better
back then. So certainly no wish to deprecate them
Cheers
- Martin
> Kind regards
> Andreas
I think deprecating it before type macros come in is too soon. That is
balanced by the fact that, if we are going to deprecate it, better do
it now. Perhaps even on 2.9.2 -- as long as people don't have to see
deprecation messages for using App.
> View Bounds
>
> It seems perverse that we would have syntactic sugar for implicit
> conversions that we propose to put under a feature flag because they
> are so easily misused. So I propose to deprecate view bounds.
Yeah, view bounds are dangerous, "T <% Ordered[T]" is still the way to
go about Ordered, which is much more convenient than Ordering. I
wouldn't do it before the ceremony of type classes gets decreased.
--
Daniel C. Sobral
I travel to the future all the time.
Your thoughts?
Cheers
- Martin
Right. That sounds like a good plan then, but the cost of migration might be pretty high.
Cheers
- Martin
>>
>>
>> Your thoughts?
>>
>> Cheers
>>
>> - Martin
>
>
>
>
> --
> Viktor Klang
>
> Akka Tech Lead
> Typesafe - The software stack for applications that scale
>
> Twitter: @viktorklang
>
--
Cheers
- Martin
>>
>>
>> Your thoughts?
>>
>> Cheers
>>
>> - Martin
>
>
>
>
> --
> Viktor Klang
>
> Akka Tech Lead
> Typesafe - The software stack for applications that scale
>
> Twitter: @viktorklang
>
--
Martin Odersky
Prof., EPFL and Chairman, Typesafe
PSED, 1015 Lausanne, Switzerland
Tel. EPFL: +41 21 693 6863
Tel. Typesafe: +41 21 691 4967
I actually found nothing in that thread that could be constructed as
an argument against JavaConversions.
- Martin
def foo[T <% Ordered[T]](x: T, y: T) = x < y
To get rid of the view bound, define a type alias
type OrderedView[T] = T => Ordered[T]
and rewrite foo to:
def foo[T: OrderedView](x: T, y: T) = x < y
The expanded result is exactly the same and it actually looks nicer!
Cheers
- Martin
--
And I also think we can get something that works like App in all
normal circumstances, and that gives a compile-time error where this
fails.
Cheers
- Martin
> DelayedInit actually saved the day for me when you introduced it because it
> allowed the creation of user defined "contexts" with "before" methods:
>
> // example of specification code
> "This is an example" in new UserContext {
> userName must have size(5)
> }
>
> // by extending Before and providing the 'before' method an action will be
> executed before each
> // example using the UserContext
> trait UserContext extends Before {
> def before = prepareDatabase
> def userName = Database.getUser.name
> }
>
> // FROM SPECS2
> /**
> * This trait adds the possibility to execute the before behavior before
> the body of the context.
> *
> * Since the delayedInit method doesn't return a Result, this only works
> with mutable
> * specifications where results are thrown as exceptions
> */
> trait Before extends org.specs2.specification.Before with DelayedInit {
> override def delayedInit(x: => Unit): Unit = { before; x }
> }
>
Interesting use case! To do exactly the same we'd need type macros,
which do not yet exist. It seems you could do it with the current
macro scheme by converting the specification language to something
like that:
"This is an example" in make[UserContext] {
userName must have size(5)
}
make could be a macro that creates the following expression:
new UserContext {
before
userName must have size(5)
}
Cheers
- Martin
OK I see now. That because Java's collection libraries have chosen to
make get less type-safe than Scala's, with get taking an Object
parameter no matter what the key type of the map is. By importing from
JavaConversions you subscribe to the Java model of things even for
Scala collections.
It's annoying, but I don't think it's a killer if documented clearly.
I believe for people who work with Java collections instead of Scala
ones JavaConversions is still very useful.
One possible improvement would be to split JavaConversions into two
objects: JavaAsScala and ScalaAsJava. That would be more targeted
because then code that just wanted to use Java collections would not
pay the penalty of having Scala collections be converted by accident
to Java collections. If we do that we could deprecate JavaConversions
subsequently.
Cheers
- Martin
Yes, I think that would be the right thing to do.
Cheers
- Martin
Cool! -- Martin
How would you do that without untyped macros? Under the current
proposal, the macro argument has to type-check before the macro gets a
chance to modify it so it couldn't use any implicit value provided by
the macro (unless you provide a dummy value in an outer scope, but that
defeats the purpose of using DelayedInit or macros).
> That leaves us with the problem how to simulate App going forward.
I quite like the current App. But for a solution that would simplify the
language instead of complicate it, have you considered adding top-level
method definitions?
package com.example.myapp
@App
def Main(args: String...) =
println("Hello, World!)
Compilation wouldn't be straight-forward (perhaps you could turn such a
def into an object with an apply method?) but usage should be. A special
annotation (implemented as a compiler plugin or as an annotation macro)
could generate a static "main" forwarder and also hide some of the Java
ugliness, e.g. support varargs instead of Arrays and discard non-Unit
return values.
> View Bounds
>
> It seems perverse that we would have syntactic sugar for implicit
> conversions that we propose to put under a feature flag because they
> are so easily misused. So I propose to deprecate view bounds.
I see them as complimentary to context bounds. While context bounds are
often the right thing to use, they don't replace view bounds, and there
are still some situations where context bounds are not a good choice.
When we only had a hammer, all problems looked like nails, but now that
we also have a screwdriver, some problems still look like nails :)
-sz
In some instances (like the one above) we can make it work with
default arguments. But in general we'll need macros with untyped
arguments for it, that's true.
- Martin
I missed your earlier reply to Josh about this. With the type alias
trick, that point is moot. You can indeed replace all view bounds by
context bounds (which are conceptually much easier to understand IMHO).
-sz
It's actually very easy to get rid of view bounds. Here's an example:
A method with a view bound:
def foo[T <% Ordered[T]](x: T, y: T) = x < y
To get rid of the view bound, define a type alias
type OrderedView[T] = T => Ordered[T]
and rewrite foo to:
def foo[T: OrderedView](x: T, y: T) = x < y
The expanded result is exactly the same and it actually looks nicer!
It's actually very easy to get rid of view bounds. Here's an example:
A method with a view bound:
def foo[T <% Ordered[T]](x: T, y: T) = x < y
To get rid of the view bound, define a type alias
type OrderedView[T] = T => Ordered[T]
and rewrite foo to:
def foo[T: OrderedView](x: T, y: T) = x < y
The expanded result is exactly the same and it actually looks nicer!
> def foo[T](x: T, y:T)(implicit t2o: T => Ordered[T]) = x < y
>
Or inline:
def foo[A: ({type L[X] = X => Ordered[X]})#L](a: A, b: A) = a < b
That's equivalent, no?
Rex Kerr wrote:
> Indeed, but the syntax is sufficiently clunky as to be less compact (and possibly less comprehensible to beginners, assuming that they hit implicits pretty early in the learning process) than the explicit implicit version.
>
> So without cleaning up the syntax for in-line type definitions, I wouldn't recommend those either. (But they're good to know about.)
You're absolutely right. I still have some limited hope that inline type definitions get some syntax love at some point, though. :) At least for partial application of type constructors.
Kind regards
Andreas
Deprecating without offering the alternative is meaningless, unless you intend to simply remove the feature. What are people supposed to do with deprecating warnings but replace the code? And what would they replace the code with?
scala> def f[T <% Int](x: T): Int = x
f: [T](x: T)(implicit evidence$1: T => Int)Int
scala> def f[K, V, T <% Map[K, V]](map: T, k: K) = map(k)
f: [K, V, T](map: T, k: K)(implicit evidence$1: T => Map[K,V])V
scala> def f[K, V, T : ({ type L[X] = T => Map[K, V] })#L](map: T, k: K)
= map(k)
f: [K, V, T](map: T, k: K)(implicit evidence$1: T =>
scala.collection.immutable.Map[K,V])V
A nicer syntax for type lambdas would help. The way these signatures
look, I'd write the desugared form directly.
-sz
On 2012-03-20 23:21, martin odersky wrote:I quite like the current App. But for a solution that would simplify the language instead of complicate it, have you considered adding top-level method definitions?That leaves us with the problem how to simulate App going forward.
package com.example.myapp
@App
def Main(args: String...) =
println("Hello, World!)
Compilation wouldn't be straight-forward (perhaps you could turn such a def into an object with an apply method?) but usage should be. A special annotation (implemented as a compiler plugin or as an annotation macro) could generate a static "main" forwarder and also hide some of the Java ugliness, e.g. support varargs instead of Arrays and discard non-Unit return values.
On Wed, Mar 21, 2012 at 4:31 AM, martin odersky wrote:It's actually very easy to get rid of view bounds. Here's an example:
A method with a view bound:
def foo[T <% Ordered[T]](x: T, y: T) = x < y
To get rid of the view bound, define a type alias
type OrderedView[T] = T => Ordered[T]
and rewrite foo to:
def foo[T: OrderedView](x: T, y: T) = x < y
The expanded result is exactly the same and it actually looks nicer!
That's great for a larger library. For something small, it's twice as much work (as you have shown with your example above). Also, it separates what you have to look at into two places: the method and the type declaration. That slows down the rate at which you can comprehend unfamiliar code.
Until you use the same bound three or four times, you should just desugar:
def foo[T](x: T, y:T)(implicit t2o: T => Ordered[T]) = x < y
And without the need for top-level defs, how about this:
@App
class Main(args: String...) {
println("Hello, World!)
}
Again, the @App annotation makes the compiler (or annotation macro)
generate a static main method that instantiates the class. The class
needs to have a constructor which takes either an Array[String], a
Seq[String], or a String*.
-sz
scala> type IntView[T] = T => Int
defined type alias IntView
scala> def f[T: IntView](x: T): Int = x
f: [T](x: T)(implicit evidence$1: T => Int)Int
The second one cannot be abstracted into a context bound except with
the local type projection Stefan showed. We'd need partial type
parameter application for that. But it's a much rarer case, so I'd
recommend to just write the evidence parameter directly in that case.
Cheers
- Martin
Well, you can still define a type alias for the projection, and it's
only slightly harder to use than the other ones:
scala> type MapView[K, V] = { type L[X] = X => Map[K, V] }
defined type alias MapView
scala> def f[K, V, T : MapView[K, V]#L](map: T, k: K) = map(k)
f: [K, V, T](map: T, k: K)(implicit evidence$1: T =>
scala.collection.immutable.Map[K,V])V
Partial application would still be nicer.
-sz
From my PoV partial application would more than pay the cost of
removing view bounds.
Cheers,
Miles
--
Miles Sabin
tel: +44 7813 944 528
gtalk: mi...@milessabin.com
skype: milessabin
g+: http://www.milessabin.com
http://twitter.com/milessabin
http://underscoreconsulting.com
http://www.chuusai.com
I also think it would be a major problem if we had to replace the
extends App
syntax again. I'd very much prefer to keep it, to keep things stable.
- Martin
Yes, please. Special case it for one or two releases before a general
replacement becomes available if you must, but don't change it again.
--
Daniel C. Sobral
I travel to the future all the time.