I have a case class that looks like this:
case class SomeType[T](x: T)
I now want to provide an empty initialization method via a companion object like this:
object SomeType {
def empty = SomeType(???)
}
How can I instantiate a generic type? I obviously do not want to instantiate it to null or Nill or Any! Is there a better way?
The issue with your question as stated is that SomeType[T]
has a value x
of type T
. What value should x
take for empty ? The only sensible answer is that it should be Nothing
but the type Nothing
is uninhabited . That is, you cannot do this:
def empty[A]: SomeType[A] = SomeType(???)
And you cannot make x
a by-name parameter (ie => T
) because you have a case class (and it would be dumb, even if you could, because someone could do this):
val st: SomeType[Int] = SomeType.empty //would require T to be covariant
val i = st.x //throws Exception
Let's take a step back and start without x
:
sealed trait SomeType[T]
object SomeType {
def empty[A]: SomeType[A] = new SomeType[A] {}
def nonEmpty[A](a: A); SomeType[A] = new SomeType[A] {}
}
If you are modelling something a bit like Option
it's worth thinking about what the fundamental combinator it should have is. Here's one way of encoding Option
for example:
sealed trait MyOption[T] {
def fold[B](n: => B, s: T => B): B //fundamental combinator
//functionality
import MyOption._
final def isEmpty = fold(true, _ => false)
final def flatMap[B](f: A => MyOption[B]): MyOption[B] = fold(empty, f)
final def map[B](f: A => B): MyOption[B] = fold(empty, f andThen nonEmpty)
}
object MyOption {
//These are effectively the constructors
def empty[A]: MyOption[A] = new MyOption[A] {
override final def fold[B](n: => B, s: A => B): B = n
}
def nonEmpty[A](a: A): MyOption[A] = new MyOption[A] {
override final def fold[B](n: => B, s: A => B): B = s(a)
}
}
You could make it covariant and make empty
a val
:
sealed trait MyOption[+T] {
def fold[B](n: => B, s: T => B): B
}
object MyOption {
val empty: MyOption[Nothing] = new MyOption[Nothing] {
override final def fold[B](n: => B, s: Nothing => B): B = n
}
def nonEmpty[A](a: A): MyOption[A] = new MyOption[A] {
override final def fold[B](n: => B, s: A => B): B = s(a)
}
}
Or as Yuval says below, make empty
a singleton:
object MyOption {
object empty extends MyOption[Nothing] {
override final def fold[B](n: => B, s: Nothing => B): B = n
}
def nonEmpty[A](a: A): MyOption[A] = new MyOption[A] {
override final def fold[B](n: => B, s: A => B): B = s(a)
}
}
I generally prefer not to do this because I would rather use the combinators than an explicit pattern-match, which is less amenable to refactoring
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.