Why is function's non-Unit return type not discarded?

385 views
Skip to first unread message

Daniel Manchester

unread,
Feb 16, 2017, 11:23:19 PM2/16/17
to scala-user
Hi,

I have a question about value discarding (discussed in section 6.26.1 of the Scala Language Specification).

Given the following method and a Unit-returning function...

def executeWithFoo(func: (String => Unit)) = { func("foo") }

val func1 = { string: String =>
  Console.println(string)
}

...the method works fine:

scala> executeWithFoo(func1)
foo

However, given a function returning something other than Unit...

val func2 = { string: String =>
  Console.println(string)
  0
}

...the method fails:

scala> executeWithFoo(func2)
<console>:13: error: type mismatch;
 found   : String => Int
 required: String => Unit
       executeWithFoo(func2)
                      ^

Why is the function's non-Unit return type not discarded? If I want the method to accept either function, is my best bet to change the type of its argument to (String => Any)? As a way to say, "if the function returns a value, the method does nothing with it", I find (String => Any) less clear than (String => Unit)...but maybe that's just me?

Thanks,
Dan

Brian Maso

unread,
Feb 17, 2017, 12:04:26 AM2/17/17
to Daniel Manchester, scala-user
Its a simple matter of the types not matching. executeWithFoo expects a single argument of type String => Unit, and you are passing an object of type String => Int. You *happen* to not be using the returned Unit value in the executeWithFoo implementation, but you might change the implementation at any time to use the Unit value returned from the func.

Brian Maso

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



--
Best regards,
Brian Maso
(949) 395-8551
Follow me: @bmaso
br...@blumenfeld-maso.com

Jeff Dyke

unread,
Feb 17, 2017, 12:07:13 AM2/17/17
to Daniel Manchester, scala-user
Given that section I appreciate your point, but what is the value of explicitly returning an Int/Non Unit, are you going to use/match on it or is the intent to prove it may be an oversight/bug (which can be fixed). Mainly I'm curious about your use case if it's not the latter.  Seems like you've been confined to the correct typing, but the documentation is off.

Just curious

Jasper-M

unread,
Feb 17, 2017, 3:58:49 AM2/17/17
to scala-user
I have written a answer on stackoverflow a while ago on a related question: http://stackoverflow.com/questions/40261998/scala-function-literals-type-issue/40264181

The question was actually why the following does work:

def foo(i: Int): Int = i
def bar(f: Int => Unit) = f

bar(foo)

If you understand why this works, I think it's easy to see why what you are doing does not work.
Your func2 is already a function, not a method, so it isn't eta expanded and there is no value to be discarded. You asked "Why is the function's non-Unit return type not discarded?" but the section in the spec is about value discarding.

Kind regards,
Jasper

Op vrijdag 17 februari 2017 05:23:19 UTC+1 schreef Daniel Manchester:

Dennis Haupt

unread,
Feb 17, 2017, 4:31:32 AM2/17/17
to Jeff Dyke, Daniel Manchester, scala-user
i've come across this a few times.
examples are methods like "java.io.File.delete" or "java.util.ArrayList.add()" that have a side effect and return something, but you don't necessarily care about that


Daniel Manchester

unread,
Feb 17, 2017, 10:30:27 AM2/17/17
to scala-user, jeff...@gmail.com, dpmanc...@gmail.com
Hi all,

Thanks for your thoughts. Regarding the use case where this arose, I was creating an API method that was to accept a side-effecting code block. For the block's type, I chose a Unit-returning function. However, this led to a usability issue: the final statement in the block couldn't have an incidental return type (Dennis gives a good example in File.delete()).

The full (non-use case) signature of Scala's foreach offers a clever solution, though: give the method a type parameter and make it the function's return type. So, given...

def executeWithFoo[U](func: String => U) = { func("foo") }

val func2 = { string: String =>
  Console.println(string)
  0
}

...this now works:

scala> executeWithFoo(func2)
foo
res0: Int = 0

I guess my takeaway from this is that, when designing an API, a "... => Unit" argument is probably never the best choice.

Thanks again,
Dan
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.

Dennis Haupt

unread,
Feb 17, 2017, 10:31:29 AM2/17/17
to Daniel Manchester, scala-user, Jeff Dyke
you can always add a magic implicit that can turn any into unit :D

To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+unsubscribe@googlegroups.com.

Martijn Hoekstra

unread,
Feb 17, 2017, 12:54:09 PM2/17/17
to scala-user
If you want the discarding to stand out like a sore thumb (which you probably do), consider doing something like

  def executeWithFoo(func: String => _) = func("foo")

To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+unsubscribe@googlegroups.com.

Daniel Manchester

unread,
Feb 17, 2017, 3:09:57 PM2/17/17
to scala-user
Wildcard as the function's return type--nice! I like that, too!

Thanks,
Dan

Clint Gilbert

unread,
Feb 17, 2017, 3:58:01 PM2/17/17
to scala...@googlegroups.com
I've achieved what seemed like the same effect by using `Any` instead of
the wildcard:

def executeWithFoo(func: String => Any): Unit = func("foo")

Are there any reasons to prefer `_` to `Any`, or vice versa, here?

On 02/17/2017 12:54 PM, Martijn Hoekstra wrote:
> def executeWithFoo(func: String => _) = func("foo")

--



signature.asc
Reply all
Reply to author
Forward
0 new messages