JADE  Environment Development Ideas

Relax requirement for foreach instruction's variable to be the type of the collection membership

Use of the foreach instruction is annoying if the collection's membership type is more general than the objects it's known to contain. For example, if I have a Collection containing Customer instances, I can't write the following:

writeCustomerNames(customers: Collection);
vars
c: Customer;
begin
foreach c in customers do
write c.name;
endforeach;

I get a compiler error - 6072 - Incompatible types.

To solve this, I need to resort to an Iterator:

vars
c : Customer;
iter : Iterator;
begin
iter := customers.createIterator();
while iter.next(c) do
write c.name;
endwhile;
epilog
delete iter;
end;


When using an Iterator, if it encounters an object in the collection that can't be assigned to the type of the variable it raises a 1086 exception (Class of object is invalid in this context).

It would be nice if the compiler supported the same semantics for the foreach instruction too. The compile should check that the iteration variable is compatible with the membership of the collection (i.e. a sub-type of its membership type) but doesn't need to be the exact type or supertype.

I believe this wouldn't be a breaking change as all existing compiled code would still work with the relaxed scheme.
  • Guest
  • May 1 2019
  • Ready for Release
HistoricalComments
FileVision UK Ltd 12/01/2018 4:52:34 AM
I'd like to vote against this - compile time errors are better than runtime errors.

Having said that, for the case where different kinds of collections could be passed but they all have the same member type, maybe something like a small subset of generic types could be used e.g. all of CustomerArray, CustomerSet and CustomerDictionary would be compatible with Collection.

Software Medical Infomatics Limited 11/01/2018 10:17:22 AM
Yes, I like that idea too. Probably easier to do it as an enhancement to the syntax than to change the semantics of the existing instruction.

Brian Johnstone 11/01/2018 9:32:03 AM
Another possible option would be to add an 'as' modifier to the foreach statement, similar to the existing 'as' modifier on the create statement. This would mean existing type checking rules could remain unchanged and make it more explicit to someone looking at the code that you expect the collection to contain only instances compatible with the supplied. Then, for those scenarios where you 'know' the content is only of a specific type you could code "foreach in as do" and get a 1086 should it encounter an instance not compatible with .
  • Attach files
  • Admin
    Hugh McColl commented
    4 Nov, 2019 02:36am

    The following feedback was posted on the FB User group:

    Kevin Saul > Might be easier to discuss this one here as we don't get notifications from JEDI when others add comments (unless they're an admin). In general, I think generic interfaces will help with the issue highlighted (especially for new code), but updating/simplifying foreach statements while updating existing code depends on surrounding code being changed to use generic interfaces. If that involves changing existing method parameters to supply a collection or iterable interface, where we may just supply a basic collection type currently, it could lead to wider code changes to update method references (which'll lead to retesting). In cases like that, we'd probably prefer to stick with the current approach if we can't justify refactoring existing code to use generic interfaces. With that in mind, the original idea to relax the foreach variable requirements still has merit in my opinion, but I also agree with other comments about compiler vs runtime checks, so it'd be good to see this implemented with BeeJay's idea of using an 'as' modifier so we can explicitly indicate what type we expect a collection to contain at runtime.

    Sam Stokell > It seems inconsistent that iterators are more flexible so I'm all for the change it will prevent all those casting lines directly after the foreach

    Craig Shearer > I'm the originator of this suggestion. It's safe for existing code - i.e. code that compiles already can't be broken by relaxing this. It's only code written in the future that isn't protected by the existing restriction - then the developer needs to be more careful and test their code, but it's no different from an Iterator. But, BJ's suggestion of an "as" qualifier seems workable, albeit with lengthier syntax.

  • John Munro commented
    15 Oct, 2019 02:22pm

    Generics sound like a great solution to me 👍

  • Admin
    Hugh McColl commented
    14 Oct, 2019 09:59pm

    The Generic Interfaces feature under development for the 2020 release is described by: https://jedi.ideas.jadeworld.com/ideas/JAD-I-429.

  • Admin
    Hugh McColl commented
    14 Aug, 2019 12:34am

    The JADE plant is actively working on a feature set that includes support for generic interfaces implemented by RootSchema classes, language support for iterating over the equivalent of Enumerable / Enumerable<T> in .NET using foreach syntax. We plan to publish a JEDI idea to show the community what this might look like and to solicit feedback.

    With the above mind, we believe generic interfaces could be applied to solve the use case shown here without compromising type safety.

    Assume for a moment that JADE supported generic interfaces where we could define a JadeIterableIF<T> interface equivalent to IEnumerable<T> in .NET or Iterable<T> in Java.

    With support for generic interfaces, we can arrange that the RootSchema::Collection class implements JadeIterableIF<T> where T is essentially equivalent to MemberType.

    With that we can change the example signature to:

     writeCustomerNames(customers: JadeIterableIF<Customer>);

    Now, given language support for foreach over JadeIterableIF<T>, the foreach int he method body would compile without error.

    Usage

    Using the example collection types in File Vision's post: The collection types

    CustomerArray, CustomerSet and CustomerDictionary would implicitly implement JadeIterableIF<Customer>, the following calls are all valid.

    writeCustomerNames(CustomerArray)
    writeCustomerNames(CustomerSet)
    writeCustomerNames(CustomerDictionary)

    We are keen to receive feedback from the originator and voters for this idea on this alternative type safe alternative approach.

    • Would use of generic interface suit the majority of use cases?

    • Do you still see a need for a downcast capability subject to runtime exceptions?

  • and 10 more