This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Matching Regex (SI-5045) -- should we change semantics?

8 replies
dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.

In the context of SI-5045
(https://issues.scala-lang.org/browse/SI-5045), a question came up on
whether a backward incompatible change was acceptable. Here's a couple
of examples to illustrate the point.

Right now, when matching a regex, a full match is required, as if the
pattern was anchored. For example, the following snippet will print
shell script without comments or empty lines (roughly):

val emptyLine = """\s*(?:#.*)?""".r
val code = """([^#]*)(?:#.*)?""".r
def stripComments(lines: Seq[String]) = lines flatMap {
case emptyLine => None
case code(x) => Some(x)
}

If the first behavior was not present, then the first match would
always be true, as the pattern "emptyLine" matches an empty string.
Given the present behavior, that will only happen if the full line is
an empty string.

Lanny Ripple proposes that substring matches be allowed. Some reasons
for that are:

* That's how perl, sed, grep, awk, ruby, python, etc work when
"matching" a regex.
* That's implied in regex by the very existence of anchors.

and, most importantly,

* To enable full line matching, one has to write something like
".*(pattern).*", which imposes a heavy performance penalty.

The alternative to the last option is to match twice: first to get the
matching substring, and then to extract the subgroups of the pattern.

Changing the code to enable substring matches is trivial. We could
also add a @migration warning to Regex.unapplySeq, which would help
people migrate their code safely. And changing code that depends on
this behavior is also trivial. For example, the above can be changed
as follow:

val emptyLine = """^\s*(?:#.*)?$""".r // Anchors added here
val code = """^([^#]*)(?:#.*)?""".r // Anchor added here, though the
previous pattern makes it unnecessary
def stripComments(lines: Seq[String]) = lines flatMap {
case emptyLine => None
case code(x) => Some(x)
}

And, taking advantage of substring matches, the "code" regex could be
changed as follow, for a faster pattern match:

val code = """^([^#]*)""".r

I prefer to change semantics, but I'm not sure that is acceptable. If
@migration was available back in 2.7.x days, I'd certainly have
proposed that. Right now, I'm not sure. It is not a simple matter of
aesthetics: there are serious performance considerations as well.

Johannes Rudolph 2
Joined: 2010-02-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Matching Regex (SI-5045) -- should we change semantics?

Regardless of how regex work somewhere else, isn't it so that Scala's
pattern matches (the ones with `match` and `case`) always match on the
complete input? Insofar, I think it is just consequent that the same
is valid for Scala's regex matches. Following this argument, if you
want 'find first' semantics instead of 'match completely' you should
be explicit about that.

Johannes

On Tue, Oct 18, 2011 at 11:32 PM, Daniel Sobral wrote:
>  In the context of SI-5045
> (https://issues.scala-lang.org/browse/SI-5045), a question came up on
> whether a backward incompatible change was acceptable. Here's a couple
> of examples to illustrate the point.
>
> Right now, when matching a regex, a full match is required, as if the
> pattern was anchored. For example, the following snippet will print
> shell script without comments or empty lines (roughly):
>
> val emptyLine = """\s*(?:#.*)?""".r
> val code = """([^#]*)(?:#.*)?""".r
> def stripComments(lines: Seq[String]) = lines flatMap {
>    case emptyLine => None
>    case code(x)   => Some(x)
> }
>
> If the first behavior was not present, then the first match would
> always be true, as the pattern "emptyLine" matches an empty string.
> Given the present behavior, that will only happen if the full line is
> an empty string.
>
> Lanny Ripple proposes that substring matches be allowed. Some reasons
> for that are:
>
> * That's how perl, sed, grep, awk, ruby, python, etc work when
> "matching" a regex.
> * That's implied in regex by the very existence of anchors.
>
> and, most importantly,
>
> * To enable full line matching, one has to write something like
> ".*(pattern).*", which imposes a heavy performance penalty.
>
> The alternative to the last option is to match twice: first to get the
> matching substring, and then to extract the subgroups of the pattern.
>
> Changing the code to enable substring matches is trivial. We could
> also add a @migration warning to Regex.unapplySeq, which would help
> people migrate their code safely. And changing code that depends on
> this behavior is also trivial. For example, the above can be changed
> as follow:
>
> val emptyLine = """^\s*(?:#.*)?$""".r  // Anchors added here
> val code = """^([^#]*)(?:#.*)?""".r // Anchor added here, though the
> previous pattern makes it unnecessary
> def stripComments(lines: Seq[String]) = lines flatMap {
>    case emptyLine => None
>    case code(x)   => Some(x)
> }
>
> And, taking advantage of substring matches, the "code" regex could be
> changed as follow, for a faster pattern match:
>
> val code = """^([^#]*)""".r
>
> I prefer to change semantics, but I'm not sure that is acceptable. If
> @migration was available back in 2.7.x days, I'd certainly have
> proposed that. Right now, I'm not sure. It is not a simple matter of
> aesthetics: there are serious performance considerations as well.
>
> --
> Daniel C. Sobral
>
> I travel to the future all the time.
>

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Matching Regex (SI-5045) -- should we change semantics?


On Wed, Oct 19, 2011 at 11:26 AM, Johannes Rudolph <johannes [dot] rudolph [at] googlemail [dot] com> wrote:
Regardless of how regex work somewhere else, isn't it so that Scala's
pattern matches (the ones with `match` and `case`) always match on the
complete input? Insofar, I think it is just consequent that the same
is valid for Scala's regex matches. Following this argument, if you
want 'find first' semantics instead of 'match completely' you should
be explicit about that.

I am also getting very nervous about changing semantics. It's a migration nightmare that one should only consider if things are definitely broken the way they are, or if the change is necessary to enable something much more powerful. For instance, changing to 2.8 collections caused some small backwards incompatible behavior change, but everyone agreed that the benefits of the new collection library were worth it. And we managed carefully with migration warnings.

The change that's proposed is much more local. Pattern matching works like X but many other languages do Y instead. There is a way to do Y in Scala as well. So, I think we should leave it at that.

Cheers

 -- Martin

Lanny Ripple
Joined: 2011-01-14,
User offline. Last seen 1 year 15 weeks ago.
Re: Matching Regex (SI-5045) -- should we change semantics?

Hi Johannes,

When I submitted the RFE Daniel's advice was to do just that. I.e.,
call a method on a regex to turn off implicit anchoring. Something
like:

val Date = """(\d{4}-\d\d-\d\d)""".r.unanchor

"copyright 2011-09-29" match {
case Date(when) => Right(when)
case _ => Left("no match")
}

I wrote up a patch but I am bumping into the fact that `pattern` in
Regex is compiled at instance construction. There is no easy way to
use the already compiled Pattern (and compiling Patterns can be
expensive) in a class that overrides unapplySeq but still types as a
Regex. I used a lazy val for `pattern` but there was concern that
this would impact all current code even if .unanchor() wasn't called.
An easy alternative would be to implement a sub-class of Regex with an
unapplySeq which did not implicitly anchor and introduce an
alternate .r() on StringOps. Introducing a new method at that level
seemed more intrusive than trying to work at the Regex level.

So I think everyone's in agreement that explicitly requesting the
unanchored behavior for Regex is a way to go. The question is how to
get there.

-ljr

On Oct 19, 4:26 am, Johannes Rudolph
wrote:
> Regardless of how regex work somewhere else, isn't it so that Scala's
> pattern matches (the ones with `match` and `case`) always match on the
> complete input? Insofar, I think it is just consequent that the same
> is valid for Scala's regex matches. Following this argument, if you
> want 'find first' semantics instead of 'match completely' you should
> be explicit about that.
>
> Johannes
>
>
>
>
>
>
>
>
>
> On Tue, Oct 18, 2011 at 11:32 PM, Daniel Sobral wrote:
> >  In the context of SI-5045
> > (https://issues.scala-lang.org/browse/SI-5045), a question came up on
> > whether a backward incompatible change was acceptable. Here's a couple
> > of examples to illustrate the point.
>
> > Right now, when matching a regex, a full match is required, as if the
> > pattern was anchored. For example, the following snippet will print
> > shell script without comments or empty lines (roughly):
>
> > val emptyLine = """\s*(?:#.*)?""".r
> > val code = """([^#]*)(?:#.*)?""".r
> > def stripComments(lines: Seq[String]) = lines flatMap {
> >    case emptyLine => None
> >    case code(x)   => Some(x)
> > }
>
> > If the first behavior was not present, then the first match would
> > always be true, as the pattern "emptyLine" matches an empty string.
> > Given the present behavior, that will only happen if the full line is
> > an empty string.
>
> > Lanny Ripple proposes that substring matches be allowed. Some reasons
> > for that are:
>
> > * That's how perl, sed, grep, awk, ruby, python, etc work when
> > "matching" a regex.
> > * That's implied in regex by the very existence of anchors.
>
> > and, most importantly,
>
> > * To enable full line matching, one has to write something like
> > ".*(pattern).*", which imposes a heavy performance penalty.
>
> > The alternative to the last option is to match twice: first to get the
> > matching substring, and then to extract the subgroups of the pattern.
>
> > Changing the code to enable substring matches is trivial. We could
> > also add a @migration warning to Regex.unapplySeq, which would help
> > people migrate their code safely. And changing code that depends on
> > this behavior is also trivial. For example, the above can be changed
> > as follow:
>
> > val emptyLine = """^\s*(?:#.*)?$""".r  // Anchors added here
> > val code = """^([^#]*)(?:#.*)?""".r // Anchor added here, though the
> > previous pattern makes it unnecessary
> > def stripComments(lines: Seq[String]) = lines flatMap {
> >    case emptyLine => None
> >    case code(x)   => Some(x)
> > }
>
> > And, taking advantage of substring matches, the "code" regex could be
> > changed as follow, for a faster pattern match:
>
> > val code = """^([^#]*)""".r
>
> > I prefer to change semantics, but I'm not sure that is acceptable. If
> > @migration was available back in 2.7.x days, I'd certainly have
> > proposed that. Right now, I'm not sure. It is not a simple matter of
> > aesthetics: there are serious performance considerations as well.
>
> > --
> > Daniel C. Sobral
>
> > I travel to the future all the time.
>
> --
> Johannes
>
> -----------------------------------------------
> Johannes Rudolphhttp://virtual-void.net

Lanny Ripple
Joined: 2011-01-14,
User offline. Last seen 1 year 15 weeks ago.
Re: Matching Regex (SI-5045) -- should we change semantics?

Hi Martin,

Normally I would say "Of course" to the argument that other languages
do something one way and this language does it another. For the most
part language X choosing to do something one way while language Y
chooses another in the case where the pros and cons of each way are
roughly equal is just how it is. In this case though with Scala's
case matching on Regex it seems the language has chosen not just to do
so in a way that differs from how other languages do it but also to go
against the ideas of regex matching itself. One of the fundamental
things about regular expressions is that, while the syntax can be
complicated, what you see is what you get. Scala breaks that
intuition about regular expressions exactly when they are used in the
simplest way the language has to use them.

val Date = """(\d{4}-\d\d-\d\d)""".r // match a date
val Date(when) = "copyright 2011-09-29" // nope!

(And then you get to have this conversation

student: "But..."
teacher: "/sigh. Well you see...")

Scala got this one wrong. It's a tiny (well ok. microscopic) wart on
the library that makes regular expressions artlessly less expressive
without having to do extra work and it should be fixed.

That's hardly going to overthrow concerns about changing current
behavior so that's the last I'll soapbox about this but it's a shame
when you can't fix broken things.

-ljr

On Oct 19, 6:28 am, martin odersky wrote:
> On Wed, Oct 19, 2011 at 11:26 AM, Johannes Rudolph <
>
> johannes [dot] rudo [dot] [dot] [dot] [at] googlemail [dot] com> wrote:
> > Regardless of how regex work somewhere else, isn't it so that Scala's
> > pattern matches (the ones with `match` and `case`) always match on the
> > complete input? Insofar, I think it is just consequent that the same
> > is valid for Scala's regex matches. Following this argument, if you
> > want 'find first' semantics instead of 'match completely' you should
> > be explicit about that.
>
> I am also getting very nervous about changing semantics. It's a migration
> nightmare that one should only consider if things are definitely broken the
> way they are, or if the change is necessary to enable something much more
> powerful. For instance, changing to 2.8 collections caused some small
> backwards incompatible behavior change, but everyone agreed that the
> benefits of the new collection library were worth it. And we managed
> carefully with migration warnings.
>
> The change that's proposed is much more local. Pattern matching works like X
> but many other languages do Y instead. There is a way to do Y in Scala as
> well. So, I think we should leave it at that.
>
> Cheers
>
>  -- Martin

roland.kuhn
Joined: 2011-02-21,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: Matching Regex (SI-5045) -- should we change semantics?

Hi Lanny,

On Oct 19, 2011, at 17:45 , Lanny Ripple wrote:

> Hi Martin,
>
> Normally I would say "Of course" to the argument that other languages
> do something one way and this language does it another. For the most
> part language X choosing to do something one way while language Y
> chooses another in the case where the pros and cons of each way are
> roughly equal is just how it is. In this case though with Scala's
> case matching on Regex it seems the language has chosen not just to do
> so in a way that differs from how other languages do it but also to go
> against the ideas of regex matching itself.

Please let me respectfully disagree as demonstrated below.

> One of the fundamental
> things about regular expressions is that, while the syntax can be
> complicated, what you see is what you get. Scala breaks that
> intuition about regular expressions exactly when they are used in the
> simplest way the language has to use them.
>
> val Date = """(\d{4}-\d\d-\d\d)""".r // match a date

Regular expressions describe regular grammars, and pattern matching is the process which determines if a given input is a valid phrase according to that grammar. What you have defined here is a grammar for a very particular string of length 10.

> val Date(when) = "copyright 2011-09-29" // nope!
>
This is consistent with how java.lang.String behaves:

Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_27).
Type in expressions to have them evaluated.
Type :help for more information.

scala> "hallo welt".matches("a")
res0: Boolean = false

scala> "hallo welt".matches("hallo welt")
res1: Boolean = true

Basically, you are asking whether that copyright string is a date, and clearly it is not. If you wanted to allow that prefix you should have included it (i.e. as ".*") in the grammar.

> (And then you get to have this conversation
>
> student: "But..."
> teacher: "/sigh. Well you see...")
>
> Scala got this one wrong. It's a tiny (well ok. microscopic) wart on
> the library that makes regular expressions artlessly less expressive
> without having to do extra work and it should be fixed.
>
> That's hardly going to overthrow concerns about changing current
> behavior so that's the last I'll soapbox about this but it's a shame
> when you can't fix broken things.
>
Does this “I’ll say no more” extend to those impertinent people who argue that it’s not actually broken? ;-)

Regards,

Roland

> -ljr
>
> On Oct 19, 6:28 am, martin odersky wrote:
>> On Wed, Oct 19, 2011 at 11:26 AM, Johannes Rudolph <
>>
>> johannes [dot] rudo [dot] [dot] [dot] [at] googlemail [dot] com> wrote:
>>> Regardless of how regex work somewhere else, isn't it so that Scala's
>>> pattern matches (the ones with `match` and `case`) always match on the
>>> complete input? Insofar, I think it is just consequent that the same
>>> is valid for Scala's regex matches. Following this argument, if you
>>> want 'find first' semantics instead of 'match completely' you should
>>> be explicit about that.
>>
>> I am also getting very nervous about changing semantics. It's a migration
>> nightmare that one should only consider if things are definitely broken the
>> way they are, or if the change is necessary to enable something much more
>> powerful. For instance, changing to 2.8 collections caused some small
>> backwards incompatible behavior change, but everyone agreed that the
>> benefits of the new collection library were worth it. And we managed
>> carefully with migration warnings.
>>
>> The change that's proposed is much more local. Pattern matching works like X
>> but many other languages do Y instead. There is a way to do Y in Scala as
>> well. So, I think we should leave it at that.
>>
>> Cheers
>>
>> -- Martin

Roland Kuhn
Typesafe – Enterprise-Grade Scala from the Experts
twitter: @rolandkuhn

Lanny Ripple
Joined: 2011-01-14,
User offline. Last seen 1 year 15 weeks ago.
Re: Matching Regex (SI-5045) -- should we change semantics?

Hi Roland,

I believe I actually said I wouldn't soapbox any more on trying to get
through a change that might be a huge effort for nothing other than
perhaps an aesthetic gain. (It's trivial to subclass Regex with a non-
implicitly anchored unapplySeq, introduce an implicit String ->
UnAnchoredRegex, and go about the job at hand. A list of computations
on the Either monad can also be quite handy if you are after blacklist/
transform/whitelist effects using regexes as well.) And I'm pretty
sure saying you are being impertinent for disagreeing with me would be
an epic case of the pot calling the kettle black. :) So...

First I totally concede that I was playing fast and loose with the
theory of regular expressions which provide a grammar for matching
strings and how to use them to best effect while programming. If you
are talking regular expression theory then little things like
backtracking or avoiding the recompilation of a grammar into an NFA
don't come into the picture. A regular expression matches a string or
doesn't and that's all you have. And Java being Java there is
a .matches() method on String so you can determine if the regex you
supply "matches" a string. Which, if that's all I wanted to do, I
don't need scala.util.matching.Regex.

Let's see. I recently saw an excellent example of this very point.
Now what was it again? :)

scala> "hallo welt".matches("a")
res0: Boolean = false

scala> "hallo welt".matches("hallo welt")
res1: Boolean = true

Yay. I'm done. All the things I would ever need a Regex for are
solved! But no. Poor little programmer than I am I want to do more
with the regex used by .matches() than just provide a simple
predicate. I want to extract parts of the string and then we're
getting into computation and all the fun that comes with computation
like implementation details and ease of use considerations. When I
look under the covers at those issues I find that Java uses Patterns
which builds NFAs along the lines of how Perl does it (because that's
where the smart money goes if you have to work with regular
expressions). And to keep track of the fiddly bits Java then gives
you a Matcher to keep track of the state involved. And, why look
[http://download.oracle.com/javase/6/docs/api/java/util/regex/
Matcher.html], Matcher can match in more ways than just the entire
string.

So to make the mistake of trying to prove a point by analogy we are
constantly informed to use abstraction, git rid of the boilerplate,
extract the essence, write declaratively and not imperatively. Java's
better than C because we abstract garbage collection. Scala's better
than Java because we abstract boilerplate. Haskel's better than
anything because we abstract everything but... oh hell, monads (and
arrows, and applicative functors, and whatever the next abstraction
will be! Fine, I want to approach my task in the easiest way possible
stating what I want and not how to get there. I read up on Regular
Expressions so I'm not stumbling around. Java's Pattern docs direct
me to Friedl and maybe if I'm feeling adventurous I give Perl's regex
docs a read over and I'm ready for the task! I want to extract a date
and I know how to do it!

val Date = """({\d{4}-\d\d-\d\d)""".r
val Date(when) = "copyright 2009-09-29"

"No.", says the Scala runtime. "I'm a computer (ok, I'm a program
running on a computer but still) and I'm supposed to be taking all the
drudgery out of human existence because that's what we computers are
good for but in this case I've decided to ignore the way other
languages with powerful regex facilities handle this sort of thing and
you need to take a big step backwards and TALK ABOUT THE FIDDLY BITS."

"Fine." I say.

// yawn. Several seconds of working through the cognitive dissonance
I'll never get back in my life.
// val Date = """({\d{4}-\d\d-\d\d)""".r // Too bad Scala doesn't
use .find(0)
// val Date = """.*?(\d{4}-\d\d-\d\d)""".r // ... or lookingAt(). /
shiver um... No.
val Date = """.*?(\d{4}-\d\d-\d\d).*""".r // Why do Patterns even
have ^ and $?
val Date(when) = data

Note the use of ".?*" to avoid excessive backtracking in the NFA.
This is trivially easy to get wrong and causes the NFA to backtrack
and attempt a match once for every character beyond the last match
from the end of the string. Consider a LARGE file represented as a
single string and """.*A.*B.*C.*""". Sadly the first thing most folks
are going to tell someone when they bump into implicit anchors in
Scala is, "Just put a .* at the start and end of your pattern." In
fact the advice you gave me on how to "fix" my regular grammar (which
it wasn't, see below) could easily fail.

val Date = """.*(\d{4}-\d\d-\d\d).*""".r
val Date(when) = "copyright 2011-09-29. Epoch 1900-01-01" // when:
String = "1900-01-01"

To be fair a silly example since the string "copyright" just blows up
where in perl-ish behavior you'd get back "" and that's its own can of
worms.

// match word at beginning of string or first word starting X
val Re = """(^\S+|X\S+)""".r
val perlishRe: PartialFunction[String,Option[String]] = { case
Re(word) => Some(word); case _=> Option.empty[String] }
"hello there Xworld" match perlishRe // Some("hello")
" hello there Xworld" match perlishRe // Some("Xworld")
" hello there world" match perlishRe // None

But no. This simplicity is denied me. (Well, out of the box. As
mentioned all very easy if I want to tell my workgroup to use this
extension of the Regex library I've written.)

So to counter your disagreement it was not my intent to form a
predicate on strings matching a particular form of 10 characters
(although it happens to be also that if I use it with String.match)
but instead to form the simplest extractor, given the "rules" of
regular expression usage as informed by Java's Pattern and Matcher
classes and thus perl and Mastering Regular Expressions by Friedl.

Just say "No" to the fiddly bits,
-ljr

PS - I made my living for over a decade in regex infested waters.
That's hardly an argument to carry the day but I do have some idea of
how to implement them, how to work with them, and what makes doing so
easy.

PPS - I probably failed at not soap-boxing. Sorry about that.

On Oct 19, 11:01 am, Roland Kuhn wrote:
> Hi Lanny,
>
> On Oct 19, 2011, at 17:45 , Lanny Ripple wrote:
>
> > Hi Martin,
>
> > Normally I would say "Of course" to the argument that other languages
> > do something one way and this language does it another.  For the most
> > part language X choosing to do something one way while language Y
> > chooses another in the case where the pros and cons of each way are
> > roughly equal is just how it is.  In this case though with Scala's
> > case matching on Regex it seems the language has chosen not just to do
> > so in a way that differs from how other languages do it but also to go
> > against the ideas of regex matching itself.
>
> Please let me respectfully disagree as demonstrated below.
>
> >  One of the fundamental
> > things about regular expressions is that, while the syntax can be
> > complicated, what you see is what you get.  Scala breaks that
> > intuition about regular expressions exactly when they are used in the
> > simplest way the language has to use them.
>
> >  val Date = """(\d{4}-\d\d-\d\d)""".r // match a date
>
> Regular expressions describe regular grammars, and pattern matching is the process which determines if a given input is a valid phrase according to that grammar. What you have defined here is a grammar for a very particular string of length 10.
>
> >  val Date(when) = "copyright 2011-09-29"  // nope!
>
> This is consistent with how java.lang.String behaves:
>
> Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_27).
> Type in expressions to have them evaluated.
> Type :help for more information.
>
> scala> "hallo welt".matches("a")
> res0: Boolean = false
>
> scala> "hallo welt".matches("hallo welt")
> res1: Boolean = true
>
> Basically, you are asking whether that copyright string is a date, and clearly it is not. If you wanted to allow that prefix you should have included it (i.e. as ".*") in the grammar.
>
> > (And then you get to have this conversation
>
> >  student: "But..."
> >  teacher: "/sigh.  Well you see...")
>
> > Scala got this one wrong.  It's a tiny (well ok. microscopic) wart on
> > the library that makes regular expressions artlessly less expressive
> > without having to do extra work and it should be fixed.
>
> > That's hardly going to overthrow concerns about changing current
> > behavior so that's the last I'll soapbox about this but it's a shame
> > when you can't fix broken things.
>
> Does this “I’ll say no more” extend to those impertinent people who argue that it’s not actually broken? ;-)
>
> Regards,
>
> Roland
>
>
>
>
>
>
>
>
>
> >  -ljr
>
> > On Oct 19, 6:28 am, martin odersky wrote:
> >> On Wed, Oct 19, 2011 at 11:26 AM, Johannes Rudolph <
>
> >> johannes [dot] rudo [dot] [dot] [dot] [at] googlemail [dot] com> wrote:
> >>> Regardless of how regex work somewhere else, isn't it so that Scala's
> >>> pattern matches (the ones with `match` and `case`) always match on the
> >>> complete input? Insofar, I think it is just consequent that the same
> >>> is valid for Scala's regex matches. Following this argument, if you
> >>> want 'find first' semantics instead of 'match completely' you should
> >>> be explicit about that.
>
> >> I am also getting very nervous about changing semantics. It's a migration
> >> nightmare that one should only consider if things are definitely broken the
> >> way they are, or if the change is necessary to enable something much more
> >> powerful. For instance, changing to 2.8 collections caused some small
> >> backwards incompatible behavior change, but everyone agreed that the
> >> benefits of the new collection library were worth it. And we managed
> >> carefully with migration warnings.
>
> >> The change that's proposed is much more local. Pattern matching works like X
> >> but many other languages do Y instead. There is a way to do Y in Scala as
> >> well. So, I think we should leave it at that.
>
> >> Cheers
>
> >>  -- Martin
>
> Roland Kuhn
> Typesafe – Enterprise-Grade Scala from the Experts
> twitter: @rolandkuhn

roland.kuhn
Joined: 2011-02-21,
User offline. Last seen 35 weeks 3 days ago.
Re: Matching Regex (SI-5045) -- should we change semantics?

Hi Lanny,

first off, I was a bit surprised by your rant, given that I thought that I had only pointed out a very simple fact.

More to the core, though, I think you misinterpreted my point. I do know—and have exploited more than my fair share—that regular expressions can be used in very powerful ways. This implies that there is a multitude of ways to apply them. Now, if you were a language designer who wanted to add the most basic and easy to understand thing, preferably consistent with other behavior, as part of the standard library, what are the options?

1) require the regex to match the whole string
2) require the regex to match at least once within the string and return the first match
3) require the regex to match at least once within the string and return all matches

The first option is consistent with all other patterns that exist in the Scala pattern match syntax, it happens to be consistent with a regex viewed as defining a regular grammar, it is easy to implement, to document and to explain. The second option may be as easy to document and explain, but it is actually quite inconvenient to use: how do I specify that I am not interested in the first, but in the third match? (before you implement a regex, let me tell you that I know how to do that, and that it is beside both our points: you want to make it easy for the user.) And the fact that it is not consistent with how all other patterns behave is not a small issue, since we are talking about the one thing that is chosen to be in the standard library and in books, etc. The third alternative, which is mystically made to work in perl (which I cherish for this, among other things), suffers from the same inconsistency and additionally violates the general contract of the match statement that the rhs for each case is executed at most once.

Of course, as you yourself said, you are free to add your own slightly modified Regex class with a different unapplySeq method, which may do 2) or 3) (concatenating matches in the Seq) or any other fancy thing you care to implement. What I hope to have shown, though, is that these—while certainly useful—are inferior to the current behavior when considering them as THE only standard library implementation choice.

Now, whether variants should be offered in addition to the current behavior is a completely different discussion (and I think that implementing 2) would make sense, FWIW).

Regards,

Roland

On Oct 19, 2011, at 20:20 , Lanny Ripple wrote:

> Hi Roland,
>
> I believe I actually said I wouldn't soapbox any more on trying to get
> through a change that might be a huge effort for nothing other than
> perhaps an aesthetic gain. (It's trivial to subclass Regex with a non-
> implicitly anchored unapplySeq, introduce an implicit String ->
> UnAnchoredRegex, and go about the job at hand. A list of computations
> on the Either monad can also be quite handy if you are after blacklist/
> transform/whitelist effects using regexes as well.) And I'm pretty
> sure saying you are being impertinent for disagreeing with me would be
> an epic case of the pot calling the kettle black. :) So...
>
> First I totally concede that I was playing fast and loose with the
> theory of regular expressions which provide a grammar for matching
> strings and how to use them to best effect while programming. If you
> are talking regular expression theory then little things like
> backtracking or avoiding the recompilation of a grammar into an NFA
> don't come into the picture. A regular expression matches a string or
> doesn't and that's all you have. And Java being Java there is
> a .matches() method on String so you can determine if the regex you
> supply "matches" a string. Which, if that's all I wanted to do, I
> don't need scala.util.matching.Regex.
>
> Let's see. I recently saw an excellent example of this very point.
> Now what was it again? :)
>
> scala> "hallo welt".matches("a")
> res0: Boolean = false
>
> scala> "hallo welt".matches("hallo welt")
> res1: Boolean = true
>
> Yay. I'm done. All the things I would ever need a Regex for are
> solved! But no. Poor little programmer than I am I want to do more
> with the regex used by .matches() than just provide a simple
> predicate. I want to extract parts of the string and then we're
> getting into computation and all the fun that comes with computation
> like implementation details and ease of use considerations. When I
> look under the covers at those issues I find that Java uses Patterns
> which builds NFAs along the lines of how Perl does it (because that's
> where the smart money goes if you have to work with regular
> expressions). And to keep track of the fiddly bits Java then gives
> you a Matcher to keep track of the state involved. And, why look
> [http://download.oracle.com/javase/6/docs/api/java/util/regex/
> Matcher.html], Matcher can match in more ways than just the entire
> string.
>
> So to make the mistake of trying to prove a point by analogy we are
> constantly informed to use abstraction, git rid of the boilerplate,
> extract the essence, write declaratively and not imperatively. Java's
> better than C because we abstract garbage collection. Scala's better
> than Java because we abstract boilerplate. Haskel's better than
> anything because we abstract everything but... oh hell, monads (and
> arrows, and applicative functors, and whatever the next abstraction
> will be! Fine, I want to approach my task in the easiest way possible
> stating what I want and not how to get there. I read up on Regular
> Expressions so I'm not stumbling around. Java's Pattern docs direct
> me to Friedl and maybe if I'm feeling adventurous I give Perl's regex
> docs a read over and I'm ready for the task! I want to extract a date
> and I know how to do it!
>
> val Date = """({\d{4}-\d\d-\d\d)""".r
> val Date(when) = "copyright 2009-09-29"
>
> "No.", says the Scala runtime. "I'm a computer (ok, I'm a program
> running on a computer but still) and I'm supposed to be taking all the
> drudgery out of human existence because that's what we computers are
> good for but in this case I've decided to ignore the way other
> languages with powerful regex facilities handle this sort of thing and
> you need to take a big step backwards and TALK ABOUT THE FIDDLY BITS."
>
> "Fine." I say.
>
> // yawn. Several seconds of working through the cognitive dissonance
> I'll never get back in my life.
> // val Date = """({\d{4}-\d\d-\d\d)""".r // Too bad Scala doesn't
> use .find(0)
> // val Date = """.*?(\d{4}-\d\d-\d\d)""".r // ... or lookingAt(). /
> shiver um... No.
> val Date = """.*?(\d{4}-\d\d-\d\d).*""".r // Why do Patterns even
> have ^ and $?
> val Date(when) = data
>
> Note the use of ".?*" to avoid excessive backtracking in the NFA.
> This is trivially easy to get wrong and causes the NFA to backtrack
> and attempt a match once for every character beyond the last match
> from the end of the string. Consider a LARGE file represented as a
> single string and """.*A.*B.*C.*""". Sadly the first thing most folks
> are going to tell someone when they bump into implicit anchors in
> Scala is, "Just put a .* at the start and end of your pattern." In
> fact the advice you gave me on how to "fix" my regular grammar (which
> it wasn't, see below) could easily fail.
>
> val Date = """.*(\d{4}-\d\d-\d\d).*""".r
> val Date(when) = "copyright 2011-09-29. Epoch 1900-01-01" // when:
> String = "1900-01-01"
>
> To be fair a silly example since the string "copyright" just blows up
> where in perl-ish behavior you'd get back "" and that's its own can of
> worms.
>
> // match word at beginning of string or first word starting X
> val Re = """(^\S+|X\S+)""".r
> val perlishRe: PartialFunction[String,Option[String]] = { case
> Re(word) => Some(word); case _=> Option.empty[String] }
> "hello there Xworld" match perlishRe // Some("hello")
> " hello there Xworld" match perlishRe // Some("Xworld")
> " hello there world" match perlishRe // None
>
> But no. This simplicity is denied me. (Well, out of the box. As
> mentioned all very easy if I want to tell my workgroup to use this
> extension of the Regex library I've written.)
>
> So to counter your disagreement it was not my intent to form a
> predicate on strings matching a particular form of 10 characters
> (although it happens to be also that if I use it with String.match)
> but instead to form the simplest extractor, given the "rules" of
> regular expression usage as informed by Java's Pattern and Matcher
> classes and thus perl and Mastering Regular Expressions by Friedl.
>
> Just say "No" to the fiddly bits,
> -ljr
>
> PS - I made my living for over a decade in regex infested waters.
> That's hardly an argument to carry the day but I do have some idea of
> how to implement them, how to work with them, and what makes doing so
> easy.
>
> PPS - I probably failed at not soap-boxing. Sorry about that.
>
> On Oct 19, 11:01 am, Roland Kuhn wrote:
>> Hi Lanny,
>>
>> On Oct 19, 2011, at 17:45 , Lanny Ripple wrote:
>>
>>> Hi Martin,
>>
>>> Normally I would say "Of course" to the argument that other languages
>>> do something one way and this language does it another. For the most
>>> part language X choosing to do something one way while language Y
>>> chooses another in the case where the pros and cons of each way are
>>> roughly equal is just how it is. In this case though with Scala's
>>> case matching on Regex it seems the language has chosen not just to do
>>> so in a way that differs from how other languages do it but also to go
>>> against the ideas of regex matching itself.
>>
>> Please let me respectfully disagree as demonstrated below.
>>
>>> One of the fundamental
>>> things about regular expressions is that, while the syntax can be
>>> complicated, what you see is what you get. Scala breaks that
>>> intuition about regular expressions exactly when they are used in the
>>> simplest way the language has to use them.
>>
>>> val Date = """(\d{4}-\d\d-\d\d)""".r // match a date
>>
>> Regular expressions describe regular grammars, and pattern matching is the process which determines if a given input is a valid phrase according to that grammar. What you have defined here is a grammar for a very particular string of length 10.
>>
>>> val Date(when) = "copyright 2011-09-29" // nope!
>>
>> This is consistent with how java.lang.String behaves:
>>
>> Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_27).
>> Type in expressions to have them evaluated.
>> Type :help for more information.
>>
>> scala> "hallo welt".matches("a")
>> res0: Boolean = false
>>
>> scala> "hallo welt".matches("hallo welt")
>> res1: Boolean = true
>>
>> Basically, you are asking whether that copyright string is a date, and clearly it is not. If you wanted to allow that prefix you should have included it (i.e. as ".*") in the grammar.
>>
>>> (And then you get to have this conversation
>>
>>> student: "But..."
>>> teacher: "/sigh. Well you see...")
>>
>>> Scala got this one wrong. It's a tiny (well ok. microscopic) wart on
>>> the library that makes regular expressions artlessly less expressive
>>> without having to do extra work and it should be fixed.
>>
>>> That's hardly going to overthrow concerns about changing current
>>> behavior so that's the last I'll soapbox about this but it's a shame
>>> when you can't fix broken things.
>>
>> Does this “I’ll say no more” extend to those impertinent people who argue that it’s not actually broken? ;-)
>>
>> Regards,
>>
>> Roland
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>> -ljr
>>
>>> On Oct 19, 6:28 am, martin odersky wrote:
>>>> On Wed, Oct 19, 2011 at 11:26 AM, Johannes Rudolph <
>>
>>>> johannes [dot] rudo [dot] [dot] [dot] [at] googlemail [dot] com> wrote:
>>>>> Regardless of how regex work somewhere else, isn't it so that Scala's
>>>>> pattern matches (the ones with `match` and `case`) always match on the
>>>>> complete input? Insofar, I think it is just consequent that the same
>>>>> is valid for Scala's regex matches. Following this argument, if you
>>>>> want 'find first' semantics instead of 'match completely' you should
>>>>> be explicit about that.
>>
>>>> I am also getting very nervous about changing semantics. It's a migration
>>>> nightmare that one should only consider if things are definitely broken the
>>>> way they are, or if the change is necessary to enable something much more
>>>> powerful. For instance, changing to 2.8 collections caused some small
>>>> backwards incompatible behavior change, but everyone agreed that the
>>>> benefits of the new collection library were worth it. And we managed
>>>> carefully with migration warnings.
>>
>>>> The change that's proposed is much more local. Pattern matching works like X
>>>> but many other languages do Y instead. There is a way to do Y in Scala as
>>>> well. So, I think we should leave it at that.
>>
>>>> Cheers
>>
>>>> -- Martin
>>
>> Roland Kuhn
>> Typesafe – Enterprise-Grade Scala from the Experts
>> twitter: @rolandkuhn

Roland Kuhn
Typesafe – Enterprise-Grade Scala from the Experts
twitter: @rolandkuhn

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Matching Regex (SI-5045) -- should we change semantics?
Does everyone know about Regex.Groups and Regex.Match? For instance,
regex.findPrefixMatchOf(text) match {  case Regex.Groups(x, y, z) => // ...  case _ => // ... }


On Wed, Oct 19, 2011 at 5:49 PM, Roland Kuhn <google [at] rkuhn [dot] info> wrote:
Hi Lanny,

first off, I was a bit surprised by your rant, given that I thought that I had only pointed out a very simple fact.

More to the core, though, I think you misinterpreted my point. I do know—and have exploited more than my fair share—that regular expressions can be used in very powerful ways. This implies that there is a multitude of ways to apply them. Now, if you were a language designer who wanted to add the most basic and easy to understand thing, preferably consistent with other behavior, as part of the standard library, what are the options?

1) require the regex to match the whole string
2) require the regex to match at least once within the string and return the first match
3) require the regex to match at least once within the string and return all matches

The first option is consistent with all other patterns that exist in the Scala pattern match syntax, it happens to be consistent with a regex viewed as defining a regular grammar, it is easy to implement, to document and to explain. The second option may be as easy to document and explain, but it is actually quite inconvenient to use: how do I specify that I am not interested in the first, but in the third match? (before you implement a regex, let me tell you that I know how to do that, and that it is beside both our points: you want to make it easy for the user.) And the fact that it is not consistent with how all other patterns behave is not a small issue, since we are talking about the one thing that is chosen to be in the standard library and in books, etc. The third alternative, which is mystically made to work in perl (which I cherish for this, among other things), suffers from the same inconsistency and additionally violates the general contract of the match statement that the rhs for each case is executed at most once.

Of course, as you yourself said, you are free to add your own slightly modified Regex class with a different unapplySeq method, which may do 2) or 3) (concatenating matches in the Seq) or any other fancy thing you care to implement. What I hope to have shown, though, is that these—while certainly useful—are inferior to the current behavior when considering them as THE only standard library implementation choice.

Now, whether variants should be offered in addition to the current behavior is a completely different discussion (and I think that implementing 2) would make sense, FWIW).

Regards,

Roland

On Oct 19, 2011, at 20:20 , Lanny Ripple wrote:

> Hi Roland,
>
> I believe I actually said I wouldn't soapbox any more on trying to get
> through a change that might be a huge effort for nothing other than
> perhaps an aesthetic gain.  (It's trivial to subclass Regex with a non-
> implicitly anchored unapplySeq, introduce an implicit String ->
> UnAnchoredRegex, and go about the job at hand.  A list of computations
> on the Either monad can also be quite handy if you are after blacklist/
> transform/whitelist effects using regexes as well.)  And I'm pretty
> sure saying you are being impertinent for disagreeing with me would be
> an epic case of the pot calling the kettle black.  :)  So...
>
> First I totally concede that I was playing fast and loose with the
> theory of regular expressions which provide a grammar for matching
> strings and how to use them to best effect while programming.  If you
> are talking regular expression theory then little things like
> backtracking or avoiding the recompilation of a grammar into an NFA
> don't come into the picture.  A regular expression matches a string or
> doesn't and that's all you have.  And Java being Java there is
> a .matches() method on String so you can determine if the regex you
> supply "matches" a string.  Which, if that's all I wanted to do, I
> don't need scala.util.matching.Regex.
>
> Let's see.  I recently saw an excellent example of this very point.
> Now what was it again?  :)
>
> scala> "hallo welt".matches("a")
> res0: Boolean = false
>
> scala> "hallo welt".matches("hallo welt")
> res1: Boolean = true
>
> Yay.  I'm done.  All the things I would ever need a Regex for are
> solved!  But no.  Poor little programmer than I am I want to do more
> with the regex used by .matches() than just provide a simple
> predicate.  I want to extract parts of the string and then we're
> getting into computation and all the fun that comes with computation
> like implementation details and ease of use considerations.  When I
> look under the covers at those issues I find that Java uses Patterns
> which builds NFAs along the lines of how Perl does it (because that's
> where the smart money goes if you have to work with regular
> expressions).  And to keep track of the fiddly bits Java then gives
> you a Matcher to keep track of the state involved.  And, why look
> [http://download.oracle.com/javase/6/docs/api/java/util/regex/
> Matcher.html], Matcher can match in more ways than just the entire
> string.
>
> So to make the mistake of trying to prove a point by analogy we are
> constantly informed to use abstraction, git rid of the boilerplate,
> extract the essence, write declaratively and not imperatively.  Java's
> better than C because we abstract garbage collection.  Scala's better
> than Java because we abstract boilerplate.  Haskel's better than
> anything because we abstract everything but... oh hell, monads (and
> arrows, and applicative functors, and whatever the next abstraction
> will be!  Fine, I want to approach my task in the easiest way possible
> stating what I want and not how to get there.  I read up on Regular
> Expressions so I'm not stumbling around.  Java's Pattern docs direct
> me to Friedl and maybe if I'm feeling adventurous I give Perl's regex
> docs a read over and I'm ready for the task!  I want to extract a date
> and I know how to do it!
>
> val Date = """({\d{4}-\d\d-\d\d)""".r
> val Date(when) = "copyright 2009-09-29"
>
> "No.", says the Scala runtime.  "I'm a computer (ok, I'm a program
> running on a computer but still) and I'm supposed to be taking all the
> drudgery out of human existence because that's what we computers are
> good for but in this case I've decided to ignore the way other
> languages with powerful regex facilities handle this sort of thing and
> you need to take a big step backwards and TALK ABOUT THE FIDDLY BITS."
>
> "Fine." I say.
>
> // yawn.  Several seconds of working through the cognitive dissonance
> I'll never get back in my life.
> // val Date = """({\d{4}-\d\d-\d\d)""".r // Too bad Scala doesn't
> use .find(0)
> // val Date = """.*?(\d{4}-\d\d-\d\d)""".r // ... or lookingAt().  /
> shiver um... No.
> val Date = """.*?(\d{4}-\d\d-\d\d).*""".r  // Why do Patterns even
> have ^ and $?
> val Date(when) = data
>
> Note the use of ".?*" to avoid excessive backtracking in the NFA.
> This is trivially easy to get wrong and causes the NFA to backtrack
> and attempt a match once for every character beyond the last match
> from the end of the string.  Consider a LARGE file represented as a
> single string and """.*A.*B.*C.*""".  Sadly the first thing most folks
> are going to tell someone when they bump into implicit anchors in
> Scala is, "Just put a .* at the start and end of your pattern."  In
> fact the advice you gave me on how to "fix" my regular grammar (which
> it wasn't, see below) could easily fail.
>
> val Date = """.*(\d{4}-\d\d-\d\d).*""".r
> val Date(when) = "copyright 2011-09-29.  Epoch 1900-01-01" // when:
> String = "1900-01-01"
>
> To be fair a silly example since the string "copyright" just blows up
> where in perl-ish behavior you'd get back "" and that's its own can of
> worms.
>
> // match word at beginning of string or first word starting X
> val Re = """(^\S+|X\S+)""".r
> val perlishRe: PartialFunction[String,Option[String]] = { case
> Re(word) => Some(word); case _=> Option.empty[String] }
> "hello there Xworld" match perlishRe // Some("hello")
> " hello there Xworld" match perlishRe // Some("Xworld")
> " hello there world" match perlishRe // None
>
> But no.  This simplicity is denied me.  (Well, out of the box.  As
> mentioned all very easy if I want to tell my workgroup to use this
> extension of the Regex library I've written.)
>
> So to counter your disagreement it was not my intent to form a
> predicate on strings matching a particular form of 10 characters
> (although it happens to be also that if I use it with String.match)
> but instead to form the simplest extractor, given the "rules" of
> regular expression usage as informed by Java's Pattern and Matcher
> classes and thus perl and Mastering Regular Expressions by Friedl.
>
> Just say "No" to the fiddly bits,
> -ljr
>
> PS - I made my living for over a decade in regex infested waters.
> That's hardly an argument to carry the day but I do have some idea of
> how to implement them, how to work with them, and what makes doing so
> easy.
>
> PPS - I probably failed at not soap-boxing.  Sorry about that.
>
> On Oct 19, 11:01 am, Roland Kuhn <goo [dot] [dot] [dot] [at] rkuhn [dot] info> wrote:
>> Hi Lanny,
>>
>> On Oct 19, 2011, at 17:45 , Lanny Ripple wrote:
>>
>>> Hi Martin,
>>
>>> Normally I would say "Of course" to the argument that other languages
>>> do something one way and this language does it another.  For the most
>>> part language X choosing to do something one way while language Y
>>> chooses another in the case where the pros and cons of each way are
>>> roughly equal is just how it is.  In this case though with Scala's
>>> case matching on Regex it seems the language has chosen not just to do
>>> so in a way that differs from how other languages do it but also to go
>>> against the ideas of regex matching itself.
>>
>> Please let me respectfully disagree as demonstrated below.
>>
>>> One of the fundamental
>>> things about regular expressions is that, while the syntax can be
>>> complicated, what you see is what you get.  Scala breaks that
>>> intuition about regular expressions exactly when they are used in the
>>> simplest way the language has to use them.
>>
>>> val Date = """(\d{4}-\d\d-\d\d)""".r // match a date
>>
>> Regular expressions describe regular grammars, and pattern matching is the process which determines if a given input is a valid phrase according to that grammar. What you have defined here is a grammar for a very particular string of length 10.
>>
>>> val Date(when) = "copyright 2011-09-29"  // nope!
>>
>> This is consistent with how java.lang.String behaves:
>>
>> Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_27).
>> Type in expressions to have them evaluated.
>> Type :help for more information.
>>
>> scala> "hallo welt".matches("a")
>> res0: Boolean = false
>>
>> scala> "hallo welt".matches("hallo welt")
>> res1: Boolean = true
>>
>> Basically, you are asking whether that copyright string is a date, and clearly it is not. If you wanted to allow that prefix you should have included it (i.e. as ".*") in the grammar.
>>
>>> (And then you get to have this conversation
>>
>>> student: "But..."
>>> teacher: "/sigh.  Well you see...")
>>
>>> Scala got this one wrong.  It's a tiny (well ok. microscopic) wart on
>>> the library that makes regular expressions artlessly less expressive
>>> without having to do extra work and it should be fixed.
>>
>>> That's hardly going to overthrow concerns about changing current
>>> behavior so that's the last I'll soapbox about this but it's a shame
>>> when you can't fix broken things.
>>
>> Does this “I’ll say no more” extend to those impertinent people who argue that it’s not actually broken? ;-)
>>
>> Regards,
>>
>> Roland
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>> -ljr
>>
>>> On Oct 19, 6:28 am, martin odersky <martin [dot] oder [dot] [dot] [dot] [at] epfl [dot] ch> wrote:
>>>> On Wed, Oct 19, 2011 at 11:26 AM, Johannes Rudolph <
>>
>>>> johannes [dot] rudo [dot] [dot] [dot] [at] googlemail [dot] com> wrote:
>>>>> Regardless of how regex work somewhere else, isn't it so that Scala's
>>>>> pattern matches (the ones with `match` and `case`) always match on the
>>>>> complete input? Insofar, I think it is just consequent that the same
>>>>> is valid for Scala's regex matches. Following this argument, if you
>>>>> want 'find first' semantics instead of 'match completely' you should
>>>>> be explicit about that.
>>
>>>> I am also getting very nervous about changing semantics. It's a migration
>>>> nightmare that one should only consider if things are definitely broken the
>>>> way they are, or if the change is necessary to enable something much more
>>>> powerful. For instance, changing to 2.8 collections caused some small
>>>> backwards incompatible behavior change, but everyone agreed that the
>>>> benefits of the new collection library were worth it. And we managed
>>>> carefully with migration warnings.
>>
>>>> The change that's proposed is much more local. Pattern matching works like X
>>>> but many other languages do Y instead. There is a way to do Y in Scala as
>>>> well. So, I think we should leave it at that.
>>
>>>> Cheers
>>
>>>> -- Martin
>>
>> Roland Kuhn
>> Typesafe – Enterprise-Grade Scala from the Experts
>> twitter: @rolandkuhn

Roland Kuhn
Typesafe – Enterprise-Grade Scala from the Experts
twitter: @rolandkuhn


Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland