According to the docs, None
object is intended to "represent non-existent values". As far as I've seen it's mostly used as an empty Option
. But do you think it's a good idea to use it for other purposes. For example, in my library I want to have an universal "Empty" object which could be assigned for various missing values, where I would just implicitly convert the "Empty" value to my types as needed:
// In library:
trait A {
implicit def noneToT1(none: Option[Nothing]): T1 = defaultT1
implicit def noneToT2(none: Option[Nothing]): T2 = defaultT2
def f1: T1
def f2: T2
}
// In the code that uses the library
class EmptyA extends A {
def f1 = None
def f2 = None
}
One reason for not (mis)using None
in this fashion is that the user would expect that f1
and f2
return Option[T1]
and Option[T2]
respectively. And they don't. Off course, I could have def f1: Option[T1]
, but in this case the values are not actually optional, they just can have some default empty value, or a real value, I just want to create the default values "under the hood" and have some uniform way of saying "default" or "empty" through the entire library. So the question is, should I use None
to express this "defaultness" or go for some custom type? Right now I'm using my own object Empty
, but it feels a bit superfluous.
EDIT: To ilustrate my question I'll add the code I am using right now:
// In library:
trait Empty
object Empty extends Empty
trait A {
implicit def emptyToT1(none: Empty): T1 = defaultT1
implicit def emptyToT2(none: Empty): T2 = defaultT2
def f1: T1
def f2: T2
}
// In the code that uses the library
class EmptyA extends A {
def f1 = Empty
def f2 = Empty
}
class HalfFullA extends A {
def f1 = Empty
def f2 = someValue2
}
class FullA extends A {
def f1 = someValue1
def f2 = someValue2
}
My question is quite simple: is it a good idea to use scala's None
instead of my Empty
?
I would just use typeclasses for this:
trait WithDefault[T] {
def default: T
}
object WithDefault {
// if T1 is an existing class
implicit val t1Default = new WithDefault[T1] {def default = defaultT1}
}
//if T2 is your own class:
class T2 ...
object T2 {
implicit val withDefault = new WithDefault[T2] {def default = defaultT2}
}
then somewhere convenient:
def default[T : WithDefault] = implicitly[WithDefault[T]].default
and use:
class EmptyA {
def f1 = default[T1]
def f2 = default[T2]
}
Update: To accomudate Vilius, one can try this:
def default = new WithDefault[Nothing]{def default = error("no default")}
implicit def toDefault[U, T](dummy: WithDefault[U])(implicit withDefault: WithDefault[T]): T = withDefault.default
class EmptyA {
def f1: T1 = default
def f2: T2 = default
}
This has the benefit over the OP's original attempt in that each new class can define its own default (and others in WithDefault), rather than have everything in a trait A
.
However, this doesn't work. See https://issues.scala-lang.org/browse/SI-2046
To work around this:
trait A {
def f1: T1
def f2: T2
implicit def toT1Default(dummy: WithDefault[Nothing]) = toDefault[T1](dummy)
implicit def toT2Default(dummy: WithDefault[Nothing]) = toDefault[T2](dummy)
}
class EmptyA extends A {
def f1 = default
def f2 = default
}
I think you should go for something much simpler. For instance, starting with your example and deleting extraneous stuff we very quickly get to,
trait A {
def noT1 = defaultT1
def noT2 = defaultT2
def f1: T1
def f2: T2
}
class EmptyA extends A {
def f1 = noT1
def f2 = noT2
}
I really don't see that the addition of Options or implicits to this would add any value, at least not unless there's some other unstated context for the question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With