Monday, November 26, 2012

Cool Monday: Functional compilers and atoms

I've seen this great talk by Daniel Spiewak on Functional Compilers. He talks about lexical and semantic analysis in particular.
First, problems with traditional lexing with scanner. You can only have regular tokens or you have do do some dirty hacking and backtrace the scanner therefore losing linearity. And you can solve this with scannerless parsing - putting regular expressions into your grammar. In fact this approach seems simpler to me, as the only proper parser I've done works this way. But this is not the interesting part.

Semantic analysis

This is where fun kicks in. After you parse the source code into an AST you need to do a bunch of operations on it. Naming, typing(even optimization in later phases). If I want to stay functional(which usually I do) my instinct tells me to do recursive traversal and rewrite the tree. And that's exactly what my language does. But there is one huge problem. AST is not a tree. It's huge misnomer. AST is just a spanning tree in the program graph. See, when you add stuff like let expressions, or types(what I'm doing currently) you get problems
let a = 1
in f a
There are edges between the siblings. Or going back up. Or skipping levels. Definitely not trees. These edges may be implicit but you still have to store the information. Traditional solution to this is to compute look-up tables(maps) and carry them along with the tree. So the AST remains a tree but it has some additional stuff that implicitly makes it into a graph. Problem is this gets nasty when you carry along a lot of information and you have to be careful with you updates.
There is one more solution. Vars. Works like a charm. Except that it's terrible to reason about quite the opposite of functional. But there exists a fix.

Atoms

Think write-once vars. But not quite. The idea is to have containers that can be written to but are only ever seen in a one state. Problem with vars is that they can be seen  in multiple states and you have to keep track of these states. Vals solve this by not letting you mutate state. And lazy vals provide machinery to delay initialization(great for solving circular dependencies). But they don't let you escape the scope. Or deal with a situation when you need to init them when you have data not when you need to read them. And this is the problem in a compiler. You compute data coming from out of the scope and you need to store it. And some time later you need to read it. And you use atoms.  First some code, then explanation.
class Atom[A] {
  private var value: A = _
  private var isSet = false
  private var isForced = false
    
  protected def populate(){
    sys.error("cannont self-populate atom")
  }
    
  def update(a: A) {
    if (!isSet || !isForced){
      value = a
      isSet = true
    }
  }
    
  def apply(): A {
    isForced = true
       
    if (isSet) {
      value
    } else {
      populate()
            
      if (!isSet) {
        sys.error("value not set")
      }
      value
    }
  }   
}
Here is the workflow... you create an atom, you can write(update) to it-in fact writes are indempotent and you can do many successive writes as long as you don't read the value. Once written to isSet flag is set and atom can be read(apply method) setting the isForced flag. If the atom isn't set when you try to read it it will try to populate itself. Populate method is intended to be overwritten and may contain data in it's closure or even perform some side-effects. And you can safely assume it will only execute once. And if everything fails and atom isn't set you get an error. Yay no bothering with nulls any more.
You can quickly see how atoms are great containers for storing computed information in the AST for passing it on to the later stages.
Enhanced by Zemanta

Friday, November 23, 2012

Nomadic programming

DISCLAIMER: This is about my opinion. And may or may not contain some boasting. And is also a bit of a brain dump.

Yep, this is not a typo. Not monadic but nomadic. Although monads are cool too.
A few days ago the CTO of a company I work for said it's time to specialize. He was talking about my career. Offering me a few things to try and then pick one. But this got me thinking.
Today another CTO told me that I'm "essentially a good dev" and that chosen software stack doesn't matter. And I agree. (Patrick does too).
20 years later all this fits in your pocket
I'm an engineer not a programmer(or god forbid, a coder). I solve problems not just write code. It just happens that code is solution to most problems I try to solve. World is increasingly more computer orientated. Now everything(literally!) has a processor inside. But processors are dumb. So you need people to program them. These people are the equivalent of the factory workers before robots started taking over. So now people need to migrate one level up on the abstraction ladder. Labor is now mental. Not everything is the same(like in a traditional factory) but it is the same concept. And you are pumping out slightly different versions.  Welcome to a modern software shop.
But all this "computerization" also pushes up the upper bound of complexity. And this is where engineers kick in. If you have complex solution you require (hopefully less) complex solutions. And you need people who can manage this complexion. Not by being machines but by understanding, inventing concepts and structuring them. Big software has millions if not billions of moving parts. Imagine a machine of such complexity. There probably isn't a human out there who could manage this in it's entirety. But software engineers are expected to do this. You need the ability to see something and relate it to known concepts or invent a new concept and relate it to oter stuff. Connect things together. And to do this well you need broad knowledge. Yes you need specialization - depth - to do something very well but this is a lesser problem. See a good engineer can go from zero to awesome in a new field very quickly. Learning a new language can be done in a week and a software stack in a bit more. I'm not saying you become an expert or the very best in the field but if you have the urge to learn you are not far behind. So a good engineer is the one who has the ability to learn and to produce real solutions with this knowledge.


Nomads

So where does nomadic programming fit into this philosophy? Going from language to language, from framework to framwork and changing software stacks. Not settling down. This (in my opinion) teaches you what can be done and haw can stuff be done. In all possible ways. So you can figure out the best solution. Learning all the time also keeps you on the bleeding edge and this is fun and engaging. Having new toys all the time! I can't image myself working with just one thing for the rest of my life. Meeting new languages and new framework is what I do in my free time. So when a customer says they need Ruby on Rails and I never used it before....it's not a problem. I've seen ruby, I know python and I know how MVC is supposed to work. Putting it all together is not that hard. You can even hit the ground running and start working right away. Of course you'll be a bit slower in the start but you can catch up.  So no, I don't want to specialize. I want to know everything that is there to know! And I believe this will make me a better engineer. Or developer. Or person. And I believe that quality of their employees shoud matter to the employer. It is developers who create their products. 

You should always strive to become better!
Enhanced by Zemanta

Monday, November 19, 2012

Cool Monday: CERC and my trip to Krakow


This week's post is a bit different. Well very different. My team qualified for CERC ICPC(european programming contest) and that means a trip to Krakow, Poland. I plan on writing this in a journal style. A bit every day.

Friday, 11. am

I got up at 6. to check news and read a bit before departing. And of course to charge my laptop and phone to 100% batter for the long trip. Then a 10 minute walk across Ljubljana(quite cold outside) and bus left at 7.15am. Yes, we're riding a bus. And we will be riding this bus for quite some time. Approximately 12 hours total! I guess it would be cheaper to buy plane tickets for low end jet-line than renting a bus(and two drivers) for whole weekend. Not to mention faster. Twelve hours!!
Anyway, first stop is Maribor to pick up some more teams and eat sandwiches. By the time we left Slovenia it was already 10 am. I honestly didn't know it takes that much to get from the country.
And it's kinda boring here on bus. Since we're in a foreign country I'm in a roaming network and data can get pricey. So no internets for me. At least the hotel we're staying in should have wifi. But I've written a blogpost anyway and wil zemanta-fy and post it when we get there. This is my second writing today.
Life gets boring here. I've talked to some interesting people..but then everybody got to do something. Bus has a DVD player, but it only plays DVDs, no avi or and other format. And of course the only thing we've got was twenty-something Poirot moveis. Not my thing. Somebody bought some empty DVD at the gas station(they have those there!!) and we've had an adventure trying to burn something. Success. But approximately two guys at the front are watching Poirot currently so we'll have to wait to see if it had in fact worked.
Oh yeah, and I can't read(or program for that matter) on a bus because reading vibrating text makes me nauseas. It's funny how I'm writing this. Laptop in my lap(that's why it's called a laptop!), screen brightness to minimum, typing and looking through the windows and around the bus. I occasionally glance at screen to correct the mistakes I've made.
I surely hope something happens because I don't know if I can stand that much boredom.

Saturday, 1. am


Slovakian border
We stopped in Slovakia for lunch. In a restaurant that was something like 20m from the highway. They only had three dishes and everything was mediocre. Reading signs was a mood lifter. Slovenian and Slovakian language are quite close, but not the same. So “out of order sign” on the toilet read as “don't eat”. Not to mention that Slovakian word for soup is a curse word in Finnish.
And back to the bus. I've grown to like Poirot. Apart from his ego, he's quite a likable character. I'd probably die of boredom if I wouldn't watch the movies. And then was fog. Lots of it. And there was the end of the highway... And we still had nearly 100km to go. All in all the trip was 14 hours long.
Food in the fancy restaurant
Hotel is real nice! Four stars and solid internet connection in rooms(that's what matters!). I got lucky and got a room of my own, while others are sharing rooms for two. Or is that bad for me?
Anyway..I check my emails, talk to my girlfriend, give up on amount of unprocessed info on social networks and go hang out with new friends. And because we're programmers hanging out means going to dinner in a fancy restaurant at midnight and drinking tea. Poland is cheap! The restaurant was really nice but I don't believe anyone paid more than 5€(converted from their currency). 
Only problem was that our hotel is something like a 35 minute walk from city center. And we had zero idea how to use public transport. And it's quite cold outside. But tea warmed us up. And time passed by quickly because conversations were interesting. I like hanging out with smart guys.

Saturday, 11. pm

My wannabe English breakfast
Today was way better. Breakfast was ok. Apart from weird meat-with-marmalade thingy that I ate accidentally. Then a trip to university where actual competition will take place. I couldn't remember it's name if my life depended on it. Starts with J. We got here at 10pm so we missed registrations on Friday so we had to do late registration today. Got some prospects and an ugly t-shirt. Opening ceremony..blablabla...and a nice lecture on cryptology. Prezi and live demos. And a good delivery. +1 for that polish student. Now I finally understand elliptic curve cryptography.

And then an organization epic fail. They mis-planed for an hour and a half. So a quick walk to hotel was in order...because we didn't have wifi at the university. Another fail.
And another with lunch. They ran out of food(that wasn't even that good) and even drinks. Kinda disappointed here.
the university
Then the practice session. Apparently the polish take security very seriously. We had to show ids to enter. And weren't even allowed snacks in the computer room. Nothing special about the problems. We've solved some and ranked somewhere in the middle. Though it may be that this problems were easy compared with what's coming up, since a few teams solved all of them pretty much in matter of minutes.
But that's not the interesting part of the day. The interesting part was seeing Krakow. This is weird coming from my mouth since I'm not much of a travel person. But I truly enjoyed seeing a different culture. Although it would be much better if it weren't for dense fog that obscures anything more than 50m away. It destroys pictures too.
And polish cuisine isn't half bad too. We visited a restaurant with traditional polish food in city centre. Had some laughs about dish names and talking to the waitresses. Our coaches are some real funny(dare I say special) guys. Two mathematicians and a sysadmin(not sure??) with a thing for Slavic languages. I surely hope for more adventures.

Sunday, 20. pm

Well things started early today. Woke up at seven to pack stuff and have breakfast before checking out at 8am and leaving for the university. And I still can't remember it's name. I tried, I really tried.
Krakow at night. In fog.
There was a brief introduction with few announcements and at 9.30 the party started. It was some intense five hours. There were 11 problems and winners solved 10 of them. We've got correct results on 2 and ranked 56 among 77 teams. But I get my piece from knowing we've got correct ideas for two more and just got a few bugs in the code and nearly got the right approach for one more. But we've failed miserably at the last problem we solved. Oh yeah, in case you don't know at CERC you get a problem, write a program and then the test system compiles it and runs tests. If it passes all of them you get a point. Else you get nothing. So that was fun. I need more practice.
Then a weird spot in the schedule. Two hours of free time instead out food there wasn't anything special. Yes food is that important to me.
Colleague walking around with his ballons
There was a lecture on problem solutions afterwards...and the hardest problems were really puzzling. Especially the one nobody solved. I didn't get any of that. It would probably help if I've read the problem statement.
Oh yeah, the really cool part is you get a helium balloon attached to your table when you solve a problem. So you can see how everybody is progressing. Like you need that when you have online real-time ranking...but it's fun having balloons.
The ceremony went on forever. They shown picture of half the teams and invited about 20 of them on stage. Banquet after was more of a failure too. More chatting and slovenian teams boarded the bus. And now' we're driving home. It's currently 8.34 an we've got something like 12 hours more to drive. Apparently we'll watch some movies, but it's gonna be one long ride. Not to mention sleeping on a bus leaves you in a much worse state. Neck pain, back pain, you-part-you-didn't-even-know-can-hur pain... Yay. CERC is in Krakow next year too, and Slovenia will probably be going with a bus again. But I still wanna qualify. Because despite all the inconveniences it means hanging out with cool guys, seeing a nice city, solving interesting problems and competing with some of the smartest people in my generation(there are many world gold medalists from central Europe). Now I just need to practice. And perhaps get a mentorship.

Enhanced by Zemanta

Saturday, November 17, 2012

Why Linux will never win the desktop


Don't get me wrong, I'm a huge linux fan. I use it on all my machines(even my phone, Android is basically linux) and even converted my sister and a few friends. But if you don't convert somebody, chance they'll try a new operating system is slim to none. Only geeks like me install funky stuff as their primary software just to try it.
But the major problem lies somewhere else. People hate making choices. And linux is all about choice. It gives you power and you choose what you'll do with it. And it starts with selecting selecting distribution.

You don't install linux. You install a distribution. You install Debian, Ubuntu, Slackware, Fedora... There's a sea of different versions what to choose! I would even argue that all this fragmentation hurts development. Sure it's great to have different implementations and approaches and let time what's the best. But there is also quite a lot of duplicate work being done. And this cost's time and money that could be put to better use.
Back to choices. I like choice. I have a problem and need a piece of software(or hardware) and spend a few hours(or days) browsing the web looking at pros and cons, reading reviews, testing stuff, or just surfing the ebay and hunt for good prices. I like to do this for fun. But I hate doing it when I need to get something done. And most people just need software to get stuff done. They don't care about open source spirit or ability to hack around...they just need something that solves their problem. And when you have a solution, why change? Why fix something that isn't broken?

I attended a great talk on future of web search(by @dusano) and somebody addressed this problem...”Why fix search if it ain't broken?”. Answer was along the lines of “Because if you don't somebody else will. And he will dominate the industry”. I'm not quite sure this is so true in the short run. It's like with promotions in the supermarket. People are used to buying one brand of product and consciously selecting another is equivalent to mental work. And people are lazy. So they'll avoid thinking about possibilities because frankly they don't care enough about value/price ratio of their coffee to invest their time into it.

And it's similar when it comes to software and computers in general. A colleague once said that people who don't understand how computers work shouldn't be allowed to use them(he's linux sysadmin), but I strongly disagree. People want to use computers as tools and they should be able to. Not everybody is a developer! And that's why MacOS is popular. See Windows is here from the dawn of time. At least from youngster like me who used computer for the first time in this millenia.
MacOS might be a BSD but it's only one and it's locked down. It doesn't give you options but everything works. Perfect for most people.

Luckily things are changing for the better. Ubuntu broke the “easy linux” taboo and make things friendlier. And on my laptop I have Linux Mint which is even better. Installation is super easy and everything works out of the box. And I'm writing this post because a friend posted a video about ElementaryOS which is another Ubuntu fork that looks very much Mac-esque.

So things like Ubuntu, Mint or Elementary might have a fighting chance on the desktop if they try real hard. But most of the converted users won't know they're actually running linux under the hood. Anyway I should stop blabbering about desktop and focus on post-pc era. But that's a whole other story.

Enhanced by Zemanta

Monday, November 12, 2012

Cool Monday: Scala Macros

Garden flower
Macro shot (Photo credit: Wikipedia)
For me the highlight of this week was discovering Bootstrap. I heard of it before but never looked into it. Probably because I wasn't doing web stuff. The thing is bloody awesome. Back on topic.

Scala 2.10 RC2 was released this Friday. Considering 2.9 had 3 RC releases, 2.10 final is probably quite near. And it brings some awesome features. One of them are macros

Macros

So what are macros basically? Code executed at compile time. And that's about it. 
So what is so great about that? Well you can do AST modifications and stuff that gets quite into compiler-plugin territory in your regular code. That means you can do pretty advanced stuff and do abstraction with performance. Oh yeah, you can also do type checking and emit compile-time errors. Safety first kids!

Usages

SLICK uses macros(in the experimental API) to transform scala expressions into SQL. At compile time! ScalaMock uses it to provide more natural API for testing. As said, you can use it for code generation or validation at compile time. Good library design will be able to minimize boilerplate code even further now. And some people will argue that macros make scala even harder language.

Type Macros

This is the best part for me. But unfortuntely it's not implemented yet. Or at least not dcumented. There are some methods with suspisious names in the API but no useful documentation. In all presentations this is referred to as "future work" but I still have my fingers crossed it makes it into final release.
So what's the fuss? Automatically generated types. Large scale code-gen. 
As in ability to programatically create types at compile time. As a consequence you can create whole classes with bunch of methods. And I already have a use case of my own. I want to make a typesafe ORM for Android that's super fast. I did YodaLib ORM while back. It uses reflection(although it's fast enough usually) and provides a Cursor that lazily wraps rows into classes. And you need to make sure by hand that your class coresponds to columns of your result set. Not very safe. I had an idea to make static-typed safe inferface for the database when I first heard about HList. You would do projection as a HList and result rows would be lazily wrapped into HLists. But using them for wrapping every row(possibly filling data in with reflection) would be a performance penalty. Not to mention a mess to implement. Now consider using a macro to generate code for wrapping. It would be no slower than accessing columns by hand. And a type macro would automatically create a case class for given projection. Heavens. I'm just waiting for official documentation on macros...this is a tempting project.
(oh yeah, it would manage your scema too, so you don't need to worry about consistency between your code and your schema)

Documentation

Here's scalamacros.org which gives some information. Also some quite useful slides. I hope now that 2.10 is in RC things stabilize, because in the milestone releases api was changing constantly. Nightly API scaladoc.... Proper documentation is apparently coming soon,

Le Code

A use case for macros, loop unrolling.
Below is a trivial sample of repetitive code.
class Manual{
    def show(n: Int){
        println("number "+n)
    }
    
    def code(){
        show(1)
        show(2)
        show(3)
        show(4)
        show(5)
    }
}
We can deal with repetition writing a loop. (Higher order function really)
for( i <- 1 to 5 ) show(i)
But this doesnt generate the same AST(and bytecode)!
Protip: use
scalac -Xprint:parser -Yshow-trees Manual.scala
to see AST after parsing.
Sometimes(rarely!) you want to unroll the loop to produce same byte code as typing all the iterations by hand.
Macros.unroll(1,5,1)(show)
With a proper unroll macro defined. I spent an hour to come up with this implementation...and then scalac started crashing on me... Is there something terrible in my code?
I gave up and went on to do useful stuff...But macros hear me! I'll be back.
import reflect.macros.{Context}
import scala.language.experimental.macros

object Macros {
  def unroll(start: Int, end: Int, step: Int)(body: Int => Unit) = 
    macro unrollImpl

  def unrollImpl(c: Context)(start: c.Expr[Int] ,end: c.Expr[Int], step: c.Expr[Int])(body: c.Expr[Int => Unit]): c.Expr[Any] = {
    import c.universe._
    val Literal(Constant(start_value: Int)) = start.tree
    val Literal(Constant(end_value: Int)) = end.tree
    val Literal(Constant(step_value: Int)) = step.tree

    val invocations = Range(start_value, end_value, step_value) map { n =>
      val n_exp = c.Expr(Literal(Constant(n)))
      reify{
        ((body.splice)(n_exp.splice))
      }.tree
    }
    c.Expr(Block(invocations:_*))
  }
}
Enhanced by Zemanta

Monday, November 5, 2012

Cool Monday: Hindley-Milner on a dynamic language

So I'm getting into type theory. Slowly. Note to self: read a proper book on this topic. I'm getting familiar with it through some practical applications. Namely scala and haskell. 
That same discussion about design patterns also included dynamic vs static typing. And I asked twitter about it. HairyFotr link this amazing talk about type inference to me. Basically there are two conclusions to be drawn
  • Every static typed language should have at least limited type inference. It's compiler's job to do so and quite trivial to implement.
  • Properly done static typed language provides all features the that dynamic typed languages can. Safely.
As I'm (still) implementing a language that happens to be dynamic(because I was too lazy to look-up how to do type checking) second point interests me more. 

Can I turn my language into a static one without changing syntax(adding type annotations) and losing features? That would be awesome!
After a day of thinking, answer seems to be YES!

Global type inference in a nutshell

So I want a dynamic-like syntax(no types anywhere). Good news is I don't have nominal types, so I can use inference to get structural types. 
Java, C# and many other mainstream languages use nominal typing at the level of the vm. This means that type A is a subtype of type B precisely when name of A is a subtype of name ob B. For example 
interface Foo{
    void method(int a);
}


interface Bar{
    void method(int b);
}
If you have a method that takes in an instance of Foo, you cannot pass an instance of Bar. Because Bar isn't subtype of Foo. Even though they are structurally the same. Fun fact: because JVM is designed like this, scala cannot have global type inference.
See, global inference(or "type reconstruction") looks at usages and reconstructs properties and structure.  Another example
a = b.c + 1
That would be legal scrat code(or python, ruby or many other things). You take b's property named c, add one to it and assign the result to a. So b must have property c. That's the first structural requirement. From the + operator you can see that this c must be numeric. And from this follows that a is also numeric as it's the result of this computation.
If this were a body of a method and b it's parameter, type requirement for be would be(in made up syntax) b: { c: Number } - object with member of Number type. But that doesn't give you no class names.
So why is this global inference? It goes through the whole block of code(usually a function body) and puts in stub types where it doesn't have enough info and then solves the system of requirements and substitutes back.

Possible problems

First of all, I have objects. This means I have to reconstruct object structure. This shouldn't be too bad. And you can quite easily figure out that A is subtype of B if set of requirements for A is a superset of requirements for B. 
Then there's "mutable types". I concluded that following code should be illegal
a = 1
a = "two"
as type of a should remain the same as in first assignment. This lets you reason about the code much more. But there's a hidden mutability. I use regular functions as object constructors returning a keyword "this" that evaluates to current scope. But throughout the body you still access this scope and it's type(it's an object after all) changes with every new (first) assignment and function definition. But this should be tracked through all possible code paths. Only problem is an if expression. Type of an if(and it's side-effects) can only be common super type of both then and else branch - an intersection of requirement sets. In a dynamic language you can reason about conditions and conclude when something should definitely be in scope and use it. Automatic reasoning about conditions? This could turn out tricky. Perhaps in later implementation.
An there's third an final problem(that I can see). Infinite types. I have not seen a practical usage but it doesn't work and that bothers me. Dynamic code in scrat just works, but same code translated to haskell yields a compiler error - "can't instantiate infinite type". But apparently infinite types can be detected, so maybe I can find a way to present them and it will compile. More about this soon.

Conclusion

There are some problems but I believe I can make it work. It would be super awesome to have a language that feels dynamic but gives you all benefits of static typing. Compilers should do the hard work after all! And they should be capable of inferring general enough types that all correct programs type check.
Or am I missing some important aspect that works only with dynamic types?

Otherwise...code coming soon.
Enhanced by Zemanta

Friday, November 2, 2012

Hunt for a web framework that works

Never Internet Explorer
Never Internet Explorer (Photo credit: Wikipedia)
I have this personal project I want to do that includes a web application and I want to learn something. So I'm on the hunt for language, environment and framework.

Other stuff

I did some PHP a few years back and definitely don't want to go there anymore. I also did some .NET and it's even part of curriculum here at FRI. But clicking on wizards in Visual Studio feels weird to me. Not like development should be done. And I also use GNU/Linux as my primary(and only) OS, so that's out of the water. I did read about java server pages and faces and even tried few things out. But luckily I didn't get to do this project I was preparing for and I didn't need it. It looked ugly anyway. I did some flirting with GWT, does that even count as a web framework? 

Node.js

I heard about nodejs quite some time ago but I put off looking into it because my js was really rusty. But recently I brushed up on my javascript skills(to do a "compiler" into js) and gave it a shot. Node is good. It's fast, it's agile, it makes you think in a different way. I was feeling empowered. I did some simple stuff and I liked it.

Static vs dynamic

Later I kinda got a job as Ruby on Rails dev. And I hated it. So I didn't take it. It would take up too much time anyway - I'm a student. Ruby is okay. Rails is okay. But problem was the size of the problem. Application we were buiding(a team of devs) was quite complex and I came into existing(moderate size) ruby code base. Learning ruby and rails as I go was fun, but navigating the project was pain in the ass. Of course documentation was non existent and IDE couldn't help me because it didn't know. So a lot of regex searching and walking around asking stuff. Also refactoring...Inevitable but hard. 
This cemented my opinion on static vs dynamic typing. (Static for everything but a short script, more on that another time).

Scala

Then I learned about the good parts of static typing through scala and haskell. Doing web in haskell seems a bit intimidating(I will give it a go eventually, I promise) so I roll with scala. I looks there are two big names here. Play! and Lift. I watched a few talks and read few blogs about both to see central points. 
Big difference seems to be their view on state. Lift goes for stateful, Play for stateless. Play kinda seems like it has a bigger community, but their documentation is stellar and they're now part of Typesafe stack. No brainer then. Play it is.

Play! framework

I dived into documentation. Reading samples and explanation about infrastructure and APIs. Samples really clicked with me - it felt like porn. No analogy, reading elegant scala sources for a web app for my first time felt like I was doing something naughty, like things shouldn't be that good.
Live reloading is great too. A friend of mine is a J2EE dev and he's constantly nagging about build and deploy times. I get that near instantaneous. And compile time checking of routes and templates? Oh my god, yes. Bear in mind, compile time is all the time. When I hit ctrl-s for "save all open files" I quickly see if compiler has any complaints, even before I refresh the browser. 
I just did some experimenting with features then...for a few hours. Everything feels so simple but powerful. Why nobody told me about this before?
Okay, it has to have some weaknesses but I didn't find them. Yet. And that's what counts.

Heroku

Now this is just a cherry for the top of my cake. It took me two minutes to deploy my hello world app, and that includes time needed to install Heroku's tookkit. You just create an app and push to remote git repo. Heroku detects it's a Play/Scala app and install dependencies. Rest is done by SBT. And it just works. Hassle free deployment for developers. Yay.

Now I have my stack and even a host. So I just need to write an awesome service and generate traffic. How hard can it be?
Enhanced by Zemanta