David and Daniel discuss the Comet and Ajax implementation in Lift.

301 views
Skip to first unread message

TylerWeir

unread,
Jul 7, 2011, 2:12:32 PM7/7/11
to lif...@googlegroups.com
Dpp started with:
dpp
Replished: Web Framework Manifesto... http://bit.ly/qMDl3M Lift holds up pretty well to the original criteria
11-07-06 3:04 PM


djspiewak
@dpp I had never read that before. Very enlightening! It explains why Lift's Comet support is tilted the way it is (toward full repaint).
11-07-06 3:50 PM
djspiewak
@dpp I still think that style of Comet is a very bad one, but at least now I know you didn't end up there by accident. :-)
11-07-06 3:50 PM
djspiewak
@dpp It's interesting also that the manifest lists a few points that haven't been hit in Lift (e.g. session tied to window). #stillokbyme
11-07-06 3:51 PM
dpp
@djspiewak I'm all ears for a better model of comet. Also, please see http://simply.liftweb.net/index-6.3.html#toc-Section-6.3
11-07-06 7:05 PM
tylerweir
@dpp @djspiewak Yes, I'm interested in any improvements in design or implementation.
11-07-06 7:25 PM
djspiewak
@dpp I've had this discussion with you before. You disagreed last time. :-)
11-07-07 1:39 PM
djspiewak
@dpp Basically, viewing AJAX and Comet as a low-level transport for synchronizing *server* controlled state is a false metaphor.
11-07-07 1:40 PM
djspiewak
@dpp Lift's whole philosophy of Comet, and to a lesser extent AJAX, is based around the notion that all state is server-controlled.
11-07-07 1:40 PM
djspiewak
@dpp This may work for simple apps that you want to jazz up with a little refresh-free interaction, but it falls over with non-trivial stuff
11-07-07 1:41 PM
djspiewak
@dpp Web apps are going further and further in the direction of non-trivial, client-managed state (e.g. OT is impossible without it).
11-07-07 1:42 PM
djspiewak
@dpp My suggestion: ditch reRender, SetHtml, and any mechanism which encourages server-driven state.
11-07-07 1:43 PM
djspiewak
@dpp Those mechanisms may be useful, but they are also *very* dangerous in the hands of those who don't know better.
11-07-07 1:43 PM
djspiewak
@dpp Those mechanisms may be useful, but they are also *very* dangerous in the hands of those who don't know better.
11-07-07 1:43 PM
djspiewak
@dpp In their place, you should have first-class support for calling into and from JavaScript, giving developers full control.
11-07-07 1:44 PM
djspiewak
@dpp In other words: partialUpdate $ Call and ajaxInvoke would be the main client-server interaction model.
11-07-07 1:45 PM
djspiewak
@dpp Now, I don't really know that it would be good for Lift to do such a complete about-face at this point, but you did ask. :-)
11-07-07 1:46 PM
tylerweir
@djspiewak @dpp So would you see Lift offering a client-side lib or something to facilitate communication? Maybe involve websockets?
11-07-07 1:47 PM
tylerweir
@djspiewak @dpp If there was a well-known method to xferring state b/n client and server, would that help?
11-07-07 1:48 PM
djspiewak
@tylerweir @dpp No need for a client-side lib. Invoke fictions via Comet, and render JavaScript functions which perform AJAX callbacks.
11-07-07 1:49 PM
djspiewak
@tylerweir @dpp Part of the beauty of this model is it can be swapped seamlessly XHR/WebSockets.
11-07-07 1:50 PM
tylerweir
@djspiewak @dpp This is similar to your response to the JS DSL, is that fair?
11-07-07 1:50 PM
djspiewak
@tylerweir @dpp Too high-level. I think clients should have full control of their state.
11-07-07 1:50 PM
djspiewak
@tylerweir @dpp Too high-level. I think clients should have full control of their state.
11-07-07 1:50 PM
djspiewak
@tylerweir @dpp In a similar vein. :-) The JS DSL discussion only came up because Lift encourages that style of programming.
11-07-07 1:51 PM
djspiewak
@tylerweir @dpp Imagine writing a chat client outside a browser. Would you have the chat server manage every bit of state for the client?
11-07-07 1:52 PM
djspiewak
@tylerweir @dpp Or would you allow the client to control its own destiny and communicate with the server as it makes sense?
11-07-07 1:53 PM
tylerweir
@djspiewak @dpp Right. I see your point and appreciate where you're coming from due to Vibe. I'm going to mock up a chat app in this style
11-07-07 1:54 PM
dpp
@djspiewak @tylerweir 1-The #Lift mailing list is the place for this discussion & 2-nothing you're asking for isn't already part of Comet
11-07-07 2:01 PM
djspiewak
@dpp @tylerweir I'm asking for *less*, not more. I know Lift can do what I want, we use it that way with Vibe, but it's like pulling teeth.
11-07-07 2:03 PM

I've included my tweets for continuity, not because I add anything :)

Would anyone else like to comment?















Antonio Salazar Cardozo

unread,
Jul 7, 2011, 2:23:10 PM7/7/11
to lif...@googlegroups.com
I don't have any deep thoughts to add, but I must say I don't see how using Lift this way is like pulling teeth. We've used Lift comet with a high degree of client-managed state since the beginning for OpenStudy, and I'm just in the endgame of transitioning us to an even more client-centric model using Knockout JS. These transitions have required far far more change on the client than on the server (indeed, in this case the comet just had to be changed from sending down XML to JSON, which was a matter of adding some lift-json rendering of the relevant entities). But I guess it depends on use case -- for myself, I've never even considered using the `default' (if it can be called that) re-rendering approach, and I've never felt disadvantaged for it.

The only part of this that has been a little more annoying has been dealing with dropping functions onto the client from a comet when I'm also dropping XML in and chaining functions in from the comet's superclass (which provides additional functionality). But that's extremely isolated nastiness that happens once or twice and can be abstracted out easily.

I'd be glad to put up more details in a blog post if anyone is interested.
Thanks,
Antonio

Diego Medina

unread,
Jul 7, 2011, 3:32:54 PM7/7/11
to lif...@googlegroups.com
On Thu, Jul 7, 2011 at 2:23 PM, Antonio Salazar Cardozo
<savedf...@gmail.com> wrote:
[cut]

> happens once or twice and can be abstracted out easily.
> I'd be glad to put up more details in a blog post if anyone is interested.

It would be great to see more blogs about Lift and specially what you
are describing.

Regards,

Diego

> Thanks,
> Antonio
>
> --
> 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/-/G_fmIZ0YPPIJ.
> 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

Daniel Spiewak

unread,
Jul 7, 2011, 5:03:25 PM7/7/11
to Lift
Let's see if I can re-summarize my opinion here, since Twitter is not
the best medium for deep discourse (it's just the easiest to use while
breezing through the Alps on a high-speed train).

First of all, I'm going to assume that we're all down with the notion
of client-managed state. If you need me to motivate that for you,
just say the word and I'll nail my theses to your door post. Suffice
it to say that many features that users are starting to expect in
modern web apps require the browser to manage a great deal of state
that *cannot* be managed by the server, simply due to concurrency and
latency issues. OT is an extreme (but fitting) example of this.

Lift's entire stack is *deeply* biased toward the server-managed state
idea. I'm actually astonished that you haven't run into the issues
this causes. The best example, one which caused us a huge amount of
grief on Vibe, is the render/fixedRender issue. If you're doing
*anything* with client-side state, you need to be using fixedRender,
otherwise the server will wack your CometActor seemingly on a whim.
Technically, this only happens if another tab has opened in that same
session showing that same CometActor *after* some partialUpdate(s)
have taken place, but that's not quite as unusual of a case as it
sounds.

Oh, and when I say "biased", I do mean "biased" and not just
"defaulted". Using fixedRender does solve the aforementioned problem,
but a) you need to know that it's there, and even David Pollak had
forgotten about it, and b) you can't *just* do a fixedRender, you also
have to def render = NodeSeq.Empty. So, render with its associated
client-state-wacking behavior is very definitely the standard mode of
operation for the framework.

Let's look at another example more obvious in the API: SHtml and all
of its AJAX goodies. How many functions does this object have now?
I've lost count, and so many of them are overloaded that a simple
search doesn't suffice. In any case, it's a lot. We have things like
ajaxText, ajaxButton, etc. All of these are components that allow you
to bind form (and sometimes non-form) elements to AJAX events,
allowing the server to fully manage the state of those components.
However, this is exactly *not* what you want when you're doing an app
which has any sort of client-side state. Instead, you want to control
the form elements yourself, dealing with them from within JavaScript
and sending the values to the server under your own control.

In order to do this, you need Lift to create a function mapping in the
session which accepts an XHR request on a particular
(cryptographically-identified) URL and routes the data to a
corresponding Scala function, which you have presumably defined to
handle the data input. In the entirety of SHtml, in all of its vast
sprawling network of components, AJAX utilities and functions, how
many API elements do you suppose are devoted to this all-important
task?

Two. (ajaxCall and ajaxInvoke; good luck figuring out the difference
between the two, and I assure you that you will need to use both)

What's worse is these functions are actually quite primitive and seem
to be intended more as low-level constructs used by higher-level,
officially blessed SHtml functions like ajaxText and ajaxButton. One
of the things we did on the Vibe project was wrap ajaxCall and
ajaxInvoke in a DSL which allows us to trivially render a Scala
function value as a JsExp (specifically, a JavaScript lambda). I
promise, we're still trying to open-source this…

Lift has a large library of combinators and tools designed
specifically to allow the server to do all of the DOM manipulation and
state management. Specifically, I'm thinking of the JsCmd/JsExp
stuff. Out of all these combinators, we should really only have one
(Call) and the value primitives (Str, JsTrue, JsFalse, etc). All that
is served by the others is to encourage Lift users to push their
JavaScript in a badly mangled form into the server, where it is bound
by XHR latency (which is very much higher than most people think) and
tangled with server-side logic.

I could go on and on. There are bugs that we have found in Lift (now
all fixed, to my knowledge) that arise specifically in the context of
heavy client-side managed state. There is also the utter lack of
documentation on how to use Lift to write applications which have such
client-managed state in any serious form. All in all, it seems like
non-trivial client-managed state is an after-thought in Lift.
Something that is supported more by accident on the basis of the fact
that it is necessary to support server-managed client state (e.g.
ajaxText delegates to ajaxInvoke). The whole framework, the API, its
examples, and its optimizations are all geared toward a very server-
oriented model of programming. Also, David's recently-republished web
framework manifesto makes it clear that he feels (or at least *felt*)
that server-managed client state is the ideal.

I believe this is a problem. No, that's not really strong enough. I
believe this is a huge, cataclysmic, will-eventually-kill-the-
framework-if-we-don't-stop-it sort of problem. Modern web
applications are starting to seriously leverage the browser as a rich
client platform. Not all webapps will need to take advantage of this
technique, but many will (and many more than we think). Lift can't
just support client-managed state as an "also ran" system, it needs to
be a first-class aspect of the framework. In fact, if I were starting
Lift from scratch, I would have made client-managed state the
*default* with server-managed state a get-er-done gimmick tucked in a
back corner right next to Rails's scaffolding.

In practical terms, this would translate into a number of
consequences:

- SHtml's AJAX support would be (blissfully) simple: a single
ajaxLambda function which takes a Scala function and renders it into a
JavaScript lambda that invokes the Scala function via AJAX. The arity
and parameter types of the Scala function can be controlled via
typeclasses (basically, this is what Vibe's AJAX API does). The
ajaxText, ajaxButton, ajax* madness would all be either eliminated or
moved into a much less default-y location
- render would behave like fixedRender does currently. I honestly
can't see any need whatsoever for the old behavior, but if we really
want it, we could tuck that into a "volatileRender" or something like
that.
- reRender would be removed. This is another one that I think is
utterly useless and causes *major* issues for anyone who uses it (if
you haven't hit those issues yet, don't worry, you will). Granted,
it's very convenient, but if we had a nicer syntax for
partialUpdate(Call(...)), then any halfway-decent web developer could
replicate the behavior of reRender in almost as many characters.
There's no need to add a default-looking syntactic sugar for something
which is so very, very hostile to client-managed state
- partialUpdate(Call(...)) would get a nice syntactic sugar. I'm not
thinking anything fancy. Maybe, cometCall("function.name", param1,
param2)? I just want something which newcomers will see that smacks
of "this is the way we're *expected* to do Comet in Lift". Right now,
that something which newcomers see is reRender, and that's a problem.
- JsCmd and JsExp would probably go away, at least to a large degree.
This is a dicy issue though, because I'm assuming that we can still
return JavaScript values from AJAX functions and pass those values
into cometCalls. Unless we want to restrict such values to simple
primitives (string, boolean, int), we need something at least a *bit*
like JsExp. Maybe the solution would be to use typeclasses, similarly
to how we would need to apply them to support the aforementioned
ajaxLambda function in SHtml.
- SetHtml, SetById, and all of the many reincarnations of this same
idea would be axed. These have the same issue as reRender: they smack
of defaultyness, they're very dangerous and they're nothing that
couldn't be replicated in two shakes by an obvious application of
Call(...) coupled with jQuery in the browser.

Obviously, all of these would be backward-incompatible changes if we
applied them to Lift as it stands today. Remember, I said that this
is what I would do if I were designing Lift from scratch, not if I
already had a largely-mature web framework with a large user base.
The trouble is that I don't know if we can improve here in a backward-
compatible way. If you go through my list above, you'll see that 90%
of it is *removing* stuff, not adding things. In fact, the only thing
of substance that I add is ajaxLambda! In short, I'm not sure this is
the right direction for Lift today, but it sure is the direction I
wish Lift had gone yesterday.

Daniel

On Jul 7, 8:23 pm, Antonio Salazar Cardozo <savedfastc...@gmail.com>
wrote:

Raoul Duke

unread,
Jul 7, 2011, 5:59:08 PM7/7/11
to lif...@googlegroups.com
hi Daniel,

What stacks / servers would you recommend looking at as food for thought?

thanks.

David Pollak

unread,
Jul 7, 2011, 6:18:09 PM7/7/11
to lif...@googlegroups.com
I basically think Daniel's full of not very legitimate complaints and I'm really quite tired of his public negative comments about Lift's Comet support.  Mostly complaint with very little positive suggestions and lots of promises of open sourcing stuff that never gets open sourced does little to make Lift better or to improve Lift's APIs.

Here's a positive blog post about the broad range in which Lift's CometActor can be used from server knowing all about DOM to server having authoritative data representation to server just proxying events from the server to the client: http://lift.la/lift-and-data-driven-comet

I am all for constructive suggestions about how Lift's Comet support and other APIs can be improved.  I am not at all up for having a "Lift is this and that and biased this way and that."  I'm all for making Lift work the way a broad range of folks need it.  I am not at all interested in being preached at.

--
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/-/hhUiFM2qeywJ.

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

Bufferine

unread,
Jul 7, 2011, 7:36:03 PM7/7/11
to Lift
I can see that in many use cases what you say might be correct, but
it's not the case in mine.

I use Lift for a (pretty much) single screen admin application which
multiple people need to use and update (on the same data, in real
time). This controls a client front end (also single page), which
whilst it has some client side state, the server needs to have the
final say on what the customer can/can't do and see (also real-time).

For the customer page, sure, I could switch to entirely javascript and
drop reRender entirely - but reRender is completely appropriate when I
flush the entire server state and all the client side info changes
(for instance)

For the admin page, I use both reRender (because as an admin interface
there's a fair bit of data, but it's simple data that I don't see the
point of adding complicated javascript to for much of) and javascript
updates in response to ajax calls (for responsiveness - while other
people viewing the same screen will get a full reRender of the
component). I used to use Javascript for everything (almost no
reRender) but found that it was over-complicated, and I saw too much
dodgy behaviour with updates not happening/erroring/happening in the
wrong order/slowly.

I'm not a front end dev, so I could easily be missing something, I
could have the wrong end of the stick entirely, and I realise that I
have an unusual use-case but losing re-Render would put me back in
acres of javascript hell and I don't want to be there.

Catherine
> ...
>
> read more »

David Pollak

unread,
Jul 7, 2011, 7:38:35 PM7/7/11
to lif...@googlegroups.com
On Thu, Jul 7, 2011 at 2:03 PM, Daniel Spiewak <djsp...@gmail.com> wrote:
Let's see if I can re-summarize my opinion here, since Twitter is not
the best medium for deep discourse (it's just the easiest to use while
breezing through the Alps on a high-speed train).

First of all, I'm going to assume that we're all down with the notion
of client-managed state.  If you need me to motivate that for you,
just say the word and I'll nail my theses to your door post.  Suffice
it to say that many features that users are starting to expect in
modern web apps require the browser to manage a great deal of state
that *cannot* be managed by the server, simply due to concurrency and
latency issues.  OT is an extreme (but fitting) example of this.

Lift's entire stack is *deeply* biased toward the server-managed state
idea.  I'm actually astonished that you haven't run into the issues
this causes.  The best example, one which caused us a huge amount of
grief on Vibe, is the render/fixedRender issue.  If you're doing
*anything* with client-side state, you need to be using fixedRender,
otherwise the server will wack your CometActor seemingly on a whim.
Technically, this only happens if another tab has opened in that same
session showing that same CometActor *after* some partialUpdate(s)
have taken place, but that's not quite as unusual of a case as it
sounds.

Daniel, you're just dead wrong about this.  While the APIs for data driven Comet sites may not be as evolved as the APIs for DOM-based Comet, this is not a "deep bias" but in fact a vestige of the feedback we get from the community.  If you spent a little more time making positive recommendations rather than (1) pissing on Lift's comet support (no, it's not the Devil) and (2) justifying that your position about Lift being deeply biased in one way or another, you'd find that I'd make the APIs improve to suit your needs.
 

Oh, and when I say "biased", I do mean "biased" and not just
"defaulted".  Using fixedRender does solve the aforementioned problem,
but a) you need to know that it's there, and even David Pollak had
forgotten about it, and b) you can't *just* do a fixedRender, you also
have to def render = NodeSeq.Empty.  So, render with its associated
client-state-wacking behavior is very definitely the standard mode of
operation for the framework.

Yeah... you know, because you can't share code with me, it took me a very long time to understand the issue you were facing.  More deeply, I think the issue is a design flaw in your code.  I think that if you want to manipulate DOM state in the client without telling the server, then your should treat the server purely as a JSON data source.  Mixing some DOM and some data and some DOM manipulation exclusively on the client and some mixed with the server is going to lead to problems.  I haven't seen your actual code, but the more you complain about issues like this, the more I get the sense that you're mixing metaphors in a suboptimal way.  Yes, Lift allows this.  Haskell's foldl also leads to huge space problems.  Doesn't mean that either is bad.
 

Let's look at another example more obvious in the API: SHtml and all
of its AJAX goodies.  How many functions does this object have now?
I've lost count, and so many of them are overloaded that a simple
search doesn't suffice.  In any case, it's a lot.  We have things like
ajaxText, ajaxButton, etc.  All of these are components that allow you
to bind form (and sometimes non-form) elements to AJAX events,
allowing the server to fully manage the state of those components.
However, this is exactly *not* what you want when you're doing an app
which has any sort of client-side state.  Instead, you want to control
the form elements yourself, dealing with them from within JavaScript
and sending the values to the server under your own control.

The stuff in SHtml has evolved based on community feedback.  If you find that there are missing APIs, just suggest them.  I'm not a mind reader and bashing the APIs rather than asking for what you want does no good.
 

In order to do this, you need Lift to create a function mapping in the
session which accepts an XHR request on a particular
(cryptographically-identified) URL and routes the data to a
corresponding Scala function, which you have presumably defined to
handle the data input.  In the entirety of SHtml, in all of its vast
sprawling network of components, AJAX utilities and functions, how
many API elements do you suppose are devoted to this all-important
task?

Two.  (ajaxCall and ajaxInvoke; good luck figuring out the difference
between the two, and I assure you that you will need to use both)

ajaxCall allows you to send data.  ajaxInvoke simply invokes a function without passing data (useful if you have a button that needs pushing).  Personally, I use jsonCall for most of my JavaScript heavy apps.  This allows the passing of a JSON data structure.
 

What's worse is these functions are actually quite primitive and seem
to be intended more as low-level constructs used by higher-level,
officially blessed SHtml functions like ajaxText and ajaxButton.  One
of the things we did on the Vibe project was wrap ajaxCall and
ajaxInvoke in a DSL which allows us to trivially render a Scala
function value as a JsExp (specifically, a JavaScript lambda).  I
promise, we're still trying to open-source this…

Great.  We're almost at a feature request here.  And if fact I would have gladly added something similar to your combinators after you outlined them at the last Scala Lift Off... I didn't take good enough notes on the type signatures so I didn't actual get to implement them.  If you give me a more concrete idea of what would make the APIs better for you, we can easily add them to Lift, but if you keep the "well, read my mind about the actual thing we built and if you can't, I'm just going to keep complaining that Lift doesn't have the thing I'm not going to tell you about."
 

Lift has a large library of combinators and tools designed
specifically to allow the server to do all of the DOM manipulation and
state management.  Specifically, I'm thinking of the JsCmd/JsExp
stuff.  Out of all these combinators, we should really only have one
(Call) and the value primitives (Str, JsTrue, JsFalse, etc).  All that
is served by the others is to encourage Lift users to push their
JavaScript in a badly mangled form into the server, where it is bound
by XHR latency (which is very much higher than most people think) and
tangled with server-side logic.

You may not find value in these items, but I do.  There's no harm in having them and if you get into performance issues, you can hand-write the JavaScript client-side and rather than passing the JavaScript.  It's a choice that the developer can make.  It may be a bad coding practice for your site.  In fact, it works for other folks.  If Lift didn't have some of the JavaScript combinators, somebody else would be complaining that Lift is JavaScript unfriendly.  I'd rather please people with a choice of stuff that works rather than forcing them into a singular development model.  Now this might be a downfall... trusting that given a wide variety of choices, a developer will choose the right one for a given project.  There's is not one "Lift Way" and I think this costs us points from the copy'n'paste crowd, I think it has led to adoption from a better class of developers.  And if you want to evangelize your way of using Lift, I encourage that, but in the frame of "this is what we did and it's very successful" rather than "I'm going to spend most of my time complaining that Lift has a pile of APIs that I don't like."
 

I could go on and on.  There are bugs that we have found in Lift (now
all fixed, to my knowledge) that arise specifically in the context of
heavy client-side managed state.  

Yeah, there have been lots of bugs in Lift.  We fix them when people find them.  More specifically, getting the whole Comet long polling thing right for lots of browsers in lots of different load scenarios with different updating methodologies is just plain non-trivial.  But there have been 3 year latent bugs in Mapper.  That doesn't mean it's not meant for doing database work.  Yes, some code paths are less used than others.  But, quite frankly, Vibe is a heavier use of Lift's Comet than anyone else out there.  I'm surprised you guys haven't found more bugs.
 
There is also the utter lack of
documentation on how to use Lift to write applications which have such
client-managed state in any serious form.

Lift documentation evolves based on the questions we hear from the community and the areas we think are important.  If you come to the community asking how to do something, we'll help.  Then you can choose to document it.  If lots of people come with similar questions, we'll write better documentation and enhance the APIs.  This happened with the whole URL Rewrite vs. Menu.param stuff.
 
 All in all, it seems like
non-trivial client-managed state is an after-thought in Lift.
Something that is supported more by accident on the basis of the fact
that it is necessary to support server-managed client state (e.g.
ajaxText delegates to ajaxInvoke).


This is just wrong.  I've done a broad mix of DOM-heavy and data heavy Lift Comet apps.  And try doing a Git blame on the fixedRender stuff in CometActor.  I'm betting that part of the API goes back to Lift 0.3 or maybe earlier. 
 
 The whole framework, the API, its
examples, and its optimizations are all geared toward a very server-
oriented model of programming.  Also, David's recently-republished web
framework manifesto makes it clear that he feels (or at least *felt*)
that server-managed client state is the ideal.

That is not at all true.  I only have the ability to see so far in the future.  At the time I wrote the manifesto, the number of mostly client-side web apps could be counted on one hand.  There was GMail.  In fact, if you take a look at Lift in comparison with every web framework available within a year of me publishing the manifesto, Lift is the most balanced in terms of supporting rich client side apps without being tied to a specific JavaScript framework.

To my knowledge, Lift provides a better balance of mostly server to mostly client than anything out there other than Node.js.  If you've got pointers to other web frameworks that give a broader range of client to server state support, please point them out to me.
 

I believe this is a problem.  No, that's not really strong enough.  I
believe this is a huge, cataclysmic, will-eventually-kill-the-
framework-if-we-don't-stop-it sort of problem.

That is totally uncalled for.

First, you're wrong about the Lift mindset.  The Lift mindset is to a great degree led by listening to the community and talking to folks in other communities.  I regularly have coffee and other face to faces with leaders in the client-side community.  I helped the 280 North guys (Cappuccino) early in their project formation and even did the Frothy proof of concept.  To my knowledge, there are no production Lift/Cap apps.  I try to meet with John Resig whenever I'm in Boston.  I hang with the SproutCore folks from time to time.  I want to do an integration with Knockout.js.  Aaron Blohowiak, one of the Lift committers, has his own JavaScript framework that he might integrate with Lift if he finds the time or the motivation.

More generally, your word carries a lot of weight.  We have this discussion privately when you called Lift's Comet support the devil in a public presentation and then a bunch of Scala folks claimed that they would not use Lift because it had architectural problems.  Claiming that Lift is going to die unless we do things your way serves no valuable purpose.  I give your positive API requests a ton of weight and priority.  I even try really hard to figure out what you mean when you wave a type signature at me for 15 seconds on a slide and later say "go implement that."  But it causes damage to Lift and the Lift community when you predict its doom unless we do things exactly the way you think they should be done.
 
 Modern web
applications are starting to seriously leverage the browser as a rich
client platform.  Not all webapps will need to take advantage of this
technique, but many will (and many more than we think).  Lift can't
just support client-managed state as an "also ran" system, it needs to
be a first-class aspect of the framework.  In fact, if I were starting
Lift from scratch, I would have made client-managed state the
*default* with server-managed state a get-er-done gimmick tucked in a
back corner right next to Rails's scaffolding.

When I started Lift, there was no de facto client side JavaScript framework and we built Lift to be agnostic to JS frameworks.  In hindsight, this was the right decision.  If I had the decision to make today, I'd probably make Lift dependent on jQuery.  That would give Lift a lot more flexibility in terms of richer client-side abstractions expressed in Scala.

And yes, there are lots of other things that I might change if I was starting Lift today.  I'd definitely be more NoSQL oriented.  I would do the whole keeping the meaning with the bytes thing differently.

But at the end of the day, Lift still has better Comet support than any other web framework out there and it's templating system is sweet and its security model is excellent.  If I had a time machine, I don't think Lift would have started off much differently in 2006.
 

In practical terms, this would translate into a number of
consequences:

- SHtml's AJAX support would be (blissfully) simple: a single
ajaxLambda function which takes a Scala function and renders it into a
JavaScript lambda that invokes the Scala function via AJAX.  The arity
and parameter types of the Scala function can be controlled via
typeclasses (basically, this is what Vibe's AJAX API does).  The
ajaxText, ajaxButton, ajax* madness would all be either eliminated or
moved into a much less default-y location

Feel very very encouraged to show me the Vibe API (not the implementation, just the method signatures) and I'll create a new SClient module that supports more streamlined APIs for client -> server call generation.
 
- render would behave like fixedRender does currently.  I honestly
can't see any need whatsoever for the old behavior, but if we really
want it, we could tuck that into a "volatileRender" or something like
that.

I'm happy to create some API docs for fixedRender and maybe some examples.
 
- reRender would be removed.  This is another one that I think is
utterly useless and causes *major* issues for anyone who uses it (if
you haven't hit those issues yet, don't worry, you will).

There are already comments in the documentation about reRender including:
  /**
   * Cause the entire component to be reRendered and pushed out
   * to any listeners.  This method will cause the entire component
   * to be rendered which can result in a huge blob of JavaScript to
   * be sent to the client.  It's a much better practice to use
   * partialUpdate for non-trivial CometActor components.
   * Daniel Spiewak claims that the use of this API will not only
   * lead to bad code quality, but may result in a pox on your house.
   */


 I'm not going to remove it as I use it sometimes when there's a radical state change.  Further, when you use the ask/answer functionality in CometActors, you need to tell the component to completely reRender.
 
 Granted,
it's very convenient, but if we had a nicer syntax for
partialUpdate(Call(...)), then any halfway-decent web developer could
replicate the behavior of reRender in almost as many characters.
There's no need to add a default-looking syntactic sugar for something
which is so very, very hostile to client-managed state
- partialUpdate(Call(...)) would get a nice syntactic sugar.  I'm not
thinking anything fancy.  Maybe, cometCall("function.name", param1,
param2)?  

Thanks.  I'll add this as an API.
 
I just want something which newcomers will see that smacks
of "this is the way we're *expected* to do Comet in Lift".  Right now,
that something which newcomers see is reRender, and that's a problem.

Okay, you stubbed your toe on reRender.  Get over it.  You sound like a broken record on this issue.  Yes, reRender is not optimal for partial updates.  We know.
 
- JsCmd and JsExp would probably go away, at least to a large degree.
This is a dicy issue though, because I'm assuming that we can still
return JavaScript values from AJAX functions and pass those values
into cometCalls.  Unless we want to restrict such values to simple
primitives (string, boolean, int), we need something at least a *bit*
like JsExp.  Maybe the solution would be to use typeclasses, similarly
to how we would need to apply them to support the aforementioned
ajaxLambda function in SHtml.

Trying to read you mind on ajaxLambda and I'm still not getting a clear picture.

 
- SetHtml, SetById, and all of the many reincarnations of this same
idea would be axed.  These have the same issue as reRender: they smack
of defaultyness, they're very dangerous and they're nothing that
couldn't be replicated in two shakes by an obvious application of
Call(...) coupled with jQuery in the browser.

I 100% disagree. While it's very reasonable to hand-craft JavaScript for a site like Vibe, there are lots and lots and lots of sites that need to be knocked together yesterday.  The JsCmd/JsExp stuff grew out of my review of Rails' RJS and trying to create type-safe calls that were similar to the APIs available in RJS.  Any you know what, lots and lots and lots of sites work just fine in Rails.  I've got nothing against giving people the option to use these kinds of APIs.
 

Obviously, all of these would be backward-incompatible changes if we
applied them to Lift as it stands today.  Remember, I said that this
is what I would do if I were designing Lift from scratch, not if I
already had a largely-mature web framework with a large user base.
The trouble is that I don't know if we can improve here in a backward-
compatible way.  If you go through my list above, you'll see that 90%
of it is *removing* stuff, not adding things.  In fact, the only thing
of substance that I add is ajaxLambda!  In short, I'm not sure this is
the right direction for Lift today, but it sure is the direction I
wish Lift had gone yesterday.

Okay... so lemme say that stating "Lift is going to fail unless you remove a bunch of APIs that offend my sense of goodness" is thoroughly unwelcome in this forum or in any other public forum.  You don't like the APIs, don't use them.  You have a bunch of patterns that you think work better, blog about them.  Show me what needs to get added to Lift.  If you spend 20% of the time and space you spent here making positive recommendations, you would have achieved a whole lot more in terms of advancing your case from improving Lift than you have in this post.

David
 

Daniel

On Jul 7, 8:23 pm, Antonio Salazar Cardozo <savedfastc...@gmail.com>
wrote:
> I don't have any deep thoughts to add, but I must say I don't see how using
> Lift this way is like pulling teeth. We've used Lift comet with a high
> degree of client-managed state since the beginning for OpenStudy, and I'm
> just in the endgame of transitioning us to an even more client-centric model
> using Knockout JS. These transitions have required far far more change on
> the client than on the server (indeed, in this case the comet just had to be
> changed from sending down XML to JSON, which was a matter of adding some
> lift-json rendering of the relevant entities). But I guess it depends on use
> case -- for myself, I've never even considered using the `default' (if it
> can be called that) re-rendering approach, and I've never felt disadvantaged
> for it.
>
> The only part of this that has been a little more annoying has been dealing
> with dropping functions onto the client from a comet when I'm also dropping
> XML in and chaining functions in from the comet's superclass (which provides
> additional functionality). But that's extremely isolated nastiness that
> happens once or twice and can be abstracted out easily.
>
> I'd be glad to put up more details in a blog post if anyone is interested.
> Thanks,
> Antonio

--
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,
Jul 7, 2011, 10:00:33 PM7/7/11
to lif...@googlegroups.com
If Daniel means what I think he means by javascript lambdas, I think I was able to write an implementation of them, with G-d's help, which is in a reactive-web branch (https://github.com/nafg/reactive/tree/wip_jseventstream). Here's an illustration (at https://github.com/nafg/reactive/blob/wip_jseventstream/reactive-web-demo/src/main/scala/reactive/web/demo/snippet/JsEventStreamDemo.scala):

package reactive.web.demo.snippet

import reactive._
import web._
import javascript._
import JsTypes._

import net.liftweb.util._
import Helpers._

class JsEventStreamDemo extends Observing {
  trait window extends JsStub {
    def alert(s: $[JsString]): $[JsVoid]
  }
  val window = $$[window]

  val clicks = DOMEventSource.click
  val jses = clicks.jsEventStream.map{ (_: $[JsObj]) => "Button clicked"$ }

  //this function will be executed in plain javascript, with no ajax involved!
  jses.foreach { v: $[JsString] => window.alert("Fired: ".$ + v) }

  //alert from the server too!
  jses.toServer[String] foreach { v => reactive.web.alert("Server says: '"+v.toString+"'") }

  def render =
    "button" #> clicks
}

This illustrates a number of different features of the new branch, but in particular, there are three function literals here, two of which are lifted into javascript code (one returns a value and one opens an alert box).



Separate point: Perhaps there is a teeny bit of an analogy (maybe without such an important issue of latency) between this discussion, and the question of using an ORM more heavily vs. being more SQL-focused, even using the database's triggers and stored functions facilities...

Antonio Salazar Cardozo

unread,
Jul 7, 2011, 10:31:41 PM7/7/11
to lif...@googlegroups.com
For what it's worth, it's precisely the JsCmd stuff that let us get started quickly with OpenStudy. We evolved away from it over time, but that's fine.

I'm going to try and assemble a few blog posts next week that walk through an implementation similar to our real-time conversation list from a reasonably-simple initial implementation (not as far as using reRender, as we never really used that) to an implementation based on Knockout. Hopefully that'll make clearer at least our approach to evolving our comet support from `get it out there now!' to `ok, now we need things to work faster and better on the client'.
Thanks,
Antonio

Francois Armand

unread,
Jul 8, 2011, 2:50:18 AM7/8/11
to lif...@googlegroups.com
Le 07/07/2011 23:03, Daniel Spiewak a �crit :

> Let's see if I can re-summarize my opinion here, since Twitter is not
> the best medium for deep discourse (it's just the easiest to use while
> breezing through the Alps on a high-speed train).
>
> First of all, I'm going to assume that we're all down with the notion
> of client-managed state. If you need me to motivate that for you,
> just say the word and I'll nail my theses to your door post. Suffice
> it to say that many features that users are starting to expect in
> modern web apps require the browser to manage a great deal of state
> that *cannot* be managed by the server, simply due to concurrency and
> latency issues. OT is an extreme (but fitting) example of this.
>
>[...]

>
> Obviously, all of these would be backward-incompatible changes if we
> applied them to Lift as it stands today. Remember, I said that this
> is what I would do if I were designing Lift from scratch, not if I
> already had a largely-mature web framework with a large user base.
> The trouble is that I don't know if we can improve here in a backward-
> compatible way. If you go through my list above, you'll see that 90%
> of it is *removing* stuff, not adding things. In fact, the only thing
> of substance that I add is ajaxLambda! In short, I'm not sure this is
> the right direction for Lift today, but it sure is the direction I
> wish Lift had gone yesterday.


Well, there is a tone of things in that post, and I'm clearly
unqualified to judge the validity of 90% of them... You know, my team
and me are simple server-side guys, who are building a server management
application. We wanted it to be nice looking, but we really seems to be
far from Vibe need, are clearly no JS developpers, and had little to no
time to spend on that...

And for our use case, Lift JS/Ajax/Comet rendering work just perfeclty,
and is amazingly simple. Ho, clearly there is no
thousand-of-users-updating-social-info-concurrently in our app, just
some server-side indicator to be update in real-time, some ajax to avoid
full page reloading, and that's all.

After almost two year of Lift, I can say that Lift's Ajax/Comet/JS
support is one of the thing I deeply like in the framework - well, that
and templating with CSS selectors that are a pure wonder.
So perhaps we clearly all the client-side content management thing...
But for now, we just cheers what Lift bring to us, javascript
illeterate, regarding what we can do in the timeframe allocated for UI
niceties.


Cheers,

--
Francois Armand
http://fanf42.blogspot.com

Daniel Spiewak

unread,
Jul 8, 2011, 10:00:35 AM7/8/11
to Lift
First of all, my post appeared to have raised more emotion than I
intended. Let me be clear: I'm not bashing Lift, I'm simply pointing
out areas where its design has been problematic for us and where I am
certain it will be problematic for others. Based on the responses, it
appears that others on this list are either writing apps which are a)
less rich-client than Vibe, or b) haven't hit these problems. I can
think of various reasons why the rich-client folks wouldn't have run
into these issues at present, the most obvious one being that they
have simply not hit them *yet*, but a slightly more reasonable
explanation is that other people (e.g. David) are using Lift
differently than we are. The majority of the points I raised are only
valid if you're new to the framework or working with a team of people
with varying Lift experience. That doesn't make my points any less
valid (IMHO), but it does explain why they may seem a bit unusual.

Also, remember that this forum is one for Lift *users*, people who
have already evaluated Lift and decided to use it. Most of the people
posting/reading here have either decided that you don't need heavy
client-managed state or you have found ways to work around the
weaknesses in that area of Lift. If I re-posted this on a Node.js
forum, I think you would see a very different set of responses.

I also want to address David's point re: negativity. Yes, my email
was a fairly negative one. However, you'll notice that I proposed
legitimate solutions to my issues, I wasn't just ranting anti-
constructively. David also mentioned that he doesn't appreciate the
fact that I (personally) am expressing negative opinions about Lift.
Let me be clear: I'm just another member of the community. I have
opinions, just like everyone else, and I'm not going to recuse myself
from every Lift discussion simply because David thinks those opinions
"carry weight". I like Lift, I think it has some really interesting
ideas and I do want it to succeed, but I'm not going to censor myself
simply to support the illusion that everything is sunshine and
daisies. Lift has some trouble spots, and it has a certain set of
applications that it is better at supporting than others. That's
nothing to be ashamed of; every framework is like that.

Now that I've got that out of the way, I'll try to address these point-
by-point…

> Daniel, you're just dead wrong about this. While the APIs for data driven
> Comet sites may not be as evolved as the APIs for DOM-based Comet, this is
> not a "deep bias" but in fact a vestige of the feedback we get from the
> community. If you spent a little more time making positive recommendations
> rather than (1) pissing on Lift's comet support (no, it's not the Devil) and
> (2) justifying that your position about Lift being deeply biased in one way
> or another, you'd find that I'd make the APIs improve to suit your needs.

I never said Lift's Comet support is "the Devil". The only time I
ever even *wrote* something remotely approaching that was in notes for
a talk I gave, notes that were *originally* intended for my eyes only.

I like a lot of things about Lift's Comet support. It is a mixed bag
though, and I can't claim to be happy with everything that's in there.

> Yeah... you know, because you can't share code with me, it took me a very
> long time to understand the issue you were facing. More deeply, I think the
> issue is a design flaw in your code. I think that if you want to manipulate
> DOM state in the client without telling the server, then your should treat
> the server purely as a JSON data source. Mixing some DOM and some data and
> some DOM manipulation exclusively on the client and some mixed with the
> server is going to lead to problems. I haven't seen your actual code, but
> the more you complain about issues like this, the more I get the sense that
> you're mixing metaphors in a suboptimal way. Yes, Lift allows this.
> Haskell's foldl also leads to huge space problems. Doesn't mean that either
> is bad.

It's not a design flaw, it's called supporting page refresh. For the
sake of full disclosure, I'll explain what's going on here, and you
can tell me if you see a better way to do it.

Vibe's editor is based on operational transformation. I know you've
read my article on the subject, so I'll skip the primer. The key
thing to keep in mind is that the server and the client *must*
maintain independent state machines, otherwise the whole thing falls
over. The transitions between these states are non-trivial, and (as
with any state machine) are a function of the previous state. Thus,
we cannot update either the client or the server state machine by
forcibly changing a JSON data structure, it needs to be a function
call (if anyone's interested, I can toss out a formal proof of this
point).

To satisfy these constraints, the client builds the OT data structures
and sets up the state machine on page load (effectively on editor
activation). The editor is primed and ready to go when the user
starts typing. As operations are created by the user (by interacting
with the editor), they are cleared through OT and sent up to the
server via AJAX. When operations are sent from the server to the
client, they go down through the CometActor which controls the
editor. These operations are sent to the client via
partialUpdate(Call(…)).

Meanwhile, an operation from the server means that the canonical
content of the message has changed. Thus, if someone were to load the
page anew, we would want them to see the latest content and initialize
their OT with it and the latest version. Each message has an actor
which controls all actions on that message, and this is where the
message content lives. When the CometActor which controls the editor
is rendered, it loads the message content from this actor and renders
it to XHTML.

Here's the hang-up: if a tab is already open in a session editing the
message and a new tab is opened on that same message, the same
CometActor will be sent AskRender. This makes sense, and is in fact
the behavior we want for caching reasons. However, there have been
updates between when the CometActor first rendered and when the new
tab has sent AskRender, thus the cache will be dropped and a new
rendered copy of the message will be created for the new tab. So far,
so good. The problem is that this new rendered copy of the message
will *also* be pushed out to the first tab, wiping out the editor and
all of its associated event handlers (as well as any data which was
attached to those DOM nodes).

The way we avoid this is via fixedRender. I suppose we could also
solve the problem by making the editor a snippet paired with a "dumb"
CometActor which renders AJAX functions and proxies Comet, but doesn't
actually correspond to any HTML. In fact, we actually do this for one
of our CometActor(s) (the navpanel/contact list). However, I was (and
still am) under the impression that this is bad Lift style and not the
recommended way to use CometActor. Perhaps this is where my error is…

> The stuff in SHtml has evolved based on community feedback. If you find
> that there are missing APIs, just suggest them. I'm not a mind reader and
> bashing the APIs rather than asking for what you want does no good.

I don't want APIs added, I want them *removed*. However, it's clear
from the responses on this thread that a majority of the current
community disagrees with me on this point. I think this is
unfortunate, but not unexpected. Remember, I said at the end of my
email that I don't think my suggestions should be taken for Lift
today, now that it has a full community and an active pool of users.
The design choices I'm advocating would produce a very different
framework.

With that said, there are certainly some APIs which could be added.
cometCall is one of them (as noted), and ajaxLambda is the other.
I'll explain the latter more fully below.

> Great. We're almost at a feature request here. And if fact I would have
> gladly added something similar to your combinators after you outlined them
> at the last Scala Lift Off... I didn't take good enough notes on the type
> signatures so I didn't actual get to implement them. If you give me a more
> concrete idea of what would make the APIs better for you, we can easily add
> them to Lift, but if you keep the "well, read my mind about the actual thing
> we built and if you can't, I'm just going to keep complaining that Lift
> doesn't have the thing I'm not going to tell you about."

I'm not asking you to read my mind, I've explained this API several
times in different contexts. I'm still attempting to get it open-
sourced, but in the meantime, here's a more complete description *for
the record*.

def ajaxLambda(f: () => JsExp): AnonFunc
def ajaxLambda[A : JsConverter](f: A => JsExp): AnonFunc
def ajaxLambda[A : JsConverter, B : JsConverter](f: (A, B) => JsExp):
AnonFunc
def ajaxLambda[A : JsConverter, B : JsConverter, C : JsConverter](f:
(A, B, C) => JsExp): AnonFunc


trait JsConverter[+A] {
// handles converting JavaScript data sent via AJAX
// from whatever form Lift gives to us into a corresponding
// Scala value
def apply(a: Any): A
}

implicit object StrConverter extends JsConverter[String] {
def apply(a: Any) = a.toString // Lift AJAX passes JavaScript
strings in a reasonable form
}

implicit object IntConverter extends JsConverter[Int] {
def apply(a: Any) = a.toString.toInt // Lift AJAX passes JavaScript
ints as strings (if I recall)
}

The ajaxLambda overloads are implemented in terms of ajaxCall, with
the exception of ajaxLambda(Function0), which is implemented in terms
of ajaxInvoke. There are a large number of JsConverter instances,
including one for functions (which is implemented by plopping the
corresponding JavaScript function object into a nasty global object in
the browser and assigning a unique id, which is then used by the
server to invoke the function as relevant).

> You may not find value in these items, but I do. There's no harm in having
> them and if you get into performance issues, you can hand-write the
> JavaScript client-side and rather than passing the JavaScript. It's a
> choice that the developer can make. It may be a bad coding practice for
> your site. In fact, it works for other folks. If Lift didn't have some of
> the JavaScript combinators, somebody else would be complaining that Lift is
> JavaScript unfriendly. I'd rather please people with a choice of stuff that
> works rather than forcing them into a singular development model. Now this
> might be a downfall... trusting that given a wide variety of choices, a
> developer will choose the right one for a given project. There's is not one
> "Lift Way" and I think this costs us points from the copy'n'paste crowd, I
> think it has led to adoption from a better class of developers. And if you
> want to evangelize your way of using Lift, I encourage that, but in the
> frame of "this is what we did and it's very successful" rather than "I'm
> going to spend most of my time complaining that Lift has a pile of APIs that
> I don't like."

This isn't just a question of "things I don't like". If it were just
me and my opinion, then I would be happy to shut up and just use
"Lift, the Good Parts". However, I'm not a solo developer. I work in
a team with a number of other (quite good) developers, and I have
spent far too much time dredging through and fixing instances where
certain Lift mechanisms have been used simply because they appear to
be the "standard" way to do things. reRender is my favorite example
of this, but SetHtml is a big one. The entire JsExp/JsCmd library has
also caused a huge number of problems in maintaining code. I can
proudly say that I have *never* used reRender or SetHtml, but I have
unfortunately spent a lot of time dealing with them because other
people didn't know better. This is precisely why I am so vocal in
attempting to discourage their use.

One thing to keep in mind is that we've been using Lift for a long
time at Novell. The documentation scene today is still a little
lacking, but it's improving and really to a point where I can stop
whining about it. However, none of that was available when we
started. We had the draft of the first Lift book, a few random blog
posts and the Lift source code. That's all. Oh, we had the scaladoc
too, but it was just the signatures since none of that was written
yet. When you're in that situation, the copy/paste crowd tends to be
the only crowd that gets anything done. You start grasping at any
function which *looks* like it might do what you want. The situation
is a lot better now (thanks, everyone who worked on docs!), but it
still seems to me like a lot of time learning Lift is spent in point-
and-shoot random experimentation. That's not necessarily a bad thing
(learn by doing and what not), but it does mean that I feel very
passionately about making sure that the point-and-shoot experiments
land on the functions which are safest and most useful.

This is why *adding* to the API doesn't help anything. The only way
to resolve the problems I see are by *removing* and refactoring. As I
said before, I don't think this sort of change would be a good thing
for Lift at this point, which is why I've never seriously asked for
action in this area.

> Yeah, there have been lots of bugs in Lift. We fix them when people find
> them. More specifically, getting the whole Comet long polling thing right
> for lots of browsers in lots of different load scenarios with different
> updating methodologies is just plain non-trivial. But there have been 3
> year latent bugs in Mapper. That doesn't mean it's not meant for doing
> database work. Yes, some code paths are less used than others. But, quite
> frankly, Vibe is a heavier use of Lift's Comet than anyone else out there.
> I'm surprised you guys haven't found more bugs.

Bugs aren't a sign that something is unusable, but they are a pretty
bright red flag indicating that something is traditionally used
*less*. As you said, Vibe makes very heavy use of Lift's Comet
support, and so we tend to find bugs in that area that no one else
will hit. The fact that we hit a lot of bugs around the framework
specifically relating to client-managed state has led me to believe
that we are similarly unusual in that area. Lift isn't often used in
that way, and so those areas tend to be less stable and well-vetted.
That's all I'm saying with the "bugs" comment.

> This is just wrong. I've done a broad mix of DOM-heavy and data heavy Lift
> Comet apps. And try doing a Git blame on the fixedRender stuff in
> CometActor. I'm betting that part of the API goes back to Lift 0.3 or maybe
> earlier.

And yet there are (to my knowledge) no public examples which make use
of it. No one knows about fixedRender; *everyone* knows about
render. That's what I mean by an "also ran" feature. Lift just isn't
often applied to client-managed state-heavy apps. Perhaps this is the
reason everything from the APIs to the implementation seems skewed
toward server-managed state, I don't know. Frankly, the "why" doesn't
matter so much. The important point is that many of the APIs needed
for client-managed state are less well-known and harder to find if you
aren't actually looking for them. Newcomers to the framework won't be
looking for these APIs, and they won't be watching for the
implementation foibles (like AskRender).

> That is not at all true. I only have the ability to see so far in the
> future. At the time I wrote the manifesto, the number of mostly client-side
> web apps could be counted on one hand. There was GMail. In fact, if you
> take a look at Lift in comparison with every web framework available within
> a year of me publishing the manifesto, Lift is the most balanced in terms of
> supporting rich client side apps without being tied to a specific JavaScript
> framework.

I'm not criticizing your foresight. The manifesto is what it is.
Comparing Lift to the frameworks available 4 years ago is a red
herring though. It doesn't matter what other frameworks can do. The
important point is what *Lift* can do.

As I said, Lift has a lot of interesting ideas and I want it to
succeed. I think it's definitely the best framework out there in
several areas, I just wish that we could improve on the areas where it
is not.

> To my knowledge, Lift provides a better balance of mostly server to mostly
> client than anything out there other than Node.js. If you've got pointers
> to other web frameworks that give a broader range of client to server state
> support, please point them out to me.

It's not a competition. If I wanted to use Node.js, I'd be using
Node.js. I want to use Lift, and I want Lift to work well for the
things I'm doing. More importantly, I want Lift to work well for
*other* people who are (or will be) doing things like the things I've
done but don't yet have the advantage of hitting my head against the
many pitfalls.

> > I believe this is a problem. No, that's not really strong enough. I
> > believe this is a huge, cataclysmic, will-eventually-kill-the-
> > framework-if-we-don't-stop-it sort of problem.
>
> That is totally uncalled for.

I'm not saying Lift is going to die. I'm just trying to stress how
important this functional area is. I apologize if my wording was
offensive.

> More generally, your word carries a lot of weight. We have this discussion
> privately when you called Lift's Comet support the devil in a public
> presentation and then a bunch of Scala folks claimed that they would not use
> Lift because it had architectural problems. Claiming that Lift is going to
> die unless we do things your way serves no valuable purpose. I give your
> positive API requests a ton of weight and priority. I even try really hard
> to figure out what you mean when you wave a type signature at me for 15
> seconds on a slide and later say "go implement that." But it causes damage
> to Lift and the Lift community when you predict its doom unless we do things
> exactly the way you think they should be done.

*sigh* First of all, as mentioned, I *never* called Lift's Comet
support "the Devil". Those words were in my notes because it was
shorter (and substantially stronger) than writing out all of the
things that I *did* say. The notes were published without censor
because I didn't think that bullet would be offensive or could be
mistaken for anything other than hyperbole. Apparently I was
mistaken.

Oh, and it was *one* person who said he would never use Lift because
of architectural problems, not "a bunch". To my knowledge, that
person wasn't (as isn't) even using Scala. I also conversed with him
privately after he tweeted about not using Lift, and it would seem
that he misunderstood certain aspects of my talk. All in all, it was
a large misunderstanding that Twitter magnified 100-fold and has now
been taken to heart by you. For that, I'm sorry, but there's nothing
I can do to change your perception of an event.

I'm not predicting Lift's doom. If I were doing that, I wouldn't have
said so on the mailing-list, I would have written a vitriolic blog
post likening Lift to a low-income region of the inner city. I'm
*trying* to improve the situation for people trying to do client-
managed stateful apps with Lift. Remember, you did *ask* for my
opinion!

> When I started Lift, there was no de facto client side JavaScript framework
> and we built Lift to be agnostic to JS frameworks. In hindsight, this was
> the right decision. If I had the decision to make today, I'd probably make
> Lift dependent on jQuery. That would give Lift a lot more flexibility in
> terms of richer client-side abstractions expressed in Scala.

I think that independence was probably the right decision then, and
actually I agree that jQuery-dependence would probably be the right
decision now. However, client-side abstractions expressed in Scala is
exactly the *opposite* of what I want. :-) We already have too many
of those (e.g. JsExp). I want the client- and the server-side
separated. The situation is not unlike the one we have with XHTML and
templating logic, but you don't see many people suggesting "richer
Java abstractions in your JSPs".

> But at the end of the day, Lift still has better Comet support than any
> other web framework out there and it's templating system is sweet and its
> security model is excellent. If I had a time machine, I don't think Lift
> would have started off much differently in 2006.

Lift *does* have excellent Comet support.

> I'm happy to create some API docs for fixedRender and maybe some examples.

I think that would be good. Just one line of scaladoc and maybe one
or two examples would be awesome. The more important thing to
document would be the whole AskRender mechanism (the part which forces
the reRender of a dirty component when opened in a different tab). If
that were documented, then I think fixedRender would be pretty
obvious.

> Okay, you stubbed your toe on reRender. Get over it. You sound like a
> broken record on this issue. Yes, reRender is not optimal for partial
> updates. We know.

I stubbed my toe on *other* people's use of reRender. That's why I
can't get over it. People need to know the pitfalls of using it. An
analogy would be long jump in C. It's still part of the stdlib
because a few power users need it to implement important
functionality, but the trail of documentation is a mile-long, warning
people not to use it unless they *really* need it. If my incessant
ranting about this feature is what it takes to create this mile of
documentation, then I will have succeeded.

In any case, while I think that adding reRender in the first place was
a bad idea, I think removing it now would be a worse one. Too much
disruption to existing applications. My list of suggestions was
carefully phrased in terms of a parallel universe, "what if" version
of Lift.

> I 100% disagree. While it's very reasonable to hand-craft JavaScript for a
> site like Vibe, there are lots and lots and lots of sites that need to be
> knocked together yesterday. The JsCmd/JsExp stuff grew out of my review of
> Rails' RJS and trying to create type-safe calls that were similar to the
> APIs available in RJS. Any you know what, lots and lots and lots of sites
> work just fine in Rails. I've got nothing against giving people the option
> to use these kinds of APIs.

I still think it's a bad idea, but I guess it falls to project-
specific coding standards to enforce policy in this department.

> Okay... so lemme say that stating "Lift is going to fail unless you remove a
> bunch of APIs that offend my sense of goodness" is thoroughly unwelcome in
> this forum or in any other public forum. You don't like the APIs, don't use
> them. You have a bunch of patterns that you think work better, blog about
> them. Show me what needs to get added to Lift. If you spend 20% of the
> time and space you spent here making positive recommendations, you would
> have achieved a whole lot more in terms of advancing your case from
> improving Lift than you have in this post.

As I said, I'm perfectly happy using "Lift, the Good Parts". My team
is more than just me though, so it's not like I can just ignore an API
and never see it again. More importantly, I've run into some pitfalls
in this area, and I would love it if other members of the community
were able to benefit from my toe-stubbing and were able to avoid these
issues for like apps.

Anyway, you asked for my opinion on Lift's Comet/AJAX support, and I
called it like I see it. To summarize: I would have done things very
differently, but Lift's path is set. I don't want Lift removing a
bunch of APIs that people depend upon. However, I do think that
client-managed stateful applications are a very important element of
the web moving forward. I do strongly feel that more attention needs
to be paid in this area, if Lift is to continue its success in the
years and perhaps decades to come. Does that mean Lift is doomed if
you don't do exactly what I say? No. It does mean that I feel very
strongly about this.

Daniel

Daniel Spiewak

unread,
Jul 8, 2011, 10:01:55 AM7/8/11
to Lift
I'm not sure there *are* many stacks that do well in this area.
Node.js comes to mind, but it's really designed to be a super low-
level, bring-your-own-crayons sort of framework. It would be nice if
Lift had better support for this style, though I'm not sure exactly
what that would entail.

Daniel

Arie

unread,
Jul 8, 2011, 10:22:31 AM7/8/11
to Lift
I agree with you, and I was considering use node.js for what I'm
working on now, but type safety, Scala as a language, Lift's REST API,
and good DB support in Mongo are very strong features to keep me with
Lift.

I was thinking about customising the cometAjax.js script and some of
the comet functionality myself and may do so when I have a bit more
time, but instead I'm opting to create a javascript object that has a
function called something like

MyProject.cometResponse

which is what is used in comet's partialUpdate and takes a json object
like so:

{
responseType: "new message",
messageBody: "hi John, hope you're doing well"
}

which is server generated.

Then on the client side I simply hook in listener functions for each
of the responseTypes that I decide to use (inside the
MyProject.cometResponse function), which are invoked if the
responseType matches. I then only have to worry about a set of string
names for 'responseTypes' on the server and sending down the
appropriate content, which is very little different from an API, but
gives comet functionality.

So I was planning to modify cometAjax.js to have this listener
mechanism built in.

I hope this gives some ideas for where you might want to go in the
future.

David Whittaker

unread,
Jul 8, 2011, 12:33:30 PM7/8/11
to lif...@googlegroups.com
Daniel,

The question in my mind after reading all of this is: whose responsibility is it that the developers on your team write codes to the standards you've adopted?  Is it the responsibility of Lift to make those standards more "de facto" or for your organization to document them and make them more easily accessible to your teammates?  I completely agree with you that the JsExp stuff should be avoided where performance is important, but I disagree that it should be removed.  The recent thread about creating a DSL for JsExp is proof enough that it is useful to many people, so while me and my team will avoid it I don't plan to tell others how to write code.  In fact, I've followed the DSL thread closely because the contributors to it are so enthusiastic and maybe something innovative will come up that changes my mind.  When it comes down to it, you're obviously a smart guy with strong opinions on what an API should look like so if Lift's API is capable, but too broad for you, why not wrap it in your own API?  If it's clear at this point that others have use for comet reRender, etc, then it seems like your effort would be better put towards creating your own internal Comet API which only allows use of the Lift constructs that suit you and your team.  Personally, I'd love to see Lift's support remain broad and suit as many users as possible, while your API maybe gets released as a module some day. 


--

batkins

unread,
Jul 8, 2011, 2:41:38 PM7/8/11
to Lift
So when you wrote all those paragraps about how Lift had fundamental
design flaws that were going to "kill the framework" but were too
ingrained to be fixed...you were really saying that you want more
scaladoc and examples for render and reRender? And a new function
called ajaxLambda?
> including one for functions (which is implemented by ...
>
> read more »

David Pollak

unread,
Jul 8, 2011, 2:47:19 PM7/8/11
to lif...@googlegroups.com
On Fri, Jul 8, 2011 at 11:41 AM, batkins <batk...@gmail.com> wrote:
So when you wrote all those paragraps about how Lift had fundamental
design flaws that were going to "kill the framework" but were too
ingrained to be fixed...you were really saying that you want more
scaladoc and examples for render and reRender? And a new function
called ajaxLambda?

That about sums it up for me.

One more thing that causes me a tremendous amount of concern... lots of people listen to you, Daniel.  Lots of people take your conclusions as gospel.  They may not read to the end of the post for the punch line, excellently summarized above.  I cannot stress how counterproductive your posts and Twitter comments are, both to Lift and to whatever future credibility you have with me.
 
--
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.

Daniel Spiewak

unread,
Jul 8, 2011, 6:11:36 PM7/8/11
to Lift

> > So when you wrote all those paragraps about how Lift had fundamental
> > design flaws that were going to "kill the framework" but were too
> > ingrained to be fixed...you were really saying that you want more
> > scaladoc and examples for render and reRender? And a new function
> > called ajaxLambda?
>
> That about sums it up for me.

:-( I didn't start out *wanting* anything. David asked me for my
opinion. Where I come from, that is an invitation to an open floor.
I gave my opinion on Twitter, and David asked me to move the
discussion to the mailing-list. I reiterated the opinion on the
mailing-list, with the clear caveat that I don't think this is the
right direction for Lift now that the die is cast. The result is a
lot of bad feelings all around, one new documentation TODO and two
small functions to be added to the API. Not what I intended to
accomplish, that is certain.

> One more thing that causes me a tremendous amount of concern... lots of
> people listen to you, Daniel.  Lots of people take your conclusions as
> gospel.  They may not read to the end of the post for the punch line,
> excellently summarized above.

That's unfortunate. I can't do much about those people, but I have
confidence that things will get sorted out in the end. Strong
communities can absorb dissent, no matter the source.

Just in case it wasn't clear the last three times I said it: I'm still
using Lift, I still like many aspects of the framework, and I would
*like* to see it improve. I'm not abandoning ship nor am I advocating
that anyone else do the same.

> I cannot stress how counterproductive your
> posts and Twitter comments are, both to Lift and to whatever future
> credibility you have with me.

And for that, I am truly sorry. My opinion hasn't changed, and I
still think my points are valid, but I'm sorry if you think this has
damaged the community.

I think some of the negative impression here comes from my unfortunate
choice of hyperbole (the whole "fix-this-or-lift-will-die" bit). In
my defense, I wrote that while extremely tired and footsore sitting in
a second-rate Geneva bed-and-breakfast, but regardless of the venue,
the intended light-hearted aspect did not convey. Hopefully it is
buried far enough in the post that the "refuse to read the punch line"
crowd won't misinterpret it.

Daniel

Viktor Hedefalk

unread,
Apr 8, 2013, 12:09:49 PM4/8/13
to lif...@googlegroups.com, djsp...@gmail.com

Sorry to bring up this old infected thread, but I have some thoughts and questions very much related.

The use case I had is this app: http://kostbevakningen.se. It's an app that calculates nutrition of food inputed in cleartext. It's in Swedish only still but the idea is that you could write or copy/paste any kind of recipe and I'll try to parse and make sense of it. Something like:


Anyway, I used a jQuery-plugin to handle ajax on typing https://github.com/dennyferra/TypeWatch which needs a js-callback. Thing is I want the same rendering both on simple gets/posts and the ajax so I used SHtml.idMemoize (I love that stuff). But then I needed to expose that server code to something callable from the client side js so I tried to do something very similar of what Daniel proposed in this thread:

 trait JsConverter[+A] {
    def apply(a: Any): A
  }

  // And then converters for all relevant types:

  implicit object StrConverter extends JsConverter[String] {
    def apply(a: Any) = a.toString) = a.toString
  }

  implicit object IntConverter extends JsConverter[Int] {
    def apply(a: Any) = a.toString.toInt 
  }


And then I have this:

 def ajaxLambda[A](f: A => JsCmd)(implicit conv: JsConverter[A]): AnonFunc =
    AnonFunc("a", SHtml.jsonCall(JsVar("a"), (a: Any) => f(conv(a)))._2)

and similar for more parameters.


 And then in my snippet I have something like:

  val server = JsVar("$.fn.server")
    val script = JsCmds.Script(OnLoad(
      server === AnonFunc(Noop) &
        server ~> JsVal("postFood") === ajaxLambda((text: String) => {input(Full(text)); outputMemo.setHtml()})(StrConverter)))

which I then can use from my client side js like:


$('#foodinput').typeWatch({
callback: function() {
$('#foodinput').each(function(ind, textarea) {
$.fn.server.postFood(textarea.value);
});
},
wait: 600
});



ANYWAY, I now saw that this SHtml.jsonCall I'm using is deprecated and referring to the jsonCall with a callback that takes a JValue. So after some geeking around I figured i can do:

 def ajaxLambda[A: Manifest](f: A => JsCmd): AnonFunc =
    AnonFunc("a", SHtml.jsonCall(JsVar("a"), (a: JsonAST.JValue) => f(a.extract[A])))

  def ajaxLambda[A: Manifest, B: Manifest](f: (A, B) => JsCmd): AnonFunc =
    AnonFunc("a, b", SHtml.jsonCall(JsObj(("a" -> JsVar("a")), ("b" -> JsVar("b"))),
      (jv: JsonAST.JValue) => f.tupled(((jv \ "a").extract[A], (jv \ "b").extract[B]))))

  def ajaxLambda[A: Manifest, B: Manifest, C: Manifest](f: (A, B, C) => JsCmd): AnonFunc =
    AnonFunc("a, b", SHtml.jsonCall(JsObj(("a" -> JsVar("a")), ("b" -> JsVar("b"))),
      (jv: JsonAST.JValue) => f.tupled(((jv \ "a").extract[A], (jv \ "b").extract[B], (jv \ "c").extract[C]))))


just using JsonAST:s own conversion and propagating the Manifest resolution up to the caller. With this I could just toast the rest of the conversion code.

But then I figured - maybe I'm reinventing something here (hate to do that) and there is a more standardized way of doing this nowadays?

Thanks,
VIktor

Viktor Hedefalk

unread,
Apr 8, 2013, 12:13:58 PM4/8/13
to lif...@googlegroups.com
>
> def ajaxLambda[A: Manifest](f: A => JsCmd): AnonFunc =
> AnonFunc("a", SHtml.jsonCall(JsVar("a"), (a: JsonAST.JValue) =>
> f(a.extract[A])))
>
> def ajaxLambda[A: Manifest, B: Manifest](f: (A, B) => JsCmd): AnonFunc =
> AnonFunc("a, b", SHtml.jsonCall(JsObj(("a" -> JsVar("a")), ("b" ->
> JsVar("b"))),
> (jv: JsonAST.JValue) => f.tupled(((jv \ "a").extract[A], (jv \
> "b").extract[B]))))
>
> def ajaxLambda[A: Manifest, B: Manifest, C: Manifest](f: (A, B, C) =>
> JsCmd): AnonFunc =
> AnonFunc("a, b", SHtml.jsonCall(JsObj(("a" -> JsVar("a")), ("b" ->
> JsVar("b"))),
> (jv: JsonAST.JValue) => f.tupled(((jv \ "a").extract[A], (jv \
> "b").extract[B], (jv \ "c").extract[C]))))
>

Sorry about the tupling/untupling - that was left from some
intermediate solution using a tuple-converter I think :) I of course
mean this:

def ajaxLambda[A: Manifest](f: A => JsCmd): AnonFunc =
AnonFunc("a", SHtml.jsonCall(JsVar("a"), (a: JsonAST.JValue) =>
f(a.extract[A])))

def ajaxLambda[A: Manifest, B: Manifest](f: (A, B) => JsCmd): AnonFunc =
AnonFunc("a, b", SHtml.jsonCall(JsObj(("a" -> JsVar("a")), ("b" ->
JsVar("b"))),
(jv: JsonAST.JValue) => f((jv \ "a").extract[A], (jv \ "b").extract[B])))

def ajaxLambda[A: Manifest, B: Manifest, C: Manifest](f: (A, B, C)
=> JsCmd): AnonFunc =
AnonFunc("a, b", SHtml.jsonCall(JsObj(("a" -> JsVar("a")), ("b" ->
JsVar("b"))),
(jv: JsonAST.JValue) => f((jv \ "a").extract[A], (jv \
Reply all
Reply to author
Forward
0 new messages