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

first make it real, then make it useful

9 replies
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Is there a sweet spot where something like this would be useful but not complicated? (Examples of complications I'm not real interested in: parameters are not each a completely distinct type, or you invent a language to communicate which expansions you want.)
class A {  @annotation.expandDefaults def f(x: Int = 2, ys: List[String] = Nil, z: Option[Float] = None): String = "" + x + ys + z}
% scalap A class A extends java.lang.Object with scala.ScalaObject {  def this() = { /* compiled code */ }  @scala.annotation.expandDefaults  def f(x : scala.Int, ys : scala.List[scala.Predef.String], z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }   def f(ys : scala.List[scala.Predef.String], z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }  def f(x : scala.Int, z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }   def f(x : scala.Int, ys : scala.List[scala.Predef.String]) : scala.Predef.String = { /* compiled code */ }  def f(z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }   def f(ys : scala.List[scala.Predef.String]) : scala.Predef.String = { /* compiled code */ }  def f(x : scala.Int) : scala.Predef.String = { /* compiled code */ }  def f() : scala.Predef.String = { /* compiled code */ } }
scala> (new A).f(List("abc"))res0: String = 2List(abc)None
scala> (new A).fres1: String = 2List()None
scala> (new A).f(Some(5.5))<console>:8: error: overloaded method value f with alternatives:  (x: Int)String <and>  (ys: List[String])String <and>   (z: Option[Float])String cannot be applied to (Some[Double])              (new A).f(Some(5.5))                      ^
scala> (new A).f(Some(5.5f))res3: String = 2List()Some(5.5)
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: first make it real, then make it useful
By the way, what got me heading that way was this.  Seems unfair to the default args.

scala> class A { def f(x: Int = 5, y: String = "abc") = () }defined class A
scala> def g(x: { def f(x: Int): Unit }) = ()g: (x: AnyRef{def f(x: Int): Unit})Unit
scala> g( new A )<console>:10: error: type mismatch;  found   : A required: AnyRef{def f(x: Int): Unit}              g( new A )                 ^
Alex Repain
Joined: 2010-07-27,
User offline. Last seen 1 year 31 weeks ago.
Re: first make it real, then make it useful


2012/2/13 Paul Phillips <paulp [at] improving [dot] org>
Is there a sweet spot where something like this would be useful but not complicated? (Examples of complications I'm not real interested in: parameters are not each a completely distinct type, or you invent a language to communicate which expansions you want.)

In which way do you find your own example complicated ? Seems to me it would work pretty well ...

class A {  @annotation.expandDefaults def f(x: Int = 2, ys: List[String] = Nil, z: Option[Float] = None): String = "" + x + ys + z}
% scalap A class A extends java.lang.Object with scala.ScalaObject {  def this() = { /* compiled code */ }  @scala.annotation.expandDefaults  def f(x : scala.Int, ys : scala.List[scala.Predef.String], z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }   def f(ys : scala.List[scala.Predef.String], z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }  def f(x : scala.Int, z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }   def f(x : scala.Int, ys : scala.List[scala.Predef.String]) : scala.Predef.String = { /* compiled code */ }  def f(z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }   def f(ys : scala.List[scala.Predef.String]) : scala.Predef.String = { /* compiled code */ }  def f(x : scala.Int) : scala.Predef.String = { /* compiled code */ }  def f() : scala.Predef.String = { /* compiled code */ } }
scala> (new A).f(List("abc"))res0: String = 2List(abc)None
scala> (new A).fres1: String = 2List()None
scala> (new A).f(Some(5.5))<console>:8: error: overloaded method value f with alternatives:  (x: Int)String <and>  (ys: List[String])String <and>   (z: Option[Float])String cannot be applied to (Some[Double])              (new A).f(Some(5.5))                      ^
scala> (new A).f(Some(5.5f)) res3: String = 2List()Some(5.5)

Chris Marshall
Joined: 2009-06-17,
User offline. Last seen 44 weeks 3 days ago.
RE: Re: first make it real, then make it useful
It would be a big win for those of us who have to think about designing scala libs which are to be used from Java (it may just be Spring)
Chris

Date: Sun, 12 Feb 2012 15:51:57 -0800
Subject: [scala-debate] Re: first make it real, then make it useful
From: paulp [at] improving [dot] org
To: scala-debate [at] googlegroups [dot] com

By the way, what got me heading that way was this.  Seems unfair to the default args.

scala> class A { def f(x: Int = 5, y: String = "abc") = () }defined class A
scala> def g(x: { def f(x: Int): Unit }) = ()g: (x: AnyRef{def f(x: Int): Unit})Unit
scala> g( new A )<console>:10: error: type mismatch;  found   : A required: AnyRef{def f(x: Int): Unit}              g( new A )                 ^
Tony Morris 2
Joined: 2009-03-20,
User offline. Last seen 42 years 45 weeks ago.
Re: first make it real, then make it useful

Perhaps this trait will help you generalise your operation:

trait Point[[F[_]] {
def point[A](a: => A): F[A]
}

On 13/02/12 09:28, Paul Phillips wrote:
> Is there a sweet spot where something like this would be useful but
> not complicated? (Examples of complications I'm not real interested
> in: parameters are not each a completely distinct type, or you invent
> a language to communicate which expansions you want.)
>
> class A {
> @annotation.expandDefaults def f(x: Int = 2, ys: List[String] = Nil,
> z: Option[Float] = None): String = "" + x + ys + z
> }
>
> % scalap A
> class A extends java.lang.Object with scala.ScalaObject {
> def this() = { /* compiled code */ }
> @scala.annotation.expandDefaults
> def f(x : scala.Int, ys : scala.List[scala.Predef.String], z :
> scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }
> def f(ys : scala.List[scala.Predef.String], z :
> scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }
> def f(x : scala.Int, z : scala.Option[scala.Float]) :
> scala.Predef.String = { /* compiled code */ }
> def f(x : scala.Int, ys : scala.List[scala.Predef.String]) :
> scala.Predef.String = { /* compiled code */ }
> def f(z : scala.Option[scala.Float]) : scala.Predef.String = { /*
> compiled code */ }
> def f(ys : scala.List[scala.Predef.String]) : scala.Predef.String =
> { /* compiled code */ }
> def f(x : scala.Int) : scala.Predef.String = { /* compiled code */ }
> def f() : scala.Predef.String = { /* compiled code */ }
> }
>
> scala> (new A).f(List("abc"))
> res0: String = 2List(abc)None
>
> scala> (new A).f
> res1: String = 2List()None
>
> scala> (new A).f(Some(5.5))
> :8: error: overloaded method value f with alternatives:
> (x: Int)String
> (ys: List[String])String
> (z: Option[Float])String
> cannot be applied to (Some[Double])
> (new A).f(Some(5.5))
> ^
>
> scala> (new A).f(Some(5.5f))
> res3: String = 2List()Some(5.5)
>

Alex Repain
Joined: 2010-07-27,
User offline. Last seen 1 year 31 weeks ago.
Re: first make it real, then make it useful
Tony, you are our Gandalf. You still speak in riddles.

2012/2/13 Tony Morris <tonymorris [at] gmail [dot] com>
Perhaps this trait will help you generalise your operation:

trait Point[[F[_]] {
   def point[A](a: => A): F[A]
}

On 13/02/12 09:28, Paul Phillips wrote:
> Is there a sweet spot where something like this would be useful but
> not complicated? (Examples of complications I'm not real interested
> in: parameters are not each a completely distinct type, or you invent
> a language to communicate which expansions you want.)
>
> class A {
>   @annotation.expandDefaults def f(x: Int = 2, ys: List[String] = Nil,
> z: Option[Float] = None): String = "" + x + ys + z
> }
>
> % scalap A
> class A extends java.lang.Object with scala.ScalaObject {
>   def this() = { /* compiled code */ }
>   @scala.annotation.expandDefaults
>   def f(x : scala.Int, ys : scala.List[scala.Predef.String], z :
> scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }
>   def f(ys : scala.List[scala.Predef.String], z :
> scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }
>   def f(x : scala.Int, z : scala.Option[scala.Float]) :
> scala.Predef.String = { /* compiled code */ }
>   def f(x : scala.Int, ys : scala.List[scala.Predef.String]) :
> scala.Predef.String = { /* compiled code */ }
>   def f(z : scala.Option[scala.Float]) : scala.Predef.String = { /*
> compiled code */ }
>   def f(ys : scala.List[scala.Predef.String]) : scala.Predef.String =
> { /* compiled code */ }
>   def f(x : scala.Int) : scala.Predef.String = { /* compiled code */ }
>   def f() : scala.Predef.String = { /* compiled code */ }
> }
>
> scala> (new A).f(List("abc"))
> res0: String = 2List(abc)None
>
> scala> (new A).f
> res1: String = 2List()None
>
> scala> (new A).f(Some(5.5))
> <console>:8: error: overloaded method value f with alternatives:
>   (x: Int)String <and>
>   (ys: List[String])String <and>
>   (z: Option[Float])String
>  cannot be applied to (Some[Double])
>               (new A).f(Some(5.5))
>                       ^
>
> scala> (new A).f(Some(5.5f))
> res3: String = 2List()Some(5.5)
>


--
Tony Morris
http://tmorris.net/



ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: first make it real, then make it useful
You can't do all combinations like you show because of multiple parameters of the same type:
  def f(x: Int = 5, y: Int = 3, z: Int = 1)
  def f(Int,Int)   // Is this x,y?  x,z?  y,z?
But you can provide all truncations:
  def f(Int,Int,Int)
  def f(Int,Int)
  def f(Int)
  def f()

  --Rex

On Sun, Feb 12, 2012 at 6:28 PM, Paul Phillips <paulp [at] improving [dot] org> wrote:
Is there a sweet spot where something like this would be useful but not complicated? (Examples of complications I'm not real interested in: parameters are not each a completely distinct type, or you invent a language to communicate which expansions you want.)
class A {  @annotation.expandDefaults def f(x: Int = 2, ys: List[String] = Nil, z: Option[Float] = None): String = "" + x + ys + z}
% scalap A class A extends java.lang.Object with scala.ScalaObject {  def this() = { /* compiled code */ }  @scala.annotation.expandDefaults  def f(x : scala.Int, ys : scala.List[scala.Predef.String], z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }   def f(ys : scala.List[scala.Predef.String], z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }  def f(x : scala.Int, z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }   def f(x : scala.Int, ys : scala.List[scala.Predef.String]) : scala.Predef.String = { /* compiled code */ }  def f(z : scala.Option[scala.Float]) : scala.Predef.String = { /* compiled code */ }   def f(ys : scala.List[scala.Predef.String]) : scala.Predef.String = { /* compiled code */ }  def f(x : scala.Int) : scala.Predef.String = { /* compiled code */ }  def f() : scala.Predef.String = { /* compiled code */ } }
scala> (new A).f(List("abc"))res0: String = 2List(abc)None
scala> (new A).fres1: String = 2List()None
scala> (new A).f(Some(5.5))<console>:8: error: overloaded method value f with alternatives:  (x: Int)String <and>  (ys: List[String])String <and>   (z: Option[Float])String cannot be applied to (Some[Double])              (new A).f(Some(5.5))                      ^
scala> (new A).f(Some(5.5f)) res3: String = 2List()Some(5.5)

Ittay Dror 2
Joined: 2010-05-05,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: first make it real, then make it useful

Wouldn't it be better to enhance the type matching of structural types than to require the class author to know in advance her class is going to be used with a structural type?

Ittay

----
(Sent from my android)

On 13 בפבר 2012 01:51, "Paul Phillips" <paulp [at] improving [dot] org> wrote:
By the way, what got me heading that way was this.  Seems unfair to the default args.

scala> class A { def f(x: Int = 5, y: String = "abc") = () }defined class A
scala> def g(x: { def f(x: Int): Unit }) = ()g: (x: AnyRef{def f(x: Int): Unit})Unit
scala> g( new A )<console>:10: error: type mismatch;  found   : A required: AnyRef{def f(x: Int): Unit}              g( new A )                 ^
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: first make it real, then make it useful


On Mon, Feb 13, 2012 at 10:34 AM, Rex Kerr <ichoran [at] gmail [dot] com> wrote:> You can't do all combinations like you show because of multiple parameters of the same type:
I suppose that's why I said "Examples of complications I'm not real interested in: parameters are not each a completely distinct type."
ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: first make it real, then make it useful
Well, something that is broken by design isn't useful unless I can envision how to fix the design.  If I think of complications for something with a broken design and I don't know the fix, how do I know whether it's actually a complication or not?

Anyway, right now Scala allows

  object O {
    def f(i: Int = 7, s: String = "fish") = s*i
    def f(a: Int) = a*a
  }

where the latter is called when one argument is given.  So the annotation would cause all sorts of collisions, potentially.  There'd need to be some sort of crisp definition of how to avoid this.

But Scala doesn't allow

  object O {
    def f(s: String, i: Int = 5) = s*i
    def f(b: Boolean, j: Int = 2) = List.fill(j)(b)
  }

which is silly as no possible conflicts exist.

If the annotation, once fixed to do whatever collision-avoidance it does, allows for the latter case, then it sounds good to me; I've argued for fixing this before.  If you are not allowed to expand defaults in more than one method, then I'm personally not terribly interested in it.

Also, you can have type parameter dependencies:

  object P {
    def f[A,B <: A](oa: Option[A] = None, ob: Option[B] = None) =
      for (a <- oa; b <- ob) yield (a,b)
  }

which would require some sort of rewriting when changed to single-parameter functions to keep the return type the same.

And then what happens when you inherit?

  class Q { @expandDefaults def f(i: Int = 2, s: String = "wish") = s*i }
  class R extends Q { override def f(a: Int, t: String = "dish") =  t+a }

In particular, what happens to f()?  Does it call f(Int) or f(Int,String) or f(String)?  If it calls f(Int,String), it's going to use the non-overridden defaults, unless it magically gets recreated.  Right now, I can call (new R).f() and get what I would expect from Liskov.  How is that to be guaranteed with expansions?

  --Rex

On Mon, Feb 13, 2012 at 2:32 PM, Paul Phillips <paulp [at] improving [dot] org> wrote:


On Mon, Feb 13, 2012 at 10:34 AM, Rex Kerr <ichoran [at] gmail [dot] com> wrote: > You can't do all combinations like you show because of multiple parameters of the same type:
I suppose that's why I said "Examples of complications I'm not real interested in: parameters are not each a completely distinct type."

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