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

Why is @tailrec rejecting this function?

3 replies
Gordon Tyler
Joined: 2009-06-10,
User offline. Last seen 42 years 45 weeks ago.

When compiling the following, I get the error "could not optimize
@tailrec annotated method", which I presume means that the code is not
tail-call compatible. I'm fairly new to functional programming, so what
am I doing wrong?

@tailrec
private def extractName(attributes:List[String],
obj:ModelObject):Map[String,String] = {
attributes match {
case head :: tail =>
// following line produces Option[String]
val value = obj.owner.getNameAttribute(obj.obj, head)
val namePair = value match {
case Some(s) => Map(head -> s)
case None => Map.empty
}
namePair ++ extractName(tail, obj)
case Nil => Map.empty
}
}

Briefly, given a List of attribute names, build up a Map of attribute
name -> value.

Thanks,
Gordon

Jorge Ortiz
Joined: 2008-12-16,
User offline. Last seen 29 weeks 4 days ago.
Re: Why is @tailrec rejecting this function?
This line:

       namePair ++ extractName(tail, obj)

Is two method calls:

       val tmp = extractName(tail, obj)
       namePair.++(tmp)

For it to be properly tail recursive, extractName needs to be the very last method call in the function.

--j

On Tue, Jun 16, 2009 at 1:30 PM, Gordon Tyler <gordon@doxxx.net> wrote:
When compiling the following, I get the error "could not optimize @tailrec annotated method", which I presume means that the code is not tail-call compatible. I'm fairly new to functional programming, so what am I doing wrong?

 @tailrec
 private def extractName(attributes:List[String],
     obj:ModelObject):Map[String,String] = {
   attributes match {
     case head :: tail =>
       // following line produces Option[String]
       val value = obj.owner.getNameAttribute(obj.obj, head)
       val namePair = value match {
         case Some(s) => Map(head -> s)
         case None => Map.empty
       }
       namePair ++ extractName(tail, obj)
     case Nil => Map.empty
   }
 }

Briefly, given a List of attribute names, build up a Map of attribute name -> value.

Thanks,
Gordon

Gordon Tyler
Joined: 2009-06-10,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is @tailrec rejecting this function?

Jorge Ortiz wrote:
> This line:
>
> namePair ++ extractName(tail, obj)
>
> Is two method calls:
>
> val tmp = extractName(tail, obj)
> namePair.++(tmp)
>
> For it to be properly tail recursive, extractName needs to be the very
> last method call in the function.

Ok, I think I understand how that breaks down. But this implies that
what I'm trying to do is impossible to do in a tail recursive fashion?

Thanks,
Gordon

Colin Bullock
Joined: 2009-01-23,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is @tailrec rejecting this function?

Ok, I think I understand how that breaks down. But this implies that what I'm trying to do is impossible to do in a tail recursive fashion?

In general, when you need to accumulate the results of a recursive call, it can (usually) be transformed into a correctly tail-recursive function by moving the accumulator (the Map[String, String], in your case) to an additional parameter value (rather than the return value).

- Colin

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