Why separate TYPE and CLASS keywords in modern Fortran?

I guess this is at the heart of what I was trying to get at. Again, this was known at the time the F2003 OOP facility was being developed but it was ignored. I just want to know why it was ignored and who was responsible for making the design decisions we are forced to live with today. Name names. The introduction of modules and derived types in F90 and expanded in F95 made Fortran a perfectly acceptable object-based (if not object oriented) language that supported aggregation and composition (aka has-a inheritance). Why didn’t they just build on that focusing more on generics and interfaces than trying to become some warmed over version of C++ (or SIMULA-67).

Sadly, the only way to fix this is to rip out the current OOP facility by the roots and start over but thats not going to happen under the current ISO standard development process. Sometimes you have to tear things down to the foundation in order to save whats worth saving.

2 Likes

This question needs to be asked to J3’s members at the time.

The fix is quite easy. It is for us as a community to adopt traits based programming for OO going forward. This is what the Traits proposal is largely about.

And we should preferably declare obsolescent both type extension and abstract derived types, so that the implementation of the new traits facilities will be eased in compilers. Because in this way one won’t have to care about interoperability with type extension whatsoever.

Technically, all of this is straightforward to do. The true question is whether the Fortran community is actually ready for this.

1 Like

(I’m not trying to undermine you or being disrespectful in anyway, but…)

You make it look like OOP in F2003 was a crime against humanity or something like that. IMHO, the (set of) feature(s) is fine, since the main focus was low friction with regards to F95. It’s simply incomplete by today’s standards (pun intended).

F90 was twice as big as F77 (and btw, the “implicit” vs “explicit” interface thing was brilliant!), so I think it was decided F95 was going to be a minor revision. Which means the OOP work was probably started along with the preparations for F95. And that, in turn, means it might even predate the first public release of Java (so the “inheritance-considered-harmful” claims may not have been widespread at the time).

(It’s funny that F90 was considered too big… how could they know C++ would eventually swallow every feature under the face of the planet? :laughing:)

J3 has public documents on some of that (see this and this, as examples.)

3 Likes

I don’t consider it a crime against humanity but a half baked attempt designed by a committee that should have dug deeper into the way OOP was heading at the time the F2003 OOP facility was being envisioned. I will admit to having a very low opinion of most things designed by committee because what you get is usually what a few alpha personalities with the loudest voices who tend to dominate committees want and thats rarely the best choice. Remember a camel is just a horse designed by a committee. I just think they could have done better and am curious as to why they didn’t. They had a chance to leap frog all the so called “modern languages” at the time and they blew it.

2 Likes

You are both right. I will just note that the OOP features in F2003 are not just incomplete, there are parts (like inheritance) which makes the traits proposal more complex than it has to be if we want to support it.

As I said, the committee did its best given the “top down” approach to features, it could have been much worse. But nevertheless it was still a mistake in my opinion. By the time it was standardized, new efforts like Rust (started 2006) already knew this is not the way, and as mentioned in this thread, I think this was already well-known by 2003 as well.

1 Like

You answered your own question. :slight_smile: It’s designed by the strongest/most powerful voices at the committee. You asked for names, and I know the names, but I won’t mention that publicly, because I think it’s not their fault: from their perspective they did the best they could and used the power they had. The failure is structural: the committee shouldn’t have worked that way, but you could also argue any committee by definition works this way (I think it does), but I also believe if the committee leadership was able and willing to do it, they could have ensured more openness and iterations with the users and other experts to ensure we are not standardizing features which are already well-known to be obsolete.

However, perhaps more importantly: the committee should only be standardizing common usage. It can be involved with new features, but those first have to be implemented in compilers. The community and compilers should take the lead on new features. I think that’s the solution to all the problems.

5 Likes

Exactly. We could have a much simpler design of the traits facilities if we don’t have to support interoperability with type extension.

Don’t get me wrong: it is technically possible to realize such interoperability. But what would be the point of it when we already know that implementation inheritance is troublesome, outdated, and hence pointless?

I think we should ultimately have a poll on whether the Fortran community nevertheless wants us to support such interoperability in LFortran’s implementation of the Traits proposal.

1 Like

Perhaps something like implicit none(type,external,extends) should be considered. A simple addition that could be added to most code bases that would speed up runtime would be high value.

Maybe I’m missing something, but it really doesn’t have to be one way or the other, does it?

I’ve used the OOP features extensively (as in hundreds of derived types with TBPs across dozens of libraries), and they’re fine. The primary thing missing is something like:

module mod1
    implicit none
    private

    ! here, the `trait` attribute is syntactic sugar for purely abstract
    type, trait, public :: my_trait
        ! only the type-bound-procedure-part after the `contains` goes here
        procedure(i_proc1) :: proc1    ! `deferred` is implied
        procedure(i_proc2) :: proc2    ! `deferred` is implied
    end type

    abstract interface
        subroutine i_proc1(this)
            import
            class(my_trait), intent(inout) :: this
        end subroutine

        pure function i_proc2(this, x) result(y)
            import
            real :: y
            class(my_trait), intent(in) :: this
            real, intent(in) :: x
        end function
    end interface

    ! here, the implements attribute is constrained to purely abstract types
    type, implements(my_trait), public :: my_type
        integer :: i
    contains
        procedure :: proc1 => impl_proc1
        procedure :: proc2 => impl_proc1
    end type

    public :: do_something

contains
    subroutine impl_proc1(this)
        class(my_type), intent(inout) :: this
    end subroutine

    pure function impl_proc2(this, x) result(y)
        real :: y
        class(my_type), intent(in) :: this
        real, intent(in) :: x
        y = 3.14
    end function

    subroutine do_something(item)
        class(my_trait), intent(inout) :: item  ! any type implementing my_trait

        call item%proc1()

        ! since `intent` is valid on the trait, runtime assertion is possible
        select type (item)
        type is (my_type)
            item%i = item%i + 1
        end select
    end subroutine
end module mod1

That’s right out of the C++ playbook (i.e., purely abstract classes), with the main difference being that extends should still accept only derived type, but implements should accept many traits.

IMHO, any trait implementation should use what’s already there, instead of attempting to deprecate everything and starting over —the Python 2->3 thing serves as a cautionary tale.

(The forall vs do concurrent thing is an example of deprecation done right, but attempting to deprecate the whole OOP stack is not a good idea.)

As I said, we can make the traits fully interoperable with type extension, as it is the case in the present proposal.

In this way one could use extends and implements together within a derived type (even if that derived type is abstract). Swift and Java have done it, so it can be done in Fortran, too.

The question is simply whether we should do it, or whether we should instead make a clean cut, declare type extension obsolescent, and thus follow the Go and Rust model, which will make things also easier to implement.

Personally, I think it makes no sense to continue supporting a feature like type extension of which there seems to be a rather wide consensus that it is fundamentally broken.

We wouldn’t deprecate the whole OOP stack. That would be foolish!

Constructors, variable declarations using both the type and class specifiers, typed allocation, polymorphic assignment, unlimited polymorphic objects together with the select type statements that they require, and so on and so forth, would continue to work as usual.

The only things that would be replaced would be the extends and abstract attributes of derived types, by the implements attribute and by traits (named abstract interfaces), respectively. Codes that don’t use the former two features would continue to work without requiring any modifications.

Codes that do use these features would first need to be refactored to inherit only deferred procedures from abstract derived types, and could then be switched over easily to use the implements feature and traits instead.

PS: Declaring a trait as a sort of abstract class, as in your example, won’t work because traits must also be implementable by intrinsic types. Check the Traits proposal for how this needs to be formulated, instead.

But that’s sort of the issue here. The Fortran primitive/intrinsic types are already well defined, so messing with that for the sake of avoiding a derived type wrapper is a long shot.

(I’m also a Go developer, so I understand where you’re going, but my point is to not to mess too much with what’s already in the language.)

Who said that we’re going to mess with the intrinsic types?

We will simply allow you to add some functionality to them that does not mess with the basic design of the rest of the language. In particular, we won’t allow, for instance, any overloading of the intrinsic operators.

Go and read the Traits proposal. It’s all explained there.

I already read your traits proposal (there was a long thread about that, right after a thread on the merits of the current templates draft).

I found it a little too cumbersome (but that’s me). I’m waiting for the LFortran implementation.

2 Likes

(@moderator How about splitting all the replies related to OOP to a new thread…? I feel the main interest of those replies are rather different from the FP64 hardware, so virtually hijacking this thread and potentially distracting for people interested in HPC.)

EDIT: For the title of such a split thread, how about “Why separate TYPE and CLASS keywords in modern Fortran?” (which is also one of my questions)

3 Likes

done!

2 Likes

Yes, we are starting soon! Btw, this is precisely the feedback we’ll need — LFortran already has the other templates/generics prototype (we’ll improve it also) and we’ll be able to see both side by side and then we can collect feedback and iterate. And if people really like it, we’ll push also for Flang / GFortran implementation.

So I am very excited about it. There are bunch of other features we can now prototype also. The only downside is that probably going forward AI will be doing most the Fortran code writing. But I still want to read it, and we still need to debug it, so I think if the feature makes the code simpler, more robust, easier to debug, less bugs, then I think we still want that.

3 Likes