score:1

Accepted answer

if you really wanted to, you could have something typeclass-based:

def convert[i, o](in: i)(implicit c: conversionrule[i, o]): o = {
  if (c.isconvertible(in)) c.convert(in)
  else c.zero
}

trait conversionrule[i, o] {
  def isconvertible(in: i): boolean
  def convert(in: i): o
  def zero: o // could possibly derive the zero from, e.g., a cats monoid instance where such exists
}

the eagle-eyed may notice that the isconvertible/convert methods match the contract of partialfunction[i, o]'s isdefinedat/apply, so may as well just use partialfunction (and rewrite convert with isdefinedat/apply)

trait conversionrule[i, o] extends partialfunction[i, o] {
  def zero: o
}

zero can be implemented in terms of partialfunction.applyorelse, but for the case where zero is constant (which is the case where referential transparency is preserved), this is much faster.

smart constructors can be defined:

object conversionrule {
  def apply[i, o](zerovalue: o)(pf: partialfunction[i, o]): conversionrule[i, o] =
    new conversionrule[i, o] {
      override def apply(i: i): o = pf(i)
      override def isdefinedat(i: i): boolean = pf.isdefinedat(i)
      val zero: o = zerovalue
    }

  def totalconversion[i, o](f: i => o): conversionrule[i, o] =
    new conversionrule[i, o] {
      override def apply(i: i) = f(i)
      override def isdefinedat(i: i) = true
      override def zero: o = throw new assertionerror("should not call since conversion is defined")
    }

  // might want to put this in a `lowpriorityimplicits` trait which this object extends
  implicit def identityconversion[i]: conversionrule[i, i] =
    totalconversion(identity)
}

identityconversion means that a convertinttoint gets automatically generated.

convertstringtoint can then be defined as

implicit val stringtointconversion = conversionrule[string, int](0) {
  case x if x.forall(_.isdigit) => x.toint
}

one can define a tostring based conversion (basically the non-lawful show proposed for alleycats):

implicit def generictostring[i]: conversionrule[i, string] =
  conversionrule.totalconversionrule(_.tostring)

and it should then be possible to define a stringviaint conversionrule derivation like:

implicit def stringviaint[i, o](implicit toint: conversionrule[i, int]): conversionrule[i, string] =
  convert(convert(in)(toint))

the only really useful thing this provides is an opt-in to usage of implicit conversions. whether that's enough of a gain to justify? shrug

(disclaimer: only the scala compiler in my head has attempted to compile this)


Related Query

More Query from same tag