« Final Mercurial... | Main | Scala Puzzlers, part... »

Scala puzzlers, part 1

Jul 18 2008, 04:17 MSD |  [  Scala  ]

I participated in the ICFP Contest this year. We decided to use Scala, and one of the reasons for using it was that we wanted to try the language out in a real-world stressful development project. There were four of us, and at least two of us (including me) had some experience with Scala. I'd been playing with the language for quite a while, and had a fair understanding of how to write programs in it. I'll probably post some write-up about ICFPC later.

During the contest we hit some trouble spots with the language, and lack of experience played some cruel tricks on us. This post is partly inspired by this experience. So, here we go, Scala puzzlers!

Puzzler #0: Weak typing

Consider the following code:

sealed abstract case class Msg
case class SaveTheWorld(from: String) extends Msg
case class FeedTheCat(name: String) extends Msg
case class GoToSleep extends Msg

object Thespian extends Actor {
  def act = {
    loop {
      react {
        case SaveTheWorld(threat) => println("Saving the world from the " + threat)
        case FeedTheCat(cat) => println("Feeding " + cat)
        case GoToSleep() => println("Zzzzz...")
        case Die(msg) => prinln(msg); exit 
      }
    }
  }
}

object WeakTyping extends Application {
  Thespian.start

  Thespian ! FeedTheCat("Panther")
  Thespian ! SaveTheWorld("Jabberwock")
  Thespian ! GoToSleep

  Thespian ! Die("The fair Ophelia! Nymph, in thy orisons be all my sins remember'd.")
}
Everything looks pretty simple, however there's a line missing in the output:
Feeding Panther
Saving the world from the Jabberwock
The fair Ophelia! Nymph, in thy orisons be all my sins remember'd.

The reason for this is that GoToSleep is not an object of type Msg, it is a function, which returns an instance of the case class GoToSleep when it is executed. You're not doing anything illegal when you're sending a function to the actor, so the compiler isn't going to stop you. And since parentheses are optional in a 0-argument method call, it's easy to forget to put the parentheses in case class instantiation.

There is another way to screw up: instead of matching on GoToSleep() one could try to pattern-match on GoToSleep in the react block.

A good way to solve this problem once and for all is to always use case objects instead of parameterless case classes. That way you won't need the parentheses and unless you have an apply method in your case object, compiler wouldn't allow you to use them.

Puzzler #1: val vs def

This one is quite obvious, but sometimes you can get caught on it during the refactoring.

def someEntity = new Entity(...)
val a = doSomething(someEntity)
val b = doSomethingElse(someEntity)

If doSomething and doSomethingElse expect to get the same object as an argument, you'd get a problem. It can be much more subtle if there is some method call instead of the explicit class instantiation in the first line. Again, it is easy to get confused because the parentheses are not required for 0-argument functions.


I have at least two more puzzlers and they are a little more involved. I'll try to publish them as soon as I get enough sleep (it's 4am already).

PS: If you know some Scala puzzlers, please send them to me, and I'll add them to the blog. Alternatively, publish them in your blog and be sure to send me the link!

Comments:

Never declare a case class without arguments. Instead, use a case object.

Posted by Tony Morris on July 18, 2008 at 12:54 PM MSD #

Thanks for the blog. The CaseClass behavior surprised me as well, because it is not the way things are generally handled in Scala. Scala will insert () parameters to functions, precisely in order to avoid traps like the one you fell into. Until before 2.7.0, case classes were treated the same as normal functions, because in effect a case class came with a compiler-generated factory method.

However, there were some modifications to the meaning of case classes last January for the 2.7.0 release. They now always come with a companion object instead of a factory method. This cleaned up some other things in the language, but caused the unintuitive behavior you were seeing. I did not know about the consequences until I read your blog. Now that I do, I think we'll need to put something in place to prevent it from happening.

-- Martin

Posted by Martin Odersky on July 18, 2008 at 01:26 PM MSD #

There are some other tricky cases with pattern matching. Matching on a lowercase variable may not do what you expect:

// "case foo" declares variable foo that shadows previous, outer definition:
x match {
case foo => "yes"
case _ => "no" /* Unreachable code */
}

// Uses existing variable foo:
x match {
case `foo` => "yes"
case _ => "no"
}

// Uses existing variable FOO:
x match {
case FOO => "yes"
case _ => "no"
}

Posted by Min Huang on July 19, 2008 at 01:53 AM MSD #

Post a Comment:
  • HTML Syntax: NOT allowed