What's a Placeholder? Why Do We Need to Define Placeholder Data Type for Generics

What's a placeholder? Why do we need to define placeholder data type for generics?

With credit to both answers provided.

Let's just say you wrote your code as below (without the <T>) and see what happens:

func inspect (value1: T, value2: T) {

// some operation with value1, value2
}

What is the type of T here? Is T a class a struct or an enum? Or it's a generic type as you intended? The compiler doesn't know. It will give the following error:

error: use of undeclared type 'T'

Just because you wrote something with T it's not going to magically transform into a generic.

To better illustrate the problem see this snippet:

func inspect (value1: T, value2: T) {
// some operation with value1, value2

var x = T()
var m = U()

}

class T {
var name = "someName"
}
class U {
var name = "someName"
}

var x = T() <-- Do you expect this to be an instance of class T or a generic type?

Without <T>, x is expected to be an instance of type T. If you haven't defined T as a type, if not then you'd just get an error.


Which is why in real code you see using <T>. The actual type will come into the 'place' where type T was 'holding' onto before. :

func inspect <T> (value1: T, value2: T) {
// some operation with value1, value2

var x = T() // error!
var m = U() // No error

}

class T {
var name = "someName"
}
class U {
var name = "someName"
}

Actually doing x = T() creates an error:

'T' cannot be constructed because it has no accessible initializers

If this error wasn't spotted by the compiler then the T inside the function would be non-generic and would come from the class definition or just unknown...

The reason we do‌ <T> is to create a local vanilla type and avoid having wrong expectations from outside. (Though if you constrain it, then it's different)

Why does Swift supersede the generic T over the class T? I assume it's because Swift gives priority to the local scope.

In java what's the difference between ?, E, T

I'm assuming you're talking about Generics. The 'E' and 'T' are placeholders and can be used interchangeably in class definitions. By convention 'E' is an Element and 'T' is a Type. The question mark is a placeholder for an unknown type. You often see things like this:

List<? extends MyObject> x;

This implies that 'x' is a list of objects that are subclasses of MyObject, but we don't know what they are exactly.

See: http://docs.oracle.com/javase/tutorial/java/generics/genTypes.html

In java, what is the exact meaning of each type parameter of a generic method in a type introduction?

It's hard to understand what exactly you're asking and what exactly you are confused about; I'll try answering your questions anyway.

Type parameters are placeholders for actual types, no matter whether you use them on a class or on a method. Note that this is exactly the same idea as with regular value parameters: they are placeholders for actual values that you supply when you call a method.

public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2)

This is a method with two type parameters, K and V, and two value parameters, p1 and p2.

When you call this method, you'll have to specify actual types for the type parameters, and actual values for the value parameters. In other words: you'll have to specify what types K and V are and you'll have to pass two instances of class Pair for p1 and p2.

The type parameters are "passed" to the type Pair in this example. So, the declaration of method compare means: I have a method named compare which takes two Pair objects with type parameters K and V. Instead of specifying actual, concrete types, I leave those as parameters. By doing this, you can call compare with any Pair with specific types for K and V.

For example, you can call compare with two Pair<Integer, String> objects. Or two Pair<BigDecimal, Long> objects, etc.

Are they placeholders for individual arguments passed into the method?

They are placeholders for types, just like regular parameters are placeholders for values.

Are they placeholders for type parameters passed to the actual object-type arguments passed into the method(i.e. Pair<K, V> //I view this as one unique type: class Pair<K, V> that receives two unique type parameters, K and V)?

Yes... (if I understand your question correctly). The method signature means that the method takes two Pair<K, V> objects, where you leave specifying the actual types to use for K and V to the caller of the method.

Or do generic methods require a unique type parameter for every variable they access whether they are passed as arguments to the method or arguments to an object, which is then passed into the method?

I don't understand what you mean by this question.

Typescript Placeholder Generic

Not sure, if I understood your use-case.

Maybe you just want to introduce a new type for all your attributes e.g.

type MyAttributes = A | B | C;

type MyDataType<T extends MyAttributes> = {
name: string;
age: number;
attributes: T
}

interface ForMyGeneralComponent{
data: MyDataType<MyAttributes>
}

How to use Extract on a Generic placeholder

The main problem is that when T is an unresolved generic type parameter, the compiler does not (as of TS3.5 anyway) perform the sort of type analysis it would need to in order to recognize that "bar" is assignable to Extract<keyof T, string> even when T is constrained to extend IAttrs. Extract is a conditional type, and the compiler generally defers most analysis of unresolved conditional types. Perhaps this particular use case could be filed in GitHub as a suggestion, but I doubt it would be given priority.

So we need to either use a resolved conditional type, or we need to use the unresolved type parameter in a non-conditional type.

  • Resolved conditional type: Since keyof T includes all of keyof IAttrs, the type keyof T is equivalent to keyof T | keyof IAttrs. So Extract<keyof T | keyof IAttrs, string> is equivalent to the problematic type. But since Extract is distributive, it will be evaluated as Extract<keyof T, string> | Extract<keyof IAttrs, string>. The rightmost constituent is a resolved conditional type which will be eagerly evaluated to be just "bar". So now we have Extract<keyof T, string> | "bar". The unresolved conditional type is still there, but now "bar" is explicitly part of the union... and the error should go away.

  • Unresolved non-conditional type: The Extract type was introduced in TS2.8 with conditional types. It filters a union type with a constraint. Before TS2.8 if we wanted to constrain a type like that, we just had to use intersections. It also used to be that intersections over unions were messy, and ("foo" | "bar") & ("bar" | "baz") would evaluate to ("foo" & "bar") | ("foo" & "baz") | "bar" | ("bar" & "baz"). Nowadays though it gets reduced to just "bar", as desired. And luckily, even unresolved type parameters are analyzed in intersections... that is, if X is known to be assignable to both A and B, then X is known to be assignable to A & B. So we can change Extract<keyof T, string> to (keyof T) & string and the error should also go away.

Let's look at some modified code that does both things:

class Foo<T> {
// intersection
get(a: keyof T & string) {
return "";
}
}

interface IAttrs {
bar: boolean;
}

class Bar<T extends IAttrs> extends Foo<T> {
constructor() {
super();
this.get("bar");
this.get2("bar");
}
// resolved conditional
get2(a: Extract<keyof T | keyof IAttrs, string>) {
return "";
}
}

class FooBar extends Foo<IAttrs> {
constructor() {
super();

this.get("bar");
}
}

Playground link

And that all compiles with no error. Okay, hope that helps; good luck!

Current Type placeholder in C# generic types?

Make the interface generic:

public class MySpecialCollection<T> where T : ISomething<T> {
...
}

public interface ISomething<T> {
T NextElement { get; }
T PreviousElement { get; }
}

public class XSomething : ISomething<XSomething> {
...
}


Related Topics



Leave a reply



Submit