Skip to content

Instantly share code, notes, and snippets.

@milessabin
Created February 17, 2013 21:59
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save milessabin/4973733 to your computer and use it in GitHub Desktop.
Save milessabin/4973733 to your computer and use it in GitHub Desktop.
Lazy pattern matching in Scala.
Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_05).
Type in expressions to have them evaluated.
Type :help for more information.
scala> def foo = { println("foo"); "foo" }
foo: String
scala> def bar = { println("bar"); "bar" }
bar: String
scala> def baz = { println("baz"); "baz" }
baz: String
scala> lazy val List(a, b, c) = List(foo, bar, baz)
a: String = <lazy>
b: String = <lazy>
c: String = <lazy>
scala> a
foo
bar
baz
res0: String = foo
scala> b
res1: String = bar
scala> c
res2: String = baz
@alissapajer
Copy link

Not what I would have expected! a, b, and c are tied together in an unfortunate way. Why must the first call to a essentially make b and c no longer lazy? The entire List(a, b, c) is lazy, so by calling one value from it, the entire List (i.e. all its contents) are called?

@milessabin
Copy link
Author

I think it's doing what I would expect: Scala's Lists are strict, not lazy, so if you force the list you force the contents. You can get even more Haskellish behaviour if you use wrap the List elements in scalaz's Need. I'll post another gist with an example of that.

@mergeconflict
Copy link

The reason all three values are forced isn't because lists are strict, it's because tuples are. When you declare:

lazy val List(a, b, c) = List(foo, bar, baz)

The compiler translates it to something like:

lazy val anon$ = List(foo, bar, baz) match { case List(a, b, c) => (a, b, c) }
lazy val a = anon$._1
lazy val b = anon$._2
lazy val c = anon$._3

When a is forced, it forces anon$ which is a tuple (constructed on the right hand side of the synthetic case clause). Tuple fields are strict. This sort of feels like a language spec bug to me... It seems like you want the compiler to synthesize some sort of LazyTuple in cases like this instead.

@mergeconflict
Copy link

By the way, just to prove the issue isn't list strictness:

scala> lazy val Stream(a,b,c) = Stream.iterate(0, 3) { x => println("oh noes"); x + 1 }
a: Int = <lazy>
b: Int = <lazy>
c: Int = <lazy>

scala> a
oh noes
oh noes
res0: Int = 0

@mergeconflict
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment