Request for feedback on the design of two equivalent types

I implemented my own Try which does not box. It goes more or less like this (Scala 2 version):

type Guard[+X] >: Throwable <: AnyRef

object Done {
  def apply[X](value :X) :Guard[X] = (value match {
    case _ :Throwable | _ :Done[_] => new Done(value)
    case _                         => value
  }).asInstanceOf[Guard[X]]
  
  def unapply[X](guard :Guard[X]) :Option[X] = (guard: Any) match {
    case _ :Throwable          => None
    case x :Done[X @unchecked] => Some(x.value)
    case x :X @unchecked       => Some(x)
  }
}

private case class Done[X](value :X) 

implicit class GuardExtension[X](private val self :Guard[X]) extends AnyVal {
  //Extension methods mimicking Try API go here.
}

As expected for a Try replacements, extension methods are executed in try/catch blocks. However, it can be just as well used in pure functional code which handles errors by explicitly creating an exception and returning it as Guard. Removing of those try/catch blocks would shrink implementation to a third (which is important for inlining efficiency) and allow a couple of additional tricks. This in my eyes is waaaaay preferable to Either[String, O]. Why not both? Lets have an identical type Outcome with the only major distinction being it does not catch exceptions. We can even reuse the underlying runtime representation, namely Done class, so one type can be safely cast to another. Such duplication through copy/paste is of course unacceptable.

My first reaction was to make Outcome a subtype of Guard. This improves interoperability and allows to put all methods which do not return a Guard/Outcome in OutcomeExtension, where they will be available for both types. This greatly simplifies things, as I can have a single Done object, which can create Guard and unapply Outcome, eliminating the need for coming up with a copy of Done for Outcome. It seemed preferable to the alternative of distinct types primarily because the latter would require separate factories. Of course, it’s possible to reuse the same generic implementation for both factories, but the need for separate singleton values would remain. The main issue is that it would create an additional entity and force users to remember that it is Done for Guard and, say, Yield for Outcome. One type being a subtype of the other seemed much cleaner.

However, as extension methods are resolved statically, I am starting to have doubts I made the right call. This design means that the same value will behave differently after upcasting, which is very much against OOP. Because Outcome is a subtype of Guard, uptyping adds checks (which ideally in a working program would be superfluous anyway) rather than remove them if it was the other way round, so the issue isn’t jaring. An alternative could still reuse Done class internally and add toGuard/toOutcome extension methods which just cast the object to the other type.

I would like to hear opinions on which solution you would prefer and if there are arguments pro/against that I haven’t considered.