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.