- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Re: Type inferencer not inferencing quite enough?
Fri, 2011-12-16, 08:27
I don't disagree to on its equational equivalence, but that doesn't equationally equal that it's necessarily "better written" the more idiomatic way.There are exceptions to every rule, except that one.
On Fri, Dec 16, 2011 at 1:58 AM, Tony Morris <tonymorris@gmail.com> wrote:
On Fri, Dec 16, 2011 at 1:58 AM, Tony Morris <tonymorris@gmail.com> wrote:
On 16/12/11 16:55, Naftoli Gugenheim wrote:
> On Thu, Dec 15, 2011 at 8:44 AM, Tony Morris <tmorris@tmorris.net> wrote:
>
>> As aside, please note that
>>
>> {
>> case None => None
>> case Some(x) => Some(x.f)
>> }
>>
>> is better written:
>>
>> .map(_.f)
>>
> Except when you're trying to reduce the number of classes your library
> weighs (as a certain very heavily used scala library is), or when it breaks
> tail recursion.
>
It's equivalent by equational reasoning. So no, not except then or then
either.
--
Tony Morris
http://tmorris.net/
Fri, 2011-12-16, 14:31
#2
Re: Type inferencer not inferencing quite enough?
I'm not exactly sure tail-recursion is an issue for this scenario. You are certainly correct that tail recursion can be an issue, and monadic/functor operations do not lend themselves to Scala's tail recursion, however "map" and "getOrElse" are pretty safe in the above example, and in 90% of my own code.
In this instance, i would agree with Tony. Option(x) map f getOrElse a // Reads better, gets my point across, and has no tail calls or stack explosions.
If you're worried about object allocation for the closures, you can pre-allocate them to some extent. I believe Option has some inlining behavior though, so things like getOrElse aren't 'boxed'.
So, while stack-optimised tail recursion can be an issue in Scala, this type of code is prevalent and acceptible. If you need TCO and you're using monadic style code, then you should certainly use Scalaz, which has the best "library" TCO I've seen (and I'd like to see it replace scala.util.control.TailCalls)
- Josh
Note: The TCO example is a bit contrived. I'm not sure I've ever seen that particular scenario with option. In my experience, the place where TCO is crucial is when using Iteratee IO and monadic workflows.
On Fri, Dec 16, 2011 at 4:39 AM, Naftoli Gugenheim <naftoligug@gmail.com> wrote:
In this instance, i would agree with Tony. Option(x) map f getOrElse a // Reads better, gets my point across, and has no tail calls or stack explosions.
If you're worried about object allocation for the closures, you can pre-allocate them to some extent. I believe Option has some inlining behavior though, so things like getOrElse aren't 'boxed'.
So, while stack-optimised tail recursion can be an issue in Scala, this type of code is prevalent and acceptible. If you need TCO and you're using monadic style code, then you should certainly use Scalaz, which has the best "library" TCO I've seen (and I'd like to see it replace scala.util.control.TailCalls)
- Josh
Note: The TCO example is a bit contrived. I'm not sure I've ever seen that particular scenario with option. In my experience, the place where TCO is crucial is when using Iteratee IO and monadic workflows.
On Fri, Dec 16, 2011 at 4:39 AM, Naftoli Gugenheim <naftoligug@gmail.com> wrote:
.map(_.f) defines a new anonymous class --- scalac translates it into .map(x => x.f) into new Function1{ def apply(x) = x.f } which of course becomes a class with anon and dollar signs in its name, which of course means a heavier jar file.
The tail recursion scenario is something like this:
@tailrec def f(a: A): A = { ... x match { case Some(y) => f(y) case None => a }
I believe the above compiles. On the other hand I'm quite sure the following does not:
@tailrec def f(a: A): A = { ... x map f getOrElse a }
Just to be clear, I'm not debating theory. I'm talking about the existence of certain specific considerations, whose importance may depend on the particular scenario.
On Fri, Dec 16, 2011 at 2:37 AM, Tony Morris <tonymorris@gmail.com> wrote:The reasons you give for this improvement are incorrect. Not only have
you not provided an "exception to this rule", but you will not be able
to because there isn't one.
You gain nothing in "reducing the number of classes", since the number
remains exactly the same. Your first point is trivially refuted.
Further, tail recursion is irrelevant here since scala.Option is not a
recursive ADT. I assume you were referring to scala.List (which is a
recursive ADT) and its map implementation, where even there, your point
is invalid because the map implementation uses heap and not stack by
hand-rolling the loop. That is to say, if you were to write map using
tail-recursion, you would lose for no gain because you'd need to perform
two list passes, where only one is necessary (assuming you have chosen
to use heap) while gaining nothing for this degeneration.
I am trying to help you be less wrong, but I cannot save you from still
being wrong. This is because the original point is correct. Please try.
On 16/12/11 17:27, Naftoli Gugenheim wrote:
> I don't disagree to on its equational equivalence, but that doesn't
> equationally equal that it's necessarily "better written" the more
> idiomatic way.
> There are exceptions to every rule, except that one.
>
>
> On Fri, Dec 16, 2011 at 1:58 AM, Tony Morris <tonymorris@gmail.com> wrote:
>
>> On 16/12/11 16:55, Naftoli Gugenheim wrote:
>>> On Thu, Dec 15, 2011 at 8:44 AM, Tony Morris <tmorris@tmorris.net>
>> wrote:
>>>> As aside, please note that
>>>>
>>>> {
>>>> case None => None
>>>> case Some(x) => Some(x.f)
>>>> }
>>>>
>>>> is better written:
>>>>
>>>> .map(_.f)
>>>>
>>> Except when you're trying to reduce the number of classes your library
>>> weighs (as a certain very heavily used scala library is), or when it
>> breaks
>>> tail recursion.
>>>
>> It's equivalent by equational reasoning. So no, not except then or then
>> either.
>>
>> --
>> Tony Morris
>> http://tmorris.net/
>>
>>
>>
--
Tony Morris
http://tmorris.net/
The tail recursion scenario is something like this:
@tailrec def f(a: A): A = { ... x match { case Some(y) => f(y) case None => a }
I believe the above compiles. On the other hand I'm quite sure the following does not:
@tailrec def f(a: A): A = { ... x map f getOrElse a }
Just to be clear, I'm not debating theory. I'm talking about the existence of certain specific considerations, whose importance may depend on the particular scenario.
On Fri, Dec 16, 2011 at 2:37 AM, Tony Morris <tonymorris@gmail.com> wrote: