How to keep a list of acceptable types for comparison in Chapel
Asked Answered
W

2

5

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?

Whoever answered 23/5, 2018 at 0:31 Comment(0)
F
4

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!                   
Foulk answered 23/5, 2018 at 10:43 Comment(0)
N
3

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!");
Northway answered 23/5, 2018 at 1:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.