A sort-of DSL for Lift's Javascript abstractions

421 views
Skip to first unread message

Peter Robinett

unread,
Jun 15, 2011, 8:35:57 PM6/15/11
to lif...@googlegroups.com
For some time know I've been thinking about how cool it'd be to be able to essentially write Javascript but with all the Scala goodiness behind the scenes. Lift already has abstractions for more common Javascript language elements, so I have suspected that it could be used as the basic for something that looked more like plain Javascript.

This evening I was looking at PhantomJS and realized that to use it for the type of work I need I would have control it with lots of Javascript files generated on the fly. So, I finally sat down and hacked around. This is what I've come up with:

val test = j_var ("test") := j_true
val test2 = j_var ('test2) := j_false

1 j_< 2
JsVar("k") j_<= 10

val a: JsVar = 'a
a++

val clause1 = j_if (j_true) {
  j_return (j_false)
} j_else {
  j_return (j_true)
}

val clause2 = j_if (j_true) {
  j_return()
} j_else {
  j_return (j_true)
}

val k: JsVar = 'k
val console_log = JsVar("console", "log")
val c = j_for (j_var (k) := 0, k j_< JsVar("arguments", "length"), k++) {
  console_log(k)
}

As you can see, for Javascript language features I've basically just put 'j_' before what are also reserved words in Scala. So, JsFalse becomes j_false. The same is true for break, continue, and return.

As you can see with j_if and j_for, those case classes are declared such that you get a more natural syntax, one approaching Javascript's. For instance, you can directly call a JsVar with 0 to n parameters, without writing Call("var_name", params0, ...). I guess that the experimental Dynamic trait could be added to the pimped JsVar (RichJsVar, of course) but that's probably going a bit far! ;-)

I've also added a bunch of implicits, both for my pimped classes but also to convert JsCmd to JsExp. Lift already has a conversion the other way around but there isn't one in this direction. I'm not sure if there is no implicit for a good reason. Perhaps this will break things?

Where is this wonderful code, you ask? Why, it's a simple Gist: https://gist.github.com/1028437!

I look forward to your comments!

Peter

TylerWeir

unread,
Jun 15, 2011, 8:40:33 PM6/15/11
to lif...@googlegroups.com
Very cool Pete.

I was just looking at phantomjs today as well.

Naftoli Gugenheim

unread,
Jun 16, 2011, 12:25:00 AM6/16/11
to lif...@googlegroups.com
There was a discussion about a javascript dsl a long time ago on the list. Marius had one version, and I posted a different DSL. Don't have a link, you can search for it.
I would be very interested in a proper discussion of pros and cons of various approaches.


On Wed, Jun 15, 2011 at 8:40 PM, TylerWeir <tyler...@gmail.com> wrote:
Very cool Pete.

I was just looking at phantomjs today as well.

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/Ax1r-rdEML4J.

To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Peter Robinett

unread,
Jun 16, 2011, 7:36:57 AM6/16/11
to lif...@googlegroups.com
You're very right. There have been several discussions including one that I started last year: https://groups.google.com/d/topic/liftweb/JMcVtSvrjYE/discussion

I didn't look at that code last night. Maybe I should have. ;-)

Anyway, I'm not proposing my code be part of Lift but just throwing it out there for other people to check out.

Peter 

David Pollak

unread,
Jun 16, 2011, 1:29:46 PM6/16/11
to lif...@googlegroups.com
On Wed, Jun 15, 2011 at 9:25 PM, Naftoli Gugenheim <nafto...@gmail.com> wrote:
There was a discussion about a javascript dsl a long time ago on the list. Marius had one version, and I posted a different DSL. Don't have a link, you can search for it.
I would be very interested in a proper discussion of pros and cons of various approaches.

I really like the stuff Peter put together in this thread, but I agree that a full discussion should be had about an improved DSL... and perhaps Daniel Spiewak can join the discussion given the about of JavaScript he's done for Vibe.

But most importantly, I'd like to discussion to reach a conclusion and an implementation to roll into Lift.  So, Peter, because you started this discussion, can you lead the discussion and take responsibility for getting an implementation into Lift?

Thanks,

a very excited David
 


On Wed, Jun 15, 2011 at 8:40 PM, TylerWeir <tyler...@gmail.com> wrote:
Very cool Pete.

I was just looking at phantomjs today as well.

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/Ax1r-rdEML4J.

To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.



--
Lift, the simply functional web framework http://liftweb.net

Antonio Salazar Cardozo

unread,
Jun 17, 2011, 12:55:18 AM6/17/11
to lif...@googlegroups.com
On a related note, one idea I toyed with but didn't have time to actually write code for was the idea of being able to convert a subset of the CSS selector transforms (namely those that don't involve generic (NodeSeq)=>NodeSeqs) to corresponding jQuery commands via JsCmd, so that these could then be applied client-side. The idea itself is still hazy, and I think it's missing a thing or two before becoming something that would be truly useful, but I have a hunch there's something interesting in that direction.

Just throwing it out there in case it touches off some cool ideas for someone else.
Thanks,
Antonio

Naftoli Gugenheim

unread,
Jun 17, 2011, 1:18:12 AM6/17/11
to lif...@googlegroups.com
Hey, I was thinking of that too! :) Actually I think I mentioned it on the list somewhere...



--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/NadSUJYoWpMJ.

Naftoli Gugenheim

unread,
Jun 17, 2011, 1:27:46 AM6/17/11
to lif...@googlegroups.com
One way of going about it would be to have the CSS selectors actually delegate their functionality to another class via implicits, so that you could drop in your own behavior with an import.
A simpler way would be to just have a separate DSL with the same syntax.

But what's really the point? Is it just to have a nice DSL for jQuery transformations?

Or is there a good use case to have one transformation specification be used both on the server and client side?

Also, you exclude NodeSeq=>NodeSeq transformations because, having scala code, they only run on the server. But maybe it could still generate javascript: store the function in the session with a GUID, and generate javascript that makes an ajax call!

Also this could be a lot more promising in the context of reactive-web, especially when pure-javascript event streams and signals are implemented, G-d willing.

Naftoli Gugenheim

unread,
Jun 17, 2011, 1:32:17 AM6/17/11
to lif...@googlegroups.com

Erik Post

unread,
Jun 17, 2011, 5:41:28 AM6/17/11
to lif...@googlegroups.com
Op donderdag 16 juni 2011 02:35:57 UTC+2 schreef Peter Robinett het volgende:
I saw Martin Odersky's Scala Days 2011 keynote [1] last night, and he talked about how they were looking at generating Javascript from Scala either through a new compiler backend, or by using a DSL. I thought it might be interesting to mention in this thread.

Cheers, Erik


Erik Post

unread,
Jun 17, 2011, 5:55:07 AM6/17/11
to lif...@googlegroups.com
Hi Naftoli,

Op vrijdag 17 juni 2011 07:27:46 UTC+2 schreef nafg het volgende:
One way of going about it would be to have the CSS selectors actually delegate their functionality to another class via implicits, so that you could drop in your own behavior with an import.
A simpler way would be to just have a separate DSL with the same syntax.

But what's really the point? Is it just to have a nice DSL for jQuery transformations?

Forgive me if this is somewhat OT, but I just thought I'd mention another templating/Scala XML transformer project called css-selectors [1] that I discovered this week. It's based on Scalate's Scuery (as in Scala/jQuery) and I couldn't find any mention of it on this list. I'm experimenting with it in my Lift app and I think it's pretty cool. You construct transformations in Scala like this:

  $("#nav li span") replace <foo/> 

This will give you a transformer function which can be applied applied to a NodeSeq.

Antonio Salazar Cardozo

unread,
Jun 17, 2011, 11:18:12 AM6/17/11
to lif...@googlegroups.com
Yeah, the `what's the point' question is why I'm saying I think there are still some missing pieces to make it truly useful. I feel like there's something useful hiding there, just under the surface ;)

The original inspiration was that when I was toying around with node, I wrote some preliminary code in a library called graft that replicated some of Lift's CSS transform goodness server side. The benefit there was that graft could run on client *and* server, so you could use the same code to render things in real time on the client and on the server (meaning you could potentially have lower load on the server side due to less XML processing and have less code to maintain). This gives some of the same benefits, but I feel like there's probably more in that direction.

Definitely agree that the NS=>NS as an ajax call is a cool idea.
Thanks,
Antonio

Peter Robinett

unread,
Jun 17, 2011, 8:56:31 PM6/17/11
to lif...@googlegroups.com
Thanks, David, glad you like it. And I know I do tend to throw out things and not follow them up. Let's make this an exception! =) I will ping Daniel, Marius and other to check out this thread so that we can get some sort of DSL into Lift once and for all.

First, while I have everyone's attention, can anyone tell me why we have JsCmd and JsExp? They both seem to start from the same place (extends HtmlFixer with ToJsCmd, to be exact), but JsCmd has nothing beyond that but a & method to chain calls. JsExp, on the other hand, has a lot of additional methods. It'd be a breaking changing, but might it be a nice change to drop JsCmd and just have JsExp as the base Javascript type?

Ok, moving to Javascript DSLs. All examples can also be seen at https://gist.github.com/1032634, including some additional notes. The first one proposed that I know of was by Marius in December 2009. Here's his example:

JsFunc('myFunc, 'param1, 'param2) {
  JsIf('param1 __< 30) {
    Var('home) := Wrap(234 __- 3) __/ 2 `;`
    Var('someArray) := JsArray(1, 2, 3, 4, 5) `;`
    'myFunc(1, 2, "do it", 'home) `;`
    $("#myID") >> 'attr("value", "123") `;`
  } ~
  JsForEach(Var('i) in 'someArray) {
    'console >> 'log("Hi there " __+ 'i) `;`
  } ~
  JsAnonFunc('arg1, 'arg2) {
   'alert("Anonymous function " __+ 'arg1 __+ 'arg2)
  }(1, 2) `;`
}

It's quite concise and it's slick how semi-colons are used to chain commands, though you do need to remember the ticks each time so it doesn't get treated as a Scala semicolon. You can read the discussion around his proposal at https://groups.google.com/d/topic/liftweb/Xdql2etoQlc/discussion and https://groups.google.com/d/topic/liftweb/tj52BFcJYmU/discussion.

Exactly a year ago, not knowing Marius' proposal, I put forward my own attempt:

For(JsEq(JsVar("k"), Num(1)), JsVar("k") < Num(10), JsVar("k") ++) {
  Call("console.log", JsVar("k"))
}

I think it's a decent attempt. The discussion can been seen here: https://groups.google.com/d/topic/liftweb/JMcVtSvrjYE/discussion

Finally, you have my most recent proposal, again done in a vacuum but perhaps subconsciously picking up on the two previous proposals:

j_for (j_var (k) := 0, k j_< JsVar("arguments", "length"), k++) {
  console_log(k)
}

So, what'll it be? Of course, we can and should combine the best elements of all of them. Just off the top of my head and looking at the previous examples and the JsExp source code, we could easily get to something like:

For (Var ('k) := 0, 'k < 'arguments->'length, 'k++) {
  'console->'log('k)
}

So, suggestions? Does anyone have any specific complicated pieces of Lift Js code that they'd be willing to share, both to discuss what they don't like about the current syntax and to try implementing it in the different proposals to see which one looks nicest.

Thanks,
Peter

David Pollak

unread,
Jun 18, 2011, 8:16:20 AM6/18/11
to lif...@googlegroups.com
On Fri, Jun 17, 2011 at 5:56 PM, Peter Robinett <pe...@bubblefoundry.com> wrote:
Thanks, David, glad you like it. And I know I do tend to throw out things and not follow them up. Let's make this an exception! =) I will ping Daniel, Marius and other to check out this thread so that we can get some sort of DSL into Lift once and for all.

First, while I have everyone's attention, can anyone tell me why we have JsCmd and JsExp?

Because in JavaScript an expression (JsExp) is different from a statement (JsCmd).  Many expressions can be built together to be a command (e.g., "1 + 1" is an expression where "var x = 1 + 1" is a statement).
 
They both seem to start from the same place (extends HtmlFixer with ToJsCmd, to be exact), but JsCmd has nothing beyond that but a & method to chain calls.

Yes.  Statements can only be chained together as multiple statements.
 
JsExp, on the other hand, has a lot of additional methods. It'd be a breaking changing, but might it be a nice change to drop JsCmd and just have JsExp as the base Javascript type?

No.  It's important to respect the distinction that the JavaScript language draws because it's not possible to write:

var x = 1 + 1 * var y = 3 + 3;

And the type system should guarantee that.
 

Ok, moving to Javascript DSLs. All examples can also be seen at https://gist.github.com/1032634, including some additional notes. The first one proposed that I know of was by Marius in December 2009. Here's his example:

JsFunc('myFunc, 'param1, 'param2) {
  JsIf('param1 __< 30) {
    Var('home) := Wrap(234 __- 3) __/ 2 `;`
    Var('someArray) := JsArray(1, 2, 3, 4, 5) `;`
    'myFunc(1, 2, "do it", 'home) `;`
    $("#myID") >> 'attr("value", "123") `;`
  } ~
  JsForEach(Var('i) in 'someArray) {
    'console >> 'log("Hi there " __+ 'i) `;`
  } ~
  JsAnonFunc('arg1, 'arg2) {
   'alert("Anonymous function " __+ 'arg1 __+ 'arg2)
  }(1, 2) `;`
}

I'm not keen on the `;` syntax.  It seems more confusing and verbose than it's worth.  The whole Scala ` thing is a huge confusion point for newbies.
 

It's quite concise and it's slick how semi-colons are used to chain commands, though you do need to remember the ticks each time so it doesn't get treated as a Scala semicolon. You can read the discussion around his proposal at https://groups.google.com/d/topic/liftweb/Xdql2etoQlc/discussion and https://groups.google.com/d/topic/liftweb/tj52BFcJYmU/discussion.

Exactly a year ago, not knowing Marius' proposal, I put forward my own attempt:

For(JsEq(JsVar("k"), Num(1)), JsVar("k") < Num(10), JsVar("k") ++) {
  Call("console.log", JsVar("k"))
}

I think it's a decent attempt. The discussion can been seen here: https://groups.google.com/d/topic/liftweb/JMcVtSvrjYE/discussion

Finally, you have my most recent proposal, again done in a vacuum but perhaps subconsciously picking up on the two previous proposals:

j_for (j_var (k) := 0, k j_< JsVar("arguments", "length"), k++) {
  console_log(k)
}

Does the above compile?  If so, how do you know what k is?
 

So, what'll it be? Of course, we can and should combine the best elements of all of them. Just off the top of my head and looking at the previous examples and the JsExp source code, we could easily get to something like:

For (Var ('k) := 0, 'k < 'arguments->'length, 'k++) {
  'console->'log('k)
}

So, suggestions? Does anyone have any specific complicated pieces of Lift Js code that they'd be willing to share, both to discuss what they don't like about the current syntax and to try implementing it in the different proposals to see which one looks nicest.

Thanks,
Peter

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/eKSZTHqaYOsJ.

To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Peter Robinett

unread,
Jun 18, 2011, 8:48:33 AM6/18/11
to lif...@googlegroups.com
Thanks, David, for the clarification on JsExp vs JsCmd. That totally makes sense.

As for my loop:

j_for (j_var (k) := 0, k j_< JsVar("arguments", "length"), k++) {
  console_log(k)
}

Yes, it does compile if you have all the things I defined (see the earlier Gist). For instance, j_for is defined as:

object j_for {
  def apply(initialExp: JsExp, condition: JsExp, incrementExp: JsExp)(body: JsExp) = JsFor(initialExp, condition, incrementExp, body)
}

And j_var:

case class j_var(name: String, right: Box[JsExp] = Empty) extends JsExp {
  def toJsCmd = if (right.isDefined) {
    "var " + name + " = " + right.open_!.toJsCmd
  } else {
    "var " + name
  }
  def :=(in: JsExp) = this.copy(right = Full(in))
}
object j_var {
  def apply(name: Symbol) = new j_var(name.name)
  def apply(name: JsVar) = new j_var(name.varName)
}

k was defined earlier as a Scala val:

val k: JsVar = 'k

I had an implicit conversion in scope that converts from Symbol to JsVar.

The ++ was possible because I have an implicit conversion to the following:

class RichJsVar(left: JsVar) {
  def ++ = JE.JsRaw(left.toJsCmd + "++")
  def -- = JE.JsRaw(left.toJsCmd + "--")
  def +=(right: JsExp) = JE.JsRaw(left.toJsCmd + " += " + right.toJsCmd)
  def -=(right: JsExp) = JE.JsRaw(left.toJsCmd + " -= " + right.toJsCmd)
  def apply(params: JsExp*) = Call(left.toJsCmd, params :_*)
}

console_log was declared along the lines of k:

val console_log = JsVar("console", "log")

As you saw above, RichJsVar has an apply method that takes 0 to n JsExps and builds a Call.

Given that this was hacked together over a few hours, I'm sure there's a bunch we could do to organize it. For instance, it would make sense to decide whether Symbols or Strings should be used across the board for variable and method names. Beyond that, I did everything with implicit conversions because I didn't want to touch the existing code. A lot of them could be avoided if we were willing to change them or provide alternative apply methods, for instance to allow the keyword (clause) { body } style.

Peter

Naftoli Gugenheim

unread,
Jun 18, 2011, 11:32:03 PM6/18/11
to lif...@googlegroups.com
Hey, you don't mention my DSL! (Although yours looks fairly similar.)


--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/eKSZTHqaYOsJ.

Peter Robinett

unread,
Jun 19, 2011, 5:53:56 AM6/19/11
to lif...@googlegroups.com
Sorry, Naftoli, I missed yours! Everyone else, here's an example of Naftoli's approach:

val jsFunc: JSFunc = Function("myFunc")("param1", "param2") {case param1 :: param2 :: Nil =>
      Var("someArray") := Array(1, 2, 3, 4, 5)
      If(param1 < 30) {
        val x = Var("x")
        x := (234 - 3) / 2  // calculation happens in scala
        Var("y") := (2:Expr) * x * 2   // calculation happens in javascript
        'jsFunc(1, 2, "do it", 'y)
        val $ = JSIdent("$")
        $("#myID") >> 'attr("value", "123")
      } Else {
        'console >> 'log(">=30")
      }
      ForEach(Var("i") In 'someArray) {
        'console >> 'log("Hi there " & 'i)
      }
      Function()("arg1", "arg2") { case arg1 :: arg2 :: Nil =>
        'alert("Anonymous function (" & arg1 & ", " & arg2 & ")")
      }(1,2)
  }
  println(jsFunc)
  ()
}

Peter

Richard Dallaway

unread,
Jun 19, 2011, 7:04:27 AM6/19/11
to lif...@googlegroups.com
Just to add to the mix, if I understood correctly, Grzegorz Kossakowski who interned at Google for the Scala->GWT work will be working with the EPFL team during the northern hemisphere summer to produce an initial prototype of a Scala-to-Javascript system of some kind.  May be compiler-like, might be DSL... I think it's early days yet.  I couldn't find any more info about it.  It was mentioned in the Scala Exchange keynote on Wednesday.

Richard


Peter

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/uVXTCuF7MTIJ.

Naftoli Gugenheim

unread,
Jun 19, 2011, 1:27:20 PM6/19/11
to lif...@googlegroups.com
Are you in the mood or able to put together a more comprehensive side-by-side?



Peter

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/uVXTCuF7MTIJ.

David Pollak

unread,
Jun 20, 2011, 1:07:28 PM6/20/11
to lif...@googlegroups.com
On Sun, Jun 19, 2011 at 4:04 AM, Richard Dallaway <ric...@dallaway.com> wrote:
Just to add to the mix, if I understood correctly, Grzegorz Kossakowski who interned at Google for the Scala->GWT work will be working with the EPFL team during the northern hemisphere summer to produce an initial prototype of a Scala-to-Javascript system of some kind.  May be compiler-like, might be DSL... I think it's early days yet.  I couldn't find any more info about it.  It was mentioned in the Scala Exchange keynote on Wednesday.

FWIW, I would be very reluctant to base any major Lift feature on experimental or unsupported code out of EPFL.  We've had enough issues with the Scala Actor library and ongoing pattern matching problems (both core parts of Scala) that I doubt I'd do anything other than veto anything that's not solid, well tested, and clearly going to be supported for 5+ years that comes out of EPFL.
 

Peter Robinett

unread,
Jun 20, 2011, 1:35:44 PM6/20/11
to lif...@googlegroups.com
Well, I think the comparisons I've been up already are decent given the major differences are ones of style (all compile and work as expected). What would you like to still see compared?

Peter

David Pollak

unread,
Jun 20, 2011, 2:06:26 PM6/20/11
to lif...@googlegroups.com
On Mon, Jun 20, 2011 at 10:35 AM, Peter Robinett <pe...@bubblefoundry.com> wrote:
Well, I think the comparisons I've been up already are decent given the major differences are ones of style (all compile and work as expected). What would you like to still see compared?

What are the goals of a JavaScript DSL?  Here are some off the top of my head, but there are likely others:

- Concise JavaScript coding
- Type safe JavaScript coding
- Late binding of Scala binding seamlessly into JavaScript (e.g., a String variable that gets inserted into the JavaScript on the server before the JavaScript get served)
- Good integration with jQuery

There are probably other goals.  If we list all the goals and then get a rough idea of which ones are most important, then we can look at each of the JavaScript DSLs and see how they compare to the goals.
 

Peter

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/jbEBEPFRxd4J.

To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Naftoli Gugenheim

unread,
Jun 20, 2011, 6:17:41 PM6/20/11
to lif...@googlegroups.com
Are you responding to my comment out a more comprehensive comparison? (It's hard to tell without any quoted text. ;) )
What I meant is that I think my snippet illustrates the DSL's syntax for much more language features than the other code samples.
Obviously, this is not a contest where there's one winner; the goal is to mix and match to get every part the best way. So it would be helpful if you would be willing and have the time to make a language-feature-for-language-feature comparison (possibly just by converting my snippet to the other DSLs, leaving out whichever parts are not implemented).


An unrelated point: For the benefit of the hopefully-coming-soon reactive-web feature allowing pure-javascript event streams and signals, I would prefer if the DSL would be typed, having the type relationships such as the following:

JsNumber <: JsType
JsString <: JsType
// etc
// maybe rename JsType to JsAny

JsExp[T <: JsType]  // not necessarily the existing JsExp, so as not to be a breaking change
JsVar[T <: JsType] <: JsExp[T]
JsLiteral[T <: JsType] <: JsExp[T]

case class Num(n: Double) extends JsLiteral[JSNumber] // or something like this
// etc.

The reason why this would be very beneficial is because then you could write something like

span.contents <<: field.value.map {s: JsExp[JsString] =>
  Str("$") + s
}

and it could generate

function(arg0) { return "$" + s; }

simply by calling the Scala function with a JsVar.

Not sure how good of an explanation that was, but the goal is a pure Scala alternative to GWT, including client-side logic.


Anyway, if necessary I could write my own DSL (and supply conversions to/from Lift's), so don't sweat too hard. :)


On Mon, Jun 20, 2011 at 1:35 PM, Peter Robinett <pe...@bubblefoundry.com> wrote:
Well, I think the comparisons I've been up already are decent given the major differences are ones of style (all compile and work as expected). What would you like to still see compared?

Peter

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/jbEBEPFRxd4J.

Naftoli Gugenheim

unread,
Jun 22, 2011, 2:08:09 AM6/22/11
to lif...@googlegroups.com
On Mon, Jun 20, 2011 at 6:17 PM, Naftoli Gugenheim <nafto...@gmail.com> wrote:
An unrelated point: For the benefit of the hopefully-coming-soon reactive-web feature allowing pure-javascript event streams and signals, I would prefer if the DSL would be typed, having the type relationships such as the following:

[ ... ]

The reason why this would be very beneficial is because then you could write something like

span.contents <<: field.value.map {s: JsExp[JsString] =>
  Str("$") + s
}

and it could generate

function(arg0) { return "$" + s; }

simply by calling the Scala function with a JsVar.

Not sure how good of an explanation that was, but the goal is a pure Scala alternative to GWT, including client-side logic.


So far here's what I came up with as far as javascript type safety (beginnings of pure-client-side event stream also included, for the curious):

package reactive
package web
package javascript

import net.liftweb.http.js.JsCmds.Run



trait JsExp[T <: JsAny] {
  def render: String
}
case class JsVar[T <: JsAny](ident: Symbol) extends JsExp[T] {
  def render = ident.toString
}
trait JsLiteral[T <: JsAny] extends JsExp[T]
object JsLiteral {
  def apply[T, J <: JsAny](x: T)(implicit conv: JsConversion[J, T]): JsLiteral[J] = conv(x)
}
trait JsRaw[T <: JsAny] extends JsExp[T]
object JsRaw {
  def apply[T <: JsAny](rendering: =>String) = new JsRaw[T] {
    def render = rendering
  }
}
trait JsAny
trait JsBoolean extends JsAny
trait JsNumber extends JsAny
trait JsString extends JsAny
trait JsDate extends JsAny
trait JsRegex extends JsAny
trait JsObj extends JsAny
trait JsArray extends JsAny
trait JsFunction[P<:JsAny,R<:JsAny] extends JsAny

class JsConversion[J <: JsAny, -S](renderer: S=>String) extends (S=>JsLiteral[J]) {
  def apply(s: S): JsLiteral[J] = new JsLiteral[J] {
    def render = renderer(s)
  }
}
object JsConversion {
  implicit val number = new JsConversion[JsNumber, Double](_.toString)
  implicit val bool = new JsConversion[JsBoolean, Boolean](_.toString)
  implicit val string = new JsConversion[JsString, String]("\"" + _ + "\"")
  implicit val date = new JsConversion[JsDate, java.util.Date]("new Date(\"" + _.toString + "\")")
  implicit val regex = new JsConversion[JsRegex, scala.util.matching.Regex]("/" + _.toString + "/")
  implicit val obj = new JsConversion[JsObj, Map[String,JsExp[_]]](_.map{case (k,v)=>"\""+k+"\":"+v.render}.mkString("{",",","}"))
  implicit val array = new JsConversion[JsArray, List[JsExp[_]]](_.map(_.render).mkString("[",",","]"))
  implicit def func[P<:JsAny,R<:JsAny] = new JsConversion[JsFunction[P,R], JsExp[P]=>JsExp[R]]("function(arg){" + _(JsVar('arg)).render + "}")
}


object JsEventStream extends net.liftweb.http.RequestVar(false) {
  def render = if(!is) {
    set(true)
    Reactions.queue(Run(
"""window.EventStream = function() {
  this.listeners = []
  this.foreach = function(f){this.listeners.push(f)}
  this.addListener = this.foreach
  this.removeListener = function(f){
    for(l in this.listeners) {
      if(this.listeners[l] === f) {
        delete this.listeners[l]
        break
      }
    }
  }
  this.fire = function(v) {
    for(l in this.listeners) {
      this.listeners[l](v)
    }
  }
  this.map = function(f) {
    var mapped = new EventStream()
    this.addListener(function(v){mapped.fire(f(v))})
    return mapped
  }
  this.flatMap = function(f) {
    var flatMapped = new EventStream()
    var lastES = null
    this.addListener(function(v){
      if(lastES) lastES.removeListener(flatMapped.fire)
      lastES = f(v)
      lastES.addListener(flatMapped.fire)
    })
    return flatMapped
  }
  this.filter = function(f) {
    var filtered = new EventStream()
    this.addListener(function(v){
      if(f(v)) filtered.fire(v)
    })
    return filtered
  }
  return this
}
"""
    ))
  }
}
class JsEventStream[T<:JsAny]()(implicit page: Page) {parent =>
  lazy val id = page.nextId
  var rendered = false
  def renderExp = "new EventStream()"
  def render = if(!rendered){
    rendered = true
    JsEventStream.render // define the constructor
    Reactions.queue(Run("window."+id+"=" + renderExp))
  }
  protected def child[U <: JsAny](renderer: =>String) = {
    render
    new JsEventStream[U]()(page) {
      override def renderExp: String = renderer
    }
  }
  def fire(v: JsExp[_]) {
    Reactions.queue(Run(id+".fire("+v.render+")"))
  }
  def foreach[F <% JsExp[JsFunction[T,JsAny]]](f: F) {
    render
    Reactions.queue(Run(id+".foreach("+f.render+")"))
  }
  def foreach(f: String=>Unit) {
    import net.liftweb.http.{S,SHtml}
    val ajaxCall: JsExp[T]=>JsExp[JsAny] = value => JsRaw[JsAny](S.fmapFunc(S.contextFuncBuilder(RElem.ajaxFunc(f))) {funcId =>
      SHtml.makeAjaxCall(
        net.liftweb.http.js.JE.JsRaw("'"+funcId+"=' + encodeURIComponent("+value+")")
      ).toJsCmd
    })
    foreach(ajaxCall)(JsConversion.func[T,JsAny])
  }
  def map[U<:JsAny, F <% JsExp[JsFunction[T,U]]](f: F): JsEventStream[U] = child("parent.map("+f.render+")")
  def flatMap[U<:JsAny, F <% JsExp[JsFunction[T,U]]](f: F): JsEventStream[U] = child("parent.flatMap("+f.render+")")
  def filter[F <% JsExp[JsFunction[T,JsBoolean]]](f: F): JsEventStream[T] = child("parent.filter("+f.render+")") 
}

Naftoli Gugenheim

unread,
Jun 22, 2011, 3:22:13 AM6/22/11
to lif...@googlegroups.com
On Mon, Jun 20, 2011 at 6:17 PM, Naftoli Gugenheim <nafto...@gmail.com> wrote:
Are you responding to my comment out a more comprehensive comparison? (It's hard to tell without any quoted text. ;) )
What I meant is that I think my snippet illustrates the DSL's syntax for much more language features than the other code samples.
Obviously, this is not a contest where there's one winner; the goal is to mix and match to get every part the best way. So it would be helpful if you would be willing and have the time to make a language-feature-for-language-feature comparison (possibly just by converting my snippet to the other DSLs, leaving out whichever parts are not implemented).


Perhaps my snippet wasn't more comprehensive after all. Anyway, let me try to start off a side-by-side comparison. It doesn't go into detail on operators.

Peter, if there are any aspects to your DSL which don't have a section here, please add more (besides otherwise editing to your heart's content).


Define a Named Function
MD:
JsFunc('functionName, 'param1, 'param2 /* , ... */ ) {
 // only one composite javascript expression here,
 // access arguments as 'symbols
}
NG:
val f: JSFunc = Function("functionName")("param1","param2"){case List(param1, param2) =>
  // Javascript expressions written here will be automatically made part of the function
  // param1 etc. are typed as JSIdent and can be used directly to form client-evaluated expressions
}
PR1:
Function 'myFunc ('param1, 'param2) {
  // allows multiple expressions, but parameters are just symbols, so compiler doesn't know when they're being accessed
}
PR2: ?

Write an Anonymous Function (and Invoke It)
MD:
  JsAnonFunc('arg1, 'arg2) {
   // one composite javascript expression here
  }(1, 2) `;`
NG:
Function()("arg1", "arg2") { case arg1 :: arg2 :: Nil =>
  // javascript here
}(1,2)
PR1:
Function ('arg1, 'arg2) { 
}(1, 2)
PR2: ?

Declare a Variable
NG:
Var("someName")
val myVar = Var("myVar")

Assign/Initialize a Variable
MD: (not sure if it's only for initializers)
Var('myVar) := expression  // yields var myVar = expression
NG: (can be used in declaration expression, but yields two statements, see below)
myVar := javascriptDSLExpression
'someName := javascriptDSLExpression
Var("myVar") := expression // yields 'var myVar' and 'myVar = expression' on two lines
PR1:
Var 'varName := expression
PR2:
j_var("name") := expression
j_var('name: JsVar) := expression

Invoke a Function
MD, PR1:
'functionName(arg1, arg2 /* , ... */ )
NG:
'functionName(arg1, arg2 /* , ... */ )
functionRef(arg1, arg2)

Invoke a Method
MD, NG, PR1:
targetJavascriptExpression >> 'methodIdentifier(arg1, arg2)
PR2:
JsVar("console","log")(expression)

Reference an Identifier
PR1, MD:
'identifierName
NG:
'identifierName
JSIdent("identifierName")
PR2:
'k: JsVar

Write Client-Side Expressions
MD:
(234: Js) + 'x / 3 `;` 
NG:
(2: Expr) * x * 2
JSNum(2) * x * 2
PR1:
Number(234) + 'x / 3
PR2:
1 j_< 2
JsVar("k") j_<= 10

Write Conditionals
MD:
JsIf(expression) {
}
NG:
If(javascriptExpression) {
  // javascript here
} Else {
  // javascript here
}
PR1:
If (expression) {
}
PR2:
val clause = j_if(expression) {
} j_else {
}

Write a for...in Loop
MD:
JsForEach(Var('i) in 'someArray) {
} ~
NG:
ForEach(Var("i") In 'someExpression) {
  // javascript here
}
PR1:
Foreach(Var 'i in 'someArray) { 
}

Write a for Loop
PR2:
j_for(j_var(k) := 0, k j_< JsVar("arguments","length), k++) {
}

Naftoli Gugenheim

unread,
Jun 22, 2011, 3:28:45 AM6/22/11
to lif...@googlegroups.com
My third consecutive post on this thread... (each is somewhat of a separate topic)

What is the ideal level of type safety for a javascript DSL?
One could argue, why not get all scalac has to offer, and make the DSL fully type safe. If you specifically want to write code that isn't 100% type safe, don't write it solely via the DSL.
One the other hand, javascript is designed not to be type safe. For example, the condition part of an if statement can be a boolean expression in which case it must be true, or of another type in which case it checks that it's defined and not null.
Personally I think that ideally it should be fully type safe, using typed scala identifiers and not just 'symbols --- but there should be ways to explicitly force the DSL to accept something of the wrong type.

Naftoli Gugenheim

unread,
Jun 22, 2011, 11:24:44 PM6/22/11
to lif...@googlegroups.com
On Wed, Jun 22, 2011 at 3:28 AM, Naftoli Gugenheim <nafto...@gmail.com> wrote:
My third consecutive post on this thread... (each is somewhat of a separate topic)

What is the ideal level of type safety for a javascript DSL?
One could argue, why not get all scalac has to offer, and make the DSL fully type safe. If you specifically want to write code that isn't 100% type safe, don't write it solely via the DSL.
One the other hand, javascript is designed not to be type safe. For example, the condition part of an if statement can be a boolean expression in which case it must be true, or of another type in which case it checks that it's defined and not null.
Personally I think that ideally it should be fully type safe, using typed scala identifiers and not just 'symbols --- but there should be ways to explicitly force the DSL to accept something of the wrong type.

Also, what are the goals/purposes of the DSL?
So that whatever javascript has to be written should be type safe?
To enable one to write lots of type-safe javascript?
Yes? No? Others?

Peter Robinett

unread,
Jun 23, 2011, 5:41:01 AM6/23/11
to lif...@googlegroups.com
Hi all,

Sorry I'm only responding now. To answer David's, and later Naftoli's, question about what the goals are for a Javascript DSL, here are mine:
  1. Concise Javascript coding in a fashion that looks, in Scala, as much like real Javascript as possible/reasonable
  2. Type safe Javascript coding
Late binding is something I hadn't thought much about but would be very cool. I'm not sure how to do that, so David or someone else would need to help me or whoever is coding the DSL. As for integration with jQuery, I would prefer to continue on the route Lift has already taken: there are the base constructs in net.liftweb.http.js, and then there is library specific code in net.liftweb.http.js.jquery and the like. This relates somewhat tangentially to the Javascript module system I proposed a long time ago...

I think that even if we only do the two points I highlighted we will have something that is quite useful and powerful, so much so that I can easily see non-Lift projects using it.

As for syntax, given my preference for approximating Javascript but the frequent keyword overlap between Javascript and Scala, my inclination now is to just capitalize the conflicting keyword (i.e. rather than the j_keyword syntax I used most recently). This gives a quite natural syntax but is possible without any compiler gyrations. Given that Scala doesn't have any sort of method_missing functionality, 'object.property' sytax is not possible. I like 'object->property', especially considering that some other languages use it instead of the dot notation.

How to get there?

I think there's one core philosophical question to answer, whether we want this DSL to be a layer on top of the existing Javascript code or should return to first principles. If the latter, Naftoli has suggested two interesting options: 1) to base the data types off of lift-json or 2) to develop a new, complete system of Javascript types with logical hierarchies. I like both ideas and am particularly intrigued by the second approach, having already done something similar with typed.js (https://github.com/pr1001/typed.js).

However, I actually prefer the pragmatic approach. I think we've got enough code present that not only is it enough to build an interesting DSL, but also a third Javascript-related system (say, net.liftweb.javascript vs net.liftweb.http.js and net.liftweb.json) would be too much. Likewise, I don't think we can or should just throw out net.liftweb.http.js: it's been around, it works, and much of both Lift's internal code and other people's code reply upon it.

So, should we extend existing objects or provide complementary ones? In the two versions I've mocked up I've taking the second approach, as I didn't want to touch Lift. However, it leads to a lot of implicit conversions and I think most, if not all, of the existing things can be safely extended, especially since it would just consist of some methods and alternate apply's. Plus, it wouldn't take too long to develop.

So, that's my preferred approach. Thoughts?

Peter

Naftoli Gugenheim

unread,
Jun 23, 2011, 4:24:47 PM6/23/11
to lif...@googlegroups.com
First, a little correction --- my javascript types were not based on lift-json. The idea occurred to me later, but looking now it doesn't seem that they are rich enough for my needs (converting a JsExp[T]=>JsExp[R] into a javascript function, for client-side event streams).

Regarding goal one, if taken in isolation, what's the gain over writing actual javascript?
Regarding goal two, again the question is what level of type safety we want. This question has two parts: How precise/comprehensive/safe the type safety should be; and at what level: just at the surface, so to speak, or on the small scale too, so to speak.

I agree that method/property access should be with ->. It may a bit roundabout, like what Map does, to work around operator precedence.

Whether to take the more idealistic or pragmatic approach, if taking the short path means a DSL that's more work to write -- and to read, then I think we should think about long term rather than short term. As far as backwards compatibility, I don't think it's a big deal to have implicits that make them interchangeable.

In general, I think we should first work out the ideal DSL syntax, and whatever it takes to implement it is worth it.

Unless I misunderstood, and the question about reusing the javascript classes is completely orthogonal to the DSL syntax? In that case then my only criteria is the level of type safety. The existing JsExp is not typed, so if it can be made typed without breaking code then fine; otherwise it would seem to require a new hierarchy.

Also I'm not sure what you meant by late binding.

My approach to javascript types is to have a scala type hierarchy that models javascript types --- but you don't actually instantiate these types. Instead, you have a separate hierarchy of
trait JsExp[T <: JsAny] {
  def render: String // generate javascript code
}
JsLiteral[T <: JsAny] <: JsExp[T]
JsRaw[T <: JsAny] <: JsExp[T]
JsBinaryOperation[T <: JsAny] <: JsExp[T]
etc.

To convert scala values into modeled javascript, you would use the JsLiteral factory (perhaps aliased as 2.j) which relies on an implicitly available ToJsConversion. Thus the system is very extensible (you can easily define elsewhere e.g. an ExtJsTable type --- just define a ToJsConversion in its companion).

Valuable to reactive but not the DSL, there would be a corresponding typeclass-based approach for send javascript to scala. It would need two methods:
trait FromJsConversion[J <: JsAny, S] {
  def encode: JsExp[J]=>JsExp[JsString]
  def decode: String => S
}
encode would generate a javascript expression to encode the value into a string that can be sent to the server and decoded by decode.



Peter

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/9HxYbfyb1ecJ.

Naftoli Gugenheim

unread,
Jun 23, 2011, 10:50:11 PM6/23/11
to lif...@googlegroups.com
Meanwhile, I've been working on this, with G-d's help. It's not very DSLish yet, but it's quite type safe. Here's an example reactive-web snippet using it:

class JsEventStreamDemo extends Observing {
  import JsLiterable._

  val jses = new JsEventStream[JsString]

  val alert = JsIdent[JsFunction1[JsString,JsVoid]]('alert)
  
  def render = {
    Reactions.inServerScope(Page.currentPage) {
      jses.foreach { v: JsExp[JsString] =>
        alert.apply(v)
      }
    }
    "button" #> (DOMEventSource.click ->> jses.fire("Button clicked".j))
  }
}


Currently the button click still goes to the server which evalutes the "jses.fire..." command, but that's because DOMEventSource hasn't been updated yet.

Here's what happens:
When Lift calls render, in a "server" (comet) scope we add a listener to the event stream. The listener looks like a Scala JsExp[JString]=>JsExp[JsVoid] function, but it can, and is, easily converted to the equivalent javascript code. When we pass the listener to jses.foreach, it (a) makes sure the browser has the javascript EventStream defined, (b) has the corresponding javascript instance defined, and (c) passes the javascript listener function to the javascript EventStream instance's foreach method.
At this point, you can open the browser's javascript console and write reactiveWebId_000000.fire("Button clicked") and you'll get an alert box with that message. Ultimately, G-d willing, that javascript code would be attached directly as an event listener. Currently, though, it's dependent on the server to send the browser the actual "fire" command.

Naftoli Gugenheim

unread,
Jun 24, 2011, 1:19:16 AM6/24/11
to lif...@googlegroups.com
New version:

class JsEventStreamDemo extends Observing {
  val jses = new JsEventStream[JsString]

  val alert = JsIdent[JsString |=> JsVoid]('alert)
  
  def render = {
    Reactions.inServerScope(Page.currentPage) {
      jses.foreach { v: JsExp[JsString] =>
        alert.apply("Fired: ".$ + v)
      }
    }
    "button" #> (DOMEventSource.click ->> jses.fire("Button clicked"$))
  }
}

When you click the button, it pops up an alert with "Fired: Button clicked".

Jeppe Nejsum Madsen

unread,
Jun 24, 2011, 9:02:40 AM6/24/11
to lif...@googlegroups.com
I haven't followed this closely, but a few comments inline....

On Thu, Jun 23, 2011 at 11:41 AM, Peter Robinett <pe...@bubblefoundry.com> wrote:
Hi all,

Sorry I'm only responding now. To answer David's, and later Naftoli's, question about what the goals are for a Javascript DSL, here are mine:
  1. Concise Javascript coding in a fashion that looks, in Scala, as much like real Javascript as possible/reasonable
  2. Type safe Javascript coding
Sounds about right. I see it for small glue code that connects server with browser.
 
Late binding is something I hadn't thought much about but would be very cool. I'm not sure how to do that, so David or someone else would need to help me or whoever is coding the DSL. As for integration with jQuery, I would prefer to continue on the route Lift has already taken: there are the base constructs in net.liftweb.http.js, and then there is library specific code in net.liftweb.http.js.jquery and the like. This relates somewhat tangentially to the Javascript module system I proposed a long time ago...

I think that even if we only do the two points I highlighted we will have something that is quite useful and powerful, so much so that I can easily see non-Lift projects using it.

As for syntax, given my preference for approximating Javascript but the frequent keyword overlap between Javascript and Scala, my inclination now is to just capitalize the conflicting keyword (i.e. rather than the j_keyword syntax I used most recently). This gives a quite natural syntax but is possible without any compiler gyrations. Given that Scala doesn't have any sort of method_missing functionality, 'object.property' sytax is not possible. I like 'object->property', especially considering that some other languages use it instead of the dot notation.


Agreed. I didn't really like the j_ syntax
 
How to get there?

I think there's one core philosophical question to answer, whether we want this DSL to be a layer on top of the existing Javascript code or should return to first principles. If the latter, Naftoli has suggested two interesting options: 1) to base the data types off of lift-json or 2) to develop a new, complete system of Javascript types with logical hierarchies. I like both ideas and am particularly intrigued by the second approach, having already done something similar with typed.js (https://github.com/pr1001/typed.js).

However, I actually prefer the pragmatic approach. I think we've got enough code present that not only is it enough to build an interesting DSL, but also a third Javascript-related system (say, net.liftweb.javascript vs net.liftweb.http.js and net.liftweb.json) would be too much. Likewise, I don't think we can or should just throw out net.liftweb.http.js: it's been around, it works, and much of both Lift's internal code and other people's code reply upon it.


Agreed
 
So, should we extend existing objects or provide complementary ones? In the two versions I've mocked up I've taking the second approach, as I didn't want to touch Lift. However, it leads to a lot of implicit conversions and I think most, if not all, of the existing things can be safely extended, especially since it would just consist of some methods and alternate apply's. Plus, it wouldn't take too long to develop.

So, that's my preferred approach. Thoughts?

Sounds about right to me. The important part is to get the ball rolling (faster, thanks for making it roll in the first place !)

So perhaps fork lift, try to implement the ideas and see how the resulting client code is going to look?

/Jeppe

Peter Robinett

unread,
Jun 24, 2011, 9:55:09 AM6/24/11
to lif...@googlegroups.com
Sorry, Naftoli, I could have sworn I saw you mention lift-json once. That's why I said you had proposed two approaches. Oops!

On goal one, I think the gain is you don't have big blocks of JsRaw("..."). Likewise, you can reuse variable components and know you're referring to the same thing, like in my for loop example.

On goal two, I think the question is whether we want to match or improve on Javascript. ;-) Javascript does all sort of type casting and type conversions behind the scenes: do we want to know about that? Even model it? I think at least initially that's a bridge too far. Maybe I've missed what you've already done (it looks very nice!) and have already addressed some of those issues.

As for whether taking our time to get it right or just going with what's quick, I'm inclined right now to take the quick route. This is something we've been talking about for a long time and it hasn't happened yet. I think the best test of things will be to get it into Lift and then see how (or even if) people use it.

Like you say, the existing JsExp is not typed, so I think adding types would require changing or dropping the existing Javascript objects. I'm not wild about that, though eventually I think type safety would be awesome.

Maybe we could have two stages: first a simple wrapper on top of the existing code, second a new, type-safe DSL? The idea would use the same basic syntax ("Var" for variables, ":=" for assignment, "->" for property access, etc?), so switching later would easier for people that have made an investment into the DSL.

On late-binding, as I understand it , and perhaps David can correct me if I'm wrong, it'd be something like (this is all pseudo code):

val f = Function ('f) ('a, 'b', 'c) {
  'console -> 'log ('a, 'b, 'c, LateBind)
}

val f1 = f with ('d)
/*
function f(a, b, c) {
  console.log(a, b, c, d);
}
*/
val f2 = f with ('e)
/*
function f(a, b, c) {
  console.log(a, b, c, e);
}
*/

Or perhaps, ignoring closures and variable names, just for values:
val js = {
  Var ('name) := LateBind &
  'console -> 'log ('name)
}
js with "Peter"
/*
  var name = "Peter";
  console.log(name);
*/
js with "Naftoli"
/*
  var name = "Naftoli";
  console.log(name);
*/

Cheers,
Peter

Peter Robinett

unread,
Jun 24, 2011, 10:59:02 AM6/24/11
to lif...@googlegroups.com
Cool, Jeppe, glad you like it. I'll try to get some time this weekend to make a fork with my ideas.

Peter

TylerWeir

unread,
Jun 24, 2011, 11:22:55 AM6/24/11
to lif...@googlegroups.com
Dpp said:
- Concise JavaScript coding
- Type safe JavaScript coding
- Late binding of Scala binding seamlessly into JavaScript (e.g., a String variable that gets inserted into the JavaScript on the server before the JavaScript get served)
- Good integration with jQuery


Peter said:
1. Concise Javascript coding in a fashion that looks, in Scala, as much like real Javascript as possible/reasonable
2. Type safe Javascript coding

I like the idea of concision in the case of JS, but what's the reason to have the DSL look like JS?
I'd prefer to have it look as much like Scala as possible, but I'd like to hear arguments.

I like the choice of JQuery, I think if we try to be friendly with all the libs, we won't get anything done, so pick a horse and go with it.

Type safety would be lovely, as would late binding, since I hack around that now.

I'll look at all the places where I use JsRaw and try and provide some use cases.

GO GO GO

Daniel Spiewak

unread,
Jun 24, 2011, 2:50:55 PM6/24/11
to lif...@googlegroups.com
Sorry it took me so long to respond on this one.  Been a bit busy…

I'm certainly willing to share my thoughts, but I don't think you're going to like it.  :-)  I'm opposed to any JavaScript internal DSL in Lift.  In fact, I'm pretty strongly opposed to the JsCmd / JsExp madness that we have right now.  In general, my opinion can be summarized as follows: if you want to write JavaScript, we already have a really good external DSL you can use (it's called "JavaScript").  Providing a framework-level DSL for generating JavaScript breaks my primary guideline for web framework design: don't try to insulate developers from JavaScript.

The technique we've used on Vibe is one which is (I believe) much more maintainable in the long-run and also much higher performance (will explain this point shortly): the only JavaScript we generate server-side are Call and primitive values like Str, JsTrue, JsFalse and such.  We have a special framework on top of ajaxCall which lets us generate AJAX lambdas, backed by server-side code, but I don't consider this generating JavaScript per se.  I'm assuming this is the framework David was talking about when he referenced the things we're doing with Vibe.

To be clear, Vibe has a huge amount of JavaScript and spends a lot of time in very dynamic and stateful interactions between the browser and the server.  I am quite confident in saying that we could not have possibly done what we are doing if we embedded our JavaScript (via a DSL) in our server-side code.  Doing this is directly analogous to embedding HTML in Scala/Java, or conversely embedding Scala in HTML.  It's no better than JSPs, really.  Actually, it's a lot worse, since the results are very difficult for browsers to optimize.

Modern browsers (Chrome, Safari, Firefox) perform JIT on script units and cache the results.  This is particularly important for pre-compiling JavaScript engines (V8, Nitro, JaegerMonkey) which perform a baseline compilation of script units down to ASM.  This works really well for external JS, since it's pretty easy to build up nice cache semantics around the HTTP resource itself.  Embedded JS (<script>) tags are harder, but still doable with a little bit of clever DOM diffing.  Unfortunately, evals are nearly impossible to optimize in this way, and that's exactly what you're doing when you return JavaScript fragments in AJAX or Comet responses.  As a result, any bit of JavaScript you generate server-side in this way is going to end up being very slow.  This is alright if all your JavaScript is doing is Call'ing into a library function in some external JS (which has already been compiled and is very JIT-able).  However, if you do anything non-trivial, you're going to be able to see the performance hit at runtime.

As such, the only place where one can generate JavaScript server-side and not take a massive performance hit is in Script(…) binds.  Unfortunately, this is still a bad thing.  From a performance standpoint, it's very bad to let <script> tags liter all over your page.  It breaks up the rendering process, causes reflow and prevents the browser from synthesizing the DOM in a concurrent manner.  Unfortunately, this is something Lift does all over the place even without Script(…) binds, so if you're serious about performance you will need to find a more general solution (we use a post-render response filter in LiftRules). 

The more pragmatic issue with Script(…) binds comes from the aforementioned point that it is literally analogous to embedding Scala in HTML (or vice versa).  It breaks separation of concerns and makes it much more difficult to maintain your client-side logic.  It's also very clumsy to write JavaScript in this way.  No matter how nice your DSL, it's not going to be as clean as JavaScript itself.

In summary, there are two problems with server-side JavaScript generated via an internal DSL: separation of concerns and performance.  The latter problem is a serious one, but one which can be overcome through special tricks and hooks into Lift's response mechanism.  The former problem is one which is fundamental to the very notion of server-side embedded JavaScript, and thus cannot be resolved at all.

My opinion in general is that these sorts of DSLs are very bad.  They encourage bad coding practices and can have surprising consequences in other areas of the framework (e.g. ajaxCall).  Don't get me wrong, I think we all appreciate the work and the effort which has been going on in this area, I am just very opposed to Lift encouraging the practice of embedded JavaScript any more than it already does.

Daniel

On Thu, Jun 16, 2011 at 12:29 PM, David Pollak <feeder.of...@gmail.com> wrote:


On Wed, Jun 15, 2011 at 9:25 PM, Naftoli Gugenheim <nafto...@gmail.com> wrote:
There was a discussion about a javascript dsl a long time ago on the list. Marius had one version, and I posted a different DSL. Don't have a link, you can search for it.
I would be very interested in a proper discussion of pros and cons of various approaches.

I really like the stuff Peter put together in this thread, but I agree that a full discussion should be had about an improved DSL... and perhaps Daniel Spiewak can join the discussion given the about of JavaScript he's done for Vibe.

But most importantly, I'd like to discussion to reach a conclusion and an implementation to roll into Lift.  So, Peter, because you started this discussion, can you lead the discussion and take responsibility for getting an implementation into Lift?

Thanks,

a very excited David
 


On Wed, Jun 15, 2011 at 8:40 PM, TylerWeir <tyler...@gmail.com> wrote:
Very cool Pete.

I was just looking at phantomjs today as well.

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/Ax1r-rdEML4J.

To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Naftoli Gugenheim

unread,
Jun 24, 2011, 2:55:55 PM6/24/11
to lif...@googlegroups.com


On Fri, Jun 24, 2011 at 11:22 AM, TylerWeir <tyler...@gmail.com> wrote:
I like the idea of concision in the case of JS, but what's the reason to have the DSL look like JS?
I'd prefer to have it look as much like Scala as possible, but I'd like to hear arguments.

I think I agree with this. Also, I don't know that others disagree --- when Peter said it should look like real javascript as much as possible, I think he meant as opposed to, for example, the current javascript DSL --- it doesn't look like any language, more like a manually constructed AST; or as opposed to some aspects of some DSL suggestions, which again look like neither.

Naftoli Gugenheim

unread,
Jun 24, 2011, 3:14:36 PM6/24/11
to lif...@googlegroups.com
On Fri, Jun 24, 2011 at 9:55 AM, Peter Robinett <pe...@bubblefoundry.com> wrote:
On goal one, I think the gain is you don't have big blocks of JsRaw("..."). Likewise, you can reuse variable components and know you're referring to the same thing, like in my for loop example.

What advantage does this provide over keeping javascript in .js files?
 

On goal two, I think the question is whether we want to match or improve on Javascript. ;-) Javascript does all sort of type casting and type conversions behind the scenes: do we want to know about that? Even model it? I think at least initially that's a bridge too far. Maybe I've missed what you've already done (it looks very nice!) and have already addressed some of those issues.

If we're not "improving" on javascript, then why use a DSL in the first place?
 

As for whether taking our time to get it right or just going with what's quick, I'm inclined right now to take the quick route. This is something we've been talking about for a long time and it hasn't happened yet. I think the best test of things will be to get it into Lift and then see how (or even if) people use it.

And then forever regret that it was done too hastily?
The fact that it hasn't happened yet is an argument to the contrary: Clearly we're not solving a "problem" but adding a "feature"; after all, everyone survived until now. If the lack of a DSL was a serious problem, then I'd agree: let's fix the problem already! But if we're adding a new "benefit" then let's do it right, and do it only once.
 

Like you say, the existing JsExp is not typed, so I think adding types would require changing or dropping the existing Javascript objects. I'm not wild about that, though eventually I think type safety would be awesome.

Maybe we could have two stages: first a simple wrapper on top of the existing code, second a new, type-safe DSL? The idea would use the same basic syntax ("Var" for variables, ":=" for assignment, "->" for property access, etc?), so switching later would easier for people that have made an investment into the DSL.

I think that if you don't know what type safety will look like then you don't know that your API won't have to change.
It seems to me that there are really two or three separate but integrated components that are involved: (1) A type safe model of javascript types and expressions (I've already written a decent amount of this); (2) a natural DSL that is easy to use but preserves the type safety of #1; and (3) maybe pre-packaged common operations, along the lines of FocusOnLoad, that build on (2).

Naftoli Gugenheim

unread,
Jun 24, 2011, 4:02:22 PM6/24/11
to lif...@googlegroups.com

On Fri, Jun 24, 2011 at 1:19 AM, Naftoli Gugenheim <nafto...@gmail.com> wrote:
val alert = JsIdent[JsString |=> JsVoid]('alert)
  
  def render = {
    Reactions.inServerScope(Page.currentPage) {
      jses.foreach { v: JsExp[JsString] =>
        alert.apply("Fired: ".$ + v)
      }
    }

Btw that 'apply' was unnecessary (it got there while fixing something). What do you think of this version:

val alert = $[JsString |=> JsVoid]('alert)
  
  def render = {
    Reactions.inServerScope(Page.currentPage) {
      jses.foreach { v: $[JsString] =>
        alert("Fired: ".$ + v)
      }
    }

Note how JsIdent (the factory) and JsExp (the type) are aliased as $.
Also you can write val myVar = $[JsInt] --- without a name --- to get a JsIdent with a generated fresh name.
Also, I think you should not have to write Var statements --- the DSL should generate them automatically.

Naftoli Gugenheim

unread,
Jun 24, 2011, 5:36:09 PM6/24/11
to lif...@googlegroups.com
Rudimentary member selection works now:
  val window = $[JsObj]('window)
  val alert = $[JsString =|> JsVoid]('alert)
  
  def render = {
    Reactions.inServerScope(Page.currentPage) {
      jses.foreach { v: $[JsString] =>
        window->alert("Fired: ".$ + v)

Peter Robinett

unread,
Jun 27, 2011, 11:57:49 AM6/27/11
to lif...@googlegroups.com

On Friday, June 24, 2011 5:22:55 PM UTC+2, TylerWeir wrote:
I like the idea of concision in the case of JS, but what's the reason to have the DSL look like JS?
I'd prefer to have it look as much like Scala as possible, but I'd like to hear arguments.

In my opinion the DSL should look like Javascript because I don't think we should be abstracting too much away from Javascript. This is different from something like CoffeScript or GWT where you're just working in another language and compiling down to Javascript.
 
I like the choice of JQuery, I think if we try to be friendly with all the libs, we won't get anything done, so pick a horse and go with it.
 
I'd like to focus on the general language. Then we can add additional bits, like is already done, for various Javascript libraries.

Type safety would be lovely, as would late binding, since I hack around that now.
 
Do you have some examples of what you're doing with late binding? Since I haven't done anything with it myself, I'd love to see your examples.

I'll look at all the places where I use JsRaw and try and provide some use cases.

Yes, that'd be great. That's exactly my goal, basically, to go from things like this:

JsRaw("""
var a = function(i) {
  console.log(i);
}
theArray.forEach(a);
""")

To this:

Var ('a) := Function('i) {
  'console->'log ('i)
}
'theArray->'forEach ('a)

Yes, it's obviously not Javascript but neither is it far removed from it. You can easily anticipate what the actual Javascript will be.

Peter

Peter Robinett

unread,
Jun 27, 2011, 11:58:37 AM6/27/11
to lif...@googlegroups.com
On Friday, June 24, 2011 8:55:55 PM UTC+2, nafg wrote:
I think I agree with this. Also, I don't know that others disagree --- when Peter said it should look like real javascript as much as possible, I think he meant as opposed to, for example, the current javascript DSL --- it doesn't look like any language, more like a manually constructed AST; or as opposed to some aspects of some DSL suggestions, which again look like neither.

Yes, exactly. Right now it just looks like a collection of case classes, which it is. ;-)

Peter 

Peter Robinett

unread,
Jun 27, 2011, 12:27:01 PM6/27/11
to lif...@googlegroups.com
Thanks, Daniel. Your points are all very valid and I appreciate your vast amount of experience and struggle with Lift's Javascript!

However, I think your needs might be a bit beyond the average Lift developer's and I wonder whether this question of Javascript might be an acceptable place for the defaults to prioritize convenience over performance.

Specifically I'm thinking of all the various SHtml helpers. I think they're very cool and helpful. I'd imagine that if one were to use your approach it would require lots of Calls and then lots of little callback functions in a (naturally separate) Javascript file. I think for many developers, such as myself, these sorts of simple interactions are really nice how Lift currently does them and the alternative would require a small but not inconsequential extra amount of time.

Maybe there be some other things that we might do to improve the default Javascript performance of Lift app without tossing or telling people to avoid the current approach. You mention a response filter, which I assume you use to merge script blocks. Might that be a sensible default for Lift?

Thanks,
Peter

David Pollak

unread,
Jun 27, 2011, 1:51:12 PM6/27/11
to lif...@googlegroups.com
On Fri, Jun 24, 2011 at 6:55 AM, Peter Robinett <pe...@bubblefoundry.com> wrote:
Sorry, Naftoli, I could have sworn I saw you mention lift-json once. That's why I said you had proposed two approaches. Oops!

On goal one, I think the gain is you don't have big blocks of JsRaw("..."). Likewise, you can reuse variable components and know you're referring to the same thing, like in my for loop example.

On goal two, I think the question is whether we want to match or improve on Javascript. ;-) Javascript does all sort of type casting and type conversions behind the scenes: do we want to know about that? Even model it? I think at least initially that's a bridge too far. Maybe I've missed what you've already done (it looks very nice!) and have already addressed some of those issues.

As for whether taking our time to get it right or just going with what's quick, I'm inclined right now to take the quick route. This is something we've been talking about for a long time and it hasn't happened yet. I think the best test of things will be to get it into Lift and then see how (or even if) people use it.

Like you say, the existing JsExp is not typed, so I think adding types would require changing or dropping the existing Javascript objects. I'm not wild about that, though eventually I think type safety would be awesome.

Maybe we could have two stages: first a simple wrapper on top of the existing code, second a new, type-safe DSL? The idea would use the same basic syntax ("Var" for variables, ":=" for assignment, "->" for property access, etc?), so switching later would easier for people that have made an investment into the DSL.

I really love the way this thread is evolving.  Peter and Naftoli... you guys are rockin' my world with this discussion.  I also really appreciate Daniel's comments (and Peter's responses.)

With that being said, the only thing I am strongly opposed to is overloading ->.  We did it for bind() and it's been a constant problem.  So, please choose another symbol for property/method access, but otherwise... just keep truckin'
 
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/0Kbwj66jD20J.

To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Peter Robinett

unread,
Jun 29, 2011, 12:36:44 PM6/29/11
to lif...@googlegroups.com
Thanks, David. What do you propose instead of ->?

Just to brainstorm, this is the order of operator precedence:

(all letters)
|
^
&
< >
= !
:
+ -
* / %
(all other special characters)
What do people think about the following syntax: 'console dot 'log ("test")? It's not as good as an actual dot but close. ;-)

Also, Naftoli has proposed to me that he just put his DSL into reactive-web instead of Lift. I think that's a reasonable to me but I guess I'm biased. ;-)

Peter

David Pollak

unread,
Jun 29, 2011, 5:40:08 PM6/29/11
to lif...@googlegroups.com
On Wed, Jun 29, 2011 at 9:36 AM, Peter Robinett <pe...@bubblefoundry.com> wrote:
Thanks, David. What do you propose instead of ->?

-*

-&

**
 

Just to brainstorm, this is the order of operator precedence:

(all letters)
|
^
&
< >
= !
:
+ -
* / %
(all other special characters)
What do people think about the following syntax: 'console dot 'log ("test")? It's not as good as an actual dot but close. ;-)

Also, Naftoli has proposed to me that he just put his DSL into reactive-web instead of Lift. I think that's a reasonable to me but I guess I'm biased. ;-)

Peter

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/J-H0PsADTI8J.

To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Peter Robinett

unread,
Jun 29, 2011, 6:04:11 PM6/29/11
to lif...@googlegroups.com
Hi David,

So you don't like 'dot'? ;-) Of the three you propose I like '-&' the best, since it suggests a sort of combination: 'console -& 'log ("test")

Peter

David Pollak

unread,
Jun 29, 2011, 6:19:02 PM6/29/11
to lif...@googlegroups.com
On Wed, Jun 29, 2011 at 3:04 PM, Peter Robinett <pe...@bubblefoundry.com> wrote:
Hi David,

So you don't like 'dot'? ;-)

No... sorry. :-(
 
Of the three you propose I like '-&' the best, since it suggests a sort of combination: 'console -& 'log ("test")

Peter

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/0H4YCMYuy1YJ.

To post to this group, send email to lif...@googlegroups.com.
To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

Peter Robinett

unread,
Jul 9, 2011, 2:37:43 PM7/9/11
to lif...@googlegroups.com
Hi all,

I have pushed an initial version of the changes we have been discussing here to the pr1001_js_dsl branch: https://github.com/lift/framework/tree/pr1001_js_dsl

It all compiles and all tests on framework pass (haven't done a superbuild yet). That makes sense, as the only changes I made to existing code were corrections to Scaladoc annotations that I figured I'd do because I was looking at the same time (and the warnings annoyed me!): https://github.com/lift/framework/commit/1b5c44768518219a3123abc4cbf2ec77fe5d2ef7

My various additions enable syntax such as this:

val console_log = Var("console") -& "log"
val k = Var("k")
For(newVar("k") === 0, k < Var("someArray") -& "length", k++) {
  console_log(k)
}

It's obviously not Javascript, but I think you'll agree that the meaning is easier to understand at a glance than:

JsFor(JsRaw("var k = 0"), JsLt(JsVar("k"), JsVar("someArray", "length")), JsVar("k") === (JsVar("k") + 1), Call("console.log", JsVar("k")))

This is honestly the best I could come up with without my additions!

All in all I'm pretty happy with things, though I had to call my new if-else construction jIf because it conflicted with the If LocParam and I suspect there are lots of people doing wildcard imports that would pull in the two. Likewise there's no Null (JsNull is still there, of course) because it'd conflict with scala.xml.Null, but I think that's less of a loss.

I did not end up adding the := Javascript assignment method that has been discussed in this thread because I noticed that === is already a method on JsExp. It'd only be a 1 character gain. That because said, I did make aliases for many of the Js* objects (JsTrue:True, JsFalse:False, etc).

Finally, I tried to had Scaladocs for every addition I made, so hopefully it's pretty easy to follow what I did.

Please tell me what you think!

Peter

Diego Medina

unread,
Jul 9, 2011, 3:08:55 PM7/9/11
to lif...@googlegroups.com
> For(newVar("k") === 0, k < Var("someArray") -& "length", k++) {

Thanks for taking this on, just one comment from a user more than a
DSL writer, the === looks more like a compare than an assignment,
while the := is just one character shorter, I have seen it used on sbt
0.10, and I think on other languages, so at first glance my be a
better choice.

The rest looked pretty straight forward, to me at least.

Thanks

Diego

>   console_log(k)
> }
> It's obviously not Javascript, but I think you'll agree that the meaning is
> easier to understand at a glance than:
> JsFor(JsRaw("var k = 0"), JsLt(JsVar("k"), JsVar("someArray", "length")),
> JsVar("k") === (JsVar("k") + 1), Call("console.log", JsVar("k")))
>
> This is honestly the best I could come up with without my additions!
> All in all I'm pretty happy with things, though I had to call my new if-else
> construction jIf because it conflicted with the If LocParam and I suspect
> there are lots of people doing wildcard imports that would pull in the two.
> Likewise there's no Null (JsNull is still there, of course) because it'd
> conflict with scala.xml.Null, but I think that's less of a loss.
> I did not end up adding the := Javascript assignment method that has been
> discussed in this thread because I noticed that === is already a method on
> JsExp. It'd only be a 1 character gain. That because said, I did make
> aliases for many of the Js* objects (JsTrue:True, JsFalse:False, etc).
> Finally, I tried to had Scaladocs for every addition I made, so hopefully
> it's pretty easy to follow what I did.
> Please tell me what you think!
> Peter
>

> --
> You received this message because you are subscribed to the Google Groups
> "Lift" group.
> To view this discussion on the web visit

> https://groups.google.com/d/msg/liftweb/-/2d7Aj52976AJ.


> To post to this group, send email to lif...@googlegroups.com.
> To unsubscribe from this group, send email to
> liftweb+u...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/liftweb?hl=en.
>

--
Diego Medina
Web Developer
(305) 788-4954
di...@fmpwizard.com
http://www.fmpwizard.com

Antonio Salazar Cardozo

unread,
Jul 9, 2011, 5:18:35 PM7/9/11
to lif...@googlegroups.com
Most critically, in JS, === *is* a comparison (non-coercing equality), so that could lead to serious confusion.
Thanks,
Antonio

Peter Robinett

unread,
Jul 17, 2011, 8:12:45 PM7/17/11
to lif...@googlegroups.com
Hi guys, your points are taken. I'm not about to remove === but adding := as an alias for === seems harmless to me. I just pushed my addition to my branch on GitHub: https://github.com/lift/framework/commit/3a093832b2b98aa3b7d539b26e18a95ee94a43f8

You'll notice I put := on JsVar, not JsExp, as I think that in 99% (100%?) of the time any assignment will be (can only be?) to Javascript variables. If there's some sort of need for assignment to something that is not a JsVar then you still can drop down to === on any JsExp.

Cheers,
Peter

Byron Weber Becker

unread,
Jul 18, 2011, 7:21:42 AM7/18/11
to lif...@googlegroups.com
I disagree that it's harmless. Multiple ways of doing things means
an increased cognitive load on the people reading the code. They have
to remember that extra bit of information that these two things do
the same thing. If they've forgotten, they need to puzzle about it
or go look it up.

In other words, this adds one more straw to the camel's back that
we call Lift's learning curve. That camel already has a LOT of straw
on it.

I've not been following this DSL discussion in any depth, but I'd
urge you to take the alias back out again.

Byron

> --
> You received this message because you are subscribed to the Google Groups "Lift" group.

> To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/wcfGh-obpkYJ.


> To post to this group, send email to lif...@googlegroups.com.
> To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.

---------------------------------------------------------
Byron Weber Becker Voice: 519-888-4567 x34661
School of Computer Science Fax: 519-885-1208
University of Waterloo Office: DC3105
Waterloo, ON N2L 3G1

Advising FAQ: http://www.cs.uwaterloo.ca/current/faq/index.shtml

Peter Robinett

unread,
Jul 18, 2011, 2:43:28 PM7/18/11
to lif...@googlegroups.com
Thanks for your input, Byron, but not surprisingly I disagree. The whole Javascript DSL I've been putting forward consists of aliases: I haven't removed a single line of code! I have been very careful to put in what I hope are reasonably descriptive Scaladocs for every method and class I've added and have only made additions to one file. So, no, I don't think it's too unreasonable to think that most should be able to figure out my additions, especially when they more closely match the Javascript constructions they represent than those that they alias.

Now, there are some that will argue (and not unreasonably, I'll add) that the whole DSL idea is a bad one. While I'm sympathetic to the arguments, I think the majority on the list, or at least in this discussion, share my opinion that the good outweighs the bad. Perhaps at some point usage will shift so far to the new constructs that we can safely deprecate and then remove the original forms, but I don't think that will happen for some time. Nor would I be upset if it never did.

Peter

Diego Medina

unread,
Jul 18, 2011, 2:54:05 PM7/18/11
to lif...@googlegroups.com
I actually like the idea of a JavaScript DSL, and I know that like any
other DSL, I would have to do many searches online to figure out how
to do things and maybe we are just nick picking on this one method out
of a lot of methods that are just plain easy to guess their meaning.

That being said, try googling for

javascript ===

at least here google seems to just ignores the ===. This will make it
harder to find out what === means. But as you said, it will all come
down to what you guys decide.

Now, thanks for adding the := and hopefully people who write
examples/blogs/anything public will just use :=

my 2 cents

Diego

> --
> You received this message because you are subscribed to the Google Groups
> "Lift" group.
> To view this discussion on the web visit

> https://groups.google.com/d/msg/liftweb/-/qukvv0uuJDUJ.


> To post to this group, send email to lif...@googlegroups.com.
> To unsubscribe from this group, send email to
> liftweb+u...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/liftweb?hl=en.
>

--

Richard Dallaway

unread,
Jul 18, 2011, 6:32:45 PM7/18/11
to lif...@googlegroups.com
Late to chip in, but my perspective is that for the little Javascript I need to produce from Lift, I'd prefer to write Scala that looks like Scala, using an API that happens to emit Javascript, rather than write in a DSL that looks somewhat like Javascript.  For larger bodies of Javascript, I'd prefer to use external Javascript files.   Essentially, I want to get to a position of sending the browser a single Javascript function to call whenever I can.  My use case is maybe different from yours.

Totally impressed with what you've built, but to me it feels like I'd be learning a third language to mediate between Scala and Javascript.

Richard

--
You received this message because you are subscribed to the Google Groups "Lift" group.
To view this discussion on the web visit https://groups.google.com/d/msg/liftweb/-/qukvv0uuJDUJ.

Andreas Joseph Krogh

unread,
Jul 18, 2011, 6:48:00 PM7/18/11
to lif...@googlegroups.com
On 07/19/2011 12:32 AM, Richard Dallaway wrote:
> Late to chip in, but my perspective is that for the little Javascript
> I need to produce from Lift, I'd prefer to write Scala that looks like
> Scala, using an API that happens to emit Javascript, rather than write
> in a DSL that looks somewhat like Javascript. For larger bodies of
> Javascript, I'd prefer to use external Javascript files.
> Essentially, I want to get to a position of sending the browser a
> single Javascript function to call whenever I can. My use case is
> maybe different from yours.
>
> Totally impressed with what you've built, but to me it feels like I'd
> be learning a third language to mediate between Scala and Javascript.
>

I think the point here is that they're trying to make that function
you're trying to call type-safe, so that when your model changes, the
function benefits from the compiler too. I certainly would like to have
the compiler help me generate JS-functions which made sense according to
my domain-model.

--
Andreas Joseph Krogh <and...@officenet.no>
Senior Software Developer / CTO
Public key: http://home.officenet.no/~andreak/public_key.asc
------------------------+---------------------------------------------+
OfficeNet AS | The most difficult thing in the world is to |
Rosenholmveien 25 | know how to do a thing and to watch |
1414 Troll�sen | somebody else doing it wrong, without |
NORWAY | comment. |
Org.nr: NO 981 479 076 | |
| |
Tlf: +47 24 15 38 90 | |
Fax: +47 24 15 38 91 | |
Mobile: +47 909 56 963 | |
------------------------+---------------------------------------------+

Reply all
Reply to author
Forward
0 new messages