Saturday, May 03, 2008

The story of Ruby and Python in Silverlight

In early March, right around the time we shipped IronPython and IronRuby with support for Silverlight 2, Michael Foord wrote a email to the IronPython Mailing List expressing a very valid point-of-view on the dynamic language integration. I recently found the email, and it rattled me a bit. I dedicate this post to Michael, thanks for inspiring me to tell this story. http://lists.ironpython.com/pipermail/users-ironpython.com/2008-March/006559.html Here's the quote:
> This is instead of the previous model (fro Silverlight 1.1) 
> where assemblies and Python files would only be fetched 
> from the server if they were actually needed. If what 
> I have just said is true then the new model is *massively* 
> inferior in my opinion.
After fighting with Silverlight not to cut features, getting screwed over in every which way possible, but then in-fact being able to preserve the same development experience as SL1.1 had for dynamic languages while improving the deployment story, reading this felt like being back in those feature planning meetings when my precious features were getting cut. But Michael didn't do anything wrong, he's actually pointing out a totally different problem altogether; we don't communicate the "why" enough. There are very good reasons dynamic languages in Silverlight work the way they do, and I want to take this opportunity to tell you the story of how we got from Silverlight 1.1 Alpha to Silverlight 2.

Silverlight and synchronous downloading

This is a very rough topic, as users insist on the feature, and no one seems to listen. Just search for "silverlight synchronous downloading" and you'll see what I mean. =) Silverlight applications start running on the UI thread of the browser. This means that all downloads on the UI thread in Silverlight must happen asynchronously, as to not hang the browser. Though there is a way to access the JavaScript XmlHttpRequest object (which supports synchronous behavior) from managed code and dispatch downloads through that, it has limited functionality (no cross domain, sync download may hang browser, browser behavior may be different, etc, etc). Presumably you should be able to download synchronously from a background thread, but Silverlight doesn't support that today. =(  

Where did this XAP thing come from?

SL1.1 was very uncomplicated. Just files. SL2 comes around, and there's this XAP thing. WTF? Any dynamic languages require it too? WTF!?! Given the synchronous=nono mindset, the Silverlight team decided to create this XAP-package, so they didn't have to programmatically download dependencies on application startup (outside of resources in XAML). Also, you can argue that packaging up the entire application is better for startup and is nicer to web servers (keeps # of requests down). If you want to keep that initial XAP size small, you can programmatically download and load assemblies after.

This SUCKS for dynamic languages!

Trust me, this was my initial reaction. When I started in July, I became in charge of everything that is Silverlight and Dynamic Languages. I fought and fought to get features that would still allow for just-text deployment, but it all boiled down to cutting a feature all this depended on: synchronous download. Why is synchronous download important to the SL1.1 dynamic language model? Because we don't support continuations. In SL1.1 when we saw an "import" we triggered a synchronous download of the python module. This always bothered me since it's not what Python does, and we provided no way of changing it. To support this asynchronously we'd have to change import to have it trigger a callback when the download was done to continue on with the program. Uck! That's not python anymore, and changing the language is not an option. So, without asynchronous downloading, the old model was as good as dead.

How Ironic?

Another quote from the same email Michael sent:
> It is also ironic that Jim Hugunin and John Lam 
> (in Mix 07) made much of how the great advantages 
> of working with dynamic languages for Silverlight 
> is that they can be deployed as text. Now of course
> they need to be 'compiled' with a custom tool 
> (chiron) and deployed in this compiled form - 
> and not deployed as text...
Keep in mind, we always wanted to support XAP as a deployment model; we just didn't want it for development. So, the real question is "how do you use XAP for development but still keep the edit-refresh experience of a dynamic languages?" The answer: Chiron.

Just-text "resurrected"

When you develop a dynamic language app, your app is just text; there should be no xap files. The only place you'd see a reference to .xap is in your HTML file. Given a app folder structure that looked like this:
Foo/
   Index.html
   App/
      App.py

You'd see this in Index.html:
<param name="source" value="App.xap" />

This just means "You're start script (App.py, by default), is in the App directory". Run Chiron from your app's root directory like this:
>>> Chiron /w

Then navigate to http://localhost:2060, and whola! You're application is running! You can edit your files, hit refresh on the browser, and the app will update with your changes! The XAP is being generated in memory when it's requested and sent to the client, so you'll never see it on the file system. In the end, it all matters about the experience of developing your app, and this gives you the same effect as SL1.1 did.

Deployment: an upgrade from SL1.1

Also, now we support the package deployment model: simply run this in your app's root directory:
>>> Chiron /d:App /z:App.xap

App.xap will be generated with the contents of your /App folder, and now you can throw the application on any web server you'd like (and remove the /App folder if you choose). Note: though Chiron seems like a web-server, it's not; It only listens on localhost. This is slightly annoying when using 2 machines to develop your app (one to code it and one to view it), so that restriction will be lifted in the future. However, do not use Chiron for a deployment server! IIS or Apache are just fine. =)

My XAP is filled with CRAP!

Here's where the broken bottles should start flying at my face: App.xap is simply a zip file. If you extract it, you'll see your original application (from the /app folder down), but with some extra gunk:
Foo.xap/
       App.py
       AppManifest.xaml
       IronPython.dll
       IronPython.Modules.dll
       Microsoft.Scripting.dll
       Microsoft.Scripting.Silverlight.dll

Eek! Why all the DLLs!? Here's another dirty little secret; Silverlight doesn't ship with IronPython anymore! =( I can't blame this totally anyone else though, this was our call ... but for good reasons. We wanted to get the new language bits to you quicker than Silverlight releases. I'll even go as far as saying I want you to be able to take our sources and compile them for Silverlight. With IronPython in the box, that wouldn't have been very pleasant. Plus, being in a shipping Microsoft product comes with a ton of red tape, and we wanted to avoid that. Lastly, Microsoft is very concerned with intellectual property, and the lawyers still haven't figured out the "open source project that takes contributions from non-Microsoft employees that ships in a Microsoft product" scenario. Since we take contributions to IronRuby today, and will in the future for IronPython, the best bet was for us to get the hell out of the Silverlight runtime. Silverlight did give us some pressure though; we would have added more size to the Silverlight runtime. They are extremely concerned about size, and given the other issues we agreed to moving outside. However we still are part of the Silverlight development process, and we still stick our bits in the Silverlight SDK for some publicity. =)

Extensions to the rescue, NOT!

Keep in mind, this decision was made long before I joined the team, and long before XAP was a word you heard in the Silverlight hallways. At the time, Silverlight was designing a extension model, such that it didn't matter that you weren't in the core, end-user applications would automatically download "extensions" as needed. So, it seemed obvious that this new-fangled extension story was exactly what we wanted to do. In SL1.1, the DLR provided hooks that the XAML Parser called when <x:code source="app.py" /> was encountered. With the knowledge of SL2 moving to a code-first model, we planned on removing these hooks, and instead use the new Silverlight application model to host the DLR and call into script code first. From there, the app developer could load XAML. Then features started getting cut. And of course, extensions were one of them. WTF!? The DLR bet its entire existence in Silverlight on this being in! This was my first experience of how shipping software works at Microsoft; lots of planning, prioritizing, and scheduling. Slipping is very frowned upon, and extensions were looked upon as a threat to shipping SL2 (contrary to popular belief, Microsoft doesn't have unlimited resources =P). This, coupled with XAP, set me completely back to square one. Or so I thought. XAP saved our ass with not having to depend on synchronous download (which was cut around the same time). But not having the extension model was kind of annoying. This meant that we had to stick our assemblies inside the XAP. Sure, a XAP is cached by your browser, but every time you download a dynamic language app for the first time you have to download the language assemblies. That was a big bummer, and that's why the DLLs are in the XAP, by default. =P

Haha! Our feature works!

But as luck would have it, though the testing for extensions were cut, the fundamental feature actually exists in SL2; the ability to point your AppManifest.xaml file at assemblies outside the XAP. Since the code to load these assemblies goes through the Silverlight network stack, these assemblies could even be cross-domain! Awesome!! So, we discovered this, and quickly make Chiron understand this. So, if you look at Chiron.exe.config, you'll see an section with some interesting contents.
<!--
    This sets the base URL to load language assemblies from, instead of
    packaging them in the XAP. If omitted, assemblies are copied from
    localAssemblyPath and inserted in the XAP file.

    For a rooted path but a relative domain name:
    <add key="urlPrefix" value="/path/to/language/assemblies" />

    For an absolute URL on a domain:
    <add key="urlPrefix" value="http://example.com/assemblies/" />
    -->

    <!-- the location of language assemblies on disk -->
    <add key="localAssemblyPath" value="." />
Word of warning: this isn't supported by the Visual Studio Silverlight tools, so don't go off expecting this to work in a C#/VB app. It's this by design feature that needs more work to be prime-time. However, since Chiron understands how it works, we use it

Assemblies get the f*** out of my XAP

The "urlPrefix" option is your savior! By default, there is no urlPrefix, so Chiron will stick the assemblies into the XAP. But, if you use the option with a rooted path:
<add key="urlPrefix" value="/lib" />
The assemblies WILL NOT BE INCLUDED IN THE XAP! You're XAP will look like this:
App.xap/
       App.py
       AppManifest.xaml
You're application will look for the assemblies in the /lib folder of whatever server you put the application on. While running the app from Chiron. it will serve the assemblies from that directory "magically", so no extra work on your part during development. However if you deploy to http://foo.com, you need to make sure all the assemblies you need are located at http://foo.com/lib.

Assemblies, go as far away as possible

Lastly, if you want to host the assemblies on a completely different domain than your app, you can!
<add key="urlPrefix" value="http://bar.com/lib" />
This will grab the assemblies from http://bar.com/lib, but there's a catch. Silverlight will enforce the existence of a clientpolicy.xml or clientaccesspolicy.xml file on http://bar.com, with the following contents:
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from>
        <domain uri="*">
      </allow-from>
      <grant-to>
        <resource path="/lib" subpaths="true" />
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>
This will give any app access to your /lib directory. You can tighten up the restrictions here (search for Silverlight clientaccesspolicy.xml, I'm sure you'll find what you need.), but this will work. Also, Silverlight respects Flash's clientpolicy.xml files. =) The beauty of having the DLLs outside the XAP is they will only be downloaded once, and cached in your browser! You're XAP will be small (around 2k, yes, the compression isn't great ... we're working on it). Actually, all the samples on http://dynamicsilverlight.net work this way, check it out if you don't believe me: http://dynamicsilverlight.net/see/ruby/clock. Fire up Firebug to see what gets downloaded. Note: some domains (like Microsoft.com) don't allow serving of DLLs. This is the main reason we didn't throw these DLLs on download.microsoft.com and enable the cross-domain download by default. Again, this is why this feature needs some more thought and isn't supported by VS tools.

Hey, it's not crap after all

So, dynamic languages in Silverlight let you:
  1. Develop with a text-editor with edit-refresh while running "Chiron /w"
  2. Deploy as a XAP file
  3. Have the Dynamic Language DLLs be served outside the XAP file.
This was the goal we had at the beginning of SL2 development, and though there were a few bumps in the road, screaming at meetings, cursing with my office door closed (I'm a New Yorker, what do you expect?), the aggravation paid off and we've met those goals.

... but there's still some crap

Granted, there is still a lot we can do to improve this.
  1. A XAP with 2 files in it is almost amusing. It really shouldn't be needed. We need to find a way to make it optional. This, as always, boils down to synchronous downloads need to be allowed on background threads. SL2 Beta1 doesn't even support asynchronous downloads on a background thread, but Beta2 will, largely due to my yelling, but not synchronous yet. That needs to get in so we can provide the option the XAP-less option.
  2. True extension support. The browser cache doesn't cut it. We need a GAC for Silverlight, where you can optionally install ahead of time, or download ONLY ONCE. This will allow versioning of those assemblies to be easier, and side-by-side version not just a matter of what URL it came from.

Don't agree with me?

Given all the things I've said, do you still have nits about dynamic languages in Silverlight? Please, let me know! I'm really a web developer acting as a Microsoft tools developer, so the littlest details piss me off until they are perfect. If you can convince me that something should change here, you can pretty much count on it getting into Silverlight. But that is if you can convince me ... I am stubborn. =P And on that note, one last quote from Michael:
> Feel free to pass my comments on to those who made these 
> decisions (I'm well aware it isn't you Dino)... On the 
> other hand I may be wrong (and would be both delighted 
> and apologetic).
I hope I proved you wrong =)

16 comments:

Bjorn Tipling said...

Nice post, but why bother supporting (and wasting all that effort) on an also-ran proprietary technology. The only way people will install silverlight is when microsoft shells out huge amounts of cash to get 'exclusive rights.' Silverlight is evil. I wont install it, ever.

Anonymous said...

Thanks, Jimmy. I feel for your situation. Feature cuts are never pleasant, but better to ship at all than to always put it off another month trying to add yet another feature.

/rant/

Amazing how people manage to think themselves still professional when they start spouting out "X is evil" just because Microsoft developed it, or that nobody would want to use it just because they themselves don't want to.

If a comment like this came up in an interview, I'd invite the twit to go elsewhere until he achieved a better attitude.

/end rant/

Bjorn Tipling said...

I never said it was because it was evil because of Microsoft. I gave many other reasons, which apparently you weren't able to read.

And you know I want to work at a Microsoft shop like I want a hole in my head, anyways. Have fun with your visual source safe and your user agent security agent. At my job I can use whatever platform I want to develop on. How about you?

Miha said...

Insightful. Thanks. I wish you (read: Microsoft) would provide such insightful and *useful* information more often.

Jimmy Schementi said...

@bjorn: For Microsoft to start a new product, there needs to be a way to make money. Silverlight is no different. However, the team is using it as an opportunity to get street cred as well; running on the Mac, collaboration with Novell, dynamic languages, etc.

We use Team Foundation Server and Source Depot (an internal version control system based on Perforce); Source Safe hasn't been used widely since 1999.

Though my team keeps it's code in TFS, I used a tfs+svn bridge to work out of svn. Now I'm moving my stuff to git. And a Macbook is my main machine, because I asked for it. I'm making this post from my Ubuntu machine at home. Because it's my choice.

@Miha Thanks. This is happening more and more. I think any extreme is bad, and being completely closed up ... especially when your making developer tools ... is definitely bad.

Bjorn Tipling said...

Jimmy, I think that's pretty cool. I actually like ASP.NET a lot. I like Visual Studio, but Silverlight bothers me because I don't think it will ever function well on my Ubuntu and Mac machine. With HTML 5 comes a lot of new functionality that I hope much of it will replace the use of Flash and Silverlight. I'm fine with whatever technology runs on the server, proprietary, fine. It's what happens on the client that I want to remain cross platform and open, and Microsoft doesn't have a good track record of that. Moonlight, is an unfulfilled promise of a future I don't think will ever come to pass.

How do you develop for Silverlight on a Mac?

Also sorry if I came across as rude, I just didn't like being called a twit. :P

Jimmy Schementi said...

I do all my Silverlight app building on a Mac with IronRuby, IronPython, or Managed JScript. If you head over to the Silverlight 2 download siteyou'll see a download for the Mac. SL1 works on all Macs, and SL2 works on Intel Macs. Then just grab the dynamic languages SDK and you're all set; you just need a text editor, terminal, and a browser. =)

Moonlight is going to happen; true cross-platform something the Silverlight leadership (Scott Guthrie and company) definitely know is important on the web.

HTML 5 is definitely not replacing Flash/Silverlight; granted it's got a ton of cool things like a multimedia tag, better document management, local database capability, and "area post" for replacing common AJAX usages, but it's still just an upgrade to HTML. It's unfortunate that we live in a world where browser plug-ins are the only way to get true client-like applications. Ideally we need to get rid of the browser altogether and just make the desktop understand web applications. But I'm musing here ... =P

Eloff said...

Very interesting to get an inside look at some of the design tradeoffs in Silverlight. Good work sticking up for the DLR, it looks like in the end we got pretty much to the same place, just by a different path (Chrion vs Sync Download)

Something people seem to miss is that yes the HTML/svg/css/javascript model is open, but it's different in every browser. On top of that it, well, blows compared to the desktop environment. Build once, deploy everywhere is impossible with HTML, but seems to be a viable dream with Silverlight. I'm actually developing faster on Silverlight Beta 1 than I was in the browser, and I have 3 weeks experience vs 9 years.

mark said...

Jimmy,
Thanks for the post, it was very interesting.

One thing that concerns me with the approach the Silverlight team has taken is that they seem to have gutted the SL Runtime of several classes that are key to certain programming 'patterns' in .net. Even the DLR seems to have been impacted by this, as seen by your private replacements of things like System.SerializableAttribute, System.NonSerializableAttribute, System.StringSplitOptions, System.Runtime.Serialization.ISerializable, etc

The DLR has not been impacted, at least as far as I can see by a missing ICommand interface but UI applications built using the DLR certainly are.

I am having to take a similar approach to what the DLR has done in some areas to achieve a common codebase.

Here is my question: What is the back story behind the decision to totally eliminate these core types and idoms from the SL runtime?

Thanks for any insite you can give.

Bjorn Tipling said...

Jimmy,

Thanks. I had no idea you could develop silverlight on a mac. Pretty cool. And using python. :) I'll check it out thanks.

Bjorn Tipling said...

Ha I just installed silverlight. And I said I would never. :(

But seriously, until moonlight works, I'm not taking Silverlight serious, and I'm not recommending it to anyone. But I'm curious about Python and developing on the mac so I'll check it out. :D

Fuzzyman said...

Thanks Jimmy this is a very useful blog entry with some good information - information which as far as I can tell is not available elsewhere (documentation need!?).

A post like this along with or shortly after the release might have saved much confusion/ranting on my part at least... :-)

One comment:

"This was the goal we had at the beginning of SL2 development"

You say outright that deployment via xap was what you always had in mind - which is very much *not* what John and Jim said at Mix 07...

Other than that, with the URL prefix stuff at least deployed xap files can be less bulky. A solution that works more globally (as you suggest) would be better of course.

Keith J. Farmer said...

Mac support was one of the *first* things they demonstrated with Silverlight.

Jimmy Schementi said...

@fuzzyman

> You say outright that deployment
> via xap was what you always had
> in mind - which is very much
> *not* what John and Jim said at
> Mix 07...

Should have been more clear: deployment with a zip file was always an *option* we wanted to support, along side of flat-files. But when push-came-to-shove, we had to compromise on the flat-file deployment model. I really hope the next version of Silverlight supports it; it'll elevate dynamic languages to more first-class citizens. We're getting close though. =)

Mark said...

These people like Bjorn are demented and have been indoctrinated by the open sores at places like Slashdork and other place.

This is coming from someone who was writing code for Linux professionally a decade ago.

Bjorn, pull your head out of your ass. You're an embarrassment to yourself.

Jimmy Schementi said...

Be nice people, no name calling ... this isn't elementary school ... :)

@Mark: bjorne has every right to be skeptical. The open-source/cross-platform mindset at Microsoft is new, and not everyone in the company is fully appreciative of those ideals, so I wouldn't expect anyone else to believe it's happening until they see it. Everything needs to start somewhere, and things like IronPython, IronRuby, Silverlight, AJAX Control Toolkit, and Codeplex itself are steps in the right direction. But, as John Lam says, we work on open-source @ Microsoft because it's challenging. :)