Suppose I have class Student
, BadStudent:Student
, GoodStudent:Student
and ExcellentStudent: Student
. I want a class method to only operate on Good
and Exceptional
students. Something like:
class AdvancedBasketWeaving {
// this is the question:
var acceptableStudentTypes: [1..2] = [GoodStudent, ExcellentStudent];
proc accept(student: Student) {
for at in this.acceptableStudentTypes {
if student.type == at then return "YES!";
}
return "Computer says 'No'";
}
}
How do I get this functionality?
There are two tools that I think you'll want to use for this pattern:
1) The first is Chapel's cast operator (:
). For classes, casting is similar to C++'s dynamic cast. In short, given a class object like the following instance of GoodStudent
:
var brian = new GoodStudent();
a cast of the object to a class type will return nil
if the object is not a subclass of that class and the class reference if it is. Thus:
...(brian: Student != nil)... // will evaluate to true
...(brian: BadStudent != nil)... // will evaluate to false
...(brian: GoodStudent != nil)... // will evaluate to true
...(brian: ExcellentStudent != nil)... // will evaluate to false
Thus, to test for a GoodStudent
or an ExcellentStudent
, you could write:
if (student:GoodStudent != nil || student:ExcellentStudent != nil) then
return "YES!";
Alternatively, if every ExcellentStudent
is also a GoodStudent
, you might consider making it a subclass of GoodStudent
in your class hierarchy rather than a sibling to it. In that case, you could simply write your conditional as:
if student:GoodStudent != nil then return "YES!";
since both GoodStudent
and ExcellentStudent
would return true for this conditional.
As an important note, it may be tempting to simply write this conditional as:
if student.type == GoodStudent
but this would not give the correct behavior in the context of your procedure since it is declared as follows:
proc accept(student: Student) { ... }
Specifically, the .type
query will return the static (compile-time) type of a class object, and in the context of this routine, the static type of student
is Student
due to its formal type. So comparing its static type would never match GoodStudent
, even if the object's dynamic type was GoodStudent
. Using the dynamic cast addresses this by changing from a static test to a dynamic one. Another approach would be to make the accept()
procedure completely generic, as follows:
proc accept(student) { ... }
but then you open up the floodgates by permitting other non-Student
types to be passed in.
2) The second thing you need (and the focus of your question) is a tuple type, which is probably the best / lightest weight way to create a collection of types. Chapel only supports arrays of values, not types, so the following line in your code is not legal:
var acceptableStudentTypes: [1..2] = [GoodStudent, ExcellentStudent];
Instead, create a tuple type to store all the types you want to compare against:
type acceptableStudentTypes = (GoodStudent, ExcellentStudent);
This leads to my suggested solution (try it online):
class Student {
}
class BadStudent: Student {
}
class GoodStudent: Student {
}
class ExcellentStudent: Student {
}
// a tuple of acceptable types
type acceptableStudentTypes = (GoodStudent, ExcellentStudent);
class AdvancedBasketWeaving {
proc accept(student: Student) {
// iterate over the tuple's size
for param i in 1..acceptableStudentTypes.size do
// see if dynamically casting the student to the type "works"
if student: acceptableStudentTypes(i) != nil then
return "YES!";
return "Computer says 'No'";
}
}
var course = new AdvancedBasketWeaving();
writeln(course.accept(new Student())); // Computer says 'No'
writeln(course.accept(new BadStudent())); // Computer says 'No'
writeln(course.accept(new GoodStudent())); // YES!
writeln(course.accept(new ExcellentStudent())); // YES!
Note that I've moved the acceptableStudentTypes
declaration from the class scope (which is logical and where you had it) to the module scope. This is because of an apparent bug in Chapel which I've filed an issue against.
Or, if you can make ExcellentStudent
a subclass of GoodStudent
, I think the following is much nicer (try it online):
class Student {
}
class BadStudent: Student {
}
class GoodStudent: Student {
}
class ExcellentStudent: GoodStudent {
}
class AdvancedBasketWeaving {
proc accept(student: Student) {
if student: GoodStudent != nil then
return "YES!";
return "Computer says 'No'";
}
}
var course = new AdvancedBasketWeaving();
writeln(course.accept(new Student())); // Computer says 'No'
writeln(course.accept(new BadStudent())); // Computer says 'No'
writeln(course.accept(new GoodStudent())); // YES!
writeln(course.accept(new ExcellentStudent())); // YES!
You can try to store the types by casting them as a string
.
For example,
var a = [int: string, string: string];
writeln(a);
var b = 1;
var c = "sdasas";
if b.type: string == a[1] then writeln("This matches!");
if c.type: string != a[1] then writeln("This doesn't match!");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With