简体   繁体   中英

Why does this scala fold return Equals types?

I'm trying to return a map in scala. Here's my code:

    val interestRegEx = """(\w+) Interests \((.+ Interest)\)""".r
    val singleAttributes = Seq("Sport Interests (Some Interest):Running,Swimming","Something else:True")
    val interests = singleAttributes.map { x =>
      // e.g. ("Sport Interests (Some Interest)", "Running,Swimming") should
      // result in ("Sport Running" -> "Some Interest", "Sport Swimming" -> "Some Interest")
      val attVal = x.split(':')

      attVal(0) match {
        case interestRegEx(interestProduct, interestAmount) =>
          Some(attVal(1).split(",").map { i =>
            Map(s"$interestProduct $i" -> interestAmount)
          }.reduce(_ ++ _))
        case _ => None
      }
    }.fold(Map[String, String]())(_) //.reduce(_ + _)

The problem is trying to reduce the collection to a single Map[String, String]. I thought the fold might work, but since it doesn't perhaps I'd even need to add a reduce(_ + _) afterwards, but that doesn't work either.

The part I don't understand is that IntelliJ tells me that interests has type ((Equals, Equals) => Equals) => Equals . WTF? Where are these Equals coming from, and why isn't it just adding all of the Maps together to return a Map containing all keys & values?

If we simplify your example we will get:

  val interests = Seq[String]().map { x =>
    Option[Map[String, String]](Map("k" -> "v"))
  }.fold(Map[String, String]())(_)

This means that we are trying to fold Seq[Option[Map[String, String]]] with initial value Map[String, String]() :

def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)

From fold definition we can see that compiler expects that Option[Map[String, String]] (each value in folding) and Map[String, String] (init value) should be of the same type.

If you inspect Option hierarchy you will see:

Option -> Product -> Equals

For Map we have the following:

Map -> GenMap -> GenMapLike -> Equals (traits hierarchy is complicated, may be another chains exist).

So we can see tha the nearest common type is Equals .

Second part of your puzzle is (_) .

This is treated by compiler as an argument of lambda:

val interests = x => /*omited*/.fold(Map[String, String]())(x)

As we saw x is (A1, A1) => A1 . And in our case it is:

(Equals, Equals) => Equals

The result of fold is A1 which is also Equals .

As a result lambda type is:

((Equals, Equals) => Equals) /*<< arg*/ => /*result >>*/ Equals

UPDATE:

To solve your problem I think you should use:

.flatten.reduce(_ ++ _)

When you get a trait like Product or Equals as the inferred type of a statement, you can usually bet you have a type mismatch in some higher order function. You typically don't get Any or AnyRef because those only happen if some set of types have nothing in common. One thing that sticks out to me is that your 2nd argument to fold only takes one parameter. Amazingly enough, that typechecks, but it's probably what's giving you the types you don't expect.

I think what you wanted to do is something more like:

val interestRegEx = """(\w+) Interests \((.+ Interest)\)""".r
val singleAttributes = Seq("Sport Interests (Some Interest):Running,Swimming","Something else:True")
val interests = singleAttributes.map { x =>
  // e.g. ("Sport Interests (Some Interest)", "Running,Swimming") should
  // result in ("Sport Running" -> "Some Interest", "Sport Swimming" -> "Some Interest")
  val attVal = x.split(':')

  attVal(0) match {
    case interestRegEx(interestProduct, interestAmount) =>
      attVal(1).split(",").map { i =>
        Map(s"$interestProduct $i" -> interestAmount)
      }.reduce(_ ++ _)
    case _ => Map.empty
  }
}.reduce(_ ++ _)

For this I got:

scala> interests
res0: scala.collection.immutable.Map[_ <: String, String] = Map(Sport Running -> Some Interest, Sport Swimming -> Some Interest)

I got rid of the Option wrapping the Map s in your regex match. Since you're planning on combining the maps, may as well use the empty map as your non-match case. Then I used reduce instead of fold for your final match, much as you had done in the inner map .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM