Sawyer
Sawyer

Reputation: 15917

chaining Option using orElse changed element type

I find that after chaining Option using orElse, the element type changed, which made me impossible to use it as expected.

For example:an xml with the type of NodeSeq.

scala> xml1.headOption map { head => None } orElse xml2.lastOption map {last => Some(last)}
res11: Option[Some[ScalaObject with Equals]] = Some(Some(None))

before the orElse, head is of type Node, which is correct, but last should also be of type Node, but the compiler thinks it is of type ScalaObject with Equals, and i cannot pass last to functions take a Node as a parameter.

I can cast it using last.asInstanceOf[Node], is there a way to avoid this cast and still make last of type Node?

Upvotes: 1

Views: 320

Answers (3)

DaoWen
DaoWen

Reputation: 33019

Try this instead (notice the added parenthesis):

(xml.headOption map { head => None }) orElse (xml.lastOption map {last => Some(last)})

The problem is that the map is being applied after orElse rather than before orElse. In other words, the expression is parsed like this:

((xml.headOption map { head => None }) orElse xml.lastOption) map {last => Some(last)}

Since the expression on the left-hand-side of orElse has type Option[Option[Node]] but the right-hand-side expression has type Option[Node] the lowest common supertype is Option[ScalaObject with Equals].

Scala's flexible syntax can make coding very nice at times, but other times it results in crazy things like this. For complex expressions like this it's a good idea to add parenthesis to make your meaning more explicit.

Upvotes: 3

Submonoid
Submonoid

Reputation: 2829

In general, I think you need to re-examine what exactly you're doing, since it doesn't make much sense. xml.headOption map { head => None } is always going to resolve to Some(None) (in your case), which is rarely what you want. My best guess is that you want something like:

xml.headOption.orElse(xml.lastOption)

Though it's worth noting that if headOption is None, so will lastOption be.

Edit: As Dao Wen points out, it's quite possible to infer the correct upper bound of Option[Node] in this example, and the issue obscuring it was that of the wrong orElse being called. However, I still think that the real issue here is that what you're doing doesn't make much sense, and should probably be reconsidered.

Upvotes: 1

Didier Dupont
Didier Dupont

Reputation: 29528

map {head => None} is probably not what you intend. It that a typo?

After that, you have an Option[None.type], a not too interesting type which has only two possible values, None and Some(None). As your xml is not empty, it happens to be Some(None).

xml.LastOption has type Option[Node]

So when you do the orElse bettwen them it looks for the most precise common supertype (least upper bound), of None.type and Node. And there is none of interest, it happens to be ScalaObject with Equals, it could just as well be AnyRef.

Your final map {last => Some(last)} is rather strange too. What do you intend there?

Upvotes: 0

Related Questions