Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28808 Discussions

Overloading Array Index for Derived Data Type

Scott_Boyce
New Contributor I
2,299 Views

I was wondering if it is possible for Fortran to define how array indexes are used for a dervied data type.

 

I doubt this is possible, but I have never found anything that either confirms or denies this. The idea is to have something equivalent to python's __getitem__ dunder method.

 

The idea is that it would allow for something like:

type(some_type):: x

call x%allocate(32)  ! allocates some internal material

x(12) = 55  ! 12 index is passed to a __getitem__ routine to get appropiate data, 
            !   which then uses the assignment(=) routine.

 

0 Kudos
12 Replies
Steve_Lionel
Honored Contributor III
2,289 Views

Not directly like that. You might be able to throw something together with 1) a type-bound function, say, getitem, that returns a pointer to the selected element of x, so you would be able to write:

x%getitem(12) = 55

then you have 2) a type-bound assignment procedure that does the actual assignment. The tricky part here is that if the type has more than one component compatible with 55 you can't determine which component to assign to.

There's been some initial discussion on the standards committee of iterators and similar concepts - I suggest you peruse the feature suggestions at Issues · j3-fortran/fortran_proposals and maybe ask for ideas at Fortran Discourse - Fortran open source community.

0 Kudos
Scott_Boyce
New Contributor I
2,278 Views

Thanks, I was hopeful it might be a feature but figured it was not.

That is an interesting way of doing it with an intermediate pointer. I will just have do it the traditional way of something like

 

call x%set_item(index=12, value=55)

 

That is the way I do it now. Just thought it be a bit syntactically cleaner the other way.

 

Thanks for your input

0 Kudos
Scott_Boyce
New Contributor I
2,278 Views

It looks like someone already put in a proposal for overloading the index at
https://github.com/j3-fortran/fortran_proposals/issues/119

Steve_Lionel
Honored Contributor III
2,259 Views

I just realized that I made it more complicated than it needed to be. You don't need the first TBP. You DO need a defined assignment that can figure out what to do with the 55.

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,228 Views

An issue I see (without this being part of the language and compiler implementation) is that it is how to disambiguate:

     X(something) = ...

from

     ... = X(somenting)

 

IOW the operator () needs to know if it is on the lhs or rhs, or possible without side, such as open(...).

In the case of without side, this is easily done with the preprocessor.

The rhs use can be done now using a contained function together with -fpp to eliminate the %funcName

The lhs use can also be done with the preprocessor... but at the expense of prohibiting use as well on the rhs. The fpp lhs would also be limited to a setter function where the rhs can be "set" through a returned pointer.

I think it is best to stick with the Fortran language, if you want the syntax of another language, use the other language.

 

Jim Dempsey

 

 

 

0 Kudos
FortranFan
Honored Contributor III
2,204 Views
..

I think it is best to stick with the Fortran language, if you want the syntax of another language, use the other language.

Re: "I think it is best to stick with the Fortran language, if you want the syntax of another language, use the other language," in certain very, very specific instances, such a position can be valid in that some practitioner might place a request for syntax that is inspired from another language but which departs so drastically from Fortran that one can conclude it is better "to stick with the Fortran".

But I do not agree that it is applicable generally, nor should it be so, definitely not in this case.  FORTRAN since its inception introduced the notion of a container with an array variable of any type having independent subscripts being the first syntax in a language more concise for the programmers and more familiar to them than the underlying the machine language.  Extending the capability of containers generally which then builds upon everything that has been introduced through its various revisions is only following the early steps by Backus et al. during the 1950s.

Moreover all the needed mechanisms are already there in the language now, all that is needed is a definition in the standard of a suitable operator overload with certain guardrails to provide better convenience for modern programmers.  An immediate use case is a string type, something which is badly needed during modern Fortran practice.

It is the programmers who were placed in the front and center of everything that drove Backus et al. to great heights.  There is a disconcerting level of inattention to many aspects of programmers now and that is not at all good.  Here you have OP who has expressed an interest that has been heard many, many times over on many forums.  Telling them to "use the other language" is not good for Fortran.  At one time, that 'other language' was IBM 704 machine language (colloquially, assembler).  It was good the early innovators were really motivated to not tell them to use the other language.  

0 Kudos
FortranFan
Honored Contributor III
2,219 Views

There is a suggestion here a defined assignment is what you need to define, I think that's inaccurate.  That is, if your use case is a scalar object `x` of a derived type and you seek array-like '(..)' reference to achieve a "get/set Item" facility in order to work with a component or an element thereof of said derived type e.g., 'x(12)=15'.  Because the language standard, as it stands now, does not permit an array-like '(..)' reference to be applied with a scalar object.

jimdempseyatthecove
Honored Contributor III
2,193 Views

FF you are correct.

For example, assume your funny type with an operator () suitable for lhs. Consider:

type(funny) :: A
type(funny) :: B(10)

A(something) = ...
B(something) = ...

In the case where something is an integer, how do you disambiguate as to if the B statement is an index or argument to operator ()?

I guess the proper way would be

B(index)(something) = ...

But now you are getting into a syntax that is not familiar with Fortran, nor C++.

Note, if "something" were not an integer (suitable for indexing), then perhaps interface checking could disambiguate the intent. But this still leaves you with using an integer.

 

Jim Dempsey

 

0 Kudos
FortranFan
Honored Contributor III
2,179 Views

Re: "B(index)(something) = ...," note Fortran has for long supported the concept with arrays of CHARACTER types with the something being an integer index or a subsection, still the notion applies.

However arrays of CHARACTER types are wholly inadequate for modern Fortran code, including in scientific and technical computing, especially because they need to deal with what are effectively jagged strings in all kinds of technical domains.  The practitioners really need an intrinsic "string" type, or an ability to author so themselves   

However there is no one even remotely close to the vision of Backus or van Rossum or Stroutsrup on the Fortran committee when it comes to advancing the language for practical needs toward anything like this, it is relentless no, no, no to such features and an attitude of what is absolutely the least we can do in the time available to get a new year stamp on a standard revision.  So nothing like this flies unless someone from a big taxpayer-funded body can come and throw their weight around - OP may want to look into this if truly motivated.  There are a lot of "other language"s available anyway now, so users can instead vote with their feet and leave Fortran to the devices of its committee(s).

0 Kudos
Steve_Lionel
Honored Contributor III
2,205 Views

@FortranFan , I don't understand your comment. Here's an example of what I was referring to:

 

module mt_mod
    implicit none
    type mytype
        integer :: foo
        real :: bar
    contains
    procedure :: mt_a_int, mt_a_real
    generic :: assignment(=) => mt_a_int, mt_a_real
    end type mytype
    
    contains
    
    subroutine mt_a_int (this, arg)
    class(mytype), intent(inout) :: this
    integer, intent(in) :: arg
    this%foo = arg
    end subroutine mt_a_int
    
    subroutine mt_a_real (this, arg)
    class(mytype), intent(inout) :: this
    real, intent(in) :: arg
    this%bar = arg
    end subroutine mt_a_real
    
end module mt_mod
    
program test
    use mt_mod
    implicit none
    
    type(mytype), allocatable :: x(:)
    
    allocate (x(32))
    
    x(12) = 55
    x(12) = 3.14159
    print *, x(12)%foo,x(12)%bar
    
end program test

When I build and run this I get:

          55   3.141590
0 Kudos
FortranFan
Honored Contributor III
2,186 Views

The example is not what is of interest to OP.

OP has a derived type object `x` of rank-0, then consumes a bound generic or a procedure to define something with said type via 'x%allocate(32)', and this is followed by "set item" as `x(12) = 55'

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,167 Views

It may be better to propose to switch languages within the same source file. Perhaps an extension to BLOCK and/or !$OMP

I suggested !$OMP for two reasons:

1) language moved SIMD into !$OMP when simd applies to non-threaded code

2) OpenMP has shared, private, firstprivate, etc... clauses that can facilitate an interface between the languages.

 

Note, the ifx is now Fortran -> llvm -> machine code as is C++. While it is generally preferred to use the interoperability feature and place the different languages in different sources, there are some situations where it might be better to place the code inline. As this can help with optimization.

 

Jim Dempsey

0 Kudos
Reply