Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs?

Showing 1-50 of 50 messages
Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Haoyi Li 1/7/14 11:10 PM
Warning: Long, with lots of (not-type-checked) code

The problem I am facing is: how do you design a nice, statically typed API for CSS rules? In particular, things like these:

float: left;
background-image: radial-gradient(red, blue);
Solution 1 may be something like:


case class Thing(s: String){
def :=(t: Thing) = s + ": " + t.s
}
val float = Thing("float")
val left = Thing("left")
val backgroundImage = Thing("background-image")
def radialGradient(s: String) = Thing(s"radialGradient($s)")
val red = Thing("red")
val blue = Thing("red")
 
float:=left
backgroundImage:=radialGradient(red, blue)

This looks great! it evaluates quickly and simply into the CSS snippet shown above. But then you think: there are tons of possible CSS values for the various rules! You have left, right, center, top, none, normal, url, etc. etc.. It would be great if you could avoid having left and url be polluting the namespace, because although it looks great here, if you add in all the css rules, it adds up to a ton of namespace pollution with very common words.

Solution 2 may be to namespace each value according to the rule it works in, e.g.


case class Thing(s: String){
def :=(t: Thing) = s + ": " + t.s
}
val float = Thing("float")
object Float{
val left = Thing("left")
}
val backgroundImage = Thing("background-image")
object Image{
def radialGradient(s: String) = Thing(s"radialGradient($s)")
}
object Color{
val red = Thing("red")
val blue = Thing("red")
}
 
float:=Float.left
backgroundImage:=Image.radialGradient(Color.red, Color.blue)

Which cuts down on the pollution greatly, but unfortunately leaves the use site syntax repetitive and verbose. I already know it's a left comes from Float and url comes from Image! Those are the only places where values to satisfy float:= and backgroundImage:= can come from!

You may come up with Solution 3 to try and mitigate it:


case class Thing(s: String){
def :=(t: Thing) = s + ": " + t.s
}
 
object float extends Thing("float"){
val left = this := Thing("left")
}
val backgroundImage = Thing("background-image")
object Image{
def radialGradient(s: Thing*) = Thing(s"radialGradient(${s.mkString(',')})")
}
object Color{
val red = Thing("red")
val blue = Thing("red")
}
 
float.left
backgroundImage.radialGradient(Color.red, Color.blue)

Both a really clean call-site syntax, almost no namespace pollution, everything seems perfect! But what do you do with red and blue? They're inside radialGradient, who could take a variable number of them. What you really want is something that can represent the hierarchical structure, which method chaining can't! You may end up with Solution 4:

case class Thing[T](s: String){
def :=(t: T => Thing) = s + ": " + t.s
}
 
val float = Thing[Float]("float")
object Float{
val left = this := Thing("left")
}
val backgroundImage = Thing[Image]("background-image")
object Image{
def radialGradient(s: Thing*) = Thing[Color](s"radialGradient($s)")
}
object Color{
val red = Thing("red")
val blue = Thing("red")
}
 
float:=(_.left)
backgroundImage:=(_.radialGradient(_.red, _.blue))

Which actually satisfies all these requirements: all the identifiers are scoped, don't pollute the ambient namespace, and don't require repetition of where they come from. But looks pretty ugly =( Ideally what I want is to be able to write

float:=left
backgroundImage:=radialGradient(red, blue)

And have left, radialGradient, red and blue scoped only to their respective method calls.

This would be doable if we had some abstraction (here called @binds) that worked like this:


object Src{
val stuff
}
 
func(thunk: @binds[Src.type] => T) = {
... thunk(Src) ... // thunk itself has a type Src.type => T
}
 
// but at the callsite, does some magic, like magic stuff
// scoped lexically to that argument
func("" + stuff)
 
// because it basically desugars into
func({ $src =>
import $src._
"" + stuff
})

// this desugaring would allow fully-qualified
// names too, as well as external names
func("" + Src.stuff)

This would basically reify the concept of an Enum in the type system: I could specify a function that takes a value that is no longer just of a type, but which is assumed to come from a specified container object, and in doing so remove the boilerplate of declaring "where" those values come from.

I feel this is a need that spans beyond just CSS DSLs. Spray, for example, documents its HTTP API as follows:

For example:

  • The defined HttpMethod instances live in the HttpMethods object.
  • The defined HttpCharset instances live in the HttpCharsets object.
  • The defined HttpEncoding instances live in the 
...
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? nafg 1/8/14 1:06 AM
Some code of mine I stopped using in favor of plain strings. :)



For example:

  • The defined HttpEncoding instances live in the HttpEncodings object.
  • ...

with either importing everything into the local namespace (i.e. Solution 1 above) or not (Solution 2). That sort of API could also benefit from the @binds annotation shown about, which would provide both short names *and* no-namespace-pollution by streamlining the intended use case: You want the parameter to only take a member of a type, whose members only live in one object.

Apart from the Enum-like-classes use case (which I find a lot of my classes fall into!) this is useful in other places too: Scala.React has examples which go:

Events.loop[B] { self =>
val es = loopUntil(outer) {
self << (self await inner)
self.pause
}
inner = isEvents(es)
}

Which could be tidied up to


Events.loop[B] {
val es = loopUntil(outer) {
<< (await inner)
pause
}
inner = isEvents(es)
}

by making loop take a @binds function instead of a normal one. Similarly, I have a code in Metascala which needs tons of tiny, dynamic scopes, and every single ones needs an import at the top to bring in the necessary implicits, which could be cleaned up by a @binds[T] where T contained the necessary implicit declarations. 

Lastly, when using projects like Scala-STM and Slick and Scala.Rx, there is a constant tension of the {Unsafe, Concise} approach of using a magic-DynamicVariable to manage your sessions/transactions, and the {Proper, Annoying} approach of making the transactional blocks take implicit arguments. With @binds, you get the nice properties of both approaches.

Has anyone else ever needed/wanted something like this? I remember reading an something from martin a while ago (http://www.infoq.com/news/2012/07/scala-macros), when macros were first announced, saying that he hoped that they would solve this use case. 

Without untyped macros I don't see any obvious macro-way of doing it right now. Have there been any plans or discussions or experiments related to these ideas? 

--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Eugene Burmako 1/8/14 2:26 AM
Great idea! I've been long considering something similar for macros, but it is indeed applicable more widely.

Some additional use cases that might benefit from these features: 1) context-specific methods whose invocations are rewritten by macros (e.g. await), 2) context injection employed by LMS and YinYang.

Some questions that we could try to figure out as the first step towards a potential sip discussion:

1) How would the identifiers be brought into scope? By the virtue of "import $src._" or by using a different mechanism (in LMS they do "new ScopeThingy { code }")? How does this interact with the recent SAM developments?

2) Can Src depend on value and type parameters of the method it is used in? Are there going to be any undesirable side-effects on the intersection with other language features?
...
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Stefan Zeiger 1/8/14 2:41 AM
On 2014-01-08 8:10, Haoyi Li wrote:
> This would basically reify the concept of an Enum in the type system:
> I could specify a function that takes a value that is no longer just
> of a type, but which is assumed to come from a specified container
> object, and in doing so remove the boilerplate of declaring "where"
> those values come from.

Kotlin does something similar:
http://confluence.jetbrains.com/display/Kotlin/Type-safe+Groovy-style+builders

-sz
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Justin du Coeur 1/8/14 8:59 AM
Just to provide another data point:

On Wed, Jan 8, 2014 at 2:10 AM, Haoyi Li <haoy...@gmail.com> wrote:
[...]

Has anyone else ever needed/wanted something like this? I remember reading an something from martin a while ago (http://www.infoq.com/news/2012/07/scala-macros), when macros were first announced, saying that he hoped that they would solve this use case. 

I actually do something vaguely along these lines in QL, the template language at the heart of Querki.  (Which is the project I'm building in the Typesafe Stack -- a sort of cloud-based database/wiki hybrid, designed to make it stupid-easy to build "little" databases.  Currently in early private Alpha, if anybody wants to kick the tires.)

Basically, I started from a viewpoint of asking what I wanted the language (which is pure-functional, and specifically data-pipeline-centric) to look like, starting from a clean slate, and reverse-engineered the rules from there.  It turned out that in QL, *all* function calls wind up essentially being runtime macros.  Instead of evaluating the parameters from the inside-out (as in nearly every programming language on earth), we instead pass them as ASTs, and evaluate them *in the context of* the function/object they are parameterizing.

The resulting syntax is insanely concise and powerful, and makes this sort of context-dependency a natural outgrowth of the language.  Which was the point of the exercise -- it allows syntax like:

CD._instances -> _sort(Artist) -> _bulleted

(That is, "Take all instances of the CD model, sort them on the Artist property, and render them in their default format as a bullet list."  This sort of thing is dead-common in Querki.)

Basically, there is a concept of "context" being passed from stage to stage of the pipeline, and references are evaluated by name in the received context.  In fact, the precise definition is that a name is evaluated by the apply method of the context.  (And yes, "apply" was lifted directly from Scala.)  So there's essentially an implicit "_." before all names.

(Yes, this makes ordinary lexical scoping kind of weird.  Suffice it to say, I'm building a separate mechanism for importing a value from the lexical scope.  But it's surprisingly uncommon to want that for these little data-pipeline problems, I've found.)

The price is, of course, that it's insanely inefficient -- while I have hopes of doing some precompilation (it's all interpreted at runtime right now), I suspect it'll never get better than 10% the speed of an seriously compiled language, since the context-dependency makes optimization kind of challenging.

It's clearly not a model to be used broadly in Scala, but I suppose it's possible that some ideas along those lines might be helpful, if we could figure out a way to bound the type problems.  (QL is currently completely untyped, and is probably never going to be better than informally typed -- Querki is specifically intended to cope sensibly with messy and inconsistent data, so there are limits to how much type-safety can be established.  For the scale of problem it's targeting, that's not a big deal...)
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Haoyi Li 1/8/14 9:34 AM
 How would the identifiers be brought into scope? By the virtue of "import $src._" or by using a different mechanism (in LMS they do "new ScopeThingy { code }")? 

I would favor $s => import src._; over new ScopeThingy, for a few reasons:

- You don't need inheritance for this, so I feel like we can avoid it (This is the story of my life learning Scala)
- You don't generally want to override `this` in your DSL, you just want your other identifiers
- You want to be able to pass in a value for the identifiers to come from, not just a type (unless you can do new ScopeThing(arg) or similar)
- It's more performant; one less anonymous-class-file generated at compilation and one less allocation at runtime
- It affords more flexibility; the calling method can always declare wrap things up himself if he wants to, but in the (several) examples I gave, I only see the need for name-injection and not for wrapping. Favoring the unwrapped case by default seems to make more sense.

I'd argue for this to be done on a per-argument basis at the callsite, since it's very conceivable that you may have a method that takes in arguments of different contextualities (e.g. Enums from different sources). In a way, it's really similar to by-name parameters, and could be desugared at the same time.

On the other hand, Any improvement would be great, so I wouldn't mind a macro that could do this en-masse =)

> How does this interact with the recent SAM developments?

The desugaring I proposed should integrate cleanly with it.

Can Src depend on value and type parameters of the method it is used in? 

Yes! with the desugaring I proposed, it would act like a normal HoF, respecting the same type inference.

> Are there going to be any undesirable side-effects on the intersection with other language features?

I don't think so, but then again, I haven't thought about it super thoroughly.

Kotlin does something similar

Yes, and I am jealous of them =P. This came up because I am currently working on Scalatags, and am ending up doing an bunch of ad-hoc solutions because we don't have a similarly nice context-injection feature.

  div(backgroundColor:=hex"ababab")
  div(color:=rgb(0, 255, 255))
  div(color.red)
It would be great if I could use a uniform := all the way, but polluting the namespace with things like rgb is bad enough, I don't want to leave names hanging around for red and the myriad of other CSS enums!

Another major case for enums is passing flags to a function: usually people do things like myFunction(1, 2, MyFunctionFlags.MY_FLAG), or accept the namespace pollution and import MY_FLAG everywhere. It would be great if I could just say myFunction(1, 2, MY_FLAG) without an import and have it clever enough to know MY_FLAG could come from an additional namespace of MyFunctionFlags

 






--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Jon Pretty 1/9/14 4:43 AM
Hi Haoyi,

I've not had time to read this whole thread yet, but I came up with a reasonably satisfactory solution here:


This works, is being used in production, and the ugliness is all in the library rather than at the call sites. I need to do a bit of work to get this code into a state where you don't need to know a whole load of dependency quirks to use it, but it's on my todo list.

When I've got some time later, I'll send a more detailed response...

Cheers,
Jon

For example:

  • The defined HttpEncoding instances live in the HttpEncodings object.
  • ...

with either importing everything into the local namespace (i.e. Solution 1 above) or not (Solution 2). That sort of API could also benefit from the @binds annotation shown about, which would provide both short names *and* no-namespace-pollution by streamlining the intended use case: You want the parameter to only take a member of a type, whose members only live in one object.

Apart from the Enum-like-classes use case (which I find a lot of my classes fall into!) this is useful in other places too: Scala.React has examples which go:

Events.loop[B] { self =>
val es = loopUntil(outer) {
self << (self await inner)
self.pause
}
inner = isEvents(es)
}

Which could be tidied up to

Events.loop[B] {
val es = loopUntil(outer) {
<< (await inner)
pause
}
inner = isEvents(es)
}

by making loop take a @binds function instead of a normal one. Similarly, I have a code in Metascala which needs tons of tiny, dynamic scopes, and every single ones needs an import at the top to bring in the necessary implicits, which could be cleaned up by a @binds[T] where T contained the necessary implicit declarations. 

Lastly, when using projects like Scala-STM and Slick and Scala.Rx, there is a constant tension of the {Unsafe, Concise} approach of using a magic-DynamicVariable to manage your sessions/transactions, and the {Proper, Annoying} approach of making the transactional blocks take implicit arguments. With @binds, you get the nice properties of both approaches.

Has anyone else ever needed/wanted something like this? I remember reading an something from martin a while ago (
...
Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Jon Pretty 1/9/14 4:46 AM
And if the `apply` method which I've overloaded 334 times doesn't make you feel queasy, nothing will...

Jon
...
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Haoyi Li 1/9/14 9:57 AM
And if the `apply` method which I've overloaded 334 times doesn't make you feel queasy, nothing will..

I've tried and failed to understand the code, but would love to hear more about how that works =)


Jon
Lastly, when using projects like Scala-STM and Slick and Scala.Rx, there is a constant tension of the {Unsafe, Concise} approach of using a magic-DynamicVariable to manage your sessions/transactions, and the {Proper, Annoying} approach of making the transactional blocks take implicit arguments. With 
...
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Jon Pretty 1/9/14 10:02 AM
On Thursday, 9 January 2014 17:57:04 UTC, Haoyi Li wrote:
And if the `apply` method which I've overloaded 334 times doesn't make you feel queasy, nothing will..

I've tried and failed to understand the code, but would love to hear more about how that works =)

Thanks for trying to make sense of it, given that I gave you no explanation at all!

When I get a chance in a couple of hours, I'll send some more details (after reminding myself how it works...).

Cheers,
Jon
Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/9/14 10:04 AM
Hi,

A few days ago I pitched Eugene an orthogonal, but similar in spirit idea, which I have now refined here: https://gist.github.com/stanch/8338601.
In short, it suggests a better way to write macros that need to look up the AST.

» And if the `apply` method which I've overloaded 334 times doesn't make you feel queasy, nothing will...
Jon, this would make an epic signature!

Nick
Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Jon Pretty 1/9/14 6:33 PM
On Thursday, 9 January 2014 18:04:48 UTC, Nick Stanchenko wrote:
» And if the `apply` method which I've overloaded 334 times doesn't make you feel queasy, nothing will...
Jon, this would make an epic signature!

Heh! Did you mean an epic email signature, or an epic monstrosity that gets displayed when you call the apply method with parameters of the wrong types? ;)

Having read more than the first screenful of Haoyi's email now, the following explanation does nothing to fix the namespace issue, though I have a suggestion for that which I'll put in another message, possibly in the morning, seeing as it's 2.30am and I like feeling alert.

Anyway, the basic idea of that css.scala file (and associated files in the same project which I didn't link to) is that you should be able to represent everything in CSS3 (properties, values and selectors) in a typesafe way. Given that it's impossible to represent exact CSS within Scala, rather than try to stay as close to CSS as possible, I've not tried to keep up the pretence, and instead I've made the DSL as close to idiomatic Scala as possible, fsvo "idiomatic".

It's quite intertwined with my HTML library (there's a typesafe DSL for HTML in there too, which I won't go into now, except to brag that it enforces pretty much all HTML5 nesting rules without using macros...).

Properties are represented by their canonical name, converted to camel case, e.g.

  background
  borderTopStyle
  hangingPunctuation

They're all values in an HtmlCss object, and instances of CssAttribute, and all have at least one `apply` method which is used to specify the value for the attribute, e.g.

  borderStyle(dashed)
  marginTop(10 px)
  background(rgb(255, 0, 0), scroll, 100 px, 0 px)

In many cases, numerous (sometimes literally hundreds) of overloaded `apply` methods are included to support all variations on the ways CSS offers to specify the values of that particular property. Types (*lots* of types) and instances of those types are provided to ensure that you can't use a value which doesn't make sense for a particular property. If I remember correctly, this does a reasonably complete job of representing all styles, except possibly some lesser-used CSS3 styles which I never got round to finishing.

Applied properties (which are typed as `Properties`) can be joined together using the `+` operator. These can be used as a parameter to a `style` attribute in HTML5, or you can define a selector for a set of properties like so:

  table / 'row / td.nthLastChild(2) := marginRight(2 px) + borderStyle(solid)

There are various differences to learn about how a CSS selector translates to the same selector in Scala. Juxtaposition (i.e. representing nesting at arbitrary depth) is represented by a slash, and CSS classes are represented by symbols. Several rules can be joined together with a `|` instead of a `,`. Hopefully these choices make logical sense in a Scala context.

You can compose several rules together by making them parameters to a `Stylesheet`, e.g.

  Stylesheet(
    table := background(gray) + border(1 px, solid, black),
    table / tr&'highlight := background(rgba(255, 255, 0, 0.5)),
    table / tr / td.lastChild := borderTop(2 px)
  )

This produces the following output:

  table {
    background: gray;
    border: 1px solid black;
  }

  table tr.highlight {
    background: rgba(255, 255, 0, 0.5);
  }

  table tr td:last-child {
    border-top: 2px;
  }

Hopefully, this all looks quite nice. I had to use a few tricks to make all the overloads work unambiguously, and at the moment, you're forced to use Rapture I/O links to represent URIs, though this is something I'd like to make configurable.

I've had this code for quite a while, though whilst it's in production use, I've never polished it up or made any announcement about it... Would people find it useful? Although I'm already quite far behind releasing other stuff I've promised, I could jump this a couple of notches up my stack if people are interested.

I'm happy to answer any other questions about how I coaxed variously-shaped pegs into contrarily-shaped holes...

Cheers,
Jon
Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Simon Ochsenreither 1/9/14 8:07 PM

In many cases, numerous (sometimes literally hundreds) of overloaded `apply` methods are included to support all variations on the ways CSS offers to specify the values of that particular property. Types (*lots* of types) and instances of those types are provided to ensure that you can't use a value which doesn't make sense for a particular property. If I remember correctly, this does a reasonably complete job of representing all styles, except possibly some lesser-used CSS3 styles which I never got round to finishing.

Isn't there something like a CSS spec which could be used by a type macro to generate the actual code from it? :-)
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Ionuț G. Stan 1/9/14 11:10 PM
Hi all,

Interesting idea. I've hit the same problem recently when trying to
design a Java-compatible DSL. I ended up using the `new ScopeThingy`
pattern, as Java can't handle scoped (static) imports. This pattern was
the only way to limit the scope of the identifiers in Java-land.

So, I tried to think more about a possible solution, type-directed
lookup (inspired from Haskell's type-directed name resolution[0]). My
thoughts are in this gist:

https://gist.github.com/igstan/8347852

I think type-directed lookup might simpler to implement. On the other
hand it looks like being able to pass the context at runtime might be a
useful feature if the context's state varies from call to call. That's
why I'm probably more for Haoyi Li's desugaring using `import $src._`.

Also, while writing that code I realized that this feature would be yet
another factor that would make it even harder to reason about code. At
least for me. It's a powerful tool, but we'd have to be careful about it.


[0]: http://www.haskell.org/haskellwiki/TypeDirectedNameResolution
> working on Scalatags <https://github.com/lihaoyi/scalatags/tree/0.2>,
> and am ending up doing an bunch of ad-hoc solutions because we don't
> have a similarly nice context-injection feature.
>
>
>    div(backgroundColor:=hex"ababab")
>    div(color:=rgb(0,  255,  255))
>    div(color.red)
>
> It would be great if I could use a uniform := all the way, but polluting
> the namespace with things like rgb is bad enough, I don't want to leave
> names hanging around for red and the myriad of other CSS enums!
>
> Another major case for enums is passing flags to a function: usually
> people do things like myFunction(1, 2, MyFunctionFlags.MY_FLAG), or
> accept the namespace pollution and import MY_FLAG everywhere. It would
> be great if I could just say myFunction(1, 2, MY_FLAG) without an import
> and have it clever enough to know MY_FLAG could come from an additional
> namespace of MyFunctionFlags
>
>
>
>
>
> On Wed, Jan 8, 2014 at 2:41 AM, Stefan Zeiger <sze...@novocode.com
> <mailto:sze...@novocode.com>> wrote:
>
>     On 2014-01-08 8:10, Haoyi Li wrote:
>
>         This would basically reify the concept of an Enum in the type
>         system: I could specify a function that takes a value that is no
>         longer just of a type, but which is assumed to come from a
>         specified container object, and in doing so remove the
>         boilerplate of declaring "where" those values come from.
>
>
>     Kotlin does something similar:
>     http://confluence.jetbrains.__com/display/Kotlin/Type-safe+__Groovy-style+builders
>     <http://confluence.jetbrains.com/display/Kotlin/Type-safe+Groovy-style+builders>
>
>     -sz
>
>
>     --
>     You received this message because you are subscribed to the Google
>     Groups "scala-debate" group.
>     To unsubscribe from this group and stop receiving emails from it,
>     send an email to scala-debate+unsubscribe@__googlegroups.com
>     <mailto:scala-debate%2Bunsubscribe@googlegroups.com>.
>     For more options, visit https://groups.google.com/__groups/opt_out
>     <https://groups.google.com/groups/opt_out>.
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "scala-debate" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to scala-debate...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.


--
Ionuț G. Stan  |  http://igstan.ro
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Haoyi Li 1/9/14 11:31 PM
Isn't there something like a CSS spec

Hehehe "spec" hahahaha *snort*

Also, while writing that code I realized that this feature would be yet another factor that would make it even harder to reason about code. At least for me. It's a powerful tool, but we'd have to be careful about it.

I agree it's a dangerous feature. On the other hand, I am much more willing to accept such dangerous features in Scala than in many other languages, simply because I can fall back on types to keep things straight. For example, if I saw something like

color := blue

and I wonder "where the hell does blue come from? I didn't define it!" It's only one jump-to-def away to see the `Colors` namespace that  `blue` comes from, or to see that := is takes a @binds[Colors] function which is why the stuff from that namespace is being imported by default. Not so bad, really, even if you have no idea what this library is.

By-name parameters are kinda in the same spot; "you can change the order of evaluation? wtf?" but they're super widely used (even though all they do is save the four characters ()=>) and because it's encoded in the types, so even if there's confusion it doesn't last for long.
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Eugene Burmako 1/10/14 5:43 AM
Adriaan, Jason, Greg, what do you think about this discussion? 

The problem outlined by the OP does exist, and its manifestations are plentiful. The solutions discussed here look quite interesting and seem to generalize well beyond the original use case. Do you think that's something that Scala would benefit from? What would be our next step here?
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Jon Pretty 1/10/14 5:44 AM
Hi Haoyi, Eugene et al,


On Wednesday, 8 January 2014 10:26:42 UTC, Eugene Burmako wrote:
Great idea! I've been long considering something similar for macros, but it is indeed applicable more widely.

I proposed something similar to Haoyi's small, "small local scope" idea ages ago. I tried to find it on the Gmane archives, but all I could find was this other idea for implicits I had, almost seven years ago... ;)

   http://thread.gmane.org/gmane.comp.lang.scala/3455

...but that was when ideas were cheap and implementations were less so.

I read various discussions on SAM a while ago, but I feel like I've missed a definitive conclusion wrt the SAM implementation, so hopefully someone can fill in the blanks for me, in particular:

 1. Is the SAM implementation in a (pre-)release build somewhere where I can try it out?
 2. Do I need to enable any flags to use it (e.g. -Xexperimental)?
 3. What was the final decision on accessing class members within a SAM definition?

On point 3, I thought the general feeling was that switching into the class's encapsulated context when defining a SAM was too subtle, as shadowing by class members could introduce a smorgasbord of confusion. I agreed with this analysis.

An alternative, which reaches a reasonable compromise (I think) on this and the namespace pollution issue, would be for just the implicit members of the class to be brought into scope, not the values. This would enable us to write this:

  @implicitNotFound("This method must be called from within a Sam")
  sealed trait Key

  class Sam[T] {
    implicit protected object SamKey extends Key
    def apply: T
  }

  def scoped(implicit key: Key) = ...

Here, the `scoped` "value" would exist everywhere, but would only be "unlocked" and usable within a subclass of `Sam` and hence (by my contrived definition) the SAM definition. You would get a descriptive warning about your mistake if you tried to use it elsewhere.

Obviously, this all depends on the SAM implementation working as required, but a similar thing should be possible with macros and tree transformation.

Cheers,
Jon
Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Jon Pretty 1/10/14 5:48 AM
Hi Simon,


On Friday, 10 January 2014 04:07:33 UTC, Simon Ochsenreither wrote:
Isn't there something like a CSS spec which could be used by a type macro to generate the actual code from it? :-)

Potentially; but macros didn't exist when I wrote it, and there are enough quirks (in both the CSS spec itself, and its encoding) that doing it by hand would probably be just as laborious, albeit less interestingly laborious.

Though the CSS spec has probably changed since I implemented it, so autogenerating has a big maintenance benefit.

Cheers,
Jon
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Sébastien Doeraene aka sjrd 1/10/14 5:50 AM
Hi everyone,

I think a feature like the one proposed by Haoyi would be immensely useful for many DSL authors. I also wanted something like this at times. So +1 from me. I also like the suggested desugaring.

To reply to Jon's factual questions:
1. Yes, it is available as of Scala 2.11.0-M7
2. Yes, you need -Xexperimental
3. Lambdas used for SAMs have the same scoping rules as lambdas used for scala.Functions, i.e., they do not introduce anything new in the scope, and `this` continues to refer to the `this` of the enclosing class.

Cheers,
Sébastien


--
You received this message because you are subscribed to a topic in the Google Groups "scala-debate" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/scala-debate/f4CLmYShX6Q/unsubscribe.
To unsubscribe from this group and all its topics, send an email to scala-debate...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.

Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Jon Pretty 1/10/14 6:01 AM
On Friday, 10 January 2014 13:50:50 UTC, Sébastien Doeraene aka sjrd wrote:
To reply to Jon's factual questions:

Many thanks, Sébastien - that's largely what I expected, though doesn't explain why it didn't work when I tried it. That, however, I'll hopefully work out with a bit of perseverance!

Cheers,
Jon
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Jason Zaugg 1/10/14 6:00 AM
On Fri, Jan 10, 2014 at 2:43 PM, Eugene Burmako <eugene....@epfl.ch> wrote:
Adriaan, Jason, Greg, what do you think about this discussion? 

The problem outlined by the OP does exist, and its manifestations are plentiful. The solutions discussed here look quite interesting and seem to generalize well beyond the original use case. Do you think that's something that Scala would benefit from? What would be our next step here?

I'd love to have these sort of discussions after 2.11 is released.

My default position is quite resistant to more language changes as I don't feel we've done a good enough job at polishing the features we already have.

-jason
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Grzegorz Kossakowski 1/10/14 6:35 AM
+1 to both of this points.

I can barely keep up with what I already have on my plate at the moment.

Once 2.11 is out I'm happy to join this discussion. I have fair amount of experience with this problem coming from my work on LMS-related stuff.

-- 
Grzegorz

Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Adriaan Moors 1/10/14 3:52 PM



On Fri, Jan 10, 2014 at 5:44 AM, Jon Pretty <googl...@accounts.propensive.com> wrote:
Hi Haoyi, Eugene et al,


On Wednesday, 8 January 2014 10:26:42 UTC, Eugene Burmako wrote:
Great idea! I've been long considering something similar for macros, but it is indeed applicable more widely.

I proposed something similar to Haoyi's small, "small local scope" idea ages ago. I tried to find it on the Gmane archives, but all I could find was this other idea for implicits I had, almost seven years ago... ;)

   http://thread.gmane.org/gmane.comp.lang.scala/3455

...but that was when ideas were cheap and implementations were less so.

I read various discussions on SAM a while ago, but I feel like I've missed a definitive conclusion wrt the SAM implementation, so hopefully someone can fill in the blanks for me, in particular:

 1. Is the SAM implementation in a (pre-)release build somewhere where I can try it out?
 2. Do I need to enable any flags to use it (e.g. -Xexperimental)?
yep, they've been in 2.11 for a while now under -Xexperimental
They'll remain experimental for 2.11.x, as we haven't had a chance to flesh them out and answer/SIP/spec questions like the one below 

 3. What was the final decision on accessing class members within a SAM definition?
it's not allowed in the current implementation:

 ~/ $ scala -Xexperimental
Welcome to Scala version 2.11.0-M7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_65).
Type in expressions to have them evaluated.
Type :help for more information.

scala> trait Sam { def foo(x: String): Int; def bar(x: String) = x.length }
defined trait Sam

scala> val f: Sam = {x => bar(x)}
<console>:8: error: not found: value bar
       val f: Sam = {x => bar(x)}
                          ^

scala> val f: Sam = {x => x.length}
f: Sam = $anonfun$1@6db17b38

scala> val f: Sam = _.length
f: Sam = $anonfun$1@64c11af5

// just to prove Sam is actually involved:
scala> trait Sam { def foo(x: String): Int; def bar(x: String) = x.length ; override def toString = "My name is Sam and I'm a SAM."}
defined trait Sam

scala> val f: Sam = _.length
f: Sam = My name is Sam and I'm a SAM.



On point 3, I thought the general feeling was that switching into the class's encapsulated context when defining a SAM was too subtle, as shadowing by class members could introduce a smorgasbord of confusion. I agreed with this analysis.

An alternative, which reaches a reasonable compromise (I think) on this and the namespace pollution issue, would be for just the implicit members of the class to be brought into scope, not the values. This would enable us to write this:

  @implicitNotFound("This method must be called from within a Sam")
  sealed trait Key

  class Sam[T] {
    implicit protected object SamKey extends Key
    def apply: T
  }

  def scoped(implicit key: Key) = ...

Here, the `scoped` "value" would exist everywhere, but would only be "unlocked" and usable within a subclass of `Sam` and hence (by my contrived definition) the SAM definition. You would get a descriptive warning about your mistake if you tried to use it elsewhere.

Obviously, this all depends on the SAM implementation working as required, but a similar thing should be possible with macros and tree transformation.

Cheers,
Jon

Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Haoyi Li 1/10/14 9:50 PM
A few days ago I pitched Eugene an orthogonal, but similar in spirit idea, which I have now refined here: https://gist.github.com/stanch/8338601.
In short, it suggests a better way to write macros that need to look up the AST.

This would be satisfied, I think, by the proposed desugaring. Rather than having layoutParams or tweak be (global) macros that can look up the enclosing scope, they would be members of some Thing[T], and ~> on the enclosing class (let's call it View[T]) would be defined as

class View[T]{
    def ~>(A)(@binds[Thing[T]] => A)(implicit tw: TweakableWith[A, T]) = ...
}
class Thing[T]{
  def layoutParams[T](...) = ...
  def tweak[T](...) = ...
}

I think this would have an advantage over ObserverMacros, in that ObserverMacros have a kind of unbounded, ad-hoc contextuality: you have no idea how far into the enclosing scope the macro will look, and how its behavior will be affected by where you put it. On the other hand, doing it via a @binds  parameter clearly delimits the scope that can affect the macro (the bounds of the @binds parameter). I feel injecting an $s => import $s._ is a much more restrictive form of "magic" than introspecting the surrounding trees, and yet is perfectly capable of what we want to provide here =).

You'd also have an additional feature that layoutParams and tweak can't sit outside of a ~> call, which may be a feature (yay less scope pollution) or bug (more annoying to abstract over the thing) depending on how you look at it.
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/11/14 2:23 AM
Well, the whole point of tweaks is that they are detached from what they are tweaking :) Here are some benefits:
1. You can combine tweaks together, like
val fooing: Tweak[TextView] = text("Foo") + On.click(Log.d("foo", "clicked")) + (tweak doing (_.someOtherStuff())
2. You can do this:
List(textView1, textView2) ~> future { Thread.sleep(1000); fooing }
I’m not sure if this is possible and easy with your encoding.

I do agree that the depth of the looking up can be an issue. But it can be bounded by the API. In fact, it will be bounded by enclosingUnit anyway. As long as the macro does nothing besides observing some context, I would say it’s relatively safe. Indeed, it will either find it or not (in which case you can supply something manually).

Regarding scope pollution, you can always use Tweaks.text, etc. Don’t forget about the qualified imports as well.

One more thing, I believe observer macros require almost no language changes and can be implemented entirely in paradise. In fact, you can already use the suggested approach (implicit params + materializers), it will just not look very nice (because of the enclosingXXX stuff).

This all is of course not to say that your proposal is not relevant :) Just might be not very logical in my case.

Nick
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/11/14 4:10 AM
P.S. I though a bit more on it, and the problem with my use-case and your proposal is that the set of tweaks is not fixed.
For example, if you have Thingy[T].x and Thingy[T].y, both of which need some context, you can also make Thingy[T].x + Thingy[T].y, which would not be a member of Thingy anymore, but would still need the context.

On Saturday, January 11, 2014 10:23:28 AM UTC, Nick Stanchenko wrote:
Well, the whole point of tweaks is that they are detached from what they are tweaking :) Here are some benefits:
1. You can combine tweaks together, like
val fooing: Tweak[TextView] = text("Foo") + On.click(Log.d("foo", "clicked")) + (tweak doing (_.someOtherStuff())
2. You can do this:
List(textView1, textView2) ~> future { Thread.sleep(1000); fooing }
I’m not sure if this is possible and easy with your encoding.

I do agree that the depth of the looking up can be an issue. But it can be bounded by the API. In fact, it will be bounded by enclosingUnit anyway. As long as the macro does nothing besides observing some context, I would say it’s relatively safe. Indeed, it will either find it or not (in which case you can supply something manually).

Regarding scope pollution, you can always use Tweaks.text, etc. Don’t forget about the qualified imports as well.

One more thing, I believe observer macros require almost no language changes and can be implemented entirely in paradise. In fact, you can already use the suggested approach (implicit params + materializers), it will just not look very nice (because of the enclosingXXX stuff).

This all is of course not to say that your proposal is not relevant :) Just might be not very logical in my case.

Nick

On Saturday, January 11, 2014 5:50:50 AM UTC, Haoyi Li wrote:
A few days ago I pitched Eugene an orthogonal, but similar in spirit idea, which I have now refined here: https://gist.github.com/stanch/8338601.
In short, it suggests a better way to write macros that need to look up the AST.

This would be satisfied, I think, by the proposed desugaring. Rather than having layoutParams or tweak be (global) macros that can look up the enclosing scope, they would be members of some Thing[T], and ~> on the enclosing class (let's call it View[T]) would be defined as

class View[T]{
    def ~>(A)(@binds[Thing[T]] => A)(implicit tw: TweakableWith[A, T]) = ...
}
class Thing[T]{
  def layoutParams[T](...) = ...
  def tweak[T](...) = ...
}

I think this would have an advantage over ObserverMacros, in that ObserverMacros have a kind of unbounded, ad-hoc contextuality: you have no idea how far into the enclosing scope the macro will look, and how its behavior will be affected by where you put it. On the other hand, doing it via a @binds  parameter clearly delimits the scope that can affect the macro (the bounds of the @binds parameter). I feel injecting an $s => import $s._ is a much more restrictive form of "magic" than introspecting the surrounding trees, and yet is perfectly capable of what we want to provide here =).

You'd also have an additional feature that layoutParams and tweak can't sit outside of a ~> call, which may be a feature (yay less scope pollution) or bug (more annoying to abstract over the thing) depending on how you look at it.


On Fri, Jan 10, 2014 at 3:52 PM, Adriaan Moors <adr...@typesafe.com> wrote:



On Fri, Jan 10, 2014 at 5:44 AM, Jon Pretty <googl...@accounts.propensive.com> wrote:
Hi Haoyi, Eugene et al,


On Wednesday, 8 January 2014 10:26:42 UTC, Eugene Burmako wrote:
Great idea! I've been long considering something similar for macros, but it is indeed applicable more widely.

I proposed something similar to Haoyi's small, "small local scope" idea ages ago. I tried to find it on the Gmane archives, but all I could find was this other idea for implicits I had, almost seven years ago... ;)

   http://thread.gmane.org/gmane.comp.lang.scala/3455

...but that was when ideas were cheap and implementations were less so.

I read various discussions on SAM a while ago, but I feel like I've missed a definitive conclusion wrt the SAM implementation, so hopefully someone can fill in the blanks for me, in particular:

 1. Is the SAM implementation in a (pre-)release build somewhere where I can try it out?
 2. Do I need to enable any flags to use it (e.g. -Xexperimental)?
yep, they've been in 2.11 for a while now under -Xexperimental
They'll remain experimental for 2.11.x, as we haven't had a chance to flesh them out and answer/SIP/spec questions like the one below 

 3. What was the final decision on accessing class members within a SAM definition?
it's not allowed in the current implementation:

 ~/ $ scala -Xexperimental
Welcome to Scala version 2.11.0-M7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_65).
Type in expressions to have them evaluated.
Type :help for more information.

scala> trait Sam { def foo(x: String): Int; def bar(x: String) = x.length }
defined trait Sam

scala> val f: Sam = {x => bar(x)}
<console>:8: error: not found: value bar
       val f: Sam = {x => bar(x)}
                          ^

scala> val f: Sam = {x => x.length}
f: Sam = $anonfun$1@6db17b38

scala> val f: Sam = _.length
f: Sam = $anonfun$1@64c11af5

// just to prove Sam is actually involved:
scala> trait Sam { def foo(x: String): Int; def bar(x: String) = x.length ; override def toString = "My name is Sam and I'm a SAM."}
defined trait Sam

scala> val f: Sam = _.length
f: Sam = My name is Sam and I'm a SAM.



On point 3, I thought the general feeling was that switching into the class's encapsulated context when defining a SAM was too subtle, as shadowing by class members could introduce a smorgasbord of confusion. I agreed with this analysis.

An alternative, which reaches a reasonable compromise (I think) on this and the namespace pollution issue, would be for just the implicit members of the class to be brought into scope, not the values. This would enable us to write this:

  @implicitNotFound("This method must be called from within a Sam")
  sealed trait Key

  class Sam[T] {
    implicit protected object SamKey extends Key
    def apply: T
  }

  def scoped(implicit key: Key) = ...

Here, the `scoped` "value" would exist everywhere, but would only be "unlocked" and usable within a subclass of `Sam` and hence (by my contrived definition) the SAM definition. You would get a descriptive warning about your mistake if you tried to use it elsewhere.

Obviously, this all depends on the SAM implementation working as required, but a similar thing should be possible with macros and tree transformation.

Cheers,
Jon


Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Eugene Burmako 1/11/14 4:19 AM
Could you provide a full example? Would it be possible to pass the context around implicitly?


--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/11/14 4:48 AM
Let’s focus on widget type inference/propagation.

Suppose we have these two tweaks:
1) on(event: String, handler: () ⇒ Unit) — sets event listener by calling a method named s"setOn$eventListener". The presence of such method depends on the type of the widget, therefore we need to know (infer) it.
2) text(t: String) — sets widget’s caption.
In line with the discussed proposal, we need to put `on` inside a Thing[W <: Widget], so that it is specialized to the type of the widget being tweaked.
Now, someone else wants to make a new tweak, which is defined as follows:
def on2(t: Sring, event: String, handler: () ⇒ Unit) = text(t) + on(event, handler)
But this one also needs to know widget type! And it’s not a member of Thing[W <: Widget]...

Another problem here is that tweaking may look like
Future(Option(List(button1, button2))) ~> List(on("click", () ⇒ ()), show)
I am not sure we have the type machinery to enforce @bind[Thing[Button]] here.

That said, it might indeed help my case if we could pass an implicit inside, rather than use scoping.
Consider this pseudo-scala:
implicit class TweakingOps[W](w: W) {
  def ~>[T](t: T witnessing implicit widgetType[W])(implicit tweakableWith: W TweakableWith T): W = { tweakableWith.tweakWith(v, t); v }
}
where innerType[W] is a macro that calculates trait WidgetType { type WT <: Widget } from W, taking into account possible type nesting (Future[Option[List[Button]]]).

The same approach solves the case with layoutParams:
def l[L <: Layout](children: Widget* witnessing implicit new LayoutType { type LT = L }) = ...

Hope you forgive my superfluous “witnessing implicit” notation.
unk...@googlegroups.com 1/11/14 4:59 AM <This message has been deleted.>
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/11/14 5:23 AM
Sorry for multi-posting, ideas keep coming :)
A better option:

def f(a: (implicit Int) ⇒ String) = { println(a(5)) }
def g(implicit x: Int) = x.toString
f("Hi, " + g) // prints "Hi, 5"
f(implicit x ⇒ "Hi, " + g(x + 1)) // prints "Hi, 6"

We could also (don’t throw stones at me!) deprecate the special form “⇒ A” and allow the following:

def f(a: () ⇒ String) = { println(a()) }
f("Hi") // prints "Hi"
f(() ⇒ "Hi") // also prints "Hi"

The common rule is to allow omitting empty or “almost invisible” arguments in anonymous functions.


On Saturday, January 11, 2014 12:59:48 PM UTC, Nick Stanchenko wrote:
So this is in fact a similar proposal, but is rewritten like this:
decl-site ||  def f(a: A witnessing implicit x: X)    →   def f(xa: X ⇒ A), a = xa(x)
use-site  ||  f(a)                                               →   f(implicit x ⇒ a)

It also bears some similarity to the existing “⇒ A” sugar:
decl-site ||  def f(a: ⇒ A)  →  def f(ѳa: () ⇒ A), a = ѳa()
use-site  ||  f(a)               →  f(() ⇒ a)

With that in mind, here’s another take on it:
decl-site ||  def f(a: (x: X = foo) ⇒ A)    →   def f(xa: X ⇒ A), a = xa(foo)
use-site  ||  f(a)                                  →   f(implicit x ⇒ a)
Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Haoyi Li 1/11/14 11:24 AM
This all is of course not to say that your proposal is not relevant :) Just might be not very logical in my case.

No offense taken! I haven't used Macroid myself, so you'll have to accept that I won't see all the edge cases and subtleties that you've probably spent a lot of time thinking of =)

In fact, you can already use the suggested approach (implicit params + materializers)

If that is true, then @binds will work, because the $s => import $s._; desugaring can be used to import implicits too!:

class Widget[T]{
    def ~>(arg: @binds Holder[T] => Tweak[T]){
        arg(new Holder[T](...))
    }
}

class Holder[T](...){
  implicit val ctx = ...
}

Then when you call

new Widget[Thing] ~> tweak

tweak will get an implicit ctx injected by ~>, which you can use to do materialization or anything else you do with implicits 

This is basically the same use case as SLICK or Scala-STM or Scala.Rx: injecting an implicit without all the boilerplate implicit ctx =>  rubbish everywhere. In those cases it doesn't drive type inferencing, and in this case it could, but it's nice to see the same mechanism being use for everything =). Furthermore, nesting widgets will cause ctx to be shadowed, so you only see the ctx of the nearest surrounding widget! That is surely what we want.

I may be missing something; as I said, Macroid is complicated in many ways, and I haven't used it. Pretty sure it'll work though =).

I feel that passing things "to" the inner code (even if in an implicit/magical way) is almost always superior to the inner code reaching out and inspecting/grabbing-things-from its external context itself. Java/Python uses stack-introspection for the same thing that Scala uses implicit parameters for, and I feel that tree-context-introspection is just as sketchy. But that's personal preference =P


Re: [scala-debate] Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/11/14 5:52 PM
If that is true, then @binds will work, because the $s => import $s._; desugaring can be used to import implicits too!:

Fair enough, for some reason I didn’t think of it. This will work then :)
Let me suggest a different syntax though:
def f(a: (import X) ⇒ Int) = { doSmthWith(a(new X)) }
f(4) // rewritten to f { x: X ⇒ import x._; 4 }
f { x: X ⇒ 4 } // compatible with X ⇒ Int
I have unified this with my previous idea, what do you think? https://gist.github.com/stanch/8371005
Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/11/14 6:26 PM
Sorry again for posting spree :)
For the record, I also realized I have tons of other use-cases for the auto-imports:

import scalaz.std.option._
import scalaz.std.anyVal._
val a = B("foo", A(List(4, 8), 5, Some(A(List(), 8, None))))
val b = mutate(a) { $ ⇒ // ----------> this will be possible to write without `$ ⇒`
    $.b.b := 9
    $.b.c.*.b += 10
}
assert(b === B("foo", A(List(4, 8), 9, Some(A(List(), 18, None)))))

More from Macroid:
Tweak[Button](_.setText("123")) → Tweak[Button](setText("123"))
w[Button] ~> tweak { x ⇒
  x.doStuff1()
  x.doStuff2()
} →
w[Button] ~> tweak {
  doStuff1()
  doStuff2()
}

Thumbs up for the initial proposal.

On Wednesday, January 8, 2014 7:10:48 AM UTC, Haoyi Li wrote:
Warning: Long, with lots of (not-type-checked) code

The problem I am facing is: how do you design a nice, statically typed API for CSS rules? In particular, things like these:
float: left;
background-image: radial-gradient(red, blue);
Solution 1 may be something like:

case class Thing(s: String){
def :=(t: Thing) = s + ": " + t.s
}
val float = Thing("float")
val left = Thing("left")
val backgroundImage = Thing("background-image")
def radialGradient(s: String) = Thing(s"radialGradient($s)")
val red = Thing("red")
val blue = Thing("red")
 
float:=left
backgroundImage:=radialGradient(red, blue)

This looks great! it evaluates quickly and simply into the CSS snippet shown above. But then you think: there are tons of possible CSS values for the various rules! You have left, right, center, top, none, normal, url, etc. etc.. It would be great if you could avoid having left and url be polluting the namespace, because although it looks great here, if you add in all the css rules, it adds up to a ton of namespace pollution with very common words.

Solution 2 may be to namespace each value according to the rule it works in, e.g.

case class Thing(s: String){
def :=(t: Thing) = s + ": " + t.s
}
val float = Thing("float")
object Float{
val left = Thing("left")
}
val backgroundImage = Thing("background-image")
object Image{
def radialGradient(s: String) = Thing(s"radialGradient($s)")
}
object Color{
val red = Thing("red")
val blue = Thing("red")
}
 
float:=Float.left
backgroundImage:=Image.radialGradient(Color.red, Color.blue)

Which cuts down on the pollution greatly, but unfortunately leaves the use site syntax repetitive and verbose. I already know it's a left comes from Float and url comes from Image! Those are the only places where values to satisfy float:= and backgroundImage:= can come from!

You may come up with Solution 3 to try and mitigate it:

case class Thing(s: String){
def :=(t: Thing) = s + ": " + t.s
}
 
object float extends Thing("float"){
val left = this := Thing("left")
}
val backgroundImage = Thing("background-image")
object Image{
def radialGradient(s: Thing*) = Thing(s"radialGradient(${s.mkString(',')})")
}
object Color{
val red = Thing("red")
val blue = Thing("red")
}
 
float.left
backgroundImage.radialGradient(Color.red, Color.blue)

Both a really clean call-site syntax, almost no namespace pollution, everything seems perfect! But what do you do with red and blue? They're inside radialGradient, who could take a variable number of them. What you really want is something that can represent the hierarchical structure, which method chaining can't! You may end up with Solution 4:

case class Thing[T](s: String){
def :=(t: T => Thing) = s + ": " + t.s
}
 
val float = Thing[Float]("float")
object Float{
val left = this := Thing("left")
}
val backgroundImage = Thing[Image]("background-image")
object Image{
def radialGradient(s: Thing*) = Thing[Color](s"radialGradient($s)")
}
object Color{
val red = Thing("red")
val blue = Thing("red")
}
 
float:=(_.left)
backgroundImage:=(_.radialGradient(_.red, _.blue))

Which actually satisfies all these requirements: all the identifiers are scoped, don't pollute the ambient namespace, and don't require repetition of where they come from. But looks pretty ugly =( Ideally what I want is to be able to write

float:=left
backgroundImage:=radialGradient(red, blue)

And have left, radialGradient, red and blue scoped only to their respective method calls.

This would be doable if we had some abstraction (here called @binds) that worked like this:

object Src{
val stuff
}
 
func(thunk: @binds[Src.type] => T) = {
... thunk(Src) ... // thunk itself has a type Src.type => T
}
 
// but at the callsite, does some magic, like magic stuff
// scoped lexically to that argument
func("" + stuff)
 
// because it basically desugars into
func({ $src =>
import $src._
"" + stuff
})

// this desugaring would allow fully-qualified
// names too, as well as external names
func("" + Src.stuff)

This would basically reify the concept of an Enum in the type system: I could specify a function that takes a value that is no longer just of a type, but which is assumed to come from a specified container object, and in doing so remove the boilerplate of declaring "where" those values come from.

I feel this is a need that spans beyond just CSS DSLs. Spray, for example, documents its HTTP API as follows:

For example:

  • The defined HttpMethod instances live in the HttpMethods object.
  • The defined HttpCharset instances live in the HttpCharsets object.
  • The defined HttpEncoding instances live in the HttpEncodings object.
  • ...

with either importing everything into the local namespace (i.e. Solution 1 above) or not (Solution 2). That sort of API could also benefit from the @binds annotation shown about, which would provide both short names *and* no-namespace-pollution by streamlining the intended use case: You want the parameter to only take a member of a type, whose members only live in one object.

Apart from the Enum-like-classes use case (which I find a lot of my classes fall into!) this is useful in other places too: Scala.React has examples which go:

Events.loop[B] { self =>
val es = loopUntil(outer) {
self << (self await inner)
self.pause
}
inner = isEvents(es)
}

Which could be tidied up to

Events.loop[B] {
val es = loopUntil(outer) {
<< (await inner)
pause
}
inner = isEvents(es)
}

by making loop take a @binds function instead of a normal one. Similarly, I have a code in Metascala which needs tons of tiny, dynamic scopes, and every single ones needs an import at the top to bring in the necessary implicits, which could be cleaned up by a @binds[T] where T contained the necessary implicit declarations. 

Lastly, when using projects like Scala-STM and Slick and Scala.Rx, there is a constant tension of the {Unsafe, Concise} approach of using a magic-DynamicVariable to manage your sessions/transactions, and the {Proper, Annoying} approach of making the transactional blocks take implicit arguments. With @binds, you get the nice properties of both approaches.

Has anyone else ever needed/wanted something like this? I remember reading an something from martin a while ago (
...
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Sébastien Doeraene aka sjrd 1/11/14 8:14 PM

Hi,

I like the import syntax very much! Excellent idea. And nice summary and unification of the two proposals, even if I feel uneasy about deprecating => A.

Cheers,
Sébastien

Le 12 janv. 2014 03:26, "Nick Stanchenko" <nick....@gmail.com> a écrit :
Has anyone else ever needed/wanted something like this? I remember reading an something from martin a while ago (http://www.infoq.com/news/2012/07/scala-macros), when m
...
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/11/14 8:26 PM
Sébastien,

Are you against deprecating core features in general or just really need ⇒ A?
One more thing I don’t like is that by-name values are not representable on their own. Allowing conversion from A to () ⇒ A would also mean that we no longer need things like https://github.com/stanch/macroid/blob/master/src/main/scala/org/macroid/util/Thunk.scala.

N
...
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Sébastien Doeraene aka sjrd 1/11/14 10:24 PM
Nick,

I am generally against deprecating core features, but here I said that because I had a vague impression that it could lead to ambiguous cases. And now I have found why. Consider:
def f(g: => (Int => Int)) = g(5)
I would call it with
f(x => x+1)
If I now define f as
f(g: () => (Int => Int)) = g()(5)
it would still kind of work, but you start to feel the issue. Granted, it's unlikely one will actually define a parameter which is a by-name of a function type. However, what if we go generic:
def f[A](body: () => A) = {
  println("do things before")
  val r = body
  println("do things after")
  f
}
and now I call this with
f {
  println("blabla")
  () => 5
}
is this supposed to mean
val h = {
  println("blabla")
  () => 5
}
f(h)
with A inferred as () => Int
or as
f { () =>
  println("blabla")
  () => 5
}

with A inferred as Int?

The former is the meaning it has with current Scala. The latter would be a possible meaning with the automatic conversion of A into () => A. And now we have an ambiguity.


One more thing I don’t like is that by-name values are not representable on their own.

I'm not sure auto-implicit and auto-import values would not be any more representable.

Cheers,
Sébastien


...
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/11/14 10:48 PM
Sébastien,

def f(g: => (Int => Int)) = g(5)
I would call it with
f(x => x+1)
If I now define f as
f(g: () => (Int => Int)) = g()(5)
it would still kind of work, but you start to feel the issue.
I am not sure I see what’s wrong with this (besides explicit `()`).
 
The former is the meaning it has with current Scala. The latter would be a possible meaning with the automatic conversion of A into () => A. And now we have an ambiguity.
That’s a good point. I was expecting there would be caveats :)
However, I believe that can be easily solved. `A` does not equal `() ⇒ A`. It is only forcefully converted, when `() ⇒ A` type is expected. Inference remains as it was.
To draw an analogy with numeric conversions (which are being killed, but for other reasons), in `def f[A](x: A) = 1; f(1)` `A` is `Int` and not e.g. `Long`.
There is another problem, however:
def f(a: () ⇒ Any) = 9
f(() ⇒ 1) // is it f(() ⇒ 1) or f(() ⇒ () ⇒ 1)?
Not sure what to do with this one.

I'm not sure auto-implicit and auto-import values would not be any more representable.
 The thing is, `[ ⇒ A]` does not exists, so `[() ⇒ A]` has to be used. However, you cannot simply supply `() ⇒ A` to `⇒ A`. So once you need to pass a thunk around,
you have to quit using `⇒ A` in all places anyway. `(implicit X) ⇒ A` and `(import X) ⇒ A` are compatible with `X ⇒ A`.

N
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Sébastien Doeraene aka sjrd 1/11/14 11:01 PM

On Sun, Jan 12, 2014 at 7:48 AM, Nick Stanchenko <nick....@gmail.com> wrote:
I am not sure I see what’s wrong with this (besides explicit `()`).

Nothing wrong. I was just introducing the next paragraph. It was part of my own reasoning, I should probably have not written it at all.
 
 
The former is the meaning it has with current Scala. The latter would be a possible meaning with the automatic conversion of A into () => A. And now we have an ambiguity.
That’s a good point. I was expecting there would be caveats :)
However, I believe that can be easily solved. `A` does not equal `() ⇒ A`. It is only forcefully converted, when `() ⇒ A` type is expected. Inference remains as it was.
To draw an analogy with numeric conversions (which are being killed, but for other reasons), in `def f[A](x: A) = 1; f(1)` `A` is `Int` and not e.g. `Long`.

That sounds reasonable.
 
There is another problem, however:
def f(a: () ⇒ Any) = 9
f(() ⇒ 1) // is it f(() ⇒ 1) or f(() ⇒ () ⇒ 1)?
Not sure what to do with this one.

I would apply the same reasoning as you did above: convert only if needed. Hence it would mean f(() => 1) since it typechecks.


I'm not sure auto-implicit and auto-import values would not be any more representable.
 The thing is, `[ ⇒ A]` does not exists, so `[() ⇒ A]` has to be used. However, you cannot simply supply `() ⇒ A` to `⇒ A`. So once you need to pass a thunk around,
you have to quit using `⇒ A` in all places anyway.

Not necessarily. You can "convert" an `x` of type `() => A`into a `=> A` by writing `x()`. Conversely, you can "convert" a `=> A` into a `() => A` by writing `() => x`.
 
`(implicit X) ⇒ A` and `(import X) ⇒ A` are compatible with `X ⇒ A`.

Yes, indeed. Good point.

Cheers,
Sébastien

Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/11/14 11:09 PM
 Sébastien,

I would apply the same reasoning as you did above: convert only if needed. Hence it would mean f(() => 1) since it typechecks.
Agreed. 

Not necessarily. You can "convert" an `x` of type `() => A` into a `=> A` by writing `x()`. Conversely, you can "convert" a `=> A` into a `() => A` by writing `() => x`.
Correct. But isn’t it a bit weird that it looks like you are evaluating `x` when you are not? }:)

Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Eugene Burmako 1/12/14 2:37 AM
Yeah, the new import syntax looks very nice!
...
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Haoyi Li 1/14/14 12:16 AM
To anyone who's interested,

I've written up a set of concrete use cases over on the DSL-paradise github page, which will be the basis for our experiments with these ideas. Feel free to read, critique and generally discuss these ideas. It's not likely to go anywhere before 2.11 lands, but it's still valuable to refine the idea.
...
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Alex Cruise 1/14/14 10:18 AM
TBH my off-the-cuff reaction is mildly negative, even though I can see the benefits in these isolated use cases.

1) It can already be difficult to figure out what some localized bit of code is doing without a type-aware IDE; this won't help. :)
2) The declaration syntax seems noisy to me, and => is already overloaded in many people's minds.  What about "def foo(bar: Bar, import baz: Baz)"?  It wouldn't help with implicits though.

-0xe1a
...
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Sébastien Doeraene aka sjrd 1/14/14 10:21 AM
Hi Alex,

This proposal does not overload the meaning of =>: such parameters would indeed be functions, and in particular, within the defining method, they would be used exactly as function values, without any "magic". If at all, it overloads the meaning of import and implicit.

Cheers,
Sébastien
...
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 1/14/14 10:22 AM
Hi,


On Tuesday, January 14, 2014 6:18:30 PM UTC, Alex Cruise wrote:
2) The declaration syntax seems noisy to me, and => is already overloaded in many people's minds.  What about "def foo(bar: Bar, import baz: Baz)"?  It wouldn't help with implicits though.
 
With your notation, what gets imported where? I guess it would help if you could provide a desugaring example.

Nick
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Alex Cruise 1/14/14 10:41 AM
On Tue, Jan 14, 2014 at 10:21 AM, Sébastien Doeraene <sjrdo...@gmail.com> wrote:
This proposal does not overload the meaning of =>: such parameters would indeed be functions, and in particular, within the defining method, they would be used exactly as function values, without any "magic". If at all, it overloads the meaning of import and implicit.

I don't think that's the case with the "scope injection" examples.

e.g. in the Spray example, Accept ...accepts... a Seq[Media-Range], not a function.  And changing an API from by-value to by-name arguments needs to be done loudly, and will still cause a lot of confusion. :)

This is more of a syntax/UX argument though, I'm starting to warm up to the general idea.

-0xe1a
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Haoyi Li 1/14/14 11:32 AM
It can already be difficult to figure out what some localized bit of code is doing without a type-aware IDE

I agree that it may become more difficult to figure out what's going on without type-help, but the same can be said of type inference in general. In a way, this is just another form of type inference! It's inferring that the Pattern flags come from the Pattern object, so you don't need to say Pattern three times.

e.g. in the Spray example, Accept ...accepts... a Seq[Media-Range], not a function.  And changing an API from by-value to by-name arguments needs to be done loudly, and will still cause a lot of confusion. :)

I agree it has to be done loudly, but I disagree it will cause a lot of confusion. I'd wager that for most of the static Enum and Enum-like use cases, you're passing in constants/simple structures, so nothing much will change. For the dynamic-scope-injection use cases, they were already by-name arguments anyway.

Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Haoyi Li 1/15/14 10:18 AM
@AlexCruise take a look at this, which is an attempt at resolving most of the issues you brought up, mostly by avoiding the by-name behavior for parameters which don't need/want it.

Declaration-site syntax is still up for grabs at the moment, focusing on the call-site semantics since that's what people are going to feel over and over if this ever becomes a thing.
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Eugene Burmako 2/23/14 2:20 AM
Hi guys,

What's the status of this project?

Cheers,
Eugene
Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Haoyi Li 2/23/14 2:29 AM
To be honest, I got distracted by other shiny things (ooo macrosss) and haven't put time into it. Looks like the @Implicit annotation is working. 


--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Re: [scala-debate] Re: Method arguments that introduce new identifiers into scope? Or: language support for Enums and contextual DSLs? Nick Stanchenko 2/23/14 2:41 AM
Hi,

I made it work for primitives (Int @Implicit => X), but then it turned out the annotation was not preserved the same way for ref types. Sadly, now I'm loaded with other stuff too :( Hopefully, I'll have some time to go further in about 1.5 weeks. You can look for details here: https://github.com/dsl-paradise/dsl-paradise/issues/9

Nick

More topics »