Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type parameters versus member types in Scala

I'd like to know how do the member types work in Scala, and how should I associate types.

One approach is to make the associated type a type parameter. The advantages of this approach is that I can prescribe the variance of the type, and I can be sure that a subtype doesn't change the type. The disadvantages are, that I cannot infer the type parameter from the type in a function.

The second approach is to make the associated type a member of the second type, which has the problem that I can't prescribe bounds on the subtypes' associated types and therefore, I can't use the type in function parameters (when x : X, X#T might not be in any relation with x.T)

A concrete example would be:

I have a trait for DFAs (could be without the type parameter)

trait DFA[S] { /* S is the type of the symbols in the alphabet */
  trait State { def next(x : S); }
  /* final type Sigma = S */
}

and I want to create a function for running this DFA over an input sequence, and I want

  • the function must take anything <% Seq[alphabet-type-of-the-dfa] as input sequence type
  • the function caller needn't specify the type parameters, all must be inferred
  • I'd like the function to be called with the concrete DFA type (but if there is a solution where the function would not have a type parameter for the DFA, it's OK)
  • the alphabet types must be unconstrained (ie. there must be a DFA for Char as well as for a yet unknown user-defined class)
  • the DFAs with different alphabet types are not subtypes

I tried this:

def runDFA[S, D <: DFA[S], SQ <% Seq[S]](d : D)(seq : SQ) = ....

this works, except the type S is not inferred here, so I have to write the whole type parameter list on each call site.

def runDFA[D <: DFA[S] forSome { type S }, SQ <% Seq[D#Sigma]]( ... same as above

this didn't work (invalid circular reference to type D??? (what is it?))

I also deleted the type parameter, created an abstract type Sigma and tried binding that type in the concrete classes. runDFA would look like

def runDFA[D <: DFA, SQ <% Seq[D#Sigma]]( ... same as above

but this inevitably runs into problems like "type mismatch: expected dfa.Sigma, got D#Sigma"

Any ideas? Pointers?

Edit:

As the answers indicate there is no simple way of doing this, could somebody elaborate more on why is it impossible and what would have to be changed so it worked?

The reasons I want runDFA ro be a free function (not a method) is that I want other similar functions, like automaton minimization, regular language operations, NFA-to-DFA conversions, language factorization etc. and having all of this inside one class is just against almost any principle of OO design.

like image 272
jpalecek Avatar asked Feb 26 '09 20:02

jpalecek


People also ask

What is type parameter in Scala?

Language. Methods in Scala can be parameterized by type as well as value. The syntax is similar to that of generic classes. Type parameters are enclosed in square brackets, while value parameters are enclosed in parentheses.

What is a type in Scala?

Scala is a statically typed programming language. This means the compiler determines the type of a variable at compile time. Type declaration is a Scala feature that enables us to declare our own types.

What is type constructor in Scala?

You can use certain types (like Int or String ) using literals ( 1 or 2 for Int or "James" for String ). Type constructors are types that need other types to be built. List or Option won't be a fully qualified type unless you pass another type to them (i.e. List[Int] or Option[String] – mfirry.

How does Scala determine types when they are not specified?

Non-value types capture properties of identifiers that are not values. For example, a type constructor does not directly specify a type of values. However, when a type constructor is applied to the correct type arguments, it yields a first-order type, which may be a value type.


1 Answers

First off, you don't need the parameterisation SQ <% Seq[S]. Write the method parameter as Seq[S]. If SQ <% Seq[S] then any instance of it is implicitly convertable to Seq[S] (that's what <% means), so when passed as Seq[S] the compiler will automatically insert the conversion.

Additionally, what Jorge said about type parameters on D and making it a method on DFA hold. Because of the way inner classes work in Scala I would strongly advise putting runDFA on DFA. Until the path dependent typing stuff works, dealing with inner classes of some external class can be a bit of a pain.

So now you have

trait DFA[S]{
  ...

  def runDFA(seq : Seq[S]) = ...
}

And runDFA is all of a sudden rather easy to infer type parameters for: It doesn't have any.

like image 138
DRMacIver Avatar answered Oct 18 '22 12:10

DRMacIver