5

Here's my code:

object Triple {
  object Num extends Enumeration {
    type Num = Value
    val one, two, three, four, five, six, seven, eight, nine = Value
  }
  val triples = for {
    first <- Num.values
    second <- Num.values
    third <- Num.values
    if (first != second)
    if (first != third)
    if (second != third)
  } yield Set(first, second, third)
}

I get an error on the line with third. It says:

Multiple markers at this line
    - not enough arguments for method map: (implicit bf: 
         scala.collection.generic.CanBuildFrom[sudoku.Triple.Num.ValueSet,scala.collection.immutable.Se    t[sudoku.Triple.Num.Value],That])That. Unspecified 
 value parameter bf.
- diverging implicit expansion for type 
 scala.collection.generic.CanBuildFrom[sudoku.Triple.Num.ValueSet,scala.collection.immutable.Set[sudoku.Triple.Num.Value],That] starting with 
 method newCanBuildFrom in object SortedSet
- not enough arguments for method map: (implicit bf: 
 scala.collection.generic.CanBuildFrom[sudoku.Triple.Num.ValueSet,scala.collection.immutable.Set[sudoku.Triple.Num.Value],That])That. Unspecified 
 value parameter bf.
- diverging implicit expansion for type 
 scala.collection.generic.CanBuildFrom[sudoku.Triple.Num.ValueSet,scala.collection.immutable.Set[sudoku.Triple.Num.Value],That] starting with 
 method newCanBuildFrom in object SortedSet

Update: Aleksey Izmailov gave a great answer. A slightly better implementation of the function is val triples=Num.values.subset(3).toSet

Joe
  • 7,922
  • 18
  • 54
  • 83
  • 1
    http://stackoverflow.com/questions/4883906/what-is-a-diverging-implicit-expansion-error – Ashalynd Jun 19 '14 at 02:34
  • 1
    Please take a look at http://stackoverflow.com/a/10938239/2684760 – ForNeVeR Jun 19 '14 at 02:52
  • 2
    @ForNeVeR - I actually found the issue linked from that question more useful: https://issues.scala-lang.org/browse/SI-5534 – DaoWen Jun 19 '14 at 03:13
  • This issue is not resolved, but behavior changed already in some cases between 2.10 and 2.11. Maybe there is another related issue that caused regression. – yǝsʞǝla Jun 19 '14 at 04:07

1 Answers1

4

Looks like what you are trying to do is this:

scala> object Num extends Enumeration {
     |     type Num = Value
     |     val one, two, three, four, five, six, seven, eight, nine = Value
     |   }
defined module Num

scala>   val triples = Num.values.toList.combinations(3)
triples: Iterator[List[Num.Value]] = non-empty iterator

scala> triples.toList
res0: List[List[Num.Value]] = List(List(one, two, three), List(one, two, four), List(one, two, five), List(one, two, six), List(one, two, seven), List(one, two, eight), List(one, two, nine), List(one, three, four), List(one, three, five), List(one, three, six), List(one, three, seven), List(one, three, eight), List(one, three, nine), List(one, four, five), List(one, four, six), List(one, four, seven), List(one, four, eight), List(one, four, nine), List(one, five, six), List(one, five, seven), List(one, five, eight), List(one, five, nine), List(one, six, seven), List(one, six, eight), List(one, six, nine), List(one, seven, eight), List(one, seven, nine), List(one, eight, nine), List(two, three, four), List(two, three, five), List(two, three, six), List(two, three, seven), List(two, three...
scala> 

If you do something like this then it works:

scala> object Triple {
     |   object Num extends Enumeration {
     |     type Num = Value
     |     val one, two, three, four, five, six, seven, eight, nine = Value
     |   }
     |   val triples = for {
     |     first <- Num.values
     |     second <- Num.values
     |     third <- Num.values
     |     if (first != second)
     |     if (first != third)
     |     if (second != third)
     |   } yield (first, second, third)
     | }
defined module Triple

scala> Triple.triples
res1: scala.collection.immutable.SortedSet[(Triple.Num.Value, Triple.Num.Value, Triple.Num.Value)] = TreeSet((one,two,three), (one,two,four), (one,two,five), (one,two,six), (one,two,seven), (one,two,eight), (one,two,nine), (one,three,two), (one,three,four), (one,three,five), (one,three,six), (one,three,seven), (one,three,eight), (one,three,nine), (one,four,two), (one,four,three), (one,four,five), (one,four,six), (one,four,seven), (one,four,eight), (one,four,nine), (one,five,two), (one,five,three), (one,five,four), (one,five,six), (one,five,seven), (one,five,eight), (one,five,nine), (one,six,two), (one,six,three), (one,six,four), (one,six,five), (one,six,seven), (one,six,eight), (one,six,nine), (one,seven,two), (one,seven,three), (one,seven,four), (one,seven,five), (one,seven,six), (one,...
scala> 

Note the result of the produced collection is a SortedSet. That result is defined by Num.values - you can convert for comprehension to map, flatMap and filter to check how the types come about.

The problem is that you can put stuff that can be sorted into SortedSet like this for example:

scala> scala.collection.immutable.SortedSet(1, 2)
res6: scala.collection.immutable.SortedSet[Int] = TreeSet(1, 2)

However, you can't put something that does not have ordering defined into a SortedSet because SortedSet guarantees that the stuff is sorted and should know how sorting is done (via implicit ordering):

scala> scala.collection.immutable.SortedSet(Set(1, 2), Set(3, 4))
<console>:8: error: No implicit Ordering defined for scala.collection.immutable.Set[Int].
              scala.collection.immutable.SortedSet(Set(1, 2), Set(3, 4))

Scala looks for implicit Ordering to be defined in scope and does not find one for the Set that you are using, or for the Set[Int] in my example.

Now let's "fix" it:

scala> implicit val dummyOrdering: Ordering[Set[Int]] = Ordering.by(_.size)
dummyOrdering: Ordering[Set[Int]] = scala.math.Ordering$$anon$9@6f00d9a7

scala> scala.collection.immutable.SortedSet(Set(1, 2), Set(3, 4))
res8: scala.collection.immutable.SortedSet[scala.collection.immutable.Set[Int]] = TreeSet(Set(3, 4))

Obviously, this is a bad fix and the real problem is that in your case types are not well chosen. First of all you don't need to use a Set for each combination since you already comparing things to avoid repetition. Just use a Tuple3. Second, if you insist on using a Set, then convert Num.values to List in the for comprehension:

scala> object Triple {
     |   object Num extends Enumeration {
     |     type Num = Value
     |     val one, two, three, four, five, six, seven, eight, nine = Value
     |   }
     |   val triples = for {
     |     first <- Num.values.toList
     |     second <- Num.values.toList
     |     third <- Num.values.toList
     |     if (first != second)
     |     if (first != third)
     |     if (second != third)
     |   } yield Set(first, second, third)
     | }
defined module Triple

Hope that makes it clear. Basically the problem is in the choice of wrong types.

yǝsʞǝla
  • 16,272
  • 2
  • 44
  • 65
  • This doesn't work in 2.11. Still, this is a good description of how it _should_ work. – wingedsubmariner Jun 19 '14 at 03:26
  • which part doesn't work in 2.11 @wingedsubmariner? I tried it in 2.10.3 REPL. – yǝsʞǝla Jun 19 '14 at 03:30
  • Replacing `Set(first, second, third)` with `(first, second, third)` doesn't change the error produced in 2.11. I can confirm that it does eliminate the error in 2.10.3. – wingedsubmariner Jun 19 '14 at 03:47
  • Very interesting. It must be a regression. strange... :) – yǝsʞǝla Jun 19 '14 at 04:05
  • 1
    Thanks for the great answer! It looks like my error was writing "Set(first,second,third)" instead of "(first,second,third". I don't really understand why that was an error though. I also don't understand why that wouldn't return a list. – Joe Jun 19 '14 at 16:53