0

I get the following match warning.

"match may not be exhaustive. It would fail on the following input: Some((x: Abstract forSome x not in (A, B, C)))"

code:

abstract class Abstract
case class A() extends Abstract
case class B() extends Abstract
case class C() extends Abstract

class matcher {
  def matcher(a: Option[Abstract]) = a match {
    case None      => true  
    case Some(A()) => false 
    case Some(B()) => false 
    case Some(C()) => false 
  }
}

In case you wonder, the case classes here have no arguments but in my real code they do. I hope the answer is not "the compiler can't know whether or not there are additional subclassess of Abstract somewhere in the program...

Is making the abstract class sealed the only solution? scala is not a very dynamic language, so how come the compiler doesn't know that the group mentioned in the compilation warning is an empty group?

matanster
  • 15,072
  • 19
  • 88
  • 167
  • 4
    Yes, making `Abstract` `sealed` is the only solution, other than matching `_`. Given the question, why would you not want to make it `sealed`? Of course implementations can be added at run time - just add a library with some more `Abstract` implementations. The compiler can only know the _compiletime_ classpath and never the _runtime_ classpath. – Boris the Spider Apr 11 '15 at 09:51
  • @BoristheSpider I reworded a little to focus the question on the latter aspect. Not sure how adding a library at runtime is possible, in swaying the compiler off its feat knowing its types. Care to try an answer? – matanster Apr 11 '15 at 09:52
  • 1
    What? One major selling point of the JVM is that you create an trait/interface and compile against it; only supplying the implementation at run time. Of course the compiler doesn't know all possible types that can be. There is nothing to stop you including a `case class Z() extends Abstract` is another compilation unit. – Boris the Spider Apr 11 '15 at 09:53
  • Would a case in point be that my code were to be a library, and it will have been used by a user project, that extended my unsealed abstract type? – matanster Apr 11 '15 at 09:57
  • 1
    Most definitely a very good example, yes. – Boris the Spider Apr 11 '15 at 09:57

1 Answers1

1

This is probably not ideal but it'll work if you don't want to seal Abstract.

abstract class Abstract
sealed abstract class SealedAbstract
case class A() extends SealedAbstract
case class B() extends SealedAbstract
case class C() extends SealedAbstract

class matcher {
    def matcher(a: Option[Abstract]) = a match {
        case None => true  
        case Some(thing) => matcher(thing)
    }
    def matcher(a: Abstract) = a match{
        case seal: SealedAbstract => matcher(seal)
        case _ => false
    }
    //this will be exhaustive
    def matcher(a: SealedAbstract) a match{
        case A() => false
        case B() => false
        case C() => false
    }
}

A better (and more maintainable/extensible) way to perform logic against the Abstract objects is using type classes.

abstract class Abstract
sealed abstract class SealedAbstract
case class A() extends SealedAbstract
case class B() extends SealedAbstract
case class C() extends SealedAbstract

trait PerformAction[Type <: Abstract]{def doSomething(in: Type): String}
implicit object SealedPerformAction extends PerformAction[SealedAbstract]{
    override def doSomething(sa: SealedAbstract): String = "It does."
}

class matcher {
    def doIfExists[Type <: Abstract](a: Option[Type])(implicit ev: PerformAction[Type]): String = a match{
        case None => ""
        case Some(thing) => ev.doSomething(thing)
    }
}

With a type class you get the exhaustive pattern matching and you get type safety outside of your library as any user of your library must implement a PerformAction if she implements her own Abstract.

Marcus Henry
  • 134
  • 4