Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More flexible SELECT TYPE #38

Open
jacobwilliams opened this issue Oct 23, 2019 · 7 comments
Open

More flexible SELECT TYPE #38

jacobwilliams opened this issue Oct 23, 2019 · 7 comments
Labels
Clause 11 Standard Clause 11: Execution control

Comments

@jacobwilliams
Copy link

Something so we can avoid these annoying nested select type blocks.

So if you want to check if several variables are integers:

! if several variables are integers
select type (a,b,c)  ! new syntax
class is (integer)
  d = a + b + c
end select

Or if you want to check if a variable is any integer kind:

select type (a)
class is (integer(kind=*))  ! new syntax
  d = a * 2
end select

Or any numeric kind:

select type (a)
class is (integer(kind=*), real(kind=*))  ! new syntax
  d = 1 * 2
end select

And all together:

select type (a,b,c)  ! new syntax
class is (integer(kind=*), real(kind=*))   ! new syntax
  ! a, b, and c are all either integers or reals, 
  ! so compiler knows what to do here:
  d = a + b + c
end select
@septcolor
Copy link

septcolor commented Oct 23, 2019

In the above codes, a, b, and c are supposed to be some dummy arguments of class(*)? (and so the purpose of the code is to determine their dynamic types in a subroutine, for example?) If that is the case, I'm wondering if the same thing may be achieved by generics with constraints (like "where" in some languages?)

@jacobwilliams
Copy link
Author

jacobwilliams commented Oct 23, 2019

Yes, something like this:

integer function multiply_by_two(a) result(d)

class(*),intent(in) :: a   

select type (a)
class is (integer(kind=*))  ! new syntax 
  d = a * 2
end select

end function multiply_by_two

Or, via some sort of a limited polymorphic dummy argument like so:

integer function multiply_by_two(a) result(d)

class(integer(kind=*)),intent(in) :: a    ! some new syntax like this  -- any integer kind

d = a * 2

end function multiply_by_two

Maybe a sort of "poor man's" generics.

@rweed
Copy link

rweed commented Oct 24, 2019

I think this is a great idea. I went through the process a few years ago of creating a library of Abstract Data Types (lists, queues, RBtrees, hashmaps etc) using unlimited polymorphic containers etc. I went the extra mile and decided to support all the intrinsic types plus a user defined type. This started as a project to learn something about UP and kind of got out of hand. I can safely say that it doesn't take long before you really really hate SELECT TYPE and baseing ADTs on unlimited polymorphic values is NOT the best approach. We really do need templates or something similar for that. I even went so far as to emulate what Jacob is suggesting by creating my own enumerator parameters for each intrinsic type and my user type along with a function that takes an unlimited polymorphic value as an argument and returns one of my enumerator parameters as an INTEGER. I then use this value as a CASE selector in SELECT CASE just so I can do

SELECT CASE(kind_type)

CASE(INT64, INT32, INT16, INT8)
    Do something with integers
CASE(REAL64, REAL32)
    Do something with reals

etc.

@tclune
Copy link
Member

tclune commented Oct 24, 2019

I have made analogous comments to the committee in a slightly different context (SELECT RANK). There was a strong objection to having multiple cases in one statement. E.g. RANK(1,3,5). The reason given was that the compiler is generating rank-specific code within the clause. Putting multiple type/kinds within the TYPE IS clause would be even worse, so I am doubtful that aspect could proceed.

As a developer, I find these arguments a bit frustrating. It is of course quite obvious how to write a preprocessor that would repeat the clause for each item in the list. But apparently this is much more difficult/subtle inside the compiler.

However, those objections would not affect the desire to put multiple variables in the SELECT TYPE statement. There is a common pattern of nested SELECT TYPE's that have been complained about during meetings on multiple occasions. I can't recall what sort of syntax was proposed if any, but I like what I see here.

@difference-scheme
Copy link

The frequent need for nested "select type" statements is indeed an abomination in present Fortran. However, I am in favor of getting completely rid of such "downcasting" as it is called in other languages and to not ever have to use it at all.

Select type statements are just a nasty symptom, and not the actual disease. The disease is that Fortran presently does not support any form of multiple inheritance. If we had Java-like interface inheritance (or traits as they have been called in Issue #125) in addition to implementation inheritance (the one that present Fortran provides), there would be no need to use "select type". The same would hold if we had C++ like multiple inheritance.

It is the conflict between having to conform to some extended type's interface while at the same time wishing to inherit implementation from a base type that typically leads to the use of "select type" statements.

@Beliavsky
Copy link

I also suggested this in a Fortran Discourse thread Allow TYPE IS to select multiple types, where there was both support and opposition. A few people said they often had identical code for several select type cases.

@tclune
Copy link
Member

tclune commented Feb 28, 2022

The compiler developers were adamant that "the way compilers work" does not allow such seemingly simple code transformations as to replicate code for multiple cases in this manner. (For both SELECT RANK and SELECT TYPE. I don't recall the TYPE IS case being discussed, but possibly the same reason exists. But it would seem to be even simpler as there is no dependent code to compile and execute in this case.

I will say that the "frequent need" for SELECT TYPE does diminish considerably in many cases with careful design. The primary ones that do not involve intrinsic types. E.g., a common case that two objects must have the same type appears as a doubly nested mess of SELECT TYPE statements. However the Visitor (also called Bridge) design pattern provides a way to implement double dispatch in a single dispatch language. Whether the extra complexity is better than the tangle of SELECT TYPE depends on the use case and the eye of the developer.

And of course, next generation Generics features in the language will also reduce the need for such nested SELECT TYPE's in the future. But that's a ways off.

@certik certik added the Clause 11 Standard Clause 11: Execution control label Apr 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Clause 11 Standard Clause 11: Execution control
Projects
None yet
Development

No branches or pull requests

7 participants