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 (i.e. => 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