This article is continuing in the series on exploring Hyperscala. If you have not already done so, I would highly recommend reading the following previous posts as many topics discussed here build on concepts previously discussed:
The Play Framework is perhaps the most popular web framework among Scala developers today. I have been asked quite a few times what is wrong with Play that made me decide against using it and instead write my own web framework. To be fair, Play is a pretty awesome framework and is a great choice for web applications. However, there are a few problems I have with it that led me to choose against using it moving forward:
- Templating Language
- Though Play does a far better job of this than pretty much anyone else I’ve seen, you’re still dealing with special syntax in your HTML templates for accessing your Scala code. This is incredibly problematic when there is a separation of designer and programmer. Life becomes quickly more difficult when the programmer injects all the Play-specific code into the HTML file that came from the designer and then the designer needs to make a change. Not only does it become difficult for the designer to even preview what the content should look like now, but it is highly likely that the designer will mess something up when they are trying to make changes to the HTML. This was something I spent a lot of time considering and trying to come up with a better solution for in Hyperscala. I have worked at a lot of large companies that have teams of designers and teams of developers and it is incredibly painful bridging that gap. In Hyperscala there are actually several solutions to this problem, but the best one for this specific scenario, I believe, is DynamicContent. We’ve discussed this in a previous post and demonstrated it in yet another post. The idea is keeping the HTML clean and pure and the developer simply loads in only what they need to manipulate by the HTML ‘id’. In my opinion, this is the simplest solution and a much cleaner separation between the designer and the developer.
- Modularity
- Yes, there is module support in Play, but it is confusing at best and extremely limited at worst. There is an inherent problem in any framework that doesn’t have complete control over the HTML and Play simply does not. The way Play works is very similar to JSPs where content is injected but the HTML is never parsed or comprehended. This leads to several problems when you have modules that need to introduce something like jQuery but shouldn’t load it multiple times and want to avoid an issue where another module is trying to load a different version. These are complicated issues that need to be dealt with in large web applications. In Hyperscala the Module system is incredibly powerful and solves all of these problems incredibly well. Not only that, but a Module in Hyperscala is dead simple to write and even more simple to use.
- Complicated Setup
- Any framework that needs its own console in order to create an application is too complicated. Yes, I agree that it’s cool that the Play framework has its handy-dandy console utility to create your application for you, setup your IDE, and probably even brush your teeth for you. However, I very much dislike “magic” that happens and is unexplained. I prefer to understand what is going on under the hood and though you can do this in Play, the decision was made as a default route to hide this from the developer. I prefer to work with frameworks in which you add a Maven dependency and then start writing some code. I don’t want configuration files. I don’t want a bunch of boilerplate code that is generated on my behalf. I want to instantiate something and run it. This is exactly what Hyperscala does. Once you include your Maven dependencies for Hyperscala you need only create an implementation of Website and add a Webpage to be up and running. In Play you have a minimum expectation of an Application.scala, index.scala.html, application.conf, and routes file.
In an effort to compare Hyperscala with Play practically I decided to take an example from Play and re-write it in Hyperscala. My hope was for a simple ‘Hello World’ example to keep it simple and straight-forward, but what Play considers their ‘Hello World’ example is quite a bit more than that. It will have to do.
First lets take a look at the Application.scala file:
This is fairly simple. The idea is that this page asks you for your name to display, the number of times to repeat it, and a color. When you hit submit, it validates the form (based on the validations specified near the top of the page) and then either displays errors or writes out the name you specified the number of times you specified in the color you specified. Notice here that the form validation process is hard-coded, so no special handling is necessary. I point this out now because in Hyperscala there is no “default” for how validation should be handled so it takes a little more code (but not much).
Next we take a look at the HTML files defined for the display.
hello.scala.html index.scala.html main.scala.html
It’s fairly clean and I’m sure once you get used to the Play syntax it becomes easier to grasp. However, this would be quite difficult for a designer to work with. I’m sure there is a better way to represent the page for designers, but then you obviously lose out on much of the power of Play.
Now lets take a look at how this same functionality would look in Hyperscala. Like I’ve said several times now, there are many ways to accomplish the same task in Hyperscala and this is just one way.
First lets look at the HTML, since this is what we really should start developing from. We can focus on getting the design exactly how we want it before we even think about Hyperscala:
play_hello_world.html
This is our basic HTML for the page. Notice there is absolutely nothing specific to Hyperscala or non-HTML in the file at all.
play_hello_world_configuration.html
I’ve extracted the ‘configure’ page out into this snippet because we’re not only going to replicate Play’s Hello World example, but we’re going to leverage the Realtime module of Hyperscala to avoid doing a POST and thus keep you on the same page for the entire experience. I could have replicated the form posting utilizing FormSupport mixin, but hopefully this will work as a good example of how easy real-time communication is in Hyperscala. Again, notice that there’s nothing special about this HTML. It can easily be previewed in the browser and edited by a designer.
Now, lets take a look at our Scala code:
PlayHelloWorldPage.scala PlayHelloWorldConfiguration
Not much to see that hasn’t already been explained in past examples but there are a few things of note. First, notice the call to Realtime.connectStandard(). This, as the scaladocs says, connects all inputs, textareas, and selects to fire change events and buttons to fire click events to the server. We can do this manually, but this saves us a few lines of code. The other thing of note here is the adding of validation. The ‘addValidation’ method is received as an implicit conversion on FormFields (input, textarea, and select) by importing org.hyperscala.ui.validation._. We utilize some built-in validations and use the ClassValidationsHandler to apply the error class to the outer container and set the message to the error container when validation fails on a field. Like I said before, how validation works in Hyperscala is not built-in, it’s part of the UI sub-project and defines some convenience functionality of how error might be handled, but opens the door to supporting validation errors any way you see fit.
In conclusion, I would argue that while in this example the lines of code may be equivalent, the benefits should be seen particularly for larger applications and for interaction with designers. I do not want this to come off as me bashing Play Framework. I have nothing but respect for the framework and the developers that created and use it. My purpose in this post is merely to compare and contrast the architectural choices in one framework against another and to hopefully better explain why I believe Hyperscala to be a better framework in many situations.
Source code referenced for the Play Hello World example came from Play samples on github:
Source code referenced for the Hyperscala comparison came from Hyperscala examples on github:
- https://github.com/darkfrog26/hyperscala/blob/master/examples/src/main/scala/org/hyperscala/examples/comparison/PlayHelloWorldPage.scala
- https://github.com/darkfrog26/hyperscala/blob/master/examples/src/main/resources/play_hello_world.html
- https://github.com/darkfrog26/hyperscala/blob/master/examples/src/main/resources/play_hello_world_configuration.html
Could not agree with you more.
Templating engines that resemble HTML but are not "pure" HTML are a nasty mixture that in my opinion are unusable both for a web-designer and a developer. Such templates fail in both fronts:
– HTML-aware tools (the ones from Adobe that web-designers use) no longer recognize the special syntax
– the developer gets something that tries to be powerful, but not powerful enough as a real programming language
If some "dynamic" manipulation/generation of HTML will take place, let this happen in the programming-language side.
If some HTML editing will take place, let HTML/CSS be left untouched. Designers should produce pages to be used as "themes", and the programmer should mix and match those components.
I also recognized this to be "the way to go" about templating, and now that I am starting with Scala, I'm glad somebody else took the initiative to implement something like this.
What I was going to ask you is why not limit yourself to code generation that can be used in existing fameworks (Play, Scalatra, etc.), but I think you more or less answered it in this post.
Hope to be using your framework soon!
Best regards,
Sebastian
@Sebastian, I appreciate your kind words. If you are just starting with Scala you have a world of amazing new ideas and power in your future. Please feel free to give additional feedback once you've had a chance to use Hyperscala. I always appreciate constructive criticism.
IMO, it is not just Play that falls under the problems described in "Templating Languages" and "Modularity", but all classic push-based MVC webframeworks. To name a few others: Grails, Rails, Spring MVC, ASP.net MVC, Symfony, CakePHP etc.
A framework that might be interesting to look at (for example for ideas) is Wicket. It's a highly object oriented framework that (like HyperScala) has no logic in the templates, their HTML tags are clean except that they use a "wicket:id" attribute for tags instead of "id". In Wicket, for every request, a Page object is created, which then initializes it's containing components (such as labels, textfields, buttons, iterating elements) and maps these components to the templates tags using the wicket:id. It's a matured and well thought framework, but it has some shortcomings: the session sizes are very large because: it is designed to be stateful using sessions, every page version and page version caused by a modification of a component is stored within the session. When you try to create a sessionless website in Wicket with some ajax features, it feels like your hacking against the principles of Wicket frameworks; it takes a lof of effort.
Where I think that Hyperscala excels is 1) easier to perform operations on the template 2) nice Scal api 3) stateless by default? 4) no large sessions?
I have placed questionmarks at point 3 and 4 because I haven't look that good at Hyperscala yet. (I need/going to learn Scala.)
@jtict, the reason you are seeing some similarities between Hyperscala and Wicket is because I used to use Wicket and a lot of my ideas for how Hyperscala should interact with HTML were based on Wicket. However, I think Wicket does have some pretty large flaws: 1.) It requires those wicket:id attributes that either you or your designers have to integrate, 2.) It requires a 1-to-1 binding between the HTML and the code. If any one part changes both sides have to change to support it. In Hyperscala I tried to make the necessity as loose as possible to allow developers to accomplish what they want.
In response to your questions:
3.) Yes and no…when you set up your resource you define the Scope. If you use Scope.Request it is stateless and the page renders and then is available for GC. If you use Page, Session, or Application then your pages are stored in memory. However, the length of time for pages to exist in memory is configurable and if you leverage Realtime then your pages can be discarded from memory almost immediately when they leave the page. 4.) Sessions in Hyperscala are highly customizable. By default they are stored in memory backed by a Map. However, there is nothing keeping you from writing an implementation that stores it in a database, filesystem, or any other medium.
While I agree with the reasoning you gave about Play, it was hard for me to follow the hyperscala example more than play's original code (i know neither). Seems like a lot of wiring code that could use a bit more structure.
One idea, maybe, is to do something similar to Android's way of using ids in markup: integrate with the build process so that a constants file is auto generated for the ids found in the markup. Maybe go further and generate Scala code that reflects the HTML markup, with variables for the elements that can then be manipulated directly. Maybe the Scala code can be thought of extending the markup code, overriding only those parts that need to be modified.
Finally, how is non HTML code handled? What if I want to format a json reply or an email message?
@Ittay, adding tighter coupling would create some problems with the methodology. The idea when coupling existing HTML with Hyperscala via DynamicContent is to be able to use only the ids I care about and ignore the rest. When I call load[tag.Input](id) I'm actually loading that block of HTML and it doesn't get parsed until then. This allows me to represent the unused portions as static blocks of text and allows it to be processed significantly faster.
Doing a JSON reply is somewhat out of scope for Hyperscala, but because Hyperscala provides its own network layer it is a simple matter of providing a custom RequestHandler or just extending from Service if you want something a bit more managed.
As for creating an email that's fairly trivial as it's likely just HTML as well. In that case you can follow exactly how any HTML page is created.
The DynamicContent has actually been simplified since this post was made, so I need to come back through and clean up a bit. If you have any suggestions for making the Hyperscala simpler I'm always open to suggestions. My goal is to make the framework highly intuitive while not hindering flexibility.
I suggested 2 thinngs:
1. generating constants for ids, so the code will not be filled with brittle strings.
2. generating methods, so instead of `main.load[tag.Input]("submit")` the call would be `submit`. The methodwill be just as lazy (calling load).
I think supporting generation of content other than HTML is important today, when rich client side requests just the raw data or using the product as a service. Without this, hyperscala is only a partial solution. IMHO, it should provide better separation between the generic part of loading/storing data from the HTTP request and the rendering of data to HTML/json/whatever
@Ittay,
1.) This is supported already either by adding those ids to the HTML yourself, or by telling Hyperscala to autogenerate ids for all tags (Realtime module uses this to communicate between client and server and reference tags specifically).
2.) This is already supported as well via the HTML2Scala generator. What it does is allows you to load an HTML file and it will convert it to Scala source code. This allows you to perfectly represent the HTML in Scala code. If you're interested in doing more with this I can give you some examples of how to call it.
Hyperscala allows you to work with any type of content fairly easily, but the focus of Hyperscala is primarily around the base of markup. I can imagine many extensions to this, but the core of Hyperscala will likely never implement this functionality directly. If you're interested in providing more specialized support I'm sure it would be very well received by other users.