8

How can one construct an object from a type alias in scala?

type MyType = List[Int]
println(List[Int]())
println(MyType())  // error: not found: value MyType

This is problematic in a function that must return a new instance of that type. Basic Example:

def foo(x: MyType): MyType = {
  if (x.head == 0) MyType() // Should Nil be used?
  else if (x.head == -1) new MyType(1,2,3,4)
  else x
}

How can foo become unaware of the actual type of MyType?

cheezsteak
  • 2,731
  • 4
  • 26
  • 41
  • you do realize that my answer gives you what you asked for whereas the answer you selected does not... right? – Nate Nov 04 '14 at 16:09
  • 1
    Yes your answer provides the explicit results requested, but it offers little explanation how it is achieved and why it is necessary. Your answer also offers no explanation as why the expected solution was not possible, where the selected answer does. Perhaps I phrased my question inappropriately; _why_ in this case is more pertinent than _how_. – cheezsteak Nov 04 '14 at 16:32
  • It's too bad that _why_ didn't show up in your question or else it could've been answered. Even the selected answer doesn't describe the pattern of type vs. companion object. – Nate Nov 04 '14 at 16:40
  • If you believe you can provide more insight than the selected question then please do. – cheezsteak Nov 04 '14 at 16:57
  • Insight has been generated (see below). I hope you've been enjoying learning Scala; it's absolutely amazing. – Nate Nov 04 '14 at 17:17

4 Answers4

13

Scala (like Java) has different namespaces for types and values, and a type alias only introduces the alias into the type namespace. In some cases you can pair the alias with a val referring to the companion object to get the effect you're looking for:

scala> case class Foo(i: Int)
defined class Foo

scala> type MyType = Foo
defined type alias MyType

scala> val MyType = Foo
MyType: Foo.type = Foo

scala> MyType(1)
res0: Foo = Foo(1)

This won't work with List[Int], though, since while both the List type and the List companion object's apply method have a type parameter, the List companion object itself doesn't.

Your best bet is to use something like Nil: MyType, but you'll find that in general using type aliases like this (i.e. just as a kind of abbreviation) often isn't the best solution.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • What's wrong with `val MyType = List`? Then `MyType[Int]()` will desugar to `MyType.apply[Int]()` and everyone is happy... – gzm0 Nov 03 '14 at 21:41
  • @gzm0: Having `MyList` be both generic (for the term) and specific to `Int` (for the type) seems like a recipe for confusion to me. – Travis Brown Nov 03 '14 at 23:18
  • Oh, yes indeed. I saw that wrong... Thought the alias is higher-kinded. Sorry for the noise. – gzm0 Nov 03 '14 at 23:29
1

It looks to me you're trying to achieve something with the wrong set of tools. But in any case there is no way to instantiate a type alias.

Is your goal to make the function work on a set of different collection types as opposed to only Lists? In that case you need more than just nominal decoupling; currently the function still relies on the method signatures of List.

You can decouple your function from the interface of List by using type classes, but you'll have to wrap every method call you'll ever want to make in MyOps:

import scala.language.higherKinds
import scala.reflect.ClassTag

trait MyOps[L[_], T] {
  def head(xs: L[T]): T
  def tail(xs: L[T]): L[T]
  def fromList(xs: List[T])(implicit ev: ClassTag[T]): L[T]
}

implicit def listMyOps[T] = new MyOps[List, T] {
  def head(xs: List[T]) = xs.head
  def tail(xs: List[T]) = xs.tail
  def fromList(xs: List[T])(implicit ev: ClassTag[T]) = xs
}

implicit def arrayMyOps[T] = new MyOps[Array, T] {
  def head(xs: Array[T]) = xs(0)
  def tail(xs: Array[T]) = xs.slice(1, xs.size)
  def fromList(xs: List[T])(implicit ev: ClassTag[T]) = xs.toArray
}

def foo[L[_]](xs: L[Int])(implicit ev: MyOps[L, Int]) = {
  ev.fromList(xs = if (ev.head(xs) == -1) List(1, 2, 3) else Nil)
}

println(foo(List(0, 1, 2, 6)))
println(foo(Array(-1, 6, 8)))

outputs:

List()
[I@54b63af0

— the first foo call takes a List and returns a List; the 2nd one Array and Array respectively.

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • 1
    The goal is clearly abstraction and identification. Users of MyType need not know it is a List[Int] and one can differentiate a MyType from any other random List[Int] – Eric Hartford Nov 03 '14 at 18:39
  • So you want to be able to swap out the backing type of `MyType` for something other than `List`? (wait, nvm; I thought you were the asker) – Erik Kaplun Nov 03 '14 at 18:45
  • Yes that is the idea. I want functions using `MyType` to be abstracted from the actual type. – cheezsteak Nov 03 '14 at 19:01
  • OK, so start by describing the values the functions need to deal with — then I'll show you how to create a type class that maps real values to your abstract values. – Erik Kaplun Nov 03 '14 at 19:47
1

A type is just a type, not any information about how to create instances of that.

You would have to provide it with a function that constructs an instance of the alias.

So for example

def foo(x: MyType)(create: List[Int] => MyType) =
  if (x.head == 0) create(Nil) // Should Nil be used?
  else if (x.head == -1) create(List(1,2,3,4))
  else x

But as Erik says, it sounds like you are doing something a bit backwards.

johanandren
  • 11,249
  • 1
  • 25
  • 30
0

In the world of Scala Collections for every collection type there exists a companion object that helps make it easier to construct instances of the collection.

For simplicity consider this class and object of the same name:

object Numeric {
  def apply(s: String): Numeric = new Numeric(s.toDouble)
}

case class Numeric(v: Double) 

If you only had the case class then writing Numeric("5.1") would be erroneous. With the object you can actually now call Numeric.apply("5.1") or (because apply is a special method) you can simply write Numeric("5.1"). object in Scala is analogous to holding all static methods that you would write in Java.

Back to your example, MyType is only the type alias to List[Int] and does not bring the List companion object into scope with the name MyType. Your example is the equivalent of my Numeric example without the companion object.

Thus, my answer, is to create a simple and generic way to construct your companion object that hides the fact that 1) MyType is an alias and 2) that it restricts the collection type to Int. If you have lots of type aliases like this one in your code then you'll probably want the more generic version here:

import scala.collection.GenTraversable
import scala.collection.generic.GenericCompanion

class TypeCompanion[CC[X] <: GenTraversable[X], T](companion: GenericCompanion[CC]) {
  type InnerType = CC[T]
  def apply(elems: T*): InnerType = companion.apply(elems: _*)
  def empty(): InnerType = companion.empty[T]
}

object MyType extends TypeCompanion[List, Int](List)
type MyType = MyType.InnerType

If you want to reduce the number of times that you write List, and will not mind the extra typing if you need to change from Int to some other type, then you may prefer this variation:

class TypeCompanion[CC[X] <: GenTraversable[X]](companion: GenericCompanion[CC]) {
  type InnerType = CC[Int]
  def apply(elems: Int*): InnerType = companion.apply(elems: _*)
  ...
}

object MyType extends TypeCompanion(List)
type MyType = MyType.InnerType

Both implementations give your foo method this implementation:

def foo(x: MyType): MyType = {
  if (x.head == 0) MyType()
  else if (x.head == -1) MyType(1,2,3,4)
  else x
}

Note that the type restrictions on GenTraversable and GenericCompanion are just a clever way of restricting to companion objects that match scala collection's conventions.

Community
  • 1
  • 1
Nate
  • 2,205
  • 17
  • 21
  • I don't understand the purpose of the downvote... But my intention here is to demonstrate how you can define a MyType that is constrained by a GenericCompanion that enables one to construct instances of MyType. – Nate Nov 03 '14 at 18:45
  • I simplified my answer greatly. With this example you have the ability to just do: MyType() to obtain what List() would do. – Nate Nov 03 '14 at 19:04
  • but what is the point of a this? give an answer that does *exactly* what was asked? because that's hardly useful. – Erik Kaplun Nov 03 '14 at 19:50