Default and named arguments in extractors?

281 views
Skip to first unread message

Hanns Holger Rutz

unread,
Oct 13, 2012, 4:13:52 PM10/13/12
to scala-debate
hi,

I was just coming across the following and wondered if this wouldn't be a nice addition which also increases regularity in the language....

Suppose I have a case class

case class Update(added: List[Any] = Nil, removed: List[Any] = Nil)

Now because both arguments have the same type, I started to write the constructor using named arguments so I do not accidentally mix them up (I might have the `removed` arg first and the `added` arg second in another situation):

Update(added = List(1, 2, 3))

So.... I was thinking that it would be great if the following worked as well:

def updated(u: Any) = u match {
case Update(added = a, removed = Nil) if a.nonEmpty => println("None removed, but added: " + a)
case _ =>
}

(1) That is to say, I can name the extractor args as well which will prevent here the confusion of `added` and `removed`.

(2) On a second thought, perhaps there is also a sensible way to omit arguments in the extractor _when using named args_, e.g.

def updated(u: Any) = u match {
case Update(added = a) if a.nonEmpty => println("Added the following (ignored removed): " + a)
}

So missing arguments in an extractor when using named arguments are treated as _.

I think at least case (1) should be rather uncontroversial. It would also be useful with `boolean` arguments for which naming is recommended anyways. Not sure about (2).

Aliasing nested extractors could look like this:

def updated(u: Any) = u match {
case Update(added = a @ (head :: tail)) => println("Added the following (ignored removed): " + a)
}

?

best, .h.h.

Hanns Holger Rutz

unread,
Oct 13, 2012, 4:20:05 PM10/13/12
to scala-debate
p.s. this would also allow to avoid the following horrible extractors:

old:

case MonoSegment( x, _, _, _ ) => ...

new:

case MonoSegment( start = x ) => ...

not sure if the following should be allowed:

case MonoSegment() => ...

Intuitively I would say this is also sensible; it would discard this kind of matches for case classes:

case m: MonoSegment => ...

which is cool when MonoSegment has type constructor parameters, which would require `case m: MonoSegment[_] => ...`.



On 13 Oct 2012, at 22:13, Hanns Holger Rutz wrote:
[...]
> (2) On a second thought, perhaps there is also a sensible way to omit arguments in the extractor _when using named args_, e.g.
[...]

Paul Phillips

unread,
Oct 13, 2012, 4:33:21 PM10/13/12
to Hanns Holger Rutz, scala-debate


On Sat, Oct 13, 2012 at 1:13 PM, Hanns Holger Rutz <con...@sciss.de> wrote:
So.... I was thinking that it would be great if the following worked as well:

    def updated(u: Any) = u match {
       case Update(added = a, removed = Nil) if a.nonEmpty => println("None removed, but added: " + a)
       case _ =>
    }

I also have thought of this, with no small amount of enthusiasm.  I think it is an unambiguously good idea, a winner across the board.  I don't know of a ticket covering this; if you wouldn't mind opening one that would be helpful.

The other thing I'd love to see wrt named arguments is to somehow make them work with java.  I got wontfixed on that one ( https://issues.scala-lang.org/browse/SI-5896 ) but it usually takes two or three beatings before I give up hope.

Hanns Holger Rutz

unread,
Oct 15, 2012, 12:55:33 PM10/15/12
to Paul Phillips, scala-debate
On 13 Oct 2012, at 22:33, Paul Phillips wrote:

>
>
> On Sat, Oct 13, 2012 at 1:13 PM, Hanns Holger Rutz <con...@sciss.de> wrote:
> So.... I was thinking that it would be great if the following worked as well:
>
> def updated(u: Any) = u match {
> case Update(added = a, removed = Nil) if a.nonEmpty => println("None removed, but added: " + a)
> case _ =>
> }
>
> I also have thought of this, with no small amount of enthusiasm. I think it is an unambiguously good idea, a winner across the board. I don't know of a ticket covering this; if you wouldn't mind opening one that would be helpful.

ok, done : https://issues.scala-lang.org/browse/SI-6524

best, .h.h.

Ryan Hendrickson

unread,
Oct 15, 2012, 1:07:36 PM10/15/12
to Hanns Holger Rutz, Paul Phillips, scala-debate
I'd be pretty excited to see this feature in some form! It seems both practical and elegant.

One potential point of contention: should a missing parameter with a default argument be treated as the _ pattern, or as equal to the default arg? I can see the intuitiveness of _, but I wonder if the broken symmetry is worth it:

case class Foo(a: Int, b: Int = 42)

assert(Foo(a = 1) == Foo(a = 1, b = 42))
assert(Foo(a = 1) != Foo(a = 1, b = 0))

val Foo(a = x) = Foo(a = 1, b = 42)
val Foo(a = x) = Foo(a = 1, b = 0)
(please forgive me my corporate legal disclaimer)

----------------------------------------

This message is intended exclusively for the individual(s) or entity to
which it is addressed. It may contain information that is proprietary,
privileged or confidential or otherwise legally exempt from disclosure.
If you are not the named addressee, you are not authorized to read,
print, retain, copy or disseminate this message or any part of it.
If you have received this message in error, please notify the sender
immediately by e-mail and delete all copies of the message.

Hanns Holger Rutz

unread,
Oct 15, 2012, 1:23:15 PM10/15/12
to Ryan Hendrickson, Paul Phillips, scala-debate
Indeed I see the problem with default arguments changing somewhat their meaning. On the other hand, I don't agree that the symmetry is broken. Because

val Foo(a = x) = Foo(a = 1, b = 42)
val Foo(a = x) = Foo(a = 1, b = 0)

would just be short for

val Foo(x, _) = Foo(a = 1, b = 42)
val Foo(x, _) = Foo(a = 1, b = 0)

and therefore, all it does is extract `a` into `x`, it has nothing to do with equality. I think assuming a missing argument is the _constructor default argument_ has multiple problems. First, that default is out of your sight when you build the pattern match and who would look up the constructor to figure out the defaults. Second (more importantly), you can perfectly decouple constructors and deconstructors; a plain extractor doesn't have any corresponding apply method, or if it had, it needn't have the same number or types of arguments etc.

best, .h.h.

Ryan Hendrickson

unread,
Oct 15, 2012, 1:47:14 PM10/15/12
to Hanns Holger Rutz, Paul Phillips, scala-debate
> Indeed I see the problem with default arguments changing somewhat their
> meaning. On the other hand, I don't agree that the symmetry is broken.
> Because
>
> val Foo(a = x) = Foo(a = 1, b = 42)
> val Foo(a = x) = Foo(a = 1, b = 0)
>
> would just be short for
>
> val Foo(x, _) = Foo(a = 1, b = 42)
> val Foo(x, _) = Foo(a = 1, b = 0)
>
> and therefore, all it does is extract `a` into `x`, it has nothing to do
> with equality.

I'm not saying your proposal isn't internally consistent or anything! I can see how that would work.

But there's currently a thing that is basically true about patterns: namely, that the syntax Foo([patterns], someFreeName) used as a pattern means equal to a value constructed with the syntax Foo([values], someBoundName) (iff [values] match [patterns]); and if named pattern parameters become a thing that is real, it might be nice to preserve that symmetry and say that the syntax Foo(a = someFreeName) means equal to a value constructed with the syntax Foo(a = someBoundName).

> I think assuming a missing argument is the _constructor
> default argument_ has multiple problems. First, that default is out of
> your sight when you build the pattern match and who would look up the
> constructor to figure out the defaults.

Not to the compiler; accessors for those default arguments are generated and are currently used whenever you call a method without a full parameter list: Foo(a = 1) desugars to Foo(1, Foo.default$apply$1) or something like that (I don't recall the exact form of the name mangling but you get the idea).

> Second (more importantly), you
> can perfectly decouple constructors and deconstructors; a plain
> extractor doesn't have any corresponding apply method, or if it had, it
> needn't have the same number or types of arguments etc.

Hmm. I was sort of assuming you were going to use some information from the constructor to do this. Otherwise... well, would you want this to work?

case class Bar(a: Int) {
val b = a * 2
}

Bar(1) match {
case Bar(b = 2) => ...

Naftoli Gugenheim

unread,
Oct 15, 2012, 1:57:37 PM10/15/12
to Ryan Hendrickson, Hanns Holger Rutz, Paul Phillips, scala-debate

On Mon, Oct 15, 2012 at 1:47 PM, Ryan Hendrickson <Ryan.Hen...@bwater.com> wrote:
Hmm. I was sort of assuming you were going to use some information from the constructor to do this. Otherwise... well, would you want this to work?

Extractors use the unapply method.

val x = Bar(10) // does not use the constructor directly, but is equivalent to Bar.apply(10)

val Bar(x) = Bar(10) // roughly equivalent to val x = Bar.unapply(Bar(10)).get

case class C(a: Int = 10) // the constructor AND the apply method have a default value

So if this proposal would go through, it would involve copying a case class's defaults to its singleton's unapply method.

Ryan Hendrickson

unread,
Oct 15, 2012, 2:05:31 PM10/15/12
to Naftoli Gugenheim, Hanns Holger Rutz, Paul Phillips, scala-debate
Let me be a little more clear. The unapply method doesn't have parameters corresponding to the apply method's parameters; it only has a return type. So how could this work:

object C {
def apply(a: Int = 10): C = new C(a)
def unapply(c: C): Option[Int] = c.a
}

val C(a = 1) = C(1)

There are three possibilities: the "a" in the pattern is matched against a member named "a" on C, OR the "a" in the pattern is matched a parameter named "a" on C.apply, OR this doesn't work. Which should it be?

Paul Phillips

unread,
Oct 15, 2012, 2:13:10 PM10/15/12
to Ryan Hendrickson, Naftoli Gugenheim, Hanns Holger Rutz, scala-debate


On Mon, Oct 15, 2012 at 11:05 AM, Ryan Hendrickson <Ryan.Hen...@bwater.com> wrote:
There are three possibilities: the "a" in the pattern is matched against a member named "a" on C, OR the "a" in the pattern is matched a parameter named "a" on C.apply, OR this doesn't work. Which should it be?

Doesn't work, until/unless we dream up some sensible unification.  Personally I only had case classes in mind, where the symmetries and names are compiler-generated and have a reliable correspondence.

Jason Zaugg

unread,
Oct 15, 2012, 2:16:42 PM10/15/12
to Paul Phillips, Ryan Hendrickson, Naftoli Gugenheim, Hanns Holger Rutz, scala-debate
If it is restricted to case classes, an author loses the flexibility
to change a case class to a regular class + extractor in a source
compatible fashion.

-jason

Hanns Holger Rutz

unread,
Oct 15, 2012, 2:20:49 PM10/15/12
to Ryan Hendrickson, Paul Phillips, scala-debate

On 15 Oct 2012, at 19:47, Ryan Hendrickson wrote:

>> Indeed I see the problem with default arguments changing somewhat their
>> meaning. On the other hand, I don't agree that the symmetry is broken.
>> Because
>>
>> val Foo(a = x) = Foo(a = 1, b = 42)
>> val Foo(a = x) = Foo(a = 1, b = 0)
>>
>> would just be short for
>>
>> val Foo(x, _) = Foo(a = 1, b = 42)
>> val Foo(x, _) = Foo(a = 1, b = 0)
>>
>> and therefore, all it does is extract `a` into `x`, it has nothing to do
>> with equality.
>
> I'm not saying your proposal isn't internally consistent or anything! I can see how that would work.
>
> But there's currently a thing that is basically true about patterns: namely, that the syntax Foo([patterns], someFreeName) used as a pattern means equal to a value constructed with the syntax Foo([values], someBoundName) (iff [values] match [patterns]); and if named pattern parameters become a thing that is real, it might be nice to preserve that symmetry and say that the syntax Foo(a = someFreeName) means equal to a value constructed with the syntax Foo(a = someBoundName).
>
>> I think assuming a missing argument is the _constructor
>> default argument_ has multiple problems. First, that default is out of
>> your sight when you build the pattern match and who would look up the
>> constructor to figure out the defaults.
>
> Not to the compiler; accessors for those default arguments are generated and are currently used whenever you call a method without a full parameter list: Foo(a = 1) desugars to Foo(1, Foo.default$apply$1) or something like that (I don't recall the exact form of the name mangling but you get the idea).

Yes, but I think that is irrelevant (and related to the possibility to decouple apply and unapply). E.g.

def test(a: Any) = a match {
case Foo(a = x) => ...
}

does it really make sense to assume that this would equivalent to

def test(a: Any) = a match {
case Foo(a = x, b = 42) => ...
}

? I would not assume that someone would look up the defaults of Foo, figure out that you can _construct_ Foo with a default of b = 42, and then decide for brevity to have a pattern match which wants filters out a Foo which has a b of default value 42... That is not just unintuitive, but also not readable (the person reading the pattern match code wouldn't know that this requires b to be 42), and would be a very marginal use case. Whereas the large majority of use cases will be to avoid

case Foo(a, _, _, _, _) =>

which is fairly common and not nice.


>
>> Second (more importantly), you
>> can perfectly decouple constructors and deconstructors; a plain
>> extractor doesn't have any corresponding apply method, or if it had, it
>> needn't have the same number or types of arguments etc.
>
> Hmm. I was sort of assuming you were going to use some information from the constructor to do this. Otherwise... well, would you want this to work?
>
> case class Bar(a: Int) {
> val b = a * 2
> }
>
> Bar(1) match {
> case Bar(b = 2) => ...
> }

No of course this case would not work (as it doesn't work now). The pattern matcher can not perform magic tricks, it can only use the automatically generated extractor the the case class which is

object Bar {
def unapply(b: Bar) : Option[Int] = Some(b.a)
}

You can use a guard for this:

case b @ Bar(_) if b.b == 2 =>

On the other hand, I realise that when going one step back from defaults to named arguments, those are also not possible with custom extractors, for obvious reasons:

case class Bar(a: Int)

object BreakBar {
// minimum "diagonale"
def unapply(b: Bar): Option[(Int, Int)] = {
val x = b.a
val seq = (1 to (x/2)).map { y => val g = gcd(x, y); x/g -> g }
val m = seq.minBy { case (x, y) => x*x + y*y }
Some( m )
}

def gcd(x:Int, y:Int): Int = {
if (x==0) y
else if (x<0) gcd(-x, y)
else if (y<0) -gcd(x, -y)
else gcd(y%x, x)
}
}

// without named
val BreakBar(f1, f2) = Bar(15)

// named
val BreakBar(name? = f1, name? = f2) = Bar(15)

so.... if I'm not mistaken, named arguments would only work for extractors which have a matching apply method. Perhaps this would be ok:

object BreakBar {
...
def apply(factor1: Int, factor2: Int) = Bar(factor1 * factor2)
}

val BreakBar(factor1 = x) = Bar(15)

?

best, .h.h.


Paul Phillips

unread,
Oct 15, 2012, 2:22:16 PM10/15/12
to Jason Zaugg, Ryan Hendrickson, Naftoli Gugenheim, Hanns Holger Rutz, scala-debate


On Mon, Oct 15, 2012 at 11:16 AM, Jason Zaugg <jza...@gmail.com> wrote:
If it is restricted to case classes, an author loses the flexibility
to change a case class to a regular class + extractor in a source
compatible fashion.

Yeah... of course you already can't do that in a "performance-compatible" fashion.  We could allow you to annotate the unapply with the names.  I don't know, it's work-outable one way or another.

// tangent: performance compatible!
object FastExtractor {
  def isDefinedAt(x: Any) = x.isInstanceOf[Bippy]
  def unapply_1(x: Bippy) = x.field1
  def unapply_2(x: Bippy) = x.field2
}

Hanns Holger Rutz

unread,
Oct 15, 2012, 2:23:31 PM10/15/12
to Jason Zaugg, Paul Phillips, Ryan Hendrickson, Naftoli Gugenheim, scala-debate
Yes, I came across this problem too, now (see my other mail). The idea I had was to allow named parameters when there is a corresponding apply method, from which the argument names could be drawn. This would include case classes and allow the refactoring towards custom extractors if the apply method is preserved.

?

best, .h.h.

Ryan Hendrickson

unread,
Oct 15, 2012, 3:25:11 PM10/15/12
to Hanns Holger Rutz, Paul Phillips, scala-debate
So let's stop talking about the decoupling of apply and unapply; as you've already concluded, for this feature to work, you'd have to have some coupling between them, either implicitly (just look for a compatible apply method and use its parameter names) or explicitly (only support this for case classes, or generate annotations on the unapply method that contain the names). Your question below is the real one at hand:

> def test(a: Any) = a match {
> case Foo(a = x) => ...
> }
>
> does it really make sense to assume that this would equivalent to
>
> def test(a: Any) = a match {
> case Foo(a = x, b = 42) => ...
> }

I want to emphasize that I'm largely playing devil's advocate here; I see your side, but I think there's an interesting argument to be made for the other side, and I'm making it so that it can be heard by people whose judgment I trust more than my own or (no offense I hope) yours, not because I deeply think you're wrong.

But why shouldn't those be equivalent? In the land of constructors, Foo(a = x) is in fact defined to be equivalent to Foo(a = x, b = 42); why shouldn't it make sense for that same equivalency to carry over into the land of extractors (modulo the name x moving from bound to free)?

> ? I would not assume that someone would look up the defaults of Foo,
> figure out that you can _construct_ Foo with a default of b = 42, and
> then decide for brevity to have a pattern match which wants filters out
> a Foo which has a b of default value 42... That is not just unintuitive,
> but also not readable (the person reading the pattern match code
> wouldn't know that this requires b to be 42), and would be a very
> marginal use case.

Isn't this a little bit like saying that it's unintuitive to construct Foo(a = 1), since you'd have to look up the default value for b, see that it is 42, and then decide for brevity that you want to omit it?

The fact is, the value that you get when you call Foo(a = 1) is not some indeterminate object with some unspecified value for b; it is a single value with a particular value for b, namely its default. You can informally think of the expression Foo(a = 1) as meaning 'I don't care what b is', but that's not the most precise interpretation; the compiler's interpretation is that Foo(a = 1) means 'Use the default value for b'. The position that I'm arguing is simply that in a pattern, Foo(a = 1) should also mean 'Match the default value for b' as opposed to 'I don't care what b is'. Especially since we already have a syntax for 'I don't care what this is', the ubiquitous underscore.

> Whereas the large majority of use cases will be to
> avoid
>
> case Foo(a, _, _, _, _) =>
>
> which is fairly common and not nice.

It's true that this is common, that your perspective enables getting rid of all those underscores, and that mine prevents that. So, some points to you. :-)

Of course, in such circumstances I often either don't mind the underscores (they remind me that I'm discarding information, and at times it's an advantage for such extraction sites to not compile when I've changed the number of parameters to Foo), or eschew the extractor altogether ({ case f: Foo => // something involving f.a }).

Simon Ochsenreither

unread,
Oct 16, 2012, 10:08:47 AM10/16/12
to scala-...@googlegroups.com, Hanns Holger Rutz, Paul Phillips, ryan.hen...@bwater.com
Imo having parameter names would be confusing, because it is not obvious which name would be chosen for binding. Additionally, why would the names even differ?
Wouldn't we have a lot of case Foo(bar = bar, baz = baz)?

I think that 90% of the benefits could be realized if the IDE would provide the parameter names from the constructor as names for pattern matching.

case class Foo(start: Int, end: Int, step: Int)

Later:

case Foo(|
         ^-- cursor


Auto-completion:

case Foo(start, end, step) =>

What do you think? Should I file a bug for scalaIDE?

Bye,

Simon

Oleg Aleshko

unread,
Oct 21, 2012, 3:05:51 PM10/21/12
to scala-...@googlegroups.com, Hanns Holger Rutz, Paul Phillips, ryan.hen...@bwater.com
Hi Simon.

That's a good point. While allowing default arguments for extractors would be clearly a win, just named arguments - maybe not so much.
However, I think you're missing one additional enhancement - the parameters left out from pattern match are ignored, e.g.
case Tuple3(_, `i`, _) is equivalent to  case Tuple3(_2 = i) 

BTW intellij shows parameter names in popup over pattern match.
Aleh.

L.W.

unread,
Oct 29, 2012, 3:25:04 PM10/29/12
to scala-...@googlegroups.com, Ryan Hendrickson, Paul Phillips
On 15 Oct 2012, at  20:19, Hanns Holger Rutz wrote:
? I would not assume that someone would look up the defaults of Foo, figure out that you can _construct_ Foo with a default of b = 42, and then decide for brevity to have a pattern match which wants filters out a Foo which has a b of default value 42... That is not just unintuitive, but also not readable (the person reading the pattern match code wouldn't know that this requires b to be 42), and would be a very marginal use case.

The dependency on the default value of some argument wouldn't even be visible in source code at the places where it has an effect on the program's behaviour;
when it comes to default values of constructor args, you can see when the default value is used somewhere (that is, when the constructor argument / field is accessed, like f.a).
 
Whereas the large majority of use cases will be to avoid

   case Foo(a, _, _, _, _) =>

which is fairly common and not nice.
 
As far as I can see, the only purpose of introducing named extractor arguments would be to improve readability, and therefore also maintanability of pattern matching code.
Anything above that can be achieved with underscores '_' (ignoring some or most of the arguments).
Having the compiler "magically" take the default value of an argument to be the value matched against would, IMO, even worsen readability,
when compared with the current situation.

And, beside that, what's with the case when a case class constructor argument doesn't have a default value?
Afaik, most case classes don't have default values for their arguments, so not allowing named extractors on those classes would render this extension practically useless.
Just ignoring the actual values of those arguments - i.e. the same behaviour as `_` - would result in inconsistent behaviour, because then you'd have:
(a) Named argument with value specified by a pattern => Match against this pattern
(b) Unmentioned argument with default value specified in the constructor => Match against this default value
(c) Unmentioned argument without a default value => "Don't care" / handle like "<name> = _".

My arguing is that (b) should behave the same way as (c), just for the sake of consistency by itself, let alone readability or understandability.

Shelby

unread,
Jul 19, 2013, 2:26:34 AM7/19/13
to scala-...@googlegroups.com
My thought is to prefer:

1. case Advance(time, isSeek)
2. case Advance(time, isSeek = false)
3. case Advance(t = time, isSeek = false)

instead of:

1. case Advance(time = time, isSeek = isSeek)
2. case Advance(time = time, isSeek = false)
3. case Advance(time = t, isSeek = false)

Named tuples may aid unification with custom extractors.

val t = new Tuple2(42,1){def first = _1; def second = _2}

t.first
res7: Int = 42

t.second
res8: Int = 1

Simon Schäfer

unread,
Jul 19, 2013, 10:33:44 AM7/19/13
to scala-debate


What do you think? Should I file a bug for scalaIDE?

Hanns Holger Rutz

unread,
Jul 19, 2013, 10:40:42 AM7/19/13
to Shelby, scala-...@googlegroups.com
I was enthusiastic in the beginning, but now I see more problems and inconsistencies, so I don't know if one can sketch out a very clean approach to support named arguments in extractors.

For example, this:

case Advance(t = time, isSeek = false)

means, you are mixing the lhs/rhs order:

case Advance(<symbol> = <name>, <name> = <value>)

while I agree that it looks better, it also confusing that you change the position of <name>.

Also, shouldn't it then be `isSeek == false`. That would make it clear that it is not an assignment:

case Advance(t = time, isSeek == false)

or

case Advance(t <- time, isSeek == false)

?

best, .h.h.
> --
> You received this message because you are subscribed to the Google Groups "scala-debate" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

---
"Si hay reelección, Capriles será reelecto"

Haoyi Li

unread,
Jul 19, 2013, 11:52:13 AM7/19/13
to Hanns Holger Rutz, Shelby, scala-...@googlegroups.com
 case Advance(t = time, isSeek == false)

In theory, with parametrized extractors, we could have something like:

// I'm just making up the "~" extractor, but it could be any identifier really
case Advance(t = time, ~(_ == false)) 

Which would be pretty nice. Although they're on the feature list for 2.11, I have no idea whether they'll come out, and in what shape or form, and have not heard anyone talking about them. Which seems odd to me since they'd be a massive change (for the better!) in the expressiveness of pattern matches. Does anyone know?

-Haoyi

Matthew Pocock

unread,
Jul 20, 2013, 4:28:44 PM7/20/13
to Shelby, scala-debate
The haskel record syntax always trips me up because of the order you use name/value in assignments and pattern matching. I'd like to avoid tribble-like underscores when extracting one or a few fields from a case class with many fields and using the fieldnames would be ideal. I think having played with some syntax that I prefer the a<-fieldName approach. So,

case class Person(name: String, age: Int)

case Person(n, a) => // legacy
case Person(n <- name) => // case Person(n, _)
case Person(a <- age) => // case Person(_, a)
case Person(a <- LegalToDrink(age)) => // case Person(_, LegalToDrink(_)@a) 

I'm not really sure where pulling in default values makes much sense here. The instance must have some value filled in already here. Pherhaps I've not understood the motivation.

Matthew


--
You received this message because you are subscribed to the Google Groups "scala-debate" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-debate...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Dr Matthew Pocock
Turing ate my hamster LTD

Integrative Bioinformatics Group, School of Computing Science, Newcastle University

skype: matthew.pocock
tel: (0191) 2566550

soul...@gmail.com

unread,
Jul 20, 2013, 5:27:27 PM7/20/13
to Matthew Pocock, scala-...@googlegroups.com, Shelby
N <-name is already valid as an expression, though. Won't this cause a problem?
Sent from my BlackBerry® wireless device

From: Matthew Pocock <turingate...@gmail.com>
Date: Sat, 20 Jul 2013 21:28:44 +0100
Cc: scala-debate<scala-...@googlegroups.com>
Subject: Re: [scala-debate] Re: Default and named arguments in extractors?

Hanns Holger Rutz

unread,
Jul 21, 2013, 4:30:08 AM7/21/13
to Matthew Pocock, Shelby, scala-debate
this looks pretty good and intuitive IMO. I concede that we should forget about the default parameters.

but how would the constraints look, like in

case Person("name", a)

when you use named arguments? Perhaps like this:

case Person(a <- LegalToDrink(age), n = "name")

?

should we update the ticket with this new suggestions?

best, .h.h.

Hanns Holger Rutz

unread,
Jul 21, 2013, 4:32:04 AM7/21/13
to soul...@gmail.com, Matthew Pocock, scala-...@googlegroups.com, Shelby
how so---can you can an example?

n <- 1 to 3 // error: ';' expected but '<-' found

As far as I know, it is only used in a for-comprehension.


On 20 Jul 2013, at 23:27, soul...@gmail.com wrote:

> N <-name is already valid as an expression, though. Won't this cause a problem?
> Sent from my BlackBerry® wireless device

Hanns Holger Rutz

unread,
Jul 21, 2013, 4:50:22 AM7/21/13
to Hanns Holger Rutz, Matthew Pocock, Shelby, scala-debate
eh.... no, wait. something's not right. I got confused by your example

case Person(a <- LegalToDrink(age))

because I thought that `a` was the argument name, not the capturing symbol. I think this example shouldn't compile because you just extract the second argument without naming (although you use `age` as an inner capturing symbol, that added to my confusion).

I think the following would be the correct way of doing it:

case Person(LegalToDrink(a) <- age)
case Person(ltd @ LegalToDrink(a) <- age)
case Person(ltd: LegalToDrink <- age)

Compare to for-comprehension syntax:

for (foo @ Some(i) <- List(Some(3), None)) {}

Voilà!

Hanns Holger Rutz

unread,
Jul 21, 2013, 4:57:49 AM7/21/13
to Hanns Holger Rutz, Matthew Pocock, Shelby, scala-debate
that would mean

case Advance(t <- time, false <- isSeek)

Intuitively, one might still think this is more readable:

case Advance(t <- time, isSeek = false)

But it is legal `for` syntax:

for (false <- List(true, false)) { println("foo") }

soul...@gmail.com

unread,
Jul 21, 2013, 5:59:55 AM7/21/13
to Hanns Holger Rutz, Matthew Pocock, scala-...@googlegroups.com, Shelby
Isn't it an expression? N less than minus name? And as such can appear legitimately in an argument position. (Not near my computer so can't try it)
Sent from my BlackBerry® wireless device

Hanns Holger Rutz

unread,
Jul 21, 2013, 8:54:49 AM7/21/13
to soul...@gmail.com, Matthew Pocock, scala-...@googlegroups.com, Shelby
`<-` is reserved

trait Foo { def <- (that: Any) {} }

<console>:1: error: identifier expected but '<-' found.
trait Foo { def <- (that: Int) {} }
^

soul...@gmail.com

unread,
Jul 21, 2013, 9:46:18 AM7/21/13
to Hanns Holger Rutz, Matthew Pocock, scala-...@googlegroups.com, Shelby
Ah yes. Sorry for the noise, I got myself confused.

Shelby

unread,
Jul 22, 2013, 12:27:57 AM7/22/13
to scala-...@googlegroups.com, Shelby
On Friday, July 19, 2013 10:40:42 PM UTC+8, Hanns Holger Rutz wrote:
I was enthusiastic in the beginning, but now I see more problems and inconsistencies, so I don't know if one can sketch out a very clean approach to support named arguments in extractors.

For example, this:

    case Advance(t = time, isSeek = false)

means, you are mixing the lhs/rhs order:

    case Advance(<symbol> = <name>, <name> = <value>) 

while I agree that it looks better, it also confusing that you change the position of <name>.


I don't see confusion. The first case is assigning a value from the name, the second is forcing the name to match a value.
 

Also, shouldn't it then be `isSeek == false`. That would make it clear that it is not an assignment:

    case Advance(t = time, isSeek == false)

I like it. Then the order confusion is even less an issue because the above is equivalent to:

case Advance(t = time, false == isSeek)
 

or

    case Advance(t <- time, isSeek == false)

I don't like it. I prefer to use concepts that even a Java or C++ programmer would understand readily.

Shelby

unread,
Jul 22, 2013, 12:35:21 AM7/22/13
to scala-...@googlegroups.com, Matthew Pocock, Shelby
On Sunday, July 21, 2013 4:30:08 PM UTC+8, Hanns Holger Rutz wrote:
should we update the ticket with this new suggestions?

My vote is to add your suggestion for using ==.

I had already added my prior thought to the ticket.

I am enthusiastic about this change, because it reduces verbosity, errors and increases SPOT (single point of truth).

Should we compose a SIP?

Shelby

unread,
Jul 22, 2013, 12:41:18 AM7/22/13
to scala-...@googlegroups.com, Matthew Pocock, Shelby
On Sunday, July 21, 2013 4:30:08 PM UTC+8, Hanns Holger Rutz wrote:
... I concede that we should forget about the default parameters.

Hmmm. Symmetry between constructors and pattern matching would be lost. Why can't we keep defaults only when all arguments of the pattern match contain "==" or an explicit value?

In order words, when we are not extracting field values, then we keep symmetry with defaults.

Shelby

unread,
Jul 23, 2013, 12:32:06 AM7/23/13
to scala-...@googlegroups.com, Matthew Pocock, Shelby
On Monday, July 22, 2013 12:35:21 PM UTC+8, Shelby wrote:
I am enthusiastic about this change, because it reduces verbosity, errors and increases SPOT (single point of truth).

SPOT (at the definition site) because the use-site isn't maintaining knowledge about the other parameters with underscores nor the order of parameters at the definition site.
Reply all
Reply to author
Forward
0 new messages