简体   繁体   中英

Instantiating a Generic Type in Scala

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM