Blog Post

#469 WebappClient (JavaScript)

andy Wed 18 Feb 2009

The new webappClient (aka JavaScript) stuff is in the latest build. This is pretty early stuff, and very subject to change. But its far enough along for me to start soliciting some feedback. I haven't written any docs on it yet, but here are the basics.

To compile a Fan class to JavaScript, you need to do two things:

  1. Specify hasJavascript=true in your build script.
  2. Use the @javascript facet on the types you want compiled to JavaScript.

What will happen is the compiler will run again, but instead of creating fcode, it will generate JavaScript source code from the AST for all types that specified the @javascript facet. All the source code gets stuck in a single source file called <podName>.js which goes into the root of the pod zipfile.

Note that the compiler runs against fan code. Which means if the JavaScript version of that API has not been implemented you'll get a runtime error. Eventually most of the sys API that is appropriate will work, but only a fraction has been completed so far.

Here's an example to see it in action:

class Test : Widget
{
  override Void onGet()
  {
    head.js(`/sys/pod/webappClient/webappClient.js`)
    head.js(`/sys/pod/myPod/myPod.js`)

    body.div("id='showme'").w("Hello, World").divEnd
    body.button("onclick='myPod_TestClient.clicked();'")
  }
}

@javascript
TestClient
{
  static Void clicked()
  {
    elem := Doc.elem("showme") // == document.getElementById
    Window.alert(elem.html)    // == alert(elem.innerHTML)
  }
}

To avoid naming collisions, the JavaScript type name is <pod>_<type>, which you'll need to use if you reference the type in script like onclick handlers. I don't really like this implementation issue leaking through, but not sure what else to do about it yet.

WebappClient includes client-side APIs you can use to access the DOM and make XmlHttpRequests. This will also eventually (probably soon) include some jQuery-like features.

To make an XHR request, use HttpReq:

elem := Doc.elem("bio")
HttpReq(uri).sendForm(["name":"Andy"]) |HttpRes res|
{
  elem.html = res.content
}

You can use the dynamic invoke operator to call off into "regular" JavaScript:

elem := Doc.elem("bio")
elem->style->display = "block"

There's a good ways to go, but I think its shaping up well so far. The convention we've been using is that client side code should have the "Client" suffix (e.g, webappClient, TestClient).

JohnDG Wed 18 Feb 2009

Nice work, Andy. As for feedback, I have the following suggestions:

  1. The main reason for using the same language on both client and server side (aside from personal preference) is that you can reuse code on both sides. So in my view, it doesn't make sense to generate JavaScript based on a facet. Better would be to tell Fan what to export, and it would trace the dependencies out and export all of those.
  2. My use case for this (and an excellent reason to switch to Fan at some point in the future) is to compile the same API to both JRE, .NET, and JavaScript. There are a few differences at the lower levels, but the vast majority of the code is the same. This use case is completely excluded.
  3. In my opinion, a killer feature is the ability to compile a Fan application for either desktop or browser. I already mentioned a JavaScript implementation of SWT which could be used for FWT; I can't see any Fan API that could not be made to work on the browser (with appropriate server-side support). Not that I expect Fan to provide such out of the box, but right now it's completely impossible.
  4. Instead of a completely different model for JavaScript, I would prefer reuse of the existing concept of "main" methods. For example, such a method could be the one exported to JavaScript (along with its complete dependency graph). The main method would contain whatever code you wanted to run when the script loaded. This is the GWT convention and the desktop convention. Now I don't think it should be required to export a main method because some people will only want to export APIs.

    To avoid naming collisions, the JavaScript type name is <pod>_<type>, which you'll need to use if you reference the type in script like onclick handlers.

I think the Pod name should become an object, with the classes in that Pod properties on the object. This is pretty standard convention in JavaScript to emulate namespaces. This would allow you to use "<pod>.<type>".

This will also eventually (probably soon) include some jQuery-like features.

I'm not sure the real advantage to this technology is in trying to be a better JavaScript. I think the real advantage of this technology is leveraging existing knowledge, allowing use of the same code base on client and server, and supporting cross-platform development.

If this is correct, then the emphasis should not be on trying to be a better JavaScript, but in trying to make the browser as invisible as possible. Why have to learn a new API for widgets on JavaScript when we could just implement FWT? Etc.

Currently I don't foresee using this feature at all. But when I first saw it, I thought to myself, "Time to seriously consider porting to Fan."

brian Thu 19 Feb 2009

John,

I like your pod namespace idea.

I definitely can see where you are coming from that everything should just port, and there is no technical reason you couldn't make FWT just run directly in a browser.

But the flip side is that you hardly ever develop a web UI using the same model, techniques, and UX as you would in a desktop app. You have to embrace the notion that there is a server side component and client side script. What is cool about this is that you can share code b/w the two now such as data manipulation, validation, etc.

So our focus is definitely on building first class web UIs, not necessarily trying to make a desktop app just port to the browser. I have been down that road and it is kludge. Our design principle is that you should never be tempted to drop down to JavaScript b/c Fan doesn't give you power you need in the browser. So we are deliberately embracing the browser, not trying to hide it.

Of course, like I said the core compiler technology is equally suited to be the porting problem and first class web ui problem. It is just a question of what we are focusing on today.

JohnDG Thu 19 Feb 2009

there is no technical reason...

The technical reason is the required @javascript facet. I think dependency analysis for lists of pods/classes/methods is a better way to determine what to export to JavaScript, and would much better support my use cases.

I have been down that road and it is kludge.

280Slides.com

Moreover, since you have control of the Fan API libraries, you have the opportunity to design APIs that are more friendlier cross-platform (in particular, any persistence API executed in a web browser will take substantially longer to complete, and could be modeled with asynchronous IO/promises/etc.).

So we are deliberately embracing the browser, not trying to hide it.

This means that you'll have your own set of APIs for the browser, which are completely unrelated to the desktop APIs and have their own learning curve.

Why should I use Fan over just JavaScript? JavaScript is supported by a community of tens of thousands, has dozens of very powerful libraries such as jQuery, and by its dynamic nature is faster than writing code in Fan.

Basically, I think it all has to come down to lowering the learning curve and making code reuse possible. I don't see that right now. The web client side will have its own set of widget, serialization, and persistence APIs designed to do what FWT does, but looking very different. And having to splice code into server and client significantly reduces the ability to reuse code.

Again, this is a great idea, but right now I see it more as, "We just prefer Fan over JavaScript," rather than, "Here are the compelling reasons why using Fan for browser development can help save you time."

andy Thu 19 Feb 2009

The technical reason is the required @javascript facet

That's just the default for build pod, and I think the most practical way. You can still force compile all code to Javascript. I think its important to note your intention though, especially since all the webappClient APIs are stubs and won't even work on the JVM/CLR.

This means that you'll have your own set of APIs for the browser, which are completely unrelated to the desktop APIs and have their own learning curve.

That's on purpose - they are entirely different environments. The DOM, CSS, etc are actually great technologies that are missing in desktop environments. Why would we want to hide them? There is a huge investment in web page knowledge already - I would argue more than any desktop toolkit. Why would we not want to leverage that?

"We just prefer Fan over JavaScript," rather than, "Here are the compelling reasons why using Fan for browser development can help save you time."

Because using Fan does save you time. We have designed all these language features to make our lives easier - why we would not want to leverage them? JavaScript is a great language, but I think it would be hard to argue Fan, static typing, and a simple powerful portable DOM API is not more productive that writing native JavaScript.

JohnDG Thu 19 Feb 2009

That's just the default for build pod, and I think the most practical way. You can still force compile all code to Javascript. I think its important to note your intention though, especially since all the webappClient APIs are stubs and won't even work on the JVM/CLR.

My situation is that nearly all code must be compiled to JavaScript, with the exception of a handful of directories. How would that be done?

The DOM, CSS, etc are actually great technologies that are missing in desktop environments. Why would we want to hide them?

Because the DOM and CSS are not supported equally by all browsers (leading to numerous workarounds for bugs and missing features), and the DOM is far too low-level an abstraction for most operations.

Witness Web Forms, drag & drop, SQL, local storage, and other desktop technologies making their way into HTML5. Web programming is becoming more like desktop programming, not less, and even with HTML5 (to say nothing of its successors), many of the APIs you are writing now will be obsolete because they're simply too low-level. Whereas, web applications written in frameworks like Objective-J will continue to work, because these frameworks are written at a high-level with the dual goals of knowledge and code reuse.

I think it would be hard to argue Fan, static typing, and a simple powerful portable DOM API is not more productive that writing native JavaScript.

Fan is great for large applications, but I think that lines of code-versus-lines of code, JavaScript will come out ahead in the typical web application.

freddy33 Thu 19 Feb 2009

Just some input from my experience:

  1. I come from Swing, then I discovered Wicket which is a Java Ajax Web Framework based on Swing concept and a little bit of Swing API (mainly model interfaces). The API/Environment/Build is totally different between Swing and Wicket but still the learning curve is very short. Architecture, concepts and language (here Java) are the same and this is what's important. A lot more than keeping the exact same API. There is always a time when I'll need real CSS/Javascript tuning from Wicket. Wicket API allows me to do it (Which I could not do if it was pure Swing wrapping). So, I think Andy decision is the good one.
  2. One of the biggest mistake of Maven 2 is to have the module type as an optional separate field and part of the key of the module. It makes a huge mess and many Maven user get totally lost. The strong example is the WAR system. Mixing Java, Web Resources and trying to make a modular system is impossible. And it is extremely bad practice to use the same pod name for JavaScript and for backend code. So, globally tagging the POD as javascript is good, and I agree that the pod system should see it.

Just my 2cts on this.

brian Thu 19 Feb 2009

My situation is that nearly all code must be compiled to JavaScript, with the exception of a handful of directories. How would that be done?

The design is that any Fan language construct can be compiled into JavaScript, so there is no technical limitation here. The issue here is the native APIs which we can support. Getting full support for everything in sys is unlikely (for example you probably won't ever have a hook to File.mmap in a JS environment). But likewise the APIs for the DOM wouldn't make sense in a JVM or CLR.

I think this is the reality that the environments are quite different. But we have to make a clear distinction between language versus APIs. If you write your code in 100% Fan, then it would be 100% portable to JS. But if you write code with a Java dependency, then we have to implement a suitable replacement in JS.

I kind of went thru the last decade thinking that the browser model could never compete against the desktop model. But I have much different perspective now, and I embrace the web model now as the future. I think the old desktop widget model can fit into this (and you suggested trying to use some CSS like patterns with FWT). But there is a huge impedance mismatch, and I think to do a first class webui you must embrace the native browser model (DOM, etc). Trying to hide it with additional layers is a mistake in my opinion.

But regardless of our philosophical differences of what we think is the right direction for web uis, this is just a matter of priorities. Andy and I are focusing the JavaScript technology on native web UIs. But in no way do we want to limit the design for people who want to work with a a FWT-like model. So if that is something the community is interested in, we will support it. It is just not something we have a lot of extra time to do ourselves right now.

Fan is great for large applications, but I think that lines of code-versus-lines of code, JavaScript will come out ahead in the typical web application.

I think the reason to use Fan is for static typing and code reuse. Yet, I disagree with your premise, b/c both languages have the same basic abstractions in which case lines of code comes down to the APIs, and we get to leverage all the high level APIs we are building.

JohnDG Thu 19 Feb 2009

If you write your code in 100% Fan, then it would be 100% portable to JS.

That's good to hear. However, I still hope you reconsider the @javascript facet in favor of dependency analysis for a list of specified classes/methods/directories.

But if you write code with a Java dependency, then we have to implement a suitable replacement in JS.

It would be good to see documentation on this after the work is more final, because it would be quite essential if we were to switch to Fan.

Also, since none of the libraries are marked with @javascript, I assumed they are wholly inaccessible on the client side, regardless of dependencies? That's quite unfortunate if true.

But there is a huge impedance mismatch, and I think to do a first class webui you must embrace the native browser model (DOM, etc)

Do you not consider 280Slides and similar applications to be first class? Objective-J prides itself on hiding the native browser model and wrapping it up in something that developers are more familiar with. For many things, such as drag & drop, undo/redo, and so forth, the desktop model is superior, more robust, and things are only going to continue moving in that direction with HTML5 and successors.

b/c both languages have the same basic abstractions

JavaScript has some extremely powerful abstractions such as function.apply() that do not have equivalents in Fan, and are the basis for some of the web frameworks. Moreover, there's a lot to be said for not having to specify any types, evaluate arbitrary code, etc., in the context of rapid web application development.

brian Fri 20 Feb 2009

To clarify the discussion, what I understand you think we might be doing wrong:

  1. using an explicit whitelist to determine what to compile
  2. supporting the DOM at the low level instead of abstracting it away
  3. not trying to get FWT to run in JS

Does that sum up your concerns? If so, here are my thoughts:

using an explicit whitelist to determine what to compile

I don't think this is a very big thing to get hung up on, we are just using the facet as a simple whitelist mechanism to get started. That doesn't preclude more sophisticated mechanisms in the future. I think the issue with dependency analysis is going to be that Fan tends to use a lot of meta-programming which makes static dependency analysis difficult. But this seems like a tactical issue we can fix in the future, not a strategic one that limits us.

supporting the DOM at the low level instead of abstracting it away

I think we might have philosophical differences about this one. Certainly I can't argue that higher level abstractions are good. But I don't want to give up the power to manipulate the DOM directly. Just because we are giving you access to this "low level" API doesn't preclude higher level abstractions. And access to the "low level" is what gives Fan the ability to work with JS libraries like google maps.

not trying to get FWT to run in JS

I think this would be very cool, but as I said it is just a priority thing. Let's say that pods written in 100% Fan are fully portable. Then the only issue is the pods which use native/FFI methods:

  • sys: high priority to port as much as we can
  • inet: I am not sure we can make this run in a browser
  • sql: would be cool, but SQL in browser has to become mainstream first
  • fwt: would be cool and is probably doable now (just not hi priority for us)

From there you can trace the dependencies to what will and will not work in JS. For example if we can't open a inet::TcpSocket, then we can't open a email::SmtpClient. Not because email isn't 100% Fan (it is), but because it needs to have a socket API which we can't port.

JavaScript has some extremely powerful abstractions such as function.apply() that do not have equivalents in Fan

How is this different than Fan's Func.callX methods? I know there is some static typing issues involved, but especially if your functions worked with duck typing it seems like you could do just about anything that JS can do.

JohnDG Fri 20 Feb 2009

using an explicit whitelist to determine what to compile

Yes. I'm glad to hear you're open to dependency analysis, as well.

supporting the DOM at the low level instead of abstracting it away

I don't actually have a problem with this. Supporting the DOM at a low level is a good thing to do, because it enables developers to stay in Fan no matter what they need to do. My issue is more with providing an alternate widget toolkit, alternate persistence library, etc. -- duplicate versions of things already in Fan, but with different APIs and completely separate learning curves.

I know you haven't done this yet, but I see it coming. :-)

not trying to get FWT to run in JS

If you don't provide any widget toolkit at all, then I have no issue with not providing a JavaScript implementation of FWT. The community can decide, assuming the back end allows arbitrary implementations of Fan libraries (and it sounds like this is supported, if not yet documented).

Not because email isn't 100% Fan (it is), but because it needs to have a socket API which we can't port.

In fact, the socket API can be ported 100%, and still work on all major browsers (including IE6). It just requires some support on the server side. Our application requires sockets (even the JavaScript version), and should we make the leap to Fan, TcpSocket would be one of the first things we would implement.

Even the File API can be ported. Under HTML5, you could map it to global persistent storage, and while a method like File::mmap would never be needed (because you'd be unlikely to store that much data), it could still be implemented.

More relevant in my line of work, online applications (word processors, slide show authoring programs, IDEs, etc.) allow users to browse directories and open files, exactly like desktop applications. When the user opens a file, the content is transferred to the client for presentation and editing. This means that a remote implementation of sys::File makes perfect sense and has a clean, logical mapping that would tremendously simplify web app development.

There's a temptation to look at web apps and desktop apps and say, "There completely different," but the harder you look, the stronger the resemblance. A web app has every need that a desktop application does: only the implementation differs.

Why is this such a concern to me? Because our licensable API is portable to Java, JavaScript, and .NET -- but achieving this today is not easy, and requires some very tricky coding and additional runtime support for .NET. Fan would automatically be portable to Java and .NET, and with some effort (i.e. implementation of Fan libraries), could be portable to JavaScript as well. But if it heads in a different direction with a different set of conflicting APIs, then those will be the officially sanctioned ones that get developer support, and it won't be worth investing any time to write third-party libraries -- the risk of abandonment would be too high.

So I feel it's necessary to strongly express the reasons why I think conflicting APIs are a bad direction, and why a unified set of APIs makes a lot of sense. Barring that, I'd be content if unified APIs were a possibility, and Fan did not ship with any conflicting APIs for client-side (now low-level DOM access is quite another thing; I've already said that's necessary and a good idea; conflicting APIs built on top of those is another thing entirely).

How is this different than Fan's Func.callX methods?

In JavaScript, you can take a "method" and execute it in the context of an arbitrary "object" -- i.e. you can change the meaning of this. eval provides the ultimate in meta programming. You can arbitrarily change existing objects and functions in a global way (despite being frowned upon and possibly abused, this is extremely powerful). Etc.

For large applications, I think the benefits of static typing start outweighing this power (except possibly for rigorously TDD'd code), but for small applications, these features are a boon to productivity.

brian Fri 20 Feb 2009

But if it heads in a different direction with a different set of conflicting APIs

Certainly the vision here is to create an ecosystem of Fan pods which can run seamlessly on the JVM, CLR, and JS. Sometimes the low levels get a little messy, but that is just life.

The APIs we plan on developing and shipping are the ones in place now: web, webapp, webappClient - the core stuff needed to handle web req/res and access the DOM.

Although I think I understand your point about multiple widget toolkits. I don't think we were planning on trying tackle anything like that, but Andy will have to share his thoughts.

But it is definitely worthwhile to try and get the community on the same page to focus our efforts on a common vision (which is why discussions like this are good to have).

andy Fri 20 Feb 2009

I think I understand where you are coming from John - but that is actually not my intention. What you see today in web, webapp, and webappClient is basically going to be it. My goal is to simply take things like HTML and the DOM and just make them easier to use without actually raising the abstraction level, since I think its important to have those low-level hooks.

Once we have a really solid base to build on, that is the correct place to start building out something like FWT in a layer above. I have no plans for including a widget toolkit at this time. But if we do, I agree it would make sense to try and port the FWT.

Login or Signup to reply.