=> A => B is not substitutable for A => B

71 views
Skip to first unread message

Chris Marshall

unread,
Apr 23, 2012, 1:16:10 PM4/23/12
to scala...@googlegroups.com
If I have a method which expects a function A => B, and I have a function => A => B, then I cannot pass my value in.

scala> def foo(i: => Int) = println("Foo"); foo _
foo: (i: => Int)Unit
res0: => Int => Unit = <function1>

scala> def bar(f: Int => Unit) = f(0)
bar: (f: Int => Unit)Unit

scala> bar(res0)
<console>:11: error: type mismatch;
 found   : => Int => Unit
 required: Int => Unit
              bar(res0)
                  ^

It's not obvious to me that this should not be allowed (I can trivially construct an A => B from a => A=> B). It also sounds like the sort of thing that has come up before but I can't find anything on it. Is there a good reason scalac won't allow this, or indeed a good reason why I should not add:

scala> implicit def unByNameA[A, B](f: (=> A) => B): A => B = a => f(a)
unlazyA: [A, B](f: => A => B)A => B

scala> bar(res0)
Foo

Cheers,

Chris

Sonnenschein

unread,
Apr 23, 2012, 2:06:33 PM4/23/12
to scala-user
Hi Chris,

both

def foo(i: Int) = println("Foo")
def bar(f: Int => Unit) = f(0)
bar(foo)

and

def foo(i: => Int) = println("Foo")
def bar(f: (=> Int) => Unit) = f(0)
bar(foo)

work as expected - or do you mean something else?

Peter

Chris Marshall

unread,
Apr 23, 2012, 3:09:29 PM4/23/12
to peter...@arcor.de, scala...@googlegroups.com
I mean what I originally said. If I have a function (=>A) => B I cannot pass it to a method which is expecting a function A => B. This is because I would like to do the following (in scalaz);

(implicitly[Semigroup[A]].append _).curried(a) <-: ab

Where ab is of type (A, B) and a is of type A. This does not work because semigroup's append method is lazy in its second parameter. It may be argued that this code is obfuscated nonsense and looks better as this anyway:

{ case (a1, b) => (a |+| a1, b) }

I could not possibly comment on that observation but there seems no reason that, given I can pass an =>A to a method which expects an A, that I should be able to pass (=>A) => B to a method expecting an A => B

Chris

> Date: Mon, 23 Apr 2012 11:06:33 -0700
> Subject: [scala-user] Re: => A => B is not substitutable for A => B
> From: peter...@arcor.de
> To: scala...@googlegroups.com

Roland Kuhn

unread,
Apr 23, 2012, 3:24:07 PM4/23/12
to Chris Marshall, peter...@arcor.de, scala...@googlegroups.com
(=>A) is a Function0[A], so why do you expect it to be a supertype of A? More precisely, A => B means that you pass it an A and apply returns a B. If you pass a (=>A)=>B instead, that will not be happy when you give it an A, because it expects a Function0[A].

Makes sense?

Roland Kuhn
Typesafe – The software stack for applications that scale.
twitter: @rolandkuhn


Som Snytt

unread,
Apr 23, 2012, 8:03:26 PM4/23/12
to scala...@googlegroups.com
On Mon, Apr 23, 2012 at 12:24 PM, Roland Kuhn <goo...@rkuhn.info> wrote:
(=>A) is a Function0[A], so why do you expect it to be a supertype of A?
On Apr 23, 2012, at 21:09 , Chris Marshall wrote:

... there seems no reason that, given I can pass an =>A to a method which expects an A, that I should [not] be able to pass (=>A) => B to a method expecting an A => B

Chris

I agree that the asymmetry is a bit confusing.  A by-name param is treated specially on function application, but not when I pass the function around.  Why does the magic stop?

Compiler support to create a def that delegates might save a function object compared to the implicit.  Since it breaks the spell of by-name magic, it might be called $anonfun$disenchant$foo$1.

I see that I can overload a method with a by-name param, though it becomes hard to use.  That is also confusing. See my confusion below.  It can be made to work by tagging as
tweedledee(i: => Int @@ ByName)
but that is less satisfactory because the param is already distinguished as by-name (and not a plain Int).

object Test {

  implicit def unByNameA[A, B](f: (=> A) => B): A => B = a => f(a)
  def f(i: Int) = i+1
  //def g(i: Int) = f(i)+1
  def g(i: => Int) = f(i)+2
  def h(i: Int) = g(i)
  def tweedledee(i: Int) = i+2
  def tweedledee(i: => Int) = i+3
  def w: Int = 0

  def main(args: Array[String]) {
    println(f(0))
    println(g(0))       // no mystery
    val v = List(1,2,3)
    println(v map f)
    println(v map g)    // if you promote me, why not demote me?
    println(v map h)    // why make me do it myself?

    println(tweedledee(0))
    //println(tweedledee(0: => Int)) // no magic
    //println(tweedledee(0: Function0[Int])) // no magic
    //println(tweedledee(w _)) // complains about overload
    val d = new DumDeeDum
    println(d.tweedledee(0)) // 2
    println(v map d.tweedledee)
    println((d: Tweedledee).tweedledee(0)) // 3
    println(v map (d: Tweedledee).tweedledee) // wanted?
  }
  trait Tweedledee {
    def tweedledee(i: => Int) = i+3
  }
  trait Tweedledum {
    def tweedledee(i: Int) = i+2
  }
  class DumDeeDum extends Tweedledee with Tweedledum
}


Chris Marshall

unread,
Apr 24, 2012, 4:34:08 AM4/24/12
to goo...@rkuhn.info, peter...@arcor.de, scala...@googlegroups.com
Because I can pass an A to a method which expects a =>A. That is, I can use A in place of Function0[A] and vice versa.

On the basis that a method:

def foo(a: A)

Can only be called with a parameter p if p is a subtype of A, then the following implies that =>A is a subtype of A.

scala> def bar[A](a: A) = println("Bar")
bar: [A](a: A)Unit

scala> def foo[A](a: =>A) = bar(a)
foo: [A](a: => A)Unit

Of course I could do this the other way round and show that A is a subtype of =>A. There are no implicits muddying the waters here, so it "makes sense" for me that (in Scala), A and =>A are essentially equivalent from the perspective of types.

Chris



Subject: Re: [scala-user] => A => B is not substitutable for A => B
From: goo...@rkuhn.info
Date: Mon, 23 Apr 2012 21:24:07 +0200
CC: peter...@arcor.de; scala...@googlegroups.com
To: oxbow...@hotmail.com


(=>A) is a Function0[A], so why do you expect it to be a supertype of A? 

Makes sense?


Roland Kuhn

unread,
Apr 24, 2012, 5:02:11 AM4/24/12
to Chris Marshall, peter...@arcor.de, scala...@googlegroups.com
On that level I agree. Forgive my engineering self for plunging a few levels down into the machinery and pointing out that the current implementation achieves what you observe by non-obvious means, which unfortunately leak up as the limitation you see (Paul, please correct me if I’m wrong).

Som Snytt

unread,
Apr 24, 2012, 8:11:38 PM4/24/12
to Chris Marshall, goo...@rkuhn.info, peter...@arcor.de, scala...@googlegroups.com
On Tue, Apr 24, 2012 at 1:34 AM, Chris Marshall <oxbow...@hotmail.com> wrote:
...it "makes sense" for me that (in Scala), A and =>A are essentially equivalent from the perspective of types.


Thanks for this interesting food for thought.  Here is a trail of crumbs (after my inexpert reading of the spec).

My exercise yesterday shows that A and =>A aren't equivalent because they overload in a signature.

My gripe was that once I overload f(=>A) with f(A) in the same class, I can't seem to invoke f(=>A) anymore.  That's in contrast to b(i: Int) and b(a: AnyVal) where I can call b(0: AnyVal).

The spec calls =>A a "parameterless method type".  It treats by-name params uniformly the same as a regular def i = 17.

I was confused because I wanted to know why f(i) called overload f(Int) instead of f(=>Int).   Isn't my "i" already a paramless method?  The answer is that by implicit evaluation of =>A to A (6.26.2), my f(i) is f(Int).

(If I comment out the overloading f(Int), I'm still not passing my "i" method to f(=>Int). You don't pass around =>A, not even promoted to Function0[Int].  I happen to be passing a closure that invokes my method and results in an int, more like f {val x = i; x}.)

Anyway, that's my story.  I may be quite mistaken and too exhausted or too exhilarated to care.

Note the wrongness of my first assumption, that overloading resolution was picking f(Int) as more specific.  I think my conclusion was that, on the basis of specificity, we'd choose f(=>Int) because it is not "applicable" to (Int), because no conversion makes Int conform to =>Int (it's magic at the call site).  Conversely, the implicit =>Int to Int makes f(Int) seem less specific.  This means that resolution depends on shape of the argument, so that Int really is not =>Int.

The conversion of methods ()A to functions ()=>A is covered in 3.3.1 and 6.26, so that's in the spec and not an implementation detail.  But it looks like the engineering of by-name as a Function0 is not mandated, which I think is what Roland was saying.


class M {
  def f(i: Int): Int = i
  def f(i: => Int) = i+i  // unused
  def g(i: () => Int) = i()+i()

  var count = 0

  // 5, 7, 14, 4.
  def run() {
    def w: Int = { count += 1; 5 }
    def v(): Int = { count += 1; 7 }
    println(f(w)) // eval w, wrap in a Func for f
    println(f(v)) // eval v() "empty application", wrap for f
    println(g(v)) // v()Int as Func0[Int]
    println(count)
  }
}



missingfaktor

unread,
May 4, 2012, 12:26:00 PM5/4/12
to Som Snytt, Chris Marshall, goo...@rkuhn.info, peter...@arcor.de, scala...@googlegroups.com
I recently faced this situation while using Squeryl and slf4s.

Squeryl's Schema class has a method named printDdl that takes String => Unit as a parameter. However Logger's debug method has type (=> String) => Unit, and so schema.printDdl(logger.debug) does not compile. schema.printDdl(logger.debug(_)) however works. So now, for the sake of consistency, I write f(_) instead of just f even when passing it where A => B is expected.
--
Cheers,

Reply all
Reply to author
Forward
0 new messages