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

implicts, meet defaults. defaults, implicits.

9 replies
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.

I can't believe I forgot to ask about this until now, because it was one
of the main things I was hoping for from default arguments. The named
arg doc doesn't mention implicits so I'm guessing the current situation
is more what fell out by happenstance than the product of explicit
design. For the 4 simple cases, there are 3 different outcomes.

We can define them:

scala> def foo(x: Int)(implicit y: String = "abc") = x + y.length
foo: (x: Int)(implicit y: String)Int

No implicit value in scope:

scala> foo(5)
:6: error: no implicit argument matching parameter type String was found.

scala> foo(5)()
res1: Int = 8

So that's basically just like a non-implicit argument. With an implicit
in scope:

scala> implicit val pp = "abcdefgh"
pp: java.lang.String = abcdefgh

scala> foo(5)
res2: Int = 13

scala> foo(5)()
res3: Int = 8

So the default argument is taking precedence over the implicit argument
in the presence of a (possibly empty) argument list.

The problem for me is that neither calling convention achieves what I
want, which is: if an implicit is in scope, it is supplied, if it is
not, the default is supplied. Instead I can choose from:

foo(5) yields either "error" or uses the implicit
foo(5)() uses default argument no matter what

What I think ought to happen:

foo(5) == foo(5)() == use implicit if available, default otherwise.

That means if an implicit is in scope you have no access to the default,
but that seems a small price to pay compared to the confusion of being
given the default when you had an active implicit. If it is considered
important to be able to use the default, then still workable is:

foo(5) == use implicit if available, default otherwise
foo(5)() == use default always.

I can think of a million uses for this feature, but maybe that is my
personal sickness. If we don't like that idea, we still need to either
disallow or specify the interactions.

As but one example of where this would be super handy: IO methods could
take an implicit like EncodingSettings, with the implicit defaulting to
the platform default charset. Now everything can be centralized in one
easy to customize place. As it stands if I try something like that none
of the encoding-dependent methods would work without an implicit in
scope, and having one in scope by default would defeat the purpose by
conflicting with the implicit which UTF8ers would prefer.

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: implicts, meet defaults. defaults, implicits.

A little more info I did not have before, thanks to ijuma: the SID never
mentions implicits, but the SIP does. I had understood the SID to
subsume the SIP and hadn't looked at it.

http://lampsvn.epfl.ch/svn-repos/scala/lamp-sip/named-args/sip.xhtml#imp...

And apparently this was discussed before and I missed it:

http://www.nabble.com/Named---Default-Arguments:-draft-SIP-td20856909.html

...now I will read that discussion, but I'm betting I'll come out of it
still rooting for the behavior I described.

DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: implicts, meet defaults. defaults, implicits.

Agreed that the behaviour you describe is the correct one.

In general I think the interaction between implicits and defaults
needs a bit more thinking about. Another thing I've been wanting:
Currently defaults resolve implicits at the definition site. It would
be nice if they were resolved at the call site instead, so that an
implicit parameter is passed to the default argument when it's
available at the call.

2009/6/4 Paul Phillips :
> I can't believe I forgot to ask about this until now, because it was one
> of the main things I was hoping for from default arguments.  The named
> arg doc doesn't mention implicits so I'm guessing the current situation
> is more what fell out by happenstance than the product of explicit
> design.  For the 4 simple cases, there are 3 different outcomes.
>
> We can define them:
>
>  scala> def foo(x: Int)(implicit y: String = "abc") = x + y.length
>  foo: (x: Int)(implicit y: String)Int
>
> No implicit value in scope:
>
>  scala> foo(5)
>  :6: error: no implicit argument matching parameter type String was found.
>
>  scala> foo(5)()
>  res1: Int = 8
>
> So that's basically just like a non-implicit argument.  With an implicit
> in scope:
>
>  scala> implicit val pp = "abcdefgh"
>  pp: java.lang.String = abcdefgh
>
>  scala> foo(5)
>  res2: Int = 13
>
>  scala> foo(5)()
>  res3: Int = 8
>
> So the default argument is taking precedence over the implicit argument
> in the presence of a (possibly empty) argument list.
>
> The problem for me is that neither calling convention achieves what I
> want, which is: if an implicit is in scope, it is supplied, if it is
> not, the default is supplied.  Instead I can choose from:
>
>  foo(5)   yields either "error" or uses the implicit
>  foo(5)() uses default argument no matter what
>
> What I think ought to happen:
>
>  foo(5) == foo(5)() == use implicit if available, default otherwise.
>
> That means if an implicit is in scope you have no access to the default,
> but that seems a small price to pay compared to the confusion of being
> given the default when you had an active implicit.  If it is considered
> important to be able to use the default, then still workable is:
>
>  foo(5) == use implicit if available, default otherwise
>  foo(5)() == use default always.
>
> I can think of a million uses for this feature, but maybe that is my
> personal sickness.  If we don't like that idea, we still need to either
> disallow or specify the interactions.
>
> As but one example of where this would be super handy: IO methods could
> take an implicit like EncodingSettings, with the implicit defaulting to
> the platform default charset.  Now everything can be centralized in one
> easy to customize place.  As it stands if I try something like that none
> of the encoding-dependent methods would work without an implicit in
> scope, and having one in scope by default would defeat the purpose by
> conflicting with the implicit which UTF8ers would prefer.
>
> --
> Paul Phillips      | All men are frauds.  The only difference between
> Everyman           | them is that some admit it.  I myself deny it.
> Empiricist         |     -- H. L. Mencken
> i pull his palp!   |----------* http://www.improving.org/paulp/ *----------
>

rytz
Joined: 2008-07-01,
User offline. Last seen 45 weeks 5 days ago.
Re: implicts, meet defaults. defaults, implicits.

>
>  foo(5)   yields either "error" or uses the implicit
>  foo(5)() uses default argument no matter what
>


The current scheme for implicits is: if you ommit an implicit parameter list,
the compiler will search values for you. If you specify the parameter list,
then you have to deliver the values.
The fact that you are now allowed to use defaults for completing your
parameter list is not related to the above per se.

I don't think that this scheme is confusing, but of course I agree that the two
features can be made work together in a better way.

 

> What I think ought to happen:
>
>  foo(5) == foo(5)() == use implicit if available, default otherwise.

This would mean:

a) a special case in using default arguments. For all other method
applications, if you want to use defaults, you still need to supply
the empty parameter list.

b) a change to implicits. It allows to selectively use implicits, no longer
nothing-or-all.


I think a) is not very nice. b) looks OK.

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: implicts, meet defaults. defaults, implicits.

[Slightly long, but there are multiple issues of some importance in here
so I hope nobody glazes over.]

On Fri, Jun 05, 2009 at 10:47:20AM +0200, Lukas Rytz wrote:
> a) a special case in using default arguments. For all other method
> applications, if you want to use defaults, you still need to supply
> the empty parameter list.

That is sort of true, but consistency is an elusive goal and pursuing
consistency in named arguments only makes sense in the larger context of
consistency for function application in general, which is what I'm after
in bringing up the default/implicit interactions.

[MULTIPLE ARGUMENT LIST TANGENT]

scala> def bar1(x: String) = x ;
def bar2(x: String)() = x ;
def bar3(x: String)()() = x
bar1: (x: String)String
bar2: (x: String)()String
bar3: (x: String)()()String

scala> bar1("a") + bar2("b") + bar3("c")
res11: java.lang.String = abc

I just looked in the spec and it says "Otherwise, if e has method
type ()T , it is implicitly applied to the empty argument list, yielding
e()." but it doesn't say that is done repeatedly.

// not sure this should work according to the spec...
scala> val x: String = bar3("abc")
x: String = abc

// .. and arguably this should, since bar("a") has type ()T where
// T = ()String. It does work with a trailing underscore.
scala> val x: () => String = bar3("abc")
:5: error: type mismatch;
found : () => String
required: String

// happily this works (with or without underscore.)
scala> val x: () => () => String = bar3("abc")
x: () => () => String =

[/END TANGENT]

Re-quoting:

> For all other method applications, if you want to use defaults, you
> still need to supply the empty parameter list.

For all other method applications, if you want to call the method at
all, you must supply a parameter list -- excepting the already special
case of empty parameter lists. Whether you have to supply a parameter
list is already the difference between implicits and regular lists, yes?

In some sense default aguments and implicit arguments are the same
thing, differing mostly in where the compiler will search. Implicits
are "call site defaults" or default args are "definition site
implicits." That's not quite accurate, but it's close enough that their
specifications need to be determined jointly, and what is or isn't a
special case should determined holistically.

[NAMED ARGUMENT CONVERSION OBSTACLE]

That reminds me, there is a separate slightly unfortunate downside to a)
quoted at top. If a method is overloaded and one of the overloads is a
no-arg version, you can't move it to using default arguments without
breaking lots of code.

def foo() = "abcdef"
def foo(x: String) = "abc" + x

One would like to unify these now that we can:

def foo(x: String = "def") = "abc" + x

...but that will break all code which invoked "foo" and not "foo()".
Personally I drop the parens almost always, so I have no doubt this
would bring some bad.

I might not have thought this case was a big deal before I discovered
the vast, unbelievable code savings to be had via named/default
arguments. Truly I had no idea how much I was missing this feature.
Refactoring old scala code gets more fun all the time.

[/END OBSTACLE]

[SCALADOC?]

Another thing we need to figure out very soon is how to express the API
documentation for methods with default args. I've never looked much at
scaladoc, but either @param needs a third argument or we need an
@optparam or similar. Am I correct that scaladoc has no maintainer?

[/END SCALADOC]

> b) a change to implicits. It allows to selectively use implicits, no
> longer nothing-or-all.
>
> I think a) is not very nice. b) looks OK.

"Selectively" expresses what's at the heart of those million features I
have in mind. I don't really agree that a) represents any more of a
special case than what I proposed -- in the end it's mostly a question
of where the "specialness" is going to reside. Your version favors
default args working more consistently considered in isolation, mine has
more consistency in their interaction with implicits.

That said, I would go for any approach which enabled my intended usage,
which since I have rambled for what must seem like years I will end by
reiterating for posterity:

// How can I express foo so that one invocation style, be it
// "foo" or "foo()" (or both) will use an implicit String if the
// caller has one in scope, and "abc" if the caller does not?
def foo(x: Int)(implicit y: String = "abc")

rytz
Joined: 2008-07-01,
User offline. Last seen 45 weeks 5 days ago.
Re: implicts, meet defaults. defaults, implicits.
Lots of useful input, thanks.
I now integrated implicits and defaults to see how it goes, the example belowshould match the behavior you described.
However, I think writing a specification for that might be nasty, namely overlaoding resolution. The problem is that applicability now depends on the context, not onlyon the method type and the argument types: isApplicable needs to check wetherimplicits can be found or not. I'll talk to Martin about that.
Lukas


scala> def f(implicit a: String = "a", b: Int, c: String) = a + b + c f: (implicit a: String,implicit b: Int,implicit c: String)java.lang.String
scala> f<console>:6: error: not enough arguments for method f: (implicit a: String,implicit b: Int,implicit c: String)java.lang.String, unspecified parameters: value b, value c        f       ^
scala> implicit val i = 2i: Int = 2
scala> f<console>:7: error: not enough arguments for method f: (implicit a: String,implicit b: Int,implicit c: String)java.lang.String, unspecified parameter: value c        f       ^
scala> f(c = "c")res2: java.lang.String = a2c
scala> implicit val s = "implicitA" s: java.lang.String = implicitA
scala> f(c = "c")                   res3: java.lang.String = implicitA2c
scala> fres4: java.lang.String = implicitA2implicitA







On Fri, Jun 5, 2009 at 13:57, Paul Phillips <paulp@improving.org> wrote:
[Slightly long, but there are multiple issues of some importance in here
so I hope nobody glazes over.]

On Fri, Jun 05, 2009 at 10:47:20AM +0200, Lukas Rytz wrote:
> a) a special case in using default arguments. For all other method
> applications, if you want to use defaults, you still need to supply
> the empty parameter list.

That is sort of true, but consistency is an elusive goal and pursuing
consistency in named arguments only makes sense in the larger context of
consistency for function application in general, which is what I'm after
in bringing up the default/implicit interactions.

[MULTIPLE ARGUMENT LIST TANGENT]

scala> def bar1(x: String) = x ;
 def bar2(x: String)() = x ;
 def bar3(x: String)()() = x
bar1: (x: String)String
bar2: (x: String)()String
bar3: (x: String)()()String

scala> bar1("a") + bar2("b") + bar3("c")
res11: java.lang.String = abc

I just looked in the spec and it says "Otherwise, if e has method
type ()T , it is implicitly applied to the empty argument list, yielding
e()." but it doesn't say that is done repeatedly.

// not sure this should work according to the spec...
scala> val x: String = bar3("abc")
x: String = abc

// .. and arguably this should, since bar("a") has type ()T where
// T = ()String.  It does work with a trailing underscore.
scala> val x: () => String = bar3("abc")
<console>:5: error: type mismatch;
 found   : () => String
 required: String

// happily this works (with or without underscore.)
scala> val x: () => () => String = bar3("abc")
x: () => () => String = <function>

[/END TANGENT]

Re-quoting:

> For all other method applications, if you want to use defaults, you
> still need to supply the empty parameter list.

For all other method applications, if you want to call the method at
all, you must supply a parameter list -- excepting the already special
case of empty parameter lists.  Whether you have to supply a parameter
list is already the difference between implicits and regular lists, yes?

In some sense default aguments and implicit arguments are the same
thing, differing mostly in where the compiler will search.  Implicits
are "call site defaults" or default args are "definition site
implicits." That's not quite accurate, but it's close enough that their
specifications need to be determined jointly, and what is or isn't a
special case should determined holistically.

[NAMED ARGUMENT CONVERSION OBSTACLE]

That reminds me, there is a separate slightly unfortunate downside to a)
quoted at top.  If a method is overloaded and one of the overloads is a
no-arg version, you can't move it to using default arguments without
breaking lots of code.

 def foo() = "abcdef"
 def foo(x: String) = "abc" + x

One would like to unify these now that we can:

 def foo(x: String = "def") = "abc" + x

...but that will break all code which invoked "foo" and not "foo()".
Personally I drop the parens almost always, so I have no doubt this
would bring some bad.

I might not have thought this case was a big deal before I discovered
the vast, unbelievable code savings to be had via named/default
arguments.  Truly I had no idea how much I was missing this feature.
Refactoring old scala code gets more fun all the time.

[/END OBSTACLE]

[SCALADOC?]

Another thing we need to figure out very soon is how to express the API
documentation for methods with default args.  I've never looked much at
scaladoc, but either @param needs a third argument or we need an
@optparam or similar.  Am I correct that scaladoc has no maintainer?

[/END SCALADOC]

> b) a change to implicits. It allows to selectively use implicits, no
> longer nothing-or-all.
>
> I think a) is not very nice. b) looks OK.

"Selectively" expresses what's at the heart of those million features I
have in mind.  I don't really agree that a) represents any more of a
special case than what I proposed -- in the end it's mostly a question
of where the "specialness" is going to reside.  Your version favors
default args working more consistently considered in isolation, mine has
more consistency in their interaction with implicits.

That said, I would go for any approach which enabled my intended usage,
which since I have rambled for what must seem like years I will end by
reiterating for posterity:

 // How can I express foo so that one invocation style, be it
 // "foo" or "foo()" (or both) will use an implicit String if the
 // caller has one in scope, and "abc" if the caller does not?
 def foo(x: Int)(implicit y: String = "abc")

--
Paul Phillips      | It is hard to believe that a man is
Protagonist        | telling the truth when you know that you
Empiricist         | would lie if you were in his place.
i pull his palp!   |     -- H. L. Mencken

rytz
Joined: 2008-07-01,
User offline. Last seen 45 weeks 5 days ago.
Re: implicts, meet defaults. defaults, implicits.
I spent some time with implicits+defaults, here are the conclusions (implemented in r18069).
- implicit parameters are allowed to have defaults (not new); if no implicit can be found, the  default will be used (new).

- implicit search is only triggered when the entire parameter list is missing.    def f(x: Int)(implicit y: Int = 1)    f(1)   // searches implicit, uses default if none can be found     f(1)() // always uses default
  if we would search impicits for individual missing parameters (similar to defaults), overloading  resolution would depend on the context of the application: to check wether an alternative is   applicable to some arguments, we would need to search the context for implicits.  this would be unbeautiful and complicate the spec.

- we keep the requirement for an empty parameter list () if one wants to use defaults, so the   follwing is not allowed:    def f(x: Int = 1) = x    val i: Int = f
  in general we would like to force people to provide the empty parameter list for procedures (which   take an empty parameter list).  the reason why a missing empty parameter list is automatically added has to do with java  interoperability only, it's about keeping the uniform access principle when calling java methods.

Lukas
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: implicts, meet defaults. defaults, implicits.

It's fantastic that you've been able to iterate so quickly - the
eventual outcome will be much better for it. Thanks! I will try to put
this to work on something interesting immediately and perhaps flush out
some more improbable interactions.

On Sat, Jun 20, 2009 at 12:05:11PM +0200, Lukas Rytz wrote:
> - implicit search is only triggered when the entire parameter list is
> missing.

I am sure that's the right answer. Let's not let this get any more
complicated than necessary.

The way it's operating now matches up perfectly with how my intuition
expects it to work.

> in general we would like to force people to provide the empty parameter
> list for procedures (which
> take an empty parameter list).

Since the presence/absence of an empty parameter list is taking on
additional semantic weight right now anyway, and I believe it has been
agreed that the () should only be optional when calling into java, it
would seem now is the time to enforce this in code. That way we can
break all the code which may or may not be using () at once without
having it come up again.

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: implicts, meet defaults. defaults, implicits.

On Sat, Jun 20, 2009 at 2:08 PM, Paul Phillips wrote:
> It's fantastic that you've been able to iterate so quickly - the
> eventual outcome will be much better for it.  Thanks! I will try to put
> this to work on something interesting immediately and perhaps flush out
> some more improbable interactions.
>
> On Sat, Jun 20, 2009 at 12:05:11PM +0200, Lukas Rytz wrote:
>> - implicit search is only triggered when the entire parameter list is
>> missing.
>
> I am sure that's the right answer.  Let's not let this get any more
> complicated than necessary.
>
> The way it's operating now matches up perfectly with how my intuition
> expects it to work.
>
>>   in general we would like to force people to provide the empty parameter
>> list for procedures (which
>>   take an empty parameter list).
>
> Since the presence/absence of an empty parameter list is taking on
> additional semantic weight right now anyway, and I believe it has been
> agreed that the () should only be optional when calling into java, it
> would seem now is the time to enforce this in code.  That way we can
> break all the code which may or may not be using () at once without
> having it come up again.
>
Yes, well, we were having second thought about this. The problem with
this scheme is that we won't break all code at once, it will come up
everytime someone converts a Java class to Scala, and we'd like to
encourage that! (I mean, converting, not breaking code) So maybe the
status quo is still the least bad.

Cheers

Kevin Wright
Joined: 2009-06-09,
User offline. Last seen 49 weeks 3 days ago.
Re: implicts, meet defaults. defaults, implicits.
Scared that I've heard the backward compatibility argument before; that time it was responsible for type erasure instead of reified generics...
Would it not be better to take the pain up front and just do the right thing?

On Sat, Jun 20, 2009 at 9:02 PM, martin odersky <martin.odersky@epfl.ch> wrote:
On Sat, Jun 20, 2009 at 2:08 PM, Paul Phillips<paulp@improving.org> wrote:
> It's fantastic that you've been able to iterate so quickly - the
> eventual outcome will be much better for it.  Thanks! I will try to put
> this to work on something interesting immediately and perhaps flush out
> some more improbable interactions.
>
> On Sat, Jun 20, 2009 at 12:05:11PM +0200, Lukas Rytz wrote:
>> - implicit search is only triggered when the entire parameter list is
>> missing.
>
> I am sure that's the right answer.  Let's not let this get any more
> complicated than necessary.
>
> The way it's operating now matches up perfectly with how my intuition
> expects it to work.
>
>>   in general we would like to force people to provide the empty parameter
>> list for procedures (which
>>   take an empty parameter list).
>
> Since the presence/absence of an empty parameter list is taking on
> additional semantic weight right now anyway, and I believe it has been
> agreed that the () should only be optional when calling into java, it
> would seem now is the time to enforce this in code.  That way we can
> break all the code which may or may not be using () at once without
> having it come up again.
>
Yes, well, we were having second thought about this. The problem with
this scheme is that we won't break all code at once, it will come up
everytime someone converts a Java class to Scala, and we'd like to
encourage that! (I mean, converting, not breaking code) So maybe the
status quo is still the least bad.

Cheers

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