Type confusion with XMLApiHelper

12 views
Skip to first unread message

Derek Chen-Becker

unread,
Oct 7, 2010, 1:28:45 PM10/7/10
to liftweb
I'm in the middle of rewriting the REST/Web service chapter in Exploring Lift along with big chunks of PocketChange to match. I'm getting an error when I use the XMLApiHelpers trait that I know how to fix, but makes me wonder about how XMLApiHelpers worked in the first place:

[ERROR] /home/software/scalastuff/pocketchangeapp/PocketChange/src/main/scala/com/pocketchangeapp/api/RestAPI.scala:21: error: type mismatch;
[INFO]  found   : scala.xml.Elem
[INFO]  required: net.liftweb.http.LiftResponse
[INFO] Note that implicit conversions are not applicable because they are ambiguous:
[INFO]  both method listElemToResponse in trait XMLApiHelper of type (Seq[scala.xml.Node])net.liftweb.http.LiftResponse
[INFO]  and method nodeSeqToResponse in trait XMLApiHelper of type (scala.xml.NodeSeq)net.liftweb.http.LiftResponse
[INFO]  are possible conversion functions from scala.xml.Elem to net.liftweb.http.LiftResponse
[INFO]       () => Full(e.toXML) // default to XML


As you can see, there are two implicit methods on XMLApiHelper that can convert a scala.xml.Elem to a LiftResponse:

listElemToResponse(x : Seq[scala.xml.Node]) : net.liftweb.http.LiftResponse
nodeSeqToResponse(x : scala.xml.NodeSeq) : net.liftweb.http.LiftResponse

My confusion is that NodeSeq and Seq[scala.xml.Node] are very intertwined, so I'm not sure under what circumstances the compiler would be able to properly determine which implicit to use. I've tried changing the return type of the e.toXML call to be an Elem, Node, NodeSeq, etc, but they all come back with the same error. I'm not sure if I'm missing something extraordinarily simple, or if there's really a problem here.

Thanks,

Derek

David Pollak

unread,
Oct 7, 2010, 4:16:14 PM10/7/10
to lif...@googlegroups.com

What version of scala are you using?

> --
> You received this message because you are subscribed to the Google Groups "Lift" group.
> To post to this group, send email to lif...@googlegroups.com.
> To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.
>

Derek Chen-Becker

unread,
Oct 7, 2010, 5:23:52 PM10/7/10
to lif...@googlegroups.com
I'm only updating the book to Lift 2.0 at this point, so Scala 2.7.7.

Derek




Naftoli Gugenheim

unread,
Oct 7, 2010, 5:29:46 PM10/7/10
to liftweb
What do the two methods do different? And why does the one that takes a Seq[Node] have 'listElem' in its name?


--

David Pollak

unread,
Oct 7, 2010, 5:31:53 PM10/7/10
to lif...@googlegroups.com
On Thu, Oct 7, 2010 at 10:23 PM, Derek Chen-Becker <dchen...@gmail.com> wrote:
I'm only updating the book to Lift 2.0 at this point, so Scala 2.7.7.

Hmmm... I wonder if implicit resolution changed between 2.7.early and 2.7.7... it's not impossible.

I know that for 2.8, the whole implicit thing got mucked about massively.
 



--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Blog: http://goodstuff.im
Surf the harmonics

Derek Chen-Becker

unread,
Oct 7, 2010, 6:50:13 PM10/7/10
to lif...@googlegroups.com
Yeah, I was wondering how many people have actually been using this interface. Naftoli, I wondered the same thing. Since NodeSeq extends Seq[Node] it seems like one of these is redundant. I mean, here's the actual code:

  implicit def nodeSeqToResponse(in: NodeSeq): LiftResponse =  
    buildResponse(true, Empty, in)  
  
  implicit def listElemToResponse(in: Seq[Node]): LiftResponse =  
    buildResponse(true, Empty, in)

For that matter, I'm not sure how many people actually deal in Seq[Node]. I suspect we could remove listElemToResponse and no one would be significantly impacted.

Derek

Naftoli Gugenheim

unread,
Oct 7, 2010, 9:17:07 PM10/7/10
to liftweb
If you remove the other one you have zero impact though, because every NodeSeq automatically qualifies as a Seq[Node].
I wonder if the new implicit prioritization mechanism (which if I understand correctly means a supertype's companion is not searched until the subtype's companion is finished being searched, as opposed to 2.7 where all companion implicits were lumped together and searched for the most specific method using overload resolution) contributes anything here. For some reason my 2.8 installation doesn't have the PDFs, including the language reference, so I would have to download it sometime...

Derek Chen-Becker

unread,
Oct 7, 2010, 10:24:56 PM10/7/10
to lif...@googlegroups.com
Right, sorry, that's what I meant. NodeSeq extends Seq[Node], so it would also match listElemToResponse.

Derek

David Pollak

unread,
Oct 8, 2010, 3:02:18 AM10/8/10
to lif...@googlegroups.com
On Fri, Oct 8, 2010 at 3:24 AM, Derek Chen-Becker <dchen...@gmail.com> wrote:
Right, sorry, that's what I meant. NodeSeq extends Seq[Node], so it would also match listElemToResponse.

It's very important to capture both NodeSeq and Seq[Node] in the implicits because:

implicit def nodeSeqToFoo(in: NodeSeq): Foo = ...

will not catch:

List(1,2,3).map(i => <span>{i}</span>)

But at least in 2.7.x, this would not catch:

List(1,2,3).map(i => <span>{i}</span>): NodeSeq

Why?  Because 2.7.7 does not look at the type parameter of the superclass when it's looking for implicits.

They both need to be there.

There may be a way to do it, however that causes fewer problems in 2.7.7:

implicit def magicNodeSeqToLiftResponse[T](in: T)(implicit f: T => NodeSeq): LiftResponse = ...

That way, Scala will find the T => NodeSeq (in this case, Seq[Node] => NodeSeq) and do the right thing.

Thanks,

David
 

Naftoli Gugenheim

unread,
Oct 8, 2010, 4:50:44 PM10/8/10
to liftweb
Why is
implicit def nodeSeqToFoo(in: Seq[Node]): Foo = ...
not sufficient for both?

David Pollak

unread,
Oct 8, 2010, 4:53:46 PM10/8/10
to lif...@googlegroups.com
On Fri, Oct 8, 2010 at 9:50 PM, Naftoli Gugenheim <nafto...@gmail.com> wrote:
Why is
implicit def nodeSeqToFoo(in: Seq[Node]): Foo = ...
not sufficient for both?


As I said in my prior message: "Why?  Because 2.7.7 does not look at the type parameter of the superclass when it's looking for implicits."

Ross Mellgren

unread,
Oct 8, 2010, 5:00:05 PM10/8/10
to lif...@googlegroups.com
On Oct 8, 2010, at 4:53 PM, David Pollak wrote:
On Fri, Oct 8, 2010 at 9:50 PM, Naftoli Gugenheim <nafto...@gmail.com> wrote:
Why is
implicit def nodeSeqToFoo(in: Seq[Node]): Foo = ...
not sufficient for both?


As I said in my prior message: "Why?  Because 2.7.7 does not look at the type parameter of the superclass when it's looking for implicits."

I'm not sure I follow, hopefully you have time to elaborate, to improve my understanding:

rmellgren@Hrazdan:~$ ~/3rd/scala-2.7.7.final/bin/scala
Welcome to Scala version 2.7.7.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.xml.Node
import scala.xml.Node

scala> case class Blah(in: Seq[Node])                            
defined class Blah

scala> implicit def seqNodeToBlah(in: Seq[Node]): Blah = Blah(in)
seqNodeToBlah: (Seq[scala.xml.Node])Blah

scala> List(1,2,3).map(i => <span>{i}</span>): Blah   
res1: Blah = Blah(List(<span>1</span>, <span>2</span>, <span>3</span>))

scala> import scala.xml.NodeSeq.view               
import scala.xml.NodeSeq.view

scala> view(List(1,2,3).map(i => <span>{i}</span>))     
res2: scala.xml.NodeSeq = <span>1</span><span>2</span><span>3</span>

scala> view(List(1,2,3).map(i => <span>{i}</span>)): Blah
res3: Blah = Blah(<span>1</span><span>2</span><span>3</span>)

It seems to have properly converted List[Node] and NodeSeq to Blah (emultating LiftResponse)

I could be entirely not following though haha :-)

-Ross

David Pollak

unread,
Oct 8, 2010, 5:13:51 PM10/8/10
to lif...@googlegroups.com
In prior versions of Scala (I believe that 2.7.7 is in this list), implicits declared in libraries did not have their type parameters checked.  Maybe it changed in 2.7.7... maybe it didn't, but it was required at one time and should not be removed without insuring that a fully built Lift jar mixed into another project will correctly detect/convert something that returns Node, NodeSeq, Seq[Node], Seq[Elem] and Elem.

Derek Chen-Becker

unread,
Oct 12, 2010, 10:51:15 AM10/12/10
to lif...@googlegroups.com
I'm returning an item of type Elem, which extends Node. Node extends NodeSeq, which matches the nodeSeqToResponse implicit, but NodeSeq also extends Seq[Node], which matches the listElemToResponse implicit. So it seems like, at least in 2.7.7, the conversions are checked all the way up the type hierarchy.

It seems that since Seq[Node] is the topmost class in the hierarchy that listElemToResponse should cover both NodeSeq and Seq[Node]. I'll go ahead and make a test branch and test the conversions as you stated to make sure that things still work if nodeSeqToResponse is gone.

Derek

David Pollak

unread,
Oct 12, 2010, 11:55:47 AM10/12/10
to lif...@googlegroups.com
On Tue, Oct 12, 2010 at 7:51 AM, Derek Chen-Becker <dchen...@gmail.com> wrote:
I'm returning an item of type Elem, which extends Node. Node extends NodeSeq, which matches the nodeSeqToResponse implicit, but NodeSeq also extends Seq[Node], which matches the listElemToResponse implicit. So it seems like, at least in 2.7.7, the conversions are checked all the way up the type hierarchy.

It seems that since Seq[Node] is the topmost class in the hierarchy that listElemToResponse should cover both NodeSeq and Seq[Node]. I'll go ahead and make a test branch and test the conversions as you stated to make sure that things still work if nodeSeqToResponse is gone.

Cool.  Please add a test for Elem, Node and NodeSeq being implicitly converted via Seq[Node] and I'm satisfied.
 

Derek Chen-Becker

unread,
Oct 12, 2010, 3:55:53 PM10/12/10
to lif...@googlegroups.com
OK, I'm almost done coding this up and things look good. One thing I noticed, though, is that the canNodeToResponse implicit takes a NodeSeq while listElemToResponse takes a Seq[Node]. If Scala is doing implicit checks the way we think it is, it shouldn't be a breaking change to broaden canNodeToResponse to take a Seq[Node], but I want to check to make sure that's OK before I slip that in.

Derek

David Pollak

unread,
Oct 12, 2010, 3:57:17 PM10/12/10
to lif...@googlegroups.com
On Tue, Oct 12, 2010 at 12:55 PM, Derek Chen-Becker <dchen...@gmail.com> wrote:
OK, I'm almost done coding this up and things look good. One thing I noticed, though, is that the canNodeToResponse implicit takes a NodeSeq while listElemToResponse takes a Seq[Node]. If Scala is doing implicit checks the way we think it is, it shouldn't be a breaking change to broaden canNodeToResponse to take a Seq[Node], but I want to check to make sure that's OK before I slip that in.

Write tests... as long as the tests pass, I'm cool with it.
 

Derek Chen-Becker

unread,
Oct 12, 2010, 4:12:50 PM10/12/10
to lif...@googlegroups.com
Oh, all I'm doing right now is writing a big Spec to cover all of XMLApiHelper. The actual change to remove nodeSeqToResponse only took about 10 seconds, including starting emacs ;)
Reply all
Reply to author
Forward
0 new messages