├── CODE_OF_CONDUCT.md ├── README.md ├── augmented_assignment └── augmented_assignment.txt ├── proposal-template.txt └── proposals ├── assert └── design_by_contract.txt ├── cmplx-real-pointers.txt ├── conditional_expressions └── 21-165.txt ├── default_optional_arguments └── proposal.txt ├── namelist_delimiters └── namelist_proposal.txt ├── namespace_modules ├── 19-246.txt └── 20-108.txt ├── protected_attribute.txt ├── run-time_polymorphism ├── Examples │ ├── AppendixA │ │ └── taylor.f90 │ ├── AppendixB │ │ ├── taylor.f90 │ │ ├── taylor.java │ │ └── taylor.rs │ └── Text │ │ └── taylor_basic.f90 ├── Fortran_OOP.bib ├── Fortran_OOP.pdf └── Fortran_OOP.tex └── traits └── 20-109.txt /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, 8 | body size, disability, ethnicity, gender identity and expression, level of 9 | experience, nationality, personal appearance, race, religion, or sexual 10 | identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account or acting as an appointed 52 | representative at an online or offline event. Representation of a project may 53 | be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing or otherwise unacceptable behavior may be 58 | reported by contacting one of the project maintainers at ondrej@certik.us or 59 | zjibben@lanl.gov. All complaints will be reviewed and investigated and will 60 | result in a response that is deemed necessary and appropriate to the 61 | circumstances. The project team is obligated to maintain confidentiality with 62 | regard to the reporter of an incident. Further details of specific enforcement 63 | policies may be posted separately. 64 | 65 | Project maintainers who do not follow or enforce the Code of Conduct in good 66 | faith may face temporary or permanent repercussions as determined by other 67 | members of the project's leadership. 68 | 69 | ## Attribution 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 72 | available at [https://contributor-covenant.org/version/1/4][version] 73 | 74 | [homepage]: https://contributor-covenant.org 75 | [version]: https://contributor-covenant.org/version/1/4/ 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fortran Proposals 2 | 3 | This repository contains proposals for the Fortran Standard Committee in the [Issues](https://github.com/j3-fortran/fortran_proposals/issues) section. The idea for this repository is to act as a public facing discussion tool to collaborate with the user community to gather proposals for the Fortran language and systematically track all discussions for each proposal. 4 | 5 | ## Workflow 6 | 7 | 1. Open a new issue with a new addition that you would like to see eventually included in the Fortran standard. 8 | 9 | 2. The issue gets discussed. 10 | 11 | 3. Start drafting a formal proposal to be submitted into [Documents](https://j3-fortran.org/doc/meeting) for the US committee. One has to find an advocate at the committee to advocate for the proposal. 12 | 13 | 4. If the draft proposal receives negative feedback from the committee, then we will close the issue and we will have documented reasons why the idea got rejected. One can open a new issue, reference the closed issue and advocate why it should be revisited. 14 | 15 | 5. Getting the proposal through the Committee is a long process, with multiple approvals, formal requirements, specifications, edits to the standard, etc. We will document this step in more detail. During this process, we can document the latest status at the issue. 16 | 17 | ### Proposal Format 18 | 19 | Proposals should be text files, using [proposal-template.txt](proposal-template.txt) as a template. The reference line is optional, and is used reference previous J3 papers (e.g. 12-345r6, not a full link). The top line should not be modified--the XX-XXX is automatically modified when uploaded. Proposals are limited to 75 characters per line, and are generally around 50 - 350 lines of text. Proposals benefit from minimal code examples and use cases. While J3 will accept PDF proposals, such detailed documents are usually be better suited as a reference, so the proposal itself can be concise. 20 | 21 | ## Plan For This Repository 22 | 23 | Currently this repository is maintained by 24 | [Ondřej Čertík](https://github.com/certik), 25 | [Zach Jibben](https://github.com/zjibben) and 26 | [Gary Klimowicz](https://github.com/gklimowicz) 27 | (all members of the Fortran Committee). We plan to maintain this repository, 28 | keep it up to date with what the committee is doing and use it to collaborate 29 | on ideas and proposals with the Fortran community. 30 | 31 | ## Code of Conduct 32 | 33 | Please note that all participants of this project are expected to follow our 34 | Code of Conduct. By participating in this project you agree to abide by its 35 | terms. See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). 36 | 37 | ## Links 38 | 39 | * [US Fortran Standards Committee](https://j3-fortran.org/) 40 | * [International Fortran Standard Committee](https://wg5-fortran.org/) 41 | -------------------------------------------------------------------------------- /augmented_assignment/augmented_assignment.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/XX-XXX 2 | From: Leonard Reuter 3 | Subject: Augmented Assignment Operators 4 | Date: 2022-July-11 5 | 6 | Proposal for Fortran Standard: 202y 7 | 8 | 9 | 1. Introduction 10 | 11 | The proposal is to introduce augmented assignment operators. Example: 12 | 13 | i +.= 1 14 | 15 | Here, `i` is incremented by 1. 16 | 17 | In the same manner, the operators `-.=`, `*.=`, `/.=`, `**.=` are proposed 18 | to be defined as well as the operator `.op.=` for a user-defined operator 19 | `.op.`. 20 | 21 | The intrinsic interpretation of e.g. '+.=' can not be overridden but should 22 | always call 'operator(+)' and 'assignment(=)'. 23 | 24 | This proposal originated at the J3 GitHub repository at [1]. The proposed 25 | syntax originated from a comment by Reinhold Bader in the J3 mail list [2]. 26 | 27 | 2. Motivation 28 | 29 | Augmented assignment operators improve the readability of code in making 30 | immediately clear to the reader, that the new value of the variable 31 | depends on the old one. 32 | 33 | Furthermore, if a function is called during the evaluation of the 34 | left-hand side, the augmented assignment operators show, that this 35 | function is only called once: 36 | 37 | real :: A(n) 38 | ... 39 | A(get_index()) +.= x 40 | 41 | If the above example were to be written without augmented assigment, how 42 | often `get_index` is called would depend on the compiler. 43 | 44 | The dot is added between the operator and `=`, since `/=` is already in 45 | use as an alias to the '.ne.' relational operator. 46 | 47 | 3. Discussing Objections 48 | 49 | 3.1. Conflict with '/=' [4] 50 | 51 | This is circumvented by introducing the dot, i.e. '/.=' 52 | 53 | 3.2. The compiler can optimize 'a = a + b*c' better than 'a +.= b*c' [5] 54 | 55 | "What happens to 'a += b * c' is often optimized to an FMA if the compiler 56 | flags or defaults allow ISO/IEEE violation" [6]. 57 | 58 | 3.3. Instead of augmented assignment, an additional symbol can be 59 | introduced to replace the LHS [7,8] 60 | 61 | If this symbol were '.LHS.', it would allow for further simplification: 62 | 63 | real :: a 64 | ... 65 | a = LOG(a) 66 | 67 | could be replaced with 68 | 69 | real :: a 70 | ... 71 | a = LOG(.LHS.) 72 | 73 | While discussing this may prove worthwhile, it lacks the intuitivity of 74 | simple augmented assignment and should be discussed independent of it. 75 | 76 | 3.4 Cognitive load [9] 77 | 78 | Most programmers are used to augmented assignment from other languages, 79 | it is more cognitive load to remember, that this is not possible in 80 | Fortran than to actually use it. 81 | 82 | 4. References 83 | 84 | [1] https://github.com/j3-fortran/fortran_proposals/issues/113 85 | [2] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013261.html 86 | [3] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013251.html 87 | [4] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013254.html 88 | [5] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013262.html 89 | [6] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013279.html 90 | [7] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013287.html 91 | [8] https://wg5-fortran.org/N1951-N2000/N1972.pdf 92 | [9] https://mailman.j3-fortran.org/pipermail/j3/2021-August/013277.html 93 | -------------------------------------------------------------------------------- /proposal-template.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/XX-XXX 2 | From: 3 | Subject: 4 | Date: 5 | #Reference: 6 | -------------------------------------------------------------------------------- /proposals/assert/design_by_contract.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/XX-XXX 2 | From: William B. Clodius 3 | Subject: Facilitate design by contract 4 | Date: August 1, 2020 5 | #Reference: 04-142.txt, 04-143.txt, 04-144.txt, 04-212.txt, 6 | 04-219.txt, 04-376.txt, 04-414.txt 7 | 8 | 9 | Proposal for Fortran Standard: 202y (NOT 202x) 10 | 11 | 12 | 1. Introduction 13 | 14 | This paper contains a proposal to allow programmers to use "Design by 15 | Contract". Inspired by languages such as C++, Java, and Ada, Fortran 16 | is considering incorporating exception handling into the language. In 17 | contrast, C++'s standard committees have decided that its users have 18 | been overusing exception handling, and has been seeking to incorporate 19 | other, simpler, error handing systems.[1] One approach, that has been 20 | incorporated into C++ 2020, is design by contract.[2,3] I believe 21 | Fortran would benefit by incorporating design by contract into the 22 | language, at a cost much less than would be incurred by incorporating 23 | exception handling. 24 | 25 | Design by contract (DbC) is intended to address programmer errors and 26 | only those errors. The idea is to specify: first the logical 27 | preconditions the inputs to a procedure are required to satisfy for 28 | its correct operation; second, the conditions the code is asserted to 29 | satisfy at various stages of its logic if the code logic is correct, 30 | and finally, the post-conditions the code ensures it will satisfy if 31 | the pre-conditions are met. To implement these checks, C++ 2020 has 32 | defined the attributes: expects, assert, and ensures, 33 | respectively. During the debugging phase of development these serve to 34 | detect when the conditions are not satisfied, stop processing when 35 | they are not satisfied, and report errors in a consistent format. In 36 | code deployment the expects attribute documents the pre-conditions the 37 | calling code must satisfy, and the ensures attribute documents the 38 | post-conditions the calling code can expect will be satisfied, 39 | providing a "contract" between the calling code and the 40 | callee. Normally the processor provides flags such that the logical 41 | tests can be active during testing, and inactive on deployment. 42 | 43 | DbC can be met with a minimum of one new statement, for testing 44 | anywhere in the code body, say ASSERT, but ideally there would be 45 | three new statements: one the equivalent of ASSERT, one to be used at 46 | the start of the procedure to test pre-conditions, say REQUIRE, and 47 | one to be used immediately before the procedure's return to test 48 | post-conditions, say ENSURE. All three statements would be simple in 49 | form and have simple semantics. 50 | 51 | 52 | 2. Problem 53 | 54 | Currently a programmer typically tests for user errors using one of: 55 | 56 | 1. Hardcoding the tests for errors using IF or SELECT CASE blocks; 57 | 2. Writing the equivalent of an ASSERT subroutine: or 58 | 3. Writing the equivalent of an ASSERT macro. 59 | 60 | The first is the most natural for single checks, but requires care to 61 | give a consistent response to the user, and adds an overhead in 62 | deployment unless commented or edited out. The second allows a 63 | consistent response to the user, but has a larger overhead than the 64 | first, that can only be removed by being commented or edited out. The 65 | third allows a consistent response to the user that can be clearer 66 | about the problem location, and can have zero overhead on deployment 67 | with appropriate pre-processor flags, but does require a dependence 68 | on a preprocessor. None document for the user the pre and post 69 | conditions the user can expect for properly working code. 70 | 71 | 72 | 3. Use cases 73 | 74 | A simple function that requires non-negative integer arguments. 75 | 76 | REAL FUNCTION FACTORIAL( n ) 77 | INTEGER, INTENT(IN) :: N 78 | INTEGER :: I 79 | REQUIRE( N >= 0 ) 80 | FACTORIAL = 1 81 | DO I=1, N 82 | FACTORIAL = FACTORIAL * I 83 | END DO 84 | ENSURE (FACTORIAL >= N ) 85 | RETURN 86 | END FUNCTION FACTORIAL 87 | 88 | A simple subroutine that swaps targets. 89 | 90 | SUBROUTINE TARGET_SWAP( A, B ) 91 | TYPE(EXAMPLE), POINTER, INTENT(INOUT) :: A, B 92 | TYPE(EXAMPLE), POINTER :: C 93 | REQUIRE( ASSOCIATED(A) ) ! Requires pointer status of A be 94 | ! defined 95 | REQUIRE( ASSOCIATED(B) ) ! Requires pointer status of B be 96 | ! defined 97 | C => A 98 | A => B 99 | B => C 100 | ENSURE( ASSOCIATED(A) ) 101 | ENSURE( ASSOCIATED(B) ) 102 | RETURN 103 | END SUBROUTINE TARGET_SWAP 104 | 105 | A procedure that requires one optional argument to be present if the 106 | other optional argument is present. 107 | 108 | SUBROUTINE OPTIONALS( A, B ) 109 | TYPE(EXAMPLE), OPTIONAL, INTENT(IN) :: A, B 110 | REQUIRE( PRESENT(A) .EQV. PRESENT(B) ) 111 | ... 112 | RETURN 113 | END SUBROUTINE OPTIONALS 114 | 115 | A test of a problematic procedure call 116 | 117 | SUBROUTINE PROBLEM_CALL ( ..., A, ... ) 118 | ... 119 | TYPE(EXAMPLE), POINTER :: A 120 | ... 121 | CALL PROBLEM( A ) 122 | ASSERT( ASSOCIATED( A ) ) 123 | ... 124 | RETURN 125 | END SUBROUTINE PROBLEM_CALL 126 | 127 | 128 | 4. Prior art 129 | 130 | The term design by contract as applied to programming languages has 131 | its origins in the language Eiffel.[4,5] Since then, in addition to 132 | C++ it has been adopted by Ada,[6] D,[7] and Fortress. The C standard 133 | library comes with the assert macro so C can be said to partially 134 | support DbC. To our knowledge no Fortran compiler has implemented 135 | anything to support DbC. Many users implement their own equivalent of 136 | an ASSERT subroutine or macro so there is some demand for these 137 | capabilities. 138 | 139 | 140 | 5. Specification 141 | 142 | All three statements will have similar behavior. All three will take 143 | as their arguments a list of scalar logical expressions, that if all 144 | .TRUE. does nothing, and if one is .FALSE. writes output to the 145 | ERROR_UNIT of ISO_FORTRAN_ENV and stops as if ERROR STOP was 146 | invoked. The writes will all indicate the location of the executed 147 | statement: either the procedure name with the name of the enclosing 148 | module, or the name of the file including the statement and the line 149 | number of the statement, or both. All three will write out the text of 150 | the logical expression that failed, with a prefix. The prefix will 151 | differ between the three statements. Example prefixes for the REQUIRE 152 | statement is "PRE-CONDITION FAILED: ", for the ASSERT statement is 153 | "ASSERTION FAILED: ". and for the ENSURE statement is "POST-CONDITION 154 | FAILED: ". 155 | 156 | The semantics of all three statements are similar. They all should 157 | respond the same to compiler flags that invoke different behavior in 158 | development versus in deployment. In development, they each perform a 159 | logical test with the .FALSE. branch writing to ERROR_UNIT, and then 160 | stopping with an stop code, and the other branch a null operation 161 | that continues processing. In deployment they are as if they were 162 | commented out. These semantics are similar to a logical test with one 163 | branch invoking ERROR STOP and the other branch continuing 164 | processing. They should have restrictions similar to those of ERROR 165 | STOP, being allowed in PURE procedures, but not in ELEMENTAL 166 | procedures. 167 | 168 | 169 | 4. Syntactic options 170 | 171 | The syntax for the statements depend on whether they should be 172 | considered executable statements, and should go in the executable 173 | part, or attributes of the sub-programs, and should go in the 174 | specification part. It is difficult to think of ASSERT as any thing 175 | other than an executable statement. For ASSERT I think the following 176 | syntax is appropriate 177 | 178 | assert-stmt is ASSERT( assertion-list ) 179 | 180 | where 181 | 182 | assertion is logical-expr 183 | 184 | REQUIRE and ENSURE can be thought of as either sub-program attributes 185 | or executable statements. If treated as sub-program attributes then 186 | the syntax could be something like 187 | 188 | requires-stmt is REQUIRES :: requires-expr-list 189 | ensures-stmt is ENSURES :: ensures-expr-list 190 | 191 | requires-expr is logical-expr 192 | ensures-expr is logical-expr 193 | 194 | Constraint: requires-expr shall be a restricted expression. 195 | 196 | If thought of as run time expressions then the appropriate syntax may 197 | be 198 | 199 | require-stmt is REQUIRE( require-expr-list ) 200 | ensure-stmt is ENSURE( ensure-expr-list ) 201 | 202 | In all of the syntactical forms each item in the effective 203 | logical-expr-list shall be tested in sequence, and a error message 204 | will be generated for the first item in the list that tests as 205 | .FALSE. that shows the logical expression that failed the test. 206 | 207 | 208 | 5. Backward compatibility 209 | 210 | The three proposed statements do not match statements implemented by 211 | any processor that I am aware of, and so their addition should not 212 | change the meaning of any directly supported application. It is 213 | likely, however, that some users already have an ASSERT macro, that 214 | approximates the ASSERT statement's semantics. I suspect they will 215 | welcome the new ASSERT, but cannot be certain. This problem could be 216 | minimized by using a name other than ASSERT, say CHECK. 217 | 218 | 219 | 6. Impact on the standard 220 | 221 | My guess is that describing each statement will take about half a page 222 | of main text, plus a half page note describing for all three 223 | statements the semantics of a quality of implementation, plus a few 224 | sentences to describe changes from the previous standard and define 225 | terms. Maybe three pages total. 226 | 227 | 228 | References 229 | 230 | [1] Herb Sutter, "Zero-overhead deterministic exceptions: Throwing 231 | values", 232 | http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0709r4.pdf 233 | [2] G. Dos Reis, J. D. Garcia, J. Lakos, A. Meredith, N. Myers, 234 | and B. Stroustrup, "A Contract Design" 235 | http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0380r1.pdf 236 | [3] G. Dos Reis, J. D. Garcia, J. Lakos, A. Meredith, N. Myers, and 237 | B. Stroustrup, "Support for contract based programming in C++" 238 | http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0542r5.html 239 | [4] B. Meyer, "Eiffel: The Language," Prentice Hall, second printing, 240 | 1992. 241 | [5] B. Meyer, "Applying 'Design by Contract'," in Computer (IEEE) 25, 242 | pp. 40-51, Oct. 1992. 243 | http://se.ethz.ch/~meyer/publications/computer/contract.pdf 244 | [6] R. Dewar, "Ada 2012: Ada with Contracts", Dr. Dobb's, Apr. 9, 245 | 2013. https://www.drdobbs.com/architecture-and-design/ada-2012-ada-with-contracts/240150569 246 | [7] W. Bright, "D Programming Language, Contract Programming", Digital 247 | Mars. https://dlang.org/spec/contracts.html 248 | -------------------------------------------------------------------------------- /proposals/cmplx-real-pointers.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/24-129 2 | From: Pierre Hugonnet 3 | Subject: allow complex pointer to reals and vice versa 4 | Date: 2024-June-10 5 | #Reference: 6 | 7 | 1. Introduction 8 | 9 | The proposal is to allow a complex pointer to be associated to a real 10 | array, and similarly to allow a real pointer to be associated to a 11 | complex scalar or array. 12 | 13 | 14 | 2. Motivation 15 | 16 | In short, the motivation is to save memory and cpu-memory bandwidth 17 | in some HPC contexts, typically when dealing with very large or huge 18 | data volumes. 19 | 20 | When working in the Fourier domain, it is common to use in-place real 21 | to complex Fourier transforms. Before the forward transform one 22 | generally stores data in a real array, and the transform outputs 23 | complex numbers in the same real array, with interleaved real and 24 | imaginary parts. Once in the Fourier domain the complex type is more 25 | appropriate to perform computations, but this requires either: 26 | - duplicating the data into a complex array after the transform, 27 | thus cancelling the advantage of the in-place transform. Note that in 28 | HPC, and particularly when dealing with large data volumes, 29 | duplicating the data is really something that we want to avoid 30 | whenever possible. 31 | - or using various non-standard tricks that rely on the fact that 32 | virtually all compilers store a complex number by the sequence 33 | real part / imaginary part. 34 | 35 | For instance, consider a code that performs a FFT based convolution 36 | at some point: 37 | 38 | program foo 39 | real, allocatable :: r1(:), r2(:), r3(:) 40 | integer, parameter :: n = 100000 41 | ! ... 42 | allocate( r1(n), r2(n), r3(n) ) 43 | ! ... some computations on r1(:) and r2(:) as reals 44 | call rfft(r1) ; call rfft(r2) 45 | call fftconvol(r1,r2,r3,n/2) ! called without any interface, 46 | ! hence type mismatchs 47 | call irfft(r3) 48 | ! ... some computations on r3(:) as real 49 | end program 50 | 51 | ! dangling routine (not contained and not in a module) 52 | subroutine fftconvol(c1,c2,c3,n) 53 | integer, intent(in) :: n 54 | complex, intent(in) :: c1(n), c2(n) 55 | complex, intent(out) :: c3(n) 56 | c3(:) = c1(:) * c2(:) 57 | end subroutine 58 | 59 | With modern Fortran another trick has got quite popular, by using the 60 | C interoperability features. Nonetheless the trick is non standard: 61 | 62 | program foo 63 | use iso_c_binding 64 | real, allocatable :: r1(:), r2(:), r3(:) 65 | integer, parameter :: n = 100000 66 | complex, pointer :: c1(:), c2(:), c3(:) 67 | ! ... 68 | allocate( r1(n), r2(n), r3(n) ) 69 | ! ... some computations on r1(:) and r2(:) as reals 70 | call rfft(r1) ; call rfft(r2) 71 | ! non-standard trick 72 | call c_f_pointer( c_loc(r1), c1, [n/2] ) 73 | call c_f_pointer( c_loc(r2), c2, [n/2] ) 74 | call c_f_pointer( c_loc(r3), c3, [n/2] ) 75 | 76 | c3(:) = c1(:) * c2(:) 77 | 78 | call irfft(r3) 79 | ! ... some computations on r3(:) as real 80 | end program 81 | 82 | The `c_f_pointer()` trick is even mentioned in the official 83 | documentation of FFTW, which is one of the most popular FFT 84 | library [1]. 85 | 86 | More generally, this is a recurrent topic on forums or on 87 | StackOverflow [2] 88 | 89 | 90 | 3. Proposed solution 91 | 92 | The proposal essentially aims at standardizing the usual practice 93 | above, by allowing a real array to be viewed as a complex array and 94 | vice-versa. The above code would become: 95 | 96 | program foo 97 | real, allocatable, target :: r1(:), r2(:), r3(:) 98 | integer, parameter :: n = 100000 99 | complex, pointer :: c1(:), c2(:), c3(:) 100 | ! ... 101 | allocate( r1(n), r2(n), r3(n) ) 102 | ! ... some computations on r1(:) and r2(:) as reals 103 | call rfft(r1) ; call rfft(r2) 104 | c1 => r1 ; c2 => r2 ; c3 => r3 105 | c3(:) = c1(:) * c2(:) 106 | call irfft(r3) 107 | ! ... some computations on r3(:) as real 108 | end program 109 | 110 | 3.1. Syntax 111 | 112 | No new syntax need to be created, the usual pointer association can be 113 | used, with additional rules and restrictions: 114 | 115 | `c => r` 116 | 117 | - `r` shall be a *contiguous* real array, which has either the target 118 | or the pointer attribute 119 | - `c` shall be a complex array pointer of the same kind as `r`, and of 120 | the same rank by default (but pointer rank remapping can be used) 121 | - `c` could also be a complex scalar pointer, in the case r is a 122 | rank-1 array of size 2 123 | - the size of the first dimension of `r` shall be even 124 | - `c%re` shall refer to the same storage as `r(1::2)` (rank-1), or 125 | `r(1::2,:)` (rank-2), etc... 126 | - `c%im` shall refer to the same storage as `r(2::2)` (rank-1), or 127 | `r(2::2,:)` (rank-2), etc... 128 | 129 | `r => c` 130 | 131 | - the exact opposite 132 | - `c` shall be a *contiguous* complex array or a complex scalar, which 133 | has either the target or the pointer attribute 134 | - `r` shall be a real array pointer of the same kind as `c`, and of the 135 | same rank by default (but pointer rank remapping can be used) 136 | - if `c` is a scalar, then `r` shall be a rank-1 pointer of size 2 137 | - same other rules as above 138 | 139 | 3.2 Alternative syntaxes 140 | 141 | If one wants to make the association between two different types more 142 | explicit and more type-safe, a new syntax may be introduced: 143 | 144 | Alternative syntax 1 (kind of pointer casting): 145 | ``` 146 | c => complex :: r 147 | r => real :: c 148 | ``` 149 | 150 | Alternative syntax 2: 151 | ``` 152 | c => complex_pointer(r) 153 | r => real_pointer(c) 154 | ``` 155 | 156 | Alternative syntax 3 (similar to the c_f_pointer() subroutine): 157 | ``` 158 | call complex_pointer(r,c[,shape][,lower]) 159 | call real_pointer(c,r[,shape][,lower]) 160 | ``` 161 | 162 | Alternative syntax 4 (more generic, in case other inter-type 163 | associations would be allowed in future versions of the standard): 164 | ``` 165 | call storage_associate(r,c[,shape][,lower]) 166 | call storage_associate(r,c[,shape][,lower]) 167 | ``` 168 | 169 | 3.3 Intermediate representation 170 | 171 | During the discussions it has been proposed tu use an intermediate 172 | representation between the real and complex view, which would be a real 173 | view with an augmented rank and first dimension of size 2. Instead of 174 | going directly from `r(n)` to `c(n/2)` one would define by rank 175 | remapping: 176 | `r2(1:2,1:n/2) => r(1:n)`, then `c => r2` 177 | And the other way: 178 | `r2 => c`, then `r(1:n) => r2(1:2,1:n/2)` 179 | 180 | This could also be coupled with a new pseudo-component notation `c%reim` 181 | that would be equivalent the `r2` respresentation (i.e. `c%reim` would be 182 | `real`, with a shape [2,n/2]). 183 | 184 | I personally find that such an intermediate representation just makes 185 | the proposal more complex than it needs to be, and that nothing really 186 | useful can be performed with it. Moreover the `c%reim` notation would 187 | require another change in the standard to allow non zero ranks on both 188 | sides of `%`. 189 | 190 | 3.4 Prototyping, etc... 191 | 192 | I think that this proposal doesn't need to have a preliminary prototype 193 | implementation, as it essentially consists in standardizing an already 194 | existing and common practice. A prototype implementation would do 195 | nothing else than mimicing the `c_f_pointer()` trick. 196 | 197 | 198 | 4. Issues / Objections / Limitations 199 | 200 | 4.1. Memory layout 201 | 202 | This proposal would constraint the memory layout of the complex type 203 | beyond what the current standard does. However, the required layout 204 | seems to be the de-facto standard, used by virtually all existing 205 | compilers (i.e. storing a complex scalar by the sequence real part/ 206 | imaginary part, in that order). 207 | 208 | Also, the proposal would not prevent alternative memory layouts for 209 | the arrays in future versions of the standard, such as the row major 210 | storage. For instance, in the case of a row major 2D array, `c%re` 211 | would refer to the same storage as `r(:,1::2)` instead of `r(1::2,:)` 212 | for the column major case. More generally the obtained view would be 213 | layout dependent, and the standard would have to describe the view for 214 | each layout. 215 | 216 | Similarly, the proposal would be compatible with the so-called 217 | split-complex layout, if introduced later (without assessing here if 218 | such a layout is possible at all). In this layout, a complex array is 219 | stored with all the real parts first, then all the imaginary parts. 220 | In this case, `c%re` would refer to the same storage as `r(1:n/2)`. 221 | 222 | 4.2. Argument association 223 | 224 | Allowing a real actual argument to be associated to a complex dummy 225 | argument -and vice-versa- has also been considered, but it would raise 226 | backward compatibility issues. So this part has been dropped from the 227 | proposal. 228 | 229 | 4.3. Alignment 230 | 231 | Considering for instance a default real type stored on 4 bytes, the 232 | default complex type is stored on 8 bytes. Compilers may prefer/want to 233 | align the complex arrays on 8 bytes boundaries, which cannot be 234 | guaranteed if a complex pointer is associated to an arbitrary real array 235 | (e.g. `c => r(2:)`). If this is a problem, the pointer association may 236 | be allowed only the other way (real pointer associated to a complex 237 | array), where alignement should not be a problem. 238 | 239 | 240 | 5. References 241 | 242 | [1] https://www.fftw.org/fftw3_doc/Reversing-array-dimensions.html 243 | 244 | [2] 245 | https://fortran-lang.discourse.group/t/ 246 | implicit-real-complex-conversion-in-fortran/7381 247 | 248 | https://stackoverflow.com/questions/31590004/ 249 | is-the-storage-of-complex-in-fortran-guaranteed-to-be-two-reals 250 | 251 | https://stackoverflow.com/questions/36977737/ 252 | pass-a-real-array-if-a-complex-array-is-expected 253 | 254 | Discussions related to this proposal: 255 | https://fortran-lang.discourse.group/t/complex-type-storage-again/7020 256 | https://github.com/j3-fortran/fortran_proposals/issues/323 257 | https://github.com/j3-fortran/fortran_proposals/pull/325 258 | -------------------------------------------------------------------------------- /proposals/conditional_expressions/21-165.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/21-165 2 | From: Ondrej Certik, Milan Curcic, Zach Jibben 3 | Subject: Conditional expressions using intrinsic function 4 | Date: 2021-June-27 5 | Reference: 21-157, 21-159 6 | 7 | Paper 21-157 proposes two syntactic forms for conditional expressions. The 8 | paper 21-159 proposes an alternative form. This paper proposes to implement 9 | conditional expressions using an intrinsic function. 10 | 11 | The syntactic forms in 21-157 are too verbose, as argued in 21-159. The 12 | form in 21-159 is concise, but based on the poll [1], it is not the most 13 | popular either. 14 | 15 | We propose the following syntactic form 16 | 17 | is ifthen( predicate, consequent, alternative ) 18 | 19 | where the `predicate` is a scalar logical expression and the `consequent` 20 | and `alternative` are compatible expressions. The `alternative` can be 21 | another `ifthen` as shown in an example below. 22 | 23 | Examples: 24 | 25 | res = ifthen(x >= 0.0, sqrt(x), -0.0) 26 | 27 | res = ifthen(present(x), a, ifthen(present(b), b, 0)) 28 | 29 | Objections raised in the past: 30 | 31 | 1) It is harder to do chaining due to the extra parenthesis at the end. 32 | 33 | Answer: it seems in practice it is not too bad, as in the second example 34 | above. Here is a longer chain: 35 | 36 | res = ifthen(present(x), x, ifthen(present(y), y, 37 | ifthen(present(z), z, 0))) 38 | 39 | 2) `ifthen` does not behave like a regular function, it is effectively new 40 | syntax, so we might as well actually introduce new syntax, to make it less 41 | confusing 42 | 43 | There is a precedent in optional arguments, where the argument is not 44 | evaluated but passed through through nested functions until it hits the 45 | function `present`. In a similar way, the arguments to `ifthen` are not 46 | evaluated ahead of time, but rather passed into the function `ifthen` 47 | itself, similar to how `present(x)` works. Both `ifthen` and `present` must 48 | be implemented by the compiler itself, they cannot be implemented in 49 | standard Fortran code. 50 | 51 | References: 52 | 53 | [1] https://fortran-lang.discourse.group/t/ 54 | poll-fortran-202x-conditional-expressions-syntax/1425 55 | 56 | 57 | -------------------------------------------------------------------------------- /proposals/default_optional_arguments/proposal.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/XX-XXX 2 | From: Milan Curcic, Jeremie Vandenplas, Zach Jibben 3 | Subject: Default value for optional arguments 4 | Date: 2020-January-10 5 | Reference: 18-136r1, 18-122r1 6 | 7 | 8 | 1. Introduction 9 | 10 | This paper contains a proposal for Fortran 202y, to allow a programmer 11 | to specify a default value for optional dummy arguments. This would 12 | allow the programmer to then safely reference such variables in 13 | expressions regardless of whether the actual argument is present or 14 | not. 15 | 16 | 2. Problem 17 | 18 | Currently, standard Fortran does not allow setting a default value for 19 | optional arguments. Default value of an optional argument is the value 20 | that the dummy argument would take if the corresponding actual argument 21 | is not present. If declaring a dummy argument as optional, the user 22 | must: 23 | 24 | * Explicitly test for the presence of the actual argument using the 25 | intrinsic function present(); 26 | * Use a separate variable inside the procedure to assign the value 27 | because the optional dummy argument that is not present must not be 28 | referenced in expressions other than as actual argument to the 29 | intrinsic function present(). 30 | 31 | This example function illustrates the problem: 32 | 33 | real function quadratic(x, a, b, c) 34 | ! returns a + b * x + c * x**2 if c is present 35 | ! and a + b * x otherwise 36 | real, intent(in) :: x, a, b 37 | real, intent(in), optional :: c 38 | real :: c_tmp ! use another var. to reference the missing arg 39 | c_tmp = 0 ! default value if c is not present 40 | if (present(c)) c_tmp = c 41 | quadratic = a + b * x + c_tmp * x**2 42 | end function quadratic 43 | 44 | For any dummy argument with the optional attribute, the programmer must 45 | use the intrinsic function present() to check for the presence of the 46 | argument. Furthermore, if the optional dummy argument is meant to be 47 | used in multiple places in the procedure, the programmer is likely to 48 | use the pattern from the example above, where a "temporary" variable is 49 | declared and used in place of the dummy argument, which disconnects the 50 | implementation from the user interface. Furthermore, this requires at 51 | least 3 lines of code (declaration of c_tmp, initialization of c_tmp, 52 | and testing for the presence of c) only to handle the scenario of a 53 | missing optional argument. 54 | 55 | This proposal seeks to address the issue that explicitly checking for 56 | presence of the optional dummy argument and using a helper variable is 57 | cumbersome and error-prone. The primary benefit of this feature is the 58 | reduction in source code needed to handle optional arguments. This 59 | benefit is even greater in scenarios where the optional argument is 60 | used in many places in the procedure, and a helper variable is used for 61 | its value instead. Reduction in needed source code would result in more 62 | readable and more correct programs. The secondary benefit of this is 63 | programmer happiness, as working with optional arguments would require 64 | less typing. 65 | 66 | 3. Proposed solution 67 | 68 | As suggested by Van Snyder in 18-136r1, the problem could be solved by 69 | allowing an optional argument to be initialized using a constant 70 | expression. The optional argument would then only be initialized if the 71 | corresponding actual argument is not provided by the caller. Example: 72 | 73 | real function quadratic(x, a, b, c) 74 | ! returns a + b * x + c * x**2 if c is present 75 | ! and a + b * x otherwise 76 | real, intent(in) :: x, a, b 77 | real, intent(in), optional :: c = 0 78 | quadratic = a + b * x + c * x**2 79 | end function quadratic 80 | 81 | In this snippet, we use the assignment operator (=) to specify the 82 | default value of the optional dummy argument. 83 | 84 | Like initializer, the optional argument can be assigned any constant 85 | expression, as defined in Section 10.1.12 of 18-007r1. 86 | 87 | While there may be concerns that the same syntax is used to implicitly 88 | set the save attribute for variables in procedures, there is no 89 | conflict because the language prohibits dummy arguments from having an 90 | initializer, or the save attribute. The change to the standard to allow 91 | this feature would thus be to allow an optional dummy argument to have 92 | an initializer, which would be triggered only when corresponding actual 93 | argument is not passed by the caller. 94 | 95 | This improvement has already been suggested by Van Snyder in 18-136r1, 96 | has received votes in the user survey for 202X (see 97 | isotc.iso.org/livelink/livelink?func=ll&objId=19530634&objAction=Open), 98 | and appeared on Data subgroup's wishlist at meeting 215 (see 18-122r1). 99 | 100 | 4. Backward compatibility 101 | 102 | This addition to the language would not break any existing standard 103 | conforming Fortran program, and thus preserves Fortran's backward 104 | compatibility. 105 | 106 | 5. Related behavior 107 | 108 | 5.1. present() 109 | 110 | There are two possible behaviors of the function present() when an 111 | initializer is provided. 112 | 113 | The first behavior would be that the function present() always returns 114 | .true. when the optional dummy argument has an initializer, and 115 | independently of the fact that an actual argument is passed or not 116 | by the caller. Therefore, with such a behavior, the function present() 117 | becomes useless for optional arguments with a default value. 118 | 119 | Example: 120 | 121 | real function foo(a, b) 122 | real, intent(in), optional :: a 123 | real, intent(in), optional :: b = 0 124 | print*, present(a) !.true. if an actual argument is provided 125 | !by the caller, .false. otherwise 126 | print*, present(b) !always .true. due to the initializer 127 | end function foo 128 | 129 | The second behavior would be that the function present() returns 130 | .true. or .false. when an actual argument is passed or not by the 131 | caller, and independently of the fact that the optional dummy 132 | argument has, or hasn't an initializer. 133 | 134 | A use case of this behavior of the function present() could be: 135 | 136 | real function foo(a, b) 137 | real, intent(in) :: a 138 | real, intent(in), optional :: b = 1.234 139 | if (present(b)) then 140 | ... !expensive input validation here 141 | end if 142 | ... 143 | end function foo 144 | 145 | 5.2. intent(out) 146 | 147 | There are several different possible behaviors for intent(out) 148 | variables. One option is to disallow default values on optional 149 | intent(out) variables. However, there are use cases where it is 150 | desirable to return the value of a variable if a caller requests it, 151 | without requiring the added logic of present() and "temporary" 152 | variables described above. 153 | 154 | Alternatively, we might treat intent(in) and intent(out) variables 155 | identically, so that the following would be valid: 156 | 157 | real function foo(a) 158 | real, intent(out), optional :: a = 0 159 | end function foo 160 | 161 | The benefit is that, just like proposed for intent(in) variables, 162 | intent(out) variables would be usable regardless of whether the value 163 | is actually passed back to the caller, removing the need for present() 164 | and "temporary" variables. The complication is that a present 165 | intent(out) variable is initially undefined, meaning the logic of 166 | present() might still be necessary in this case. Furthermore, the 167 | behavior suggested above where present() = .true. always for variables 168 | with a specified default would not be desired for intent(out) 169 | variables. A possible use case for this behavior could be: 170 | 171 | integer function open(filename, mode, iostat) result(u) 172 | character(*), intent(in) :: filename 173 | character(*), intent(in), optional :: mode 174 | integer, intent(out), optional :: iostat = 0 175 | character(3) :: mode_ 176 | character(:),allocatable :: action_, position_, status_, access_ 177 | character(:),allocatable :: form_ 178 | !some code 179 | !.... 180 | open(newunit=u, file=filename, & 181 | action = action_, position = position_, status = status_, & 182 | access = access_, form = form_, & 183 | iostat = iostat) 184 | end function 185 | 186 | A third option is that optional intent(out) variables with a specified 187 | default are always initialized to that default, even when present, 188 | rather than leaving them undefined. 189 | 190 | 5.3 intent(inout) 191 | 192 | Dummy arguments with intent(inout) or no specified intent follow from 193 | the behavior for intent(in) and intent(out). If an optional 194 | intent(inout) variable is present, the existing behavior is 195 | followed. The dummy variable has the value of the actual argument on 196 | invocation of the procedure, its data may be used or manipulated during 197 | the scope of the procedure, then it is returned to the scoping unit. If 198 | an actual argument is not provided for an optional dummy argument with 199 | a specified default and intent(inout), the variable recieves the 200 | default value and may be used or modified throughout the procedure. The 201 | data is then not returned to the scoping unit, since no actual argument 202 | was provided. 203 | 204 | This would allow the following routine: 205 | 206 | subroutine foo(x) 207 | integer, intent(inout), optional :: x = 0 208 | 209 | ! do stuff that could use or assign to x 210 | end subroutine foo 211 | 212 | This would be equivalent to: 213 | 214 | subroutine foo(x) 215 | integer, intent(inout), optional :: x 216 | 217 | integer :: x_ 218 | 219 | if (present(x)) then 220 | x_ = x 221 | else 222 | x_ = 0 223 | end if 224 | 225 | ! do stuff that could use or assign to x_ 226 | 227 | if (present(x)) x = x_ 228 | end subroutine foo 229 | 230 | 231 | 5.4 Arrays 232 | 233 | Default values for array dummy arguments present a difficulty, 234 | particularly expressions involving a scalar. Some decision must be made 235 | for the behavior in the following instances: 236 | 237 | real, intent(in), optional :: a(:) = 0 238 | real, intent(inout), optional :: b(:) = 0 239 | real, intent(inout), allocatable, optional :: c(:) = 0 240 | 241 | In each case, the size of the array is not determined if an actual 242 | argument is not provided. One option is of course to forbid defaults 243 | for arrays. This isn't preferable, but allowing defaults for scalars 244 | only may still address the majority of use cases. Another option is to 245 | forbid array broadcasting as a default specifier. That is, allow only 246 | defaults such as [0,0] for arrays, where the length is clear. 247 | 248 | 6. Further discussion 249 | 250 | Online discussion that led to this proposal can be found at 251 | https://github.com/j3-fortran/fortran_proposals/issue/22. 252 | -------------------------------------------------------------------------------- /proposals/namelist_delimiters/namelist_proposal.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/XX-XXX 2 | From: Marshall Ward 3 | Subject: Require delimiters for character arrays in namelist output 4 | Date: 19 November 2019 5 | 6 | Proposal for Fortran Standard: 202y 7 | 8 | 9 | 1. Introduction 10 | 11 | According to the current standard, a WRITE statement can write a 12 | namelist file that does not conform to the namelist specification. This 13 | happens when the namelist group contains a character array and the DELIM 14 | specifier has a value of NONE. In particular, this is the default 15 | behavior of a WRITE statement whose input is a namelist. 16 | 17 | Our proposal is to require delimiters when using WRITE to write a 18 | namelist to a file, by either requiring a value of DELIM which is 19 | namelist-compliant, or by overriding a value of NONE when the input is a 20 | namelist. 21 | 22 | 23 | 2. Motivation 24 | 25 | The namelist format is described in section 13.11 of the standard, and 26 | 13.11.3.3p7 requires that character arrays in namelist groups must be 27 | delimited with single or double quotes. 28 | 29 | When the next effective item is of type character, the input form 30 | consists of a sequence of zero or more rep-chars whose kind type 31 | parameter is implied by the kind of the corresponding list item, 32 | delimited by apostrophes or quotes. 33 | 34 | Any namelist whose character arrays are non-delimited is non-conformant. 35 | Any parsing of this output is therefore considered to be unformatted, 36 | and the interpretation is at the discretion of the interpreter. 37 | 38 | Without delimiters, many character arrays become unparseable. If a 39 | character array contains any lexical namelist tokens, such as `&` or 40 | `/`, then any non-delimited values may be misinterpreted as part of the 41 | namelist object structure. 42 | 43 | The standard acknowledges the limitations of non-delimited character 44 | array parsing, and specifically directs the interpreter to ignore the 45 | value of DELIM when reading a namelist (12.5.6.8). 46 | 47 | The scalar-default-char-expr shall evaluate to APOSTROPHE, QUOTE, or 48 | NONE. The DELIM= specifier is permitted only for a connection for 49 | formatted input/output. It specifies the delimiter mode (12.6.2.8) 50 | for list-directed (13.10.4) and namelist (13.11.4.2) output for the 51 | connection. This mode has no effect on input. 52 | 53 | However, despite the acknowledgment of the issues above, the default 54 | behavior of a WRITE command is to produce non-delimited character 55 | arrays. From 13.11.4.2p1, 56 | 57 | Values in namelist output records are edited as for list-directed 58 | output (13.10.4). 59 | 60 | This is done despite the fact that list-directed output follows 61 | different I/O rules for character arrays. From 13.10.3.1p7 (my 62 | emphasis), 63 | 64 | When the next effective item is of type character, the input form 65 | consists of a **possibly** delimited sequence of zero or more 66 | rep-chars whose kind type parameter is implied by the kind of the 67 | effective item. 68 | 69 | The namelist specification 13.11.3.3p7 deliberately omits "possibly" 70 | from its description. 71 | 72 | In other words, list-directed output permits non-delimited arrays, 73 | whereas namelists do not. In addition, the default value of DELIM is to 74 | revert back to its value in the OPEN call. From 12.5.6.8p1, 75 | 76 | If this specifier is omitted in an OPEN statement that initiates a 77 | connection, the default value is NONE. 78 | 79 | The default behavior of a WRITE call using namelist data is therefore to 80 | produce an output which is non-conformant with the namelist standard. 81 | 82 | 83 | 3. Example 84 | 85 | Consider the program listed below, which will produce a namelist 86 | containing a single group `sample_nml`, containing a single character 87 | array, `input`. 88 | 89 | program writenml 90 | implicit none 91 | character(len=20) :: input 92 | namelist /sample_nml/ input 93 | 94 | input = trim("some/path/to/file") 95 | open(5, file="out.nml") 96 | write(5, nml=sample_nml) 97 | end program writenml 98 | 99 | According to the interpretation above, the absence of a DELIM argument 100 | means that `input` is formatted with no delimiter. A 101 | standard-conforming output would be 102 | 103 | &SAMPLE_NML 104 | INPUT = some/path/to/file 105 | / 106 | 107 | For this example, we have used the output produced by the Intel Fortran 108 | Compiler 19.0.5.281. 109 | 110 | Now consider the following program, which reads this namelist. 111 | 112 | program readnml 113 | implicit none 114 | character(len=20) :: input 115 | namelist /sample_nml/ input 116 | 117 | open(5, file='out.nml') 118 | read(5, nml=sample_nml) 119 | 120 | open(6, file='new.nml') 121 | write(6, nml=sample_nml) 122 | end program readnml 123 | 124 | The namelist `new.nml` produced by this program is the following. 125 | 126 | &SAMPLE_NML 127 | INPUT = some 128 | / 129 | 130 | The namelist group `sample_nml` is terminated after the first `/` token, 131 | and any characters following the token are ignored. 132 | 133 | Although the interpretation is correct, it also means that a write 134 | statement of the following form 135 | 136 | write(unit, nml=filename) 137 | 138 | where the DELIM argument is unset will produce namelists which are 139 | non-conforming. The fact that this is not only possible, but is the 140 | default behavior, is counterintuitive and is likely to introduce errors 141 | into namelist I/O operations. 142 | 143 | As an aside, we note that GNU Fortran explicitly breaks from the 144 | standard and does produce a quote-delimited namelist, such as the one 145 | shown below. 146 | 147 | &SAMPLE_NML 148 | INPUT="some/path/to/file ", 149 | / 150 | 151 | This namelist above was produced by GNU Fortran 9.2.1. 152 | 153 | 154 | 4. Proposal 155 | 156 | We propose one of the following additions to the *io-control-spec-list*, 157 | detailed in 12.6.2.1. 158 | 159 | A. If *namelist-group-name* appears, then a DELIM= specifier with the 160 | value of either APOSTROPHE or QUOTE shall also appear. 161 | 162 | Option A would take the current recommended advice to always use DELIM 163 | when writing namelist output and turn it into an explicit rule. The 164 | following statement would constitute an error 165 | 166 | write(unit, nml=filename) 167 | 168 | and would require the user to include a DELIM argument, e.g. 169 | 170 | write(unit, nml=filename, delim="quote") 171 | 172 | This would also mean that currently compliant code missing a DELIM would 173 | be non-compliant, and may require modifications if used by future 174 | interpreters. 175 | 176 | B. If *namelist-group-name* appears and a DELIM= specifier has the value 177 | of NONE, then this value is ignored and the data transfer uses a 178 | value of APOSTROPHE. 179 | 180 | Option B would change the behavior of existing standard-compliant 181 | interpreters, in that non-delimited character arrays would be replaced 182 | with apostrophe-delimited arrays. But existing source code would 183 | otherwise remain compliant and continue to compile on both older and 184 | newer interpreters. 185 | 186 | 187 | 5. Reference 188 | 189 | Discussion of this issue on the Intel Fortran forums: 190 | 191 | https://software.intel.com/en-us/forums/intel-fortran-compiler/topic/831685 192 | 193 | Discussion of GNU Fortran's decision to use quote delimiters: 194 | 195 | https://gcc.gnu.org/ml/gcc-patches/2014-03/msg00030.html 196 | 197 | Initial submission and discussion to the J3 Fortran Github repository: 198 | 199 | https://github.com/j3-fortran/fortran_proposals/pull/94 200 | -------------------------------------------------------------------------------- /proposals/namespace_modules/19-246.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/19-246 2 | From: Ondrej Certik 3 | Subject: Namespace For Modules 4 | Date: 2019-October-16 5 | 6 | Proposal for Fortran Standard: 202y (NOT 202x) 7 | 8 | 9 | 1. Introduction 10 | 11 | The proposal is to allow import a module as a namespace and access its 12 | members using the % operator. Example: 13 | 14 | use, namespace :: utils 15 | ... 16 | call utils%savetxt(...) 17 | 18 | Where `utils` is the only name that is imported in the local namespace. 19 | `savetxt` is not accesible directly, only via `utils%`. 20 | 21 | 2. Motivation 22 | 23 | Fortran module usage is equivalent to Python: 24 | 25 | Python Fortran 26 | 27 | from A import foo use A, only: foo 28 | from A import foo as Afoo use A, only: Afoo => foo 29 | from A import * use A 30 | 31 | Except: 32 | 33 | Python Fortran 34 | 35 | import A N/A 36 | import A as B N/A 37 | 38 | This proposal proposes to fill in the missing functionality as follows: 39 | 40 | Python Fortran 41 | 42 | import A use, namespace :: A 43 | import A as B use, namespace :: B => A 44 | 45 | 3. Use Cases 46 | 47 | 3.1 Same function names in multiple modules 48 | 49 | In Python a very common idiom is: 50 | 51 | import math 52 | import numpy as np 53 | import sympy as sym 54 | ... 55 | e1 = np.sin(np.pi) # NumPy expression 56 | e2 = math.sin(math.pi) # Built-in Python math expression 57 | e3 = sym.sin(sym.pi) # SymPy expression 58 | 59 | In Fortran currently one has to do: 60 | 61 | use math, only: math_sin => sin, math_pi => pi 62 | use numpy, only: np_sin => sin, np_pi => pi 63 | use sympy, only: sym_sin => sin, sym_pi => pi 64 | ... 65 | e1 = np_sin(np_pi) ! NumPy expression 66 | e2 = math_sin(math_pi) ! Built-in Python math expression 67 | e3 = sym_sin(sym_pi) ! SymPy expression 68 | 69 | With this proposal one could also do: 70 | 71 | use, namespace :: math 72 | use, namespace :: np => numpy 73 | use, namespace :: sym => sympy 74 | ... 75 | e1 = np%sin(np%pi) ! NumPy expression 76 | e2 = math%sin(math%pi) ! Built-in Python math expression 77 | e3 = sym%sin(sym%pi) ! SymPy expression 78 | 79 | 80 | 3.2 Need to import lots of functions from a module 81 | 82 | Exising code (https://github.com/certik/fortran-utils/blob/ 83 | b43bd24cd421509a5bc6d3b9c3eeae8ce856ed88/src/linalg.f90): 84 | 85 | use lapack, only: dsyevd, dsygvd, ilaenv, zgetri, zgetrf, zheevd, & 86 | dgeev, zgeev, zhegvd, dgesv, zgesv, dgetrf, dgetri, dgelsy, & 87 | zgelsy, dgesvd, zgesvd, dgeqrf, dorgqr, dpotrf, dtrtrs 88 | ... 89 | call dgeev('N', 'V', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & 90 | work, lwork, info) 91 | ... 92 | call dgetrf(n, n, Amt, lda, ipiv, info) 93 | ... 94 | 95 | Instead, one can write it as: 96 | 97 | use, namespace :: lapack 98 | ... 99 | call lapack%dgeev('N', 'V', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & 100 | work, lwork, info) 101 | ... 102 | call lapack%dgetrf(n, n, Amt, lda, ipiv, info) 103 | ... 104 | 105 | Then when another subroutine must be called from the `lapack` module, one 106 | can just call it, without having to modify the `use` statement. 107 | 108 | 109 | -------------------------------------------------------------------------------- /proposals/namespace_modules/20-108.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/20-108 2 | From: Ondrej Certik 3 | Subject: Namespace For Modules 4 | Date: 2020-February-23 5 | 6 | Proposal for Fortran Standard: 202y (NOT 202x) 7 | 8 | 9 | 1. Introduction 10 | 11 | The proposal is to allow import a module as a namespace and access its 12 | members using the % operator. Example: 13 | 14 | use, namespace :: utils 15 | ... 16 | call utils%savetxt(...) 17 | 18 | Where `utils` is the only name that is imported in the local namespace. 19 | `savetxt` is not accesible directly, only via `utils%`. 20 | 21 | This proposal originated at the J3 GitHub repository at [1]. 22 | 23 | 2. Motivation 24 | 25 | Fortran module usage is equivalent to Python: 26 | 27 | Python Fortran 28 | 29 | from A import foo use A, only: foo 30 | from A import foo as Afoo use A, only: Afoo => foo 31 | from A import * use A 32 | 33 | Except: 34 | 35 | Python Fortran 36 | 37 | import A N/A 38 | import A as B N/A 39 | 40 | This proposal proposes to fill in the missing functionality as follows: 41 | 42 | Python Fortran 43 | 44 | import A use, namespace :: A 45 | import A as B use, namespace :: B => A 46 | 47 | 3. Use Cases 48 | 49 | 3.1 Same function names in multiple modules 50 | 51 | In Python a very common idiom is: 52 | 53 | import math 54 | import numpy as np 55 | import sympy as sym 56 | ... 57 | e1 = np.sin(np.pi) # NumPy expression 58 | e2 = math.sin(math.pi) # Built-in Python math expression 59 | e3 = sym.sin(sym.pi) # SymPy expression 60 | 61 | In Fortran currently one has to do: 62 | 63 | use math, only: math_sin => sin, math_pi => pi 64 | use numpy, only: np_sin => sin, np_pi => pi 65 | use sympy, only: sym_sin => sin, sym_pi => pi 66 | ... 67 | e1 = np_sin(np_pi) ! NumPy expression 68 | e2 = math_sin(math_pi) ! Built-in Python math expression 69 | e3 = sym_sin(sym_pi) ! SymPy expression 70 | 71 | With this proposal one could also do: 72 | 73 | use, namespace :: math 74 | use, namespace :: np => numpy 75 | use, namespace :: sym => sympy 76 | ... 77 | e1 = np%sin(np%pi) ! NumPy expression 78 | e2 = math%sin(math%pi) ! Built-in Python math expression 79 | e3 = sym%sin(sym%pi) ! SymPy expression 80 | 81 | 82 | 3.2 Need to import lots of functions from a module 83 | 84 | Existing code (https://github.com/certik/fortran-utils/blob/ 85 | b43bd24cd421509a5bc6d3b9c3eeae8ce856ed88/src/linalg.f90): 86 | 87 | use lapack, only: dsyevd, dsygvd, ilaenv, zgetri, zgetrf, zheevd, & 88 | dgeev, zgeev, zhegvd, dgesv, zgesv, dgetrf, dgetri, dgelsy, & 89 | zgelsy, dgesvd, zgesvd, dgeqrf, dorgqr, dpotrf, dtrtrs 90 | ... 91 | call dgeev('N', 'V', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & 92 | work, lwork, info) 93 | ... 94 | call dgetrf(n, n, Amt, lda, ipiv, info) 95 | ... 96 | 97 | Instead, one can write it as: 98 | 99 | use, namespace :: lapack 100 | ... 101 | call lapack%dgeev('N', 'V', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & 102 | work, lwork, info) 103 | ... 104 | call lapack%dgetrf(n, n, Amt, lda, ipiv, info) 105 | ... 106 | 107 | Then when another subroutine must be called from the `lapack` module, one 108 | can just call it, without having to modify the `use` statement. 109 | 110 | 4. References 111 | 112 | [1] https://github.com/j3-fortran/fortran_proposals/issues/1 113 | 114 | 115 | -------------------------------------------------------------------------------- /proposals/protected_attribute.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/##-### 2 | From: Balint Aradi 3 | Subject: Protected attribute for derived type components 4 | Date: 2019-October-21 5 | 6 | Proposal for Fortran Standard: 202y (NOT 202x) 7 | 8 | 9 | 1. Introduction 10 | 11 | The proposal is to allow specify the protected attribute for components in 12 | derived types. Example: 13 | 14 | 15 | type :: prot_t 16 | integer, allocatable, protected :: array(:) 17 | end type prot_t 18 | 19 | 20 | 2. Motivation 21 | 22 | Data hiding using derived types with private components allows programmer 23 | to provide robust types, where internals can only be changed in controlled 24 | fashion. The usual implementation is to provide setter and getter routines 25 | for writing and reading components. Both operations involve copying the 26 | passed data, which can be inefficient when storing large arrays in the 27 | derived type instance. While in the setter routine the copying serves 28 | robustness by "disentangling" the data stored in the derived type from the 29 | original data, the getter routine and the connected copy operation might be 30 | superfluous, when the consumer only wants to read but not modify the data 31 | stored in the derived type intance. 32 | 33 | By allowing for a direct read-only access, one could enhance efficiency in 34 | those cases without sacrificing the consistency of the encapsulated 35 | data. Direct access is possible with current Fortran already, but only if 36 | the respective derived type component is public. This would jeopardize data 37 | consistency, though, as a consumer could change the respective component 38 | arbitrarily, without using the provided setter routine. 39 | 40 | This proposal suggests to allow the "protected" attribute already used for 41 | module variables being used for derived type components as well. It would 42 | also remedy the asymmetry between the attributes "private"/"public" and the 43 | attribute "protected", as the former two can be applied to both, module 44 | variables and derived type components, while the latter only for module 45 | variables so far. 46 | 47 | 48 | 3. Use Cases 49 | 50 | Derived types storing large amount of data could enable read-only access to 51 | components without the necessity of a getter routine and the connected copy 52 | operation: 53 | 54 | module data_m 55 | implicit none 56 | 57 | type :: prot_t 58 | ! Large array component 59 | integer, allocatable, protected :: array(:) 60 | contains 61 | procedure :: set 62 | end type prot_t 63 | 64 | contains 65 | 66 | subroutine set(this, array) 67 | type(prot_t), intent(out) :: this 68 | 69 | this%array = array 70 | 71 | end subroutine set 72 | 73 | end module data_m 74 | 75 | 76 | 77 | program use_data 78 | use data_m 79 | implicit none 80 | 81 | type(prot_t) :: storage 82 | integer, allocatable :: large_array(:) 83 | 84 | ! Filling up and allocating the large array 85 | ! ... 86 | 87 | ! Storing the large array in the derived type instance 88 | call storage%set(large_array) 89 | 90 | ! Accessing large array stored in the derived type directly 91 | ! No getter routine and no copy necessary 92 | ! Dummy argument of the called routine must be intent(in) 93 | call some_routine_processing_but_not_changing_the_array(storage%array) 94 | 95 | ! Inconsistent change, consumer is supposed to call set() to change 96 | ! encapsulated data. 97 | ! Uncommenting the next line should trigger a compiler error. 98 | !storage%array(1) = -1 99 | 100 | end program use_data 101 | 102 | 4. Online discussion 103 | 104 | This proposal derived from the discussion on the j3-fortran github page: 105 | https://github.com/j3-fortran/fortran_proposals/issues/16 106 | -------------------------------------------------------------------------------- /proposals/run-time_polymorphism/Examples/AppendixA/taylor.f90: -------------------------------------------------------------------------------- 1 | ! Fortran implementation of the Taylor series example program based on 2 | ! subclassing given in Appendix A of the proposal "Improved run-time 3 | ! polymorphism for Fortran". 4 | ! 5 | 6 | module derivs 7 | 8 | type, abstract :: Deriv 9 | contains 10 | procedure(pderiv), deferred, nopass :: deriv1 11 | end type Deriv 12 | 13 | abstract interface 14 | subroutine pderiv() 15 | end subroutine pderiv 16 | end interface 17 | 18 | type, extends(Deriv) :: DerivF 19 | contains 20 | procedure, nopass :: deriv1 => deriv1f 21 | end type DerivF 22 | 23 | type, extends(DerivF) :: HDerivF 24 | contains 25 | procedure, nopass :: deriv2 => deriv2f 26 | end type HDerivF 27 | 28 | type, extends(Deriv) :: DerivG 29 | contains 30 | procedure, nopass :: deriv1 => deriv1g 31 | end type DerivG 32 | 33 | type, extends(DerivG) :: HDerivG 34 | contains 35 | procedure, nopass :: deriv2 => deriv2g 36 | end type HDerivG 37 | 38 | contains 39 | 40 | subroutine deriv1f() 41 | write(*,*) ' 1st derivative of function F!' 42 | end subroutine deriv1f 43 | 44 | subroutine deriv2f() 45 | write(*,*) ' 2nd derivative of function F!' 46 | end subroutine deriv2f 47 | 48 | subroutine deriv1g() 49 | write(*,*) ' 1st derivative of function G!' 50 | end subroutine deriv1g 51 | 52 | subroutine deriv2g() 53 | write(*,*) ' 2nd derivative of function G!' 54 | end subroutine deriv2g 55 | 56 | end module derivs 57 | 58 | module series 59 | 60 | use derivs, only: Deriv, HDerivF, HDerivG 61 | 62 | type :: Taylor 63 | class(Deriv), allocatable :: calc 64 | contains 65 | procedure :: term1 66 | procedure :: evaluate 67 | end type Taylor 68 | 69 | type, extends(Taylor) :: HTaylor 70 | contains 71 | procedure :: term2 => hterm2 72 | procedure :: evaluate => hevaluate 73 | end type HTaylor 74 | 75 | contains 76 | 77 | subroutine term1(self) 78 | class(Taylor), intent(in) :: self 79 | call self%calc%deriv1() 80 | end subroutine term1 81 | 82 | subroutine evaluate(self) 83 | class(Taylor), intent(in) :: self 84 | write(*,*) 'Evaluating Taylor series using' 85 | call self%term1() 86 | end subroutine evaluate 87 | 88 | subroutine hterm2(self) 89 | class(HTaylor), intent(in) :: self 90 | select type ( calc => self%calc ) 91 | class is ( HDerivF ) 92 | call calc%deriv2() 93 | class is ( HDerivG ) 94 | call calc%deriv2() 95 | class default 96 | write(*,*) 'Unknown type!' 97 | end select 98 | end subroutine hterm2 99 | 100 | subroutine hevaluate(self) 101 | class(HTaylor), intent(in) :: self 102 | write(*,*) 'Evaluating Taylor series using' 103 | call self%term1() 104 | call self%term2() 105 | end subroutine hevaluate 106 | 107 | end module series 108 | 109 | program client 110 | 111 | use derivs, only: DerivG, HDerivG, Deriv 112 | use series, only: Taylor, HTaylor 113 | 114 | class(Deriv), allocatable :: derv 115 | class(Taylor), allocatable :: teval 116 | 117 | derv = DerivG() 118 | teval = Taylor(derv) 119 | call teval%evaluate() 120 | 121 | write(*,*) 122 | 123 | derv = HDerivG() 124 | teval = HTaylor(derv) 125 | call teval%evaluate() 126 | 127 | end program client 128 | -------------------------------------------------------------------------------- /proposals/run-time_polymorphism/Examples/AppendixB/taylor.f90: -------------------------------------------------------------------------------- 1 | ! Fortran implementation of the Taylor series example program based on 2 | ! subtyping given in Appendix B of the proposal "Improved run-time 3 | ! polymorphism for Fortran". 4 | ! 5 | 6 | module interfaces 7 | 8 | abstract interface :: IDeriv 9 | subroutine deriv1() 10 | end subroutine deriv1 11 | end interface IDeriv 12 | 13 | abstract interface, extends(IDeriv) :: IHDeriv 14 | subroutine deriv2() 15 | end subroutine deriv2 16 | end interface IHDeriv 17 | 18 | end module interfaces 19 | 20 | module derivs 21 | 22 | use interfaces, only: IDeriv, IHDeriv 23 | 24 | type, implements(IDeriv) :: DerivF 25 | contains 26 | procedure, nopass :: deriv1 => deriv1f 27 | end type DerivF 28 | 29 | type, implements(IHDeriv) :: HDerivF 30 | contains 31 | procedure, nopass :: deriv1 => deriv1f 32 | procedure, nopass :: deriv2 => deriv2f 33 | end type HDerivF 34 | 35 | type, implements(IDeriv) :: DerivG 36 | contains 37 | procedure, nopass :: deriv1 => deriv1g 38 | end type DerivG 39 | 40 | type, implements(IHDeriv) :: HDerivG 41 | contains 42 | procedure, nopass :: deriv1 => deriv1g 43 | procedure, nopass :: deriv2 => deriv2g 44 | end type HDerivG 45 | 46 | contains 47 | 48 | subroutine deriv1f() 49 | write(*,*) " 1st derivative of function F!" 50 | end subroutine deriv1f 51 | 52 | subroutine deriv2f() 53 | write(*,*) " 2nd derivative of function F!" 54 | end subroutine deriv2f 55 | 56 | subroutine deriv1g() 57 | write(*,*) " 1st derivative of function G!" 58 | end subroutine deriv1g 59 | 60 | subroutine deriv2g() 61 | write(*,*) " 2nd derivative of function G!" 62 | end subroutine deriv2g 63 | 64 | end module derivs 65 | 66 | module series 67 | 68 | use interfaces, only: IDeriv, IHDeriv 69 | 70 | type :: Taylor 71 | class(IDeriv), allocatable :: calc 72 | contains 73 | procedure :: term1 74 | procedure :: evaluate 75 | end type Taylor 76 | 77 | type :: HTaylor 78 | class(IHDeriv), allocatable :: calc 79 | contains 80 | procedure :: term1 => hterm1 81 | procedure :: term2 => hterm2 82 | procedure :: evaluate => hevaluate 83 | end type HTaylor 84 | 85 | contains 86 | 87 | subroutine term1(self) 88 | class(Taylor), intent(in) :: self 89 | call self%calc%deriv1() 90 | end subroutine term1 91 | 92 | subroutine evaluate(self) 93 | class(Taylor), intent(in) :: self 94 | write(*,*) 'Evaluating Taylor series using' 95 | call self%term1() 96 | end subroutine evaluate 97 | 98 | subroutine hterm1(self) 99 | class(HTaylor), intent(in) :: self 100 | call self%calc%deriv1() 101 | end subroutine hterm1 102 | 103 | subroutine hterm2(self) 104 | class(HTaylor), intent(in) :: self 105 | call self%calc%deriv2() 106 | end subroutine hterm2 107 | 108 | subroutine hevaluate(self) 109 | class(HTaylor), intent(in) :: self 110 | write(*,*) 'Evaluating Taylor series using' 111 | call self%term1() 112 | call self%term2() 113 | end subroutine hevaluate 114 | 115 | end module series 116 | 117 | program client 118 | 119 | use derivs, only: DerivG, HDerivG 120 | use series, only: Taylor, HTaylor 121 | 122 | type(Taylor), allocatable :: teval 123 | type(HTaylor), allocatable :: hteval 124 | 125 | teval = Taylor( DerivG() ) 126 | call teval%evaluate() 127 | 128 | write(*,*) 129 | 130 | hteval = HTaylor( HDerivG() ) 131 | call hteval%evaluate() 132 | 133 | end program client 134 | -------------------------------------------------------------------------------- /proposals/run-time_polymorphism/Examples/AppendixB/taylor.java: -------------------------------------------------------------------------------- 1 | // Java implementation of the Taylor series example program based on 2 | // subtyping given in Appendix B of the proposal "Improved run-time 3 | // polymorphism for Fortran". 4 | // 5 | 6 | interface IDeriv { 7 | void deriv1(); 8 | } 9 | 10 | interface IHDeriv extends IDeriv { 11 | void deriv2(); 12 | } 13 | 14 | class DerivF implements IDeriv { 15 | public void deriv1() { 16 | System.out.println(" 1st derivative of function F!"); 17 | } 18 | } 19 | 20 | class HDerivF implements IHDeriv { 21 | public void deriv1() { 22 | System.out.println(" 1st derivative of function F!"); 23 | } 24 | public void deriv2() { 25 | System.out.println(" 2nd derivative of function F!"); 26 | } 27 | } 28 | 29 | class DerivG implements IDeriv { 30 | public void deriv1() { 31 | System.out.println(" 1st derivative of function G!"); 32 | } 33 | } 34 | 35 | class HDerivG implements IHDeriv { 36 | public void deriv1() { 37 | System.out.println(" 1st derivative of function G!"); 38 | } 39 | public void deriv2() { 40 | System.out.println(" 2nd derivative of function G!"); 41 | } 42 | } 43 | 44 | class Taylor { 45 | IDeriv calc; 46 | Taylor(IDeriv calculator) { 47 | calc = calculator; 48 | } 49 | public void term1() { 50 | calc.deriv1(); 51 | } 52 | public void evaluate() { 53 | System.out.println("Evaluating Taylor series using"); 54 | term1(); 55 | } 56 | } 57 | 58 | class HTaylor { 59 | IHDeriv calc; 60 | HTaylor(IHDeriv calculator) { 61 | calc = calculator; 62 | } 63 | public void term1() { 64 | calc.deriv1(); 65 | } 66 | public void term2() { 67 | calc.deriv2(); 68 | } 69 | public void evaluate() { 70 | System.out.println("Evaluating Taylor series using"); 71 | term1(); 72 | term2(); 73 | } 74 | } 75 | 76 | class ClientApp { 77 | 78 | public static void main(String[] args) { 79 | 80 | Taylor eval = new Taylor( new DerivG() ); 81 | eval.evaluate(); 82 | 83 | System.out.println(""); 84 | 85 | HTaylor heval = new HTaylor( new HDerivG() ); 86 | heval.evaluate(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /proposals/run-time_polymorphism/Examples/AppendixB/taylor.rs: -------------------------------------------------------------------------------- 1 | // Rust implementation of the Taylor series example program based on 2 | // subtyping given in Appendix B of the proposal "Improved run-time 3 | // polymorphism for Fortran". 4 | // 5 | 6 | pub mod interfaces { 7 | 8 | pub trait IDeriv { 9 | fn deriv1(&self); 10 | } 11 | 12 | pub trait IHDeriv { 13 | fn deriv1(&self); 14 | fn deriv2(&self); 15 | } 16 | } 17 | 18 | pub mod derivs { 19 | 20 | use interfaces::IDeriv; 21 | use interfaces::IHDeriv; 22 | 23 | pub struct DerivF { 24 | } 25 | impl IDeriv for DerivF { 26 | fn deriv1(&self) { 27 | println!(" 1st derivative of function F!"); 28 | } 29 | } 30 | 31 | pub struct HDerivF { 32 | } 33 | impl IHDeriv for HDerivF { 34 | fn deriv1(&self) { 35 | println!(" 1st derivative of function F!"); 36 | } 37 | fn deriv2(&self) { 38 | println!(" 2nd derivative of function F!"); 39 | } 40 | } 41 | 42 | pub struct DerivG { 43 | } 44 | impl IDeriv for DerivG { 45 | fn deriv1(&self) { 46 | println!(" 1st derivative of function G!"); 47 | } 48 | } 49 | 50 | pub struct HDerivG { 51 | } 52 | impl IHDeriv for HDerivG { 53 | fn deriv1(&self) { 54 | println!(" 1st derivative of function G!"); 55 | } 56 | fn deriv2(&self) { 57 | println!(" 2nd derivative of function G!"); 58 | } 59 | } 60 | } 61 | 62 | pub mod series { 63 | 64 | use interfaces::IDeriv; 65 | use interfaces::IHDeriv; 66 | 67 | pub struct Taylor { 68 | pub calc: Box 69 | } 70 | impl Taylor { 71 | pub fn term1(&self) { 72 | self.calc.deriv1(); 73 | } 74 | pub fn evaluate(&self) { 75 | println!("Evaluating Taylor series using"); 76 | self.term1(); 77 | } 78 | } 79 | 80 | pub struct HTaylor { 81 | pub calc: Box 82 | } 83 | impl HTaylor { 84 | pub fn term1(&self) { 85 | self.calc.deriv1(); 86 | } 87 | pub fn term2(&self) { 88 | self.calc.deriv2(); 89 | } 90 | pub fn evaluate(&self) { 91 | println!("Evaluating Taylor series using"); 92 | self.term1(); 93 | self.term2(); 94 | } 95 | } 96 | } 97 | 98 | fn main() { 99 | 100 | use derivs::DerivG; 101 | use derivs::HDerivG; 102 | 103 | use series::Taylor; 104 | use series::HTaylor; 105 | 106 | let derivg = Box::new( DerivG{} ); 107 | let eval = Box::new( Taylor{calc: derivg} ); 108 | eval.evaluate(); 109 | 110 | println!(""); 111 | 112 | let hderivg = Box::new( HDerivG{} ); 113 | let heval = Box::new( HTaylor{calc: hderivg} ); 114 | heval.evaluate(); 115 | 116 | } 117 | -------------------------------------------------------------------------------- /proposals/run-time_polymorphism/Examples/Text/taylor_basic.f90: -------------------------------------------------------------------------------- 1 | module derivs 2 | type :: DerivF 3 | contains 4 | procedure, nopass :: deriv1 => deriv1f 5 | end type DerivF 6 | contains 7 | subroutine deriv1f() 8 | write(*,*) ' 1st derivative of function F!' 9 | end subroutine deriv1f 10 | end module derivs 11 | 12 | module series 13 | use derivs, only: DerivF 14 | type :: Taylor 15 | type(DerivF), allocatable :: calc 16 | contains 17 | procedure :: term1 18 | procedure :: evaluate 19 | end type Taylor 20 | contains 21 | subroutine term1(self) 22 | class(Taylor), intent(in) :: self 23 | call self%calc%deriv1() 24 | end subroutine term1 25 | 26 | subroutine evaluate(self) 27 | class(Taylor), intent(in) :: self 28 | write(*,*) 'Evaluating Taylor series using' 29 | call self%term1() 30 | end subroutine evaluate 31 | end module series 32 | 33 | program client 34 | use derivs, only: DerivF 35 | use series, only: Taylor 36 | type(Taylor), allocatable :: teval 37 | teval = Taylor( DerivF() ) 38 | call teval%evaluate() 39 | end program client 40 | -------------------------------------------------------------------------------- /proposals/run-time_polymorphism/Fortran_OOP.bib: -------------------------------------------------------------------------------- 1 | @INPROCEEDINGS{Snyder86, 2 | author = {Alan Snyder}, 3 | title = {Encapsulation and Inheritance in Object-Oriented 4 | Programming Languages}, 5 | booktitle = {OOPSLA'86 Proceedings}, 6 | year = {1986}, 7 | month = "September", 8 | pages = {38--45}, 9 | publisher = "ACM" 10 | } 11 | 12 | @book{Bates_et_al_09, 13 | author = "Bates, Bert and Sierra, Kathy and Freeman, Eric and 14 | Robson, Elisabeth", 15 | title = "Head First Design Patterns", 16 | year = "2009", 17 | publisher = "O'Reilly Media", 18 | address = "" 19 | } 20 | 21 | @book{Gamma_et_al_94, 22 | author = "Gamma, Erich and Helm, Richard and Johnson, Ralph and 23 | Vlissides, John", 24 | title = "Design Patterns. 25 | Elements of Reusable Object-Oriented Software", 26 | year = "1994", 27 | publisher = "Prentice Hall", 28 | address = "" 29 | } 30 | 31 | @book{Martin_03, 32 | author = "Martin, Robert C.", 33 | title = "Agile Software Development: 34 | Principles, Patterns, and Practices", 35 | year = "2002", 36 | publisher = "Pearson", 37 | address = "" 38 | } 39 | 40 | @misc{Weck_Szyperski, 41 | author = "Weck, Wolfgang and Szyperski, Clemens", 42 | title = "{Do We Need Inheritance?}", 43 | howpublished = {\url{https://www.researchgate.net/publication/2297653_Do_We_Need_Inheritance}}, 44 | note = {Accessed February 4, 2020}, 45 | } 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /proposals/run-time_polymorphism/Fortran_OOP.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j3-fortran/fortran_proposals/608c3c2d22029d97589d42fe856cc362a6f4ecb1/proposals/run-time_polymorphism/Fortran_OOP.pdf -------------------------------------------------------------------------------- /proposals/run-time_polymorphism/Fortran_OOP.tex: -------------------------------------------------------------------------------- 1 | \documentclass[11pt,oneside]{article} 2 | \usepackage[a4paper, total={6.5in, 9in}]{geometry} 3 | \usepackage{natbib} 4 | \usepackage{url} 5 | \usepackage{listings} 6 | \usepackage{xcolor} 7 | \usepackage[title]{appendix} 8 | 9 | \frenchspacing 10 | 11 | \definecolor{codegreen}{rgb}{0,0.6,0} 12 | \definecolor{codegray}{rgb}{0.5,0.5,0.5} 13 | \definecolor{codepurple}{rgb}{0.58,0,0.82} 14 | \definecolor{backcolour}{rgb}{0.95,0.95,0.92} 15 | 16 | \lstdefinestyle{mystyle}{ 17 | % backgroundcolor=\color{backcolour}, 18 | commentstyle=\color{codegreen}, 19 | % keywordstyle=\color{magenta}, 20 | numberstyle=\tiny\color{codegray}, 21 | stringstyle=\color{codepurple}, 22 | basicstyle=\ttfamily\footnotesize, 23 | breakatwhitespace=false, 24 | breaklines=true, 25 | captionpos=b, 26 | keepspaces=true, 27 | % numbers=left, 28 | numbers=none, 29 | numbersep=5pt, 30 | showspaces=false, 31 | showstringspaces=false, 32 | showtabs=false, 33 | tabsize=2 34 | } 35 | 36 | \lstset{style=mystyle} 37 | 38 | \begin{document} 39 | 40 | \title{Improved run-time polymorphism for Fortran \\ [1em] 41 | \large{Proposal to J3 for inclusion into Fortran 202y} 42 | } 43 | \author{K. Kifonidis} 44 | 45 | \maketitle 46 | 47 | \abstract{A case is made for extending Fortran's support for 48 | object-oriented (OO) programming (i.e. run-time polymorphism) in 49 | order to enable and encourage the use of best practices for the 50 | design and implementation of flexible OO software, and to improve 51 | the safety and efficiency of the language. It is proposed to 52 | introduce multiple inheritance of specification, in order to 53 | supplement the single inheritance of implementation that is already 54 | contained in the language. The new feature extends Fortran's 55 | abstract interfaces and derived types by functionality akin to what 56 | is present in Java, and some other, recent, languages. It is 57 | essential for writing code that conforms to the dependency inversion 58 | principle of OO programming. This will encourage the development of 59 | software that forgoes dependency upon concretions in favor of 60 | dependency on abstractions. The decoupling of classes that can be 61 | achieved in this way will lead to significantly more flexible and 62 | extensible numerical codes, that are also more efficient. The new 63 | feature enables a programming style that dispenses with the use of 64 | implementation inheritance, and therefore helps to eliminate type 65 | conflicts whose resolution requires ``select type'' (down-casting) 66 | statements. These, and their associated run-time overhead, have been 67 | a frequent gripe of Fortran OO programmers since the introduction of 68 | Fortran 2003. As a bonus, the feature should be relatively easy to 69 | implement in Fortran compilers that adhere to at least the Fortran 70 | 2003 standard.} 71 | 72 | \section{Introduction} 73 | 74 | Over the last decades, a significant body of experience has 75 | accumulated concerning the practical use of object-oriented 76 | programming (OOP). This experience has markedly shaped the design of 77 | some very recent programming languages, like Rust and Go, and it has 78 | also led to some notable revision of our notions of what actually 79 | constitutes OOP. In this modern view, the very essence of OOP, and in 80 | fact its ultimate upshot, is the use of (run-time) polymorphism to 81 | manage (i.e. minimize) code dependencies 82 | \cite{Martin_03,Bates_et_al_09}. Viewed in this way, OOP is simply a 83 | means to organize code so that it achieves a maximum amount of 84 | \emph{decoupling}. 85 | 86 | The crucial idea in this context is that polymorphism has the unique 87 | ability to allow one to invert the flow of dependency in a computer 88 | code, which normally points from higher level modules to lower level 89 | modules (read ``classes'' in the OOP context). Polymorphism makes it 90 | possible to replace (``invert'') the dependency of a higher level 91 | class upon a lower level class by a dependency of both these classes 92 | upon a pure abstraction, which is called an interface (or a contract, 93 | protocol, or trait). Thereby, high-level policy gets decoupled from 94 | low-level implementation detail. This is known as the dependency 95 | inversion principle \cite{Martin_03}. If strictly followed, this 96 | principle leads to the development of loosely coupled, easily 97 | extensible, easily testable, modular, maintainable, and thus 98 | future-proof software. It is this decoupling which allows for the 99 | re-usability and flexibility of well-written OO code. 100 | 101 | The situation encountered in practice is usually a different one, 102 | though. Experience has shown that OO software which is written in 103 | Fortran 2003 (and its successors) seldom displays these desirable 104 | characteristics. It will be argued in the following that it is the 105 | combination of a lack of appropriate features in the language, for 106 | encouraging (or even enforcing) a programming style that upholds the 107 | dependency inversion principle, and the consequent indiscriminate use 108 | of features that the language \emph{does} provide (in particular 109 | inheritance of implementation), that has to be blamed for this 110 | situation. 111 | 112 | \section{Deficiencies in Fortran 2018 addressed by this proposal} 113 | \label{sect:F08_deficits} 114 | 115 | \subsection{No explicit distinction between different inheritance types} 116 | 117 | In statically typed languages, like Fortran, run-time polymorphism is 118 | tightly bound to inheritance. In order to manage dependencies in the 119 | way it was described above, the users of these languages must employ 120 | some form of inheritance mechanism. It is important to realize that it 121 | is \emph{inheritance of (object) specification}, i.e. inheritance of 122 | interfaces, that is required for this purpose. It is this (restricted) 123 | form of inheritance that is today viewed as being indispensable for 124 | OOP. Interface inheritance allows for the aforementioned 125 | interface-based programming style, that embodies the dependency 126 | inversion principle. 127 | 128 | Fortran, on the other hand, makes exclusive use of \emph{inheritance 129 | of implementation}, i.e. inheritance of classes. A class encompasses 130 | both an interface (given by the union of its methods' signatures, 131 | through which it communicates with the external world), \emph{and} the 132 | implementation details of an OO algorithm (in the form of data 133 | variables, and the implementation bodies of its methods). A class is 134 | therefore not a proper abstraction (for other classes or procedures) 135 | to depend upon. It is not sufficiently generic. It contains 136 | implementation-dependent detail, which other classes end up being 137 | needlessly coupled to, if they make use of it via either inheritance 138 | or object composition. Only the class's interface is free from such 139 | detail and should be depended upon for these purposes, to achieve a 140 | maximum amount of decoupling in an application. 141 | 142 | Fortran presently makes no explicit distinction between implementation 143 | inheritance and specification inheritance. The latter can only be 144 | \emph{emulated} in Fortran by \emph{re-purposing} class inheritance, 145 | and exploiting (or rather abusing) the fact that classes intermingle 146 | implementation with specification. Fortran offers abstract types 147 | (i.e. abstract classes) for related use cases, which can contain 148 | deferred (i.e. abstract) procedures, for which ``abstract interfaces'' 149 | have to be specified by the user. However, abstract classes (as all 150 | other classes) are explicitly allowed to \emph{also} contain variables 151 | and concrete methods, i.e. state, as well as implementation code, 152 | which makes abstract classes dangerous to depend upon for inheriting 153 | specification from. 154 | 155 | In contrast to Java, C\#, D, Swift, Rust, Go and some other modern 156 | languages, Fortran does not offer a means to inherit specification 157 | directly from its ``abstract interfaces'', which --- though available, 158 | and guaranteed to be completely free of implementation detail --- play 159 | only a subordinate, instead of a cardinal role in the language. Hence 160 | it is not possible to enforce, at the language level, the strict 161 | separation of the implementation of an OO algorithm from its 162 | interface, that is required for adherence to the dependency inversion 163 | principle. 164 | 165 | \subsection{Problems due to the lack of distinction between inheritance types} 166 | \label{sect:problems} 167 | 168 | Interface inheritance has the sole purpose of enabling polymorphism, 169 | in order to invert dependencies and thereby make parts of an algorithm 170 | interchangeable (i.e. to achieve high-level code reuse 171 | \cite{Gamma_et_al_94}). This is also called \emph{sub-typing}. In 172 | contrast, class inheritance is meant to be used primarily for sharing 173 | common implementation code between parent and child classes. This is 174 | also called \emph{sub-classing}, and can be viewed as a form of 175 | low-level code reuse. 176 | 177 | There is a significant body of evidence that demonstrates that 178 | sub-classing is a dangerous practice, which, moreover, is not 179 | essential to OOP. Snyder \cite{Snyder86} showed already in the 180 | eighties that sub-classing breaks encapsulation. Weck and Szyperski 181 | re-emphasized this \cite{Weck_Szyperski}. They proposed to abolish 182 | sub-classing, since they regard it as being prone to abuse. Even 183 | moderately incautious use of sub-classing typically leads to rigid, 184 | brittle code, that is tightly coupled to the details of concrete 185 | (implementation) classes, and is therefore unmaintainable and 186 | in-extensible. This is especially the case when sub-classing is used in 187 | combination with method overriding, or changes to a base object's 188 | state \cite{Weck_Szyperski}. Moreover, when sub-classing is employed 189 | along with object composition in an application (which is inevitable 190 | in practice), type conflicts can often result. These can then only be 191 | resolved by down-casting, i.e. by the use of \texttt{select type} 192 | statements in Fortran, which lead to unnecessary run-time 193 | overhead. This can be viewed as resulting from a violation of the 194 | dependency inversion principle, which states that inheritance should 195 | be used \emph{exclusively} for sub-typing. 196 | 197 | All these issues could be avoided, if Fortran would be extended to 198 | allow for (multiple) inheritance of interfaces, in order to provide 199 | its programmers with easy and safe access to sub-typing. Sub-classing 200 | (i.e. type extension) could then be mostly shunned in the development 201 | of new codes. Clear guidelines could then be given to Fortran 202 | programmers to rely instead on object composition for low-level, and 203 | on the features proposed in this proposal for high-level code reuse, 204 | in the vast majority of their code. A coding style that is in 205 | accordance with the dependency inversion principle would thus be 206 | encouraged that would eliminate type conflicts, the need for 207 | \texttt{select type} statements, and their run-time overhead. A 208 | potential drawback of object composition is the need for some 209 | boilerplate code, i.e. for methods that delegate functionality to 210 | composed classes. However, efficiency can still be expected to improve 211 | over present Fortran OOP codes, especially in cases where 212 | \texttt{select type} statements appear in low-level code, e.g. in 213 | computational kernels. 214 | 215 | The importance of such a coding style is actually not some entirely 216 | new insight. Among the very first recommendations that one finds in 217 | the pioneering work of Gamma et al. \cite{Gamma_et_al_94} on OO design 218 | patterns are the statements ``program to an interface not an 219 | implementation'' and ``favor object composition over class 220 | inheritance''. What had apparently not been recognized widely is that 221 | these are also recommendations for language design. This has changed 222 | recently, with the availability of the Rust and Go languages. In these 223 | languages, interface inheritance is the \emph{only} type of inheritance 224 | that is supported. These languages therefore \emph{enforce} an OOP 225 | style that is in accordance with the dependency inversion principle. 226 | 227 | 228 | \section{Proposed additions to Fortran 202y} 229 | \label{sect:propositions} 230 | 231 | The new features proposed to solve the problems discussed in the last 232 | section are 233 | 234 | \begin{itemize} 235 | \item 236 | A possibility to declare \emph{named} versions of Fortran's abstract 237 | interfaces, from which derived types would then be able to inherit 238 | specification. 239 | \item 240 | An \texttt{extends} attribute-specifier for the declaration header of 241 | these named abstract interfaces, to allow hierarchies of such (named) 242 | abstract interfaces to be built\footnote{Hierarchies of interfaces 243 | do not pose the dangers that sub-classing poses, as only 244 | \emph{specification} is shared, see \cite{Weck_Szyperski}.}. 245 | \item 246 | A new \texttt{implements} type-attribute-specifier for derived types, to 247 | enable these types to inherit specification from one or more named 248 | abstract interfaces. 249 | \item 250 | An extension of Fortran's \texttt{class} declaration-type-specifier 251 | for polymorphic variables, to accept named abstract interfaces. This 252 | would enable one to declare polymorphic variables of named abstract 253 | interfaces, in order to make use of sub-typing polymorphism. 254 | 255 | \end{itemize} 256 | 257 | \subsection{Extended declaration syntax for abstract interfaces} 258 | \label{sect:declare} 259 | 260 | The following listing shows an example of an extended syntax for 261 | interface declarations. In a module named \texttt{interfaces}, four 262 | named abstract interfaces are declared: \texttt{ISolver}, 263 | \texttt{IPrinter}, \texttt{IClient}, and 264 | \texttt{IRelaxationSolver}. \texttt{ISolver} and \texttt{IPrinter} 265 | contain the declarations of subroutines \texttt{solve\_system}, and 266 | \texttt{print\_result}, respectively. Notice that \texttt{IClient}, on 267 | the other, hand contains declarations for \emph{two} procedures: 268 | subroutines \texttt{solve\_system}, \emph{and} \texttt{print\_result}, 269 | which it inherits from \texttt{ISolver} and \texttt{IPrinter}, 270 | respectively, by means of the \texttt{extends} attribute-specifier. 271 | 272 | \begin{lstlisting}[language=Fortran] 273 | module interfaces 274 | abstract interface :: ISolver 275 | subroutine solve_system(a,b,x) 276 | real, dimension(:,:), intent(in) :: a 277 | real, dimension(:), intent(in) :: b 278 | real, dimension(:), intent(out) :: x 279 | end subroutine solve_system 280 | end interface ISolver 281 | 282 | abstract interface :: IPrinter 283 | subroutine print_result(x) 284 | real, dimension(:), intent(in) :: x 285 | end subroutine print_result 286 | end interface IPrinter 287 | 288 | abstract interface, extends(ISolver,IPrinter) :: IClient 289 | end interface IClient 290 | 291 | abstract interface :: IRelaxationSolver 292 | subroutine driver(self,d,rhs,x) 293 | import :: IRelaxationSolver 294 | class(IRelaxationSolver), intent(in) :: self 295 | real, dimension(:,:,:,:), intent(in) :: d 296 | real, dimension(:,:,:), intent(in) :: rhs 297 | real, dimension(:,:,:), intent(out) :: x 298 | end subroutine driver 299 | subroutine relax(blksolver,d,rhs,x) 300 | import :: ISolver 301 | class(ISolver), intent(in) :: blksolver 302 | real, dimension(:,:,:,:), intent(in) :: d 303 | real, dimension(:,:,:), intent(in) :: rhs 304 | real, dimension(:,:,:), intent(out) :: x 305 | end subroutine relax 306 | end interface IRelaxationSolver 307 | end module interfaces 308 | \end{lstlisting} 309 | \label{list:interface} 310 | Finally, the declaration of the \texttt{IRelaxationSolver} interface 311 | illustrates that a named abstract interface may depend on other named 312 | abstract interfaces, not only via an \texttt{extends} 313 | attribute-specifier (as shown above), but also through the procedure 314 | declarations that it contains. Namely, the latter may contain 315 | polymorphic arguments that are declared with the help of the 316 | \texttt{class} declaration-type-specifier (see 317 | Sect.~\ref{sect:declarations}), to give these procedures access to 318 | either the data fields (variables) of the type that \emph{implements} 319 | the abstract interface (i.e. to the descendant of that interface) or 320 | to the descendants of \emph{other} abstract interfaces. In these 321 | cases, Fortran's \texttt{import} statement would be required in order 322 | to bring such external names into the procedure declarations' own 323 | scope. 324 | 325 | Other ways to express and implement the proposed functionality are 326 | possible, and need to be considered. The one discussed here has the 327 | advantages that abstract interfaces are already a part of the language 328 | and are merely extended here by functionality that does not affect 329 | their other uses. It does, however, elevate their status in the 330 | language as it makes them central to expressing interface 331 | inheritance. This syntax also makes the distinction between sub-typing 332 | and sub-classing apparent, which is a major plus. It has the further 333 | advantage that named abstract interfaces could also be used in future 334 | extensions of the language, e.g. in connection with \emph{intrinsic} 335 | types, and with generics. This is the approach taken in the Swift 336 | programming language where (named) abstract interfaces (called 337 | ``protocols'' there) are so central to its functionality, that Swift 338 | has been called a ``protocol-based'' language. It is not our purpose 339 | here to delve deeper into syntactic or extensibility issues. We rather 340 | need some syntax that is able to express the following code examples, 341 | and the present one does so satisfactorily. 342 | 343 | 344 | \subsection{Facility to implement interfaces} 345 | 346 | Once a named abstract interface has been declared, it can be 347 | implemented by a derived type that needs to conform to that interface, 348 | by using the new \texttt{implements} type-attribute-specifier as 349 | follows 350 | \begin{lstlisting}[language=Fortran] 351 | module lu_decomp 352 | use interfaces, only: ISolver 353 | type, implements(ISolver) :: LUDecomposition 354 | contains 355 | procedure, nopass :: solve_system 356 | end type LUDecomposition 357 | contains 358 | subroutine solve_system(a,b,x) 359 | real, dimension(:,:), intent(in) :: a 360 | real, dimension(:), intent(in) :: b 361 | real, dimension(:), intent(out) :: x 362 | ! Implementation of LU decomposition goes here 363 | end subroutine solve_system 364 | end module lu_decomp 365 | \end{lstlisting} 366 | In case the derived type is abstract, it is allowed to provide an only 367 | partial implementation of the interface(s) that it implements. Any 368 | non-abstract derived type that extends the abstract type must, 369 | however, provide a full implementation. 370 | 371 | \subsection{Polymorphism via sub-typing} 372 | \label{sect:declarations} 373 | 374 | To make actual use of the sub-typing polymorphism that we have just 375 | set up, polymorphic variables of named abstract interfaces would then 376 | be declared with the help of the (extended) \texttt{class} 377 | declaration-type-specifier; for instance, as arguments of a procedure, 378 | or within another derived type. They could then be used in the actual 379 | implementation of this derived type's bound procedures, e.g. as 380 | follows: 381 | \begin{lstlisting}[language=Fortran] 382 | type, implements(IClient) :: Client 383 | class(ISolver), allocatable :: solver 384 | class(IPrinter), pointer :: printer => null() 385 | contains 386 | procedure, nopass :: solve_system 387 | procedure, nopass :: print_result 388 | end type Client 389 | \end{lstlisting} 390 | Here, two polymorphic variables, \texttt{solver} and \texttt{printer}, 391 | that conform to the \texttt{ISolver} and \texttt{IPrinter} interfaces, 392 | respectively, are declared for the purpose of object composition 393 | within type \texttt{Client}, which itself implements the 394 | \texttt{IClient} interface, and thus has to provide implementations 395 | for the procedures \texttt{solve\_system} and \texttt{print\_result} 396 | (whose actual code is omitted here, and would simply employ delegation 397 | to \texttt{solver} and \texttt{printer} to provide the required 398 | functionality). 399 | 400 | Notice also, that the proposed features allow multiple interface 401 | inheritance. In case \texttt{Client} has to conform to either 402 | \texttt{ISolver} or \texttt{IPrinter}, one could, for instance, 403 | provide an implementation much as in the last example, but would use 404 | the \texttt{implements} specifier as follows 405 | \begin{lstlisting}[language=Fortran] 406 | type, implements(ISolver,IPrinter) :: Client 407 | class(ISolver), allocatable :: solver 408 | class(IPrinter), pointer :: printer => null() 409 | contains 410 | procedure, nopass :: solve_system 411 | procedure, nopass :: print_result 412 | end type Client 413 | \end{lstlisting} 414 | 415 | The next example shows, in detail, how sub-typing polymorphism would 416 | be used together with delegation, and also how type-bound procedures 417 | with the \texttt{pass} attribute would be handled 418 | \begin{lstlisting}[language=Fortran] 419 | module jacobi 420 | use interfaces, only: IRelaxationSolver, ISolver 421 | type, implements(IRelaxationSolver) :: BlockJacobi 422 | class(ISolver), allocatable :: blksolver 423 | contains 424 | procedure, pass :: driver 425 | procedure, nopass :: relax 426 | end type BlockJacobi 427 | contains 428 | subroutine driver(self,d,rhs,x) 429 | class(BlockJacobi), intent(in) :: self 430 | real, dimension(:,:,:,:), intent(in) :: d 431 | real, dimension(:,:,:), intent(in) :: rhs 432 | real, dimension(:,:,:), intent(out) :: x 433 | call self%relax(self%blksolver,d,rhs,x) 434 | end subroutine driver 435 | subroutine relax(blksolver,d,rhs,x) 436 | class(ISolver), intent(in) :: blksolver 437 | real, dimension(:,:,:,:), intent(in) :: d 438 | real, dimension(:,:,:), intent(in) :: rhs 439 | real, dimension(:,:,:), intent(out) :: x 440 | integer :: i, j 441 | do j = 1, size(x,3) 442 | do i = 1, size(x,2) 443 | call blksolver%solve_system(d(:,:,i,j),rhs(:,i,j),x(:,i,j)) 444 | end do 445 | end do 446 | end subroutine relax 447 | end module jacobi 448 | \end{lstlisting} 449 | Notice that \texttt{BlockJacobi} is an implementor (i.e. descendant) 450 | of \texttt{IRelaxationSolver} and that therefore the above signature 451 | of method \texttt{driver} is in accordance with its declaration in 452 | module \texttt{interfaces}. To use \texttt{BlockJacobi}, one would 453 | have to simply inject into it, via a constructor, an instance of 454 | \texttt{LUDecomposition} (or any other object that implements the 455 | \texttt{ISolver} interface) in order to intialize the allocatable, 456 | polymorphic variable \texttt{blksolver}. 457 | 458 | \subsection{Combination of sub-classing with sub-typing} 459 | 460 | The new features need to be inter-operable also with type extension 461 | (i.e. sub-classing). They need to enable one to declare derived types 462 | that conform to some interfaces, and inherit at the same time 463 | implementation from some other derived type, e.g. the procedure 464 | \texttt{solve\_system} implemented by type \texttt{LUDecomposition} in 465 | the following example: 466 | \begin{lstlisting}[language=Fortran] 467 | type, extends(LUDecomposition), implements(ISolver,IPrinter) :: Client 468 | class(IPrinter), pointer :: printer => null() 469 | contains 470 | procedure, nopass :: print_result 471 | end type Client 472 | \end{lstlisting} 473 | In the Java language, from which this idea is borrowed, the convention 474 | is that \texttt{extends} shall precede \texttt{implements}. The 475 | combination of sub-typing and sub-classing will allow advanced users 476 | of the language, who have a good understanding of OO design, to employ 477 | some advanced techniques that make use of both the flexibility that 478 | sub-typing affords one, with the raw reduction of code lines that 479 | sub-classing permits. 480 | 481 | 482 | \section{Example of a use case: a Taylor series application} 483 | 484 | To illustrate the difficulties that were discussed in 485 | Sect.~\ref{sect:problems}, and to demonstrate how the use of the new 486 | features will contribute to the solution of these problems, we will 487 | present here a small case study that exhibits a pattern that is 488 | encountered often in practical applications, namely the coupling of 489 | two separate inheritance hierarchies through object composition. It is 490 | truly only the pattern that is of importance here, but in order to 491 | provide a concrete example, we will present it in the framework of a 492 | somewhat fictional application that calculates the Taylor series 493 | expansion of some function up to certain orders of accuracy. 494 | 495 | \subsection{The basic design} 496 | \label{sect:basic_design} 497 | 498 | Suppose that we have an application that needs to calculate the Taylor 499 | series of a function $F(x)$ for some concrete $x$ --- and it needs to 500 | do so only up to the linear term, i.e. it only needs access to the 501 | first derive, $F'(x)$, of $F$ with respect to $x$. Ignoring all the 502 | details of how this first derivative might be calculated, a code 503 | skeleton for using it within our application might look as 504 | follows. Declare a derived type \texttt{DerivF} that contains a 505 | procedure \texttt{deriv1} that calculates $F'(x)$, and a separate 506 | type, named \texttt{Taylor}, that makes use of type \texttt{DerivF} 507 | via object composition in order to \texttt{evaluate} the actual Taylor 508 | series approximation itself (whose details, like calculation of the 509 | zeroth order term, etc., are immaterial here): 510 | \begin{lstlisting}[language=Fortran] 511 | module derivs 512 | type :: DerivF 513 | contains 514 | procedure, nopass :: deriv1 => deriv1f 515 | end type DerivF 516 | contains 517 | subroutine deriv1f() 518 | write(*,*) ' 1st derivative of function F!' 519 | end subroutine deriv1f 520 | end module derivs 521 | 522 | module series 523 | use derivs, only: DerivF 524 | type :: Taylor 525 | type(DerivF), allocatable :: calc 526 | contains 527 | procedure :: term1 528 | procedure :: evaluate 529 | end type Taylor 530 | contains 531 | subroutine term1(self) 532 | class(Taylor), intent(in) :: self 533 | call self%calc%deriv1() 534 | end subroutine term1 535 | 536 | subroutine evaluate(self) 537 | class(Taylor), intent(in) :: self 538 | write(*,*) 'Evaluating Taylor series using' 539 | call self%term1() 540 | end subroutine evaluate 541 | end module series 542 | \end{lstlisting} 543 | Our client application would then set up an object \texttt{teval} of 544 | type \texttt{Taylor} in order to evaluate the Taylor series 545 | approximation it needs, e.g. as follows 546 | 547 | \newpage 548 | 549 | \begin{lstlisting}[language=Fortran] 550 | program client 551 | use derivs, only: DerivF 552 | use series, only: Taylor 553 | type(Taylor), allocatable :: teval 554 | teval = Taylor( DerivF() ) 555 | call teval%evaluate() 556 | end program client 557 | \end{lstlisting} 558 | 559 | Suppose now, that our requirements on the application have changed. 560 | We need some additional functionality. We want the application to be 561 | able to also calculate, if required, a higher-order accurate 562 | approximation to the Taylor series of $F$, say up to the quadratic 563 | term. In addition, we also want it to be able to use some other 564 | function, say $G$, instead of $F$. Moreover, we do not want to change 565 | any of the present code structure, if possible. We only wish to extend 566 | it by the new functionality. 567 | 568 | 569 | \subsection{Extension by sub-classing} 570 | \label{sect:sub-classing-example} 571 | 572 | We will concentrate on implementing the functionality related to the 573 | higher-order-accuracy capability first. To accomplish this, we will 574 | use Fortran's \texttt{extends} feature for derived types 575 | (i.e. sub-classing). We extend type \texttt{DerivF} by a child type, 576 | \texttt{HDerivF}, that contains a procedure \texttt{deriv2} to 577 | calculate also the higher (i.e. second) order derivative of $F$. 578 | \begin{lstlisting}[language=Fortran] 579 | type :: DerivF 580 | contains 581 | procedure, nopass :: deriv1 => deriv1f 582 | end type DerivF 583 | 584 | type, extends(DerivF) :: HDerivF 585 | contains 586 | procedure, nopass :: deriv2 => deriv2f 587 | end type HDerivF 588 | \end{lstlisting} 589 | We also extend the type \texttt{Taylor} by a child type, 590 | \texttt{HTaylor}, to deal with the evaluation of the higher order 591 | Taylor series: 592 | \begin{lstlisting}[language=Fortran] 593 | type :: Taylor 594 | type(DerivF), allocatable :: calc 595 | contains 596 | procedure :: term1 597 | procedure :: evaluate 598 | end type Taylor 599 | 600 | type, extends(Taylor) :: HTaylor 601 | contains 602 | procedure :: term2 => hterm2 603 | procedure :: evaluate => hevaluate 604 | end type HTaylor 605 | \end{lstlisting} 606 | Here, we inherited method \texttt{term1} from the parent type, and 607 | added method \texttt{term2} to enable us to calculate also the 608 | second-order Taylor term. We also had to override the 609 | \texttt{evaluate} method of the parent type, since the higher-order 610 | evaluation of the complete series needs to account for this 611 | additional, i.e. second-order, term. We have thus implemented the 612 | skeleton of the high-order functionality, using types that make up two 613 | separate inheritance hierarchies. However, we are still bound to 614 | exclusive use of the function $F$. To implement the capability to use 615 | different functions, we are going to employ the strategy pattern 616 | \cite{Gamma_et_al_94}. We introduce a further inheritance hierarchy 617 | made up of two new types, \texttt{DerivG}, and \texttt{HDerivG}, to 618 | provide the same functionality for function $G$, as it was done above 619 | for function $F$. We also need to make these hierarchies, connected to 620 | $F$ and $G$, interchangeable, so that we can use either of them within 621 | the code that evaluates the Taylor series. For this purpose, we 622 | introduce the following abstract type 623 | \begin{lstlisting}[language=Fortran] 624 | type, abstract :: Deriv 625 | contains 626 | procedure(pderiv), deferred, nopass :: deriv1 627 | end type Deriv 628 | 629 | abstract interface 630 | subroutine pderiv() 631 | end subroutine pderiv 632 | end interface 633 | \end{lstlisting} 634 | Now we can make both the inheritance hierarchies connected to $F$ and $G$ 635 | derive from this abstract type, and hence merge them into a single 636 | hierarchy. Its $G$-related branch, for instance, looks as follows 637 | \begin{lstlisting}[language=Fortran] 638 | type, extends(Deriv) :: DerivG 639 | contains 640 | procedure, nopass :: deriv1 => deriv1g 641 | end type DerivG 642 | 643 | type, extends(DerivG) :: HDerivG 644 | contains 645 | procedure, nopass :: deriv2 => deriv2g 646 | end type HDerivG 647 | \end{lstlisting} 648 | whereas the $F$-related branch looks completely analogous. The last 649 | step, in implementing the strategy pattern, is to transform the variable 650 | \texttt{calc} in type \texttt{Taylor} into a polymorphic variable of 651 | \texttt{class(Deriv)} 652 | \begin{lstlisting}[language=Fortran] 653 | type :: Taylor 654 | class(Deriv), allocatable :: calc 655 | contains 656 | \end{lstlisting} 657 | in order to accept \texttt{Deriv} types from either branch of our 658 | merged hierarchy. The complete code, that we end up with, is given 659 | in Appendix~\ref{sect:example1}. This code\footnote{compiled 660 | with gfortran Version 9} produces the following output 661 | \begin{lstlisting}[language=Fortran] 662 | Evaluating Taylor series using 663 | 1st derivative of function G! 664 | 665 | Evaluating Taylor series using 666 | 1st derivative of function G! 667 | 2nd derivative of function G! 668 | \end{lstlisting} 669 | That is, the code calculates first the low-order approximation of $G$, 670 | and subsequently its high-order approximation, as intended. It works 671 | correctly. Yet, it is extremely bad, rigid code! The most conspicuous 672 | symptom of its rigidity is the appearance of the \texttt{select type} 673 | statement in the subroutine \texttt{hterm2}, that we reproduce here for 674 | illustration: 675 | \begin{lstlisting}[language=Fortran] 676 | subroutine hterm2(self) 677 | class(HTaylor), intent(in) :: self 678 | select type ( calc => self%calc ) 679 | class is ( HDerivF ) 680 | call calc%deriv2() 681 | class is ( HDerivG ) 682 | call calc%deriv2() 683 | class default 684 | write(*,*) 'Unknown type!' 685 | end select 686 | end subroutine hterm2 687 | \end{lstlisting} 688 | Obviously, this routine expects the \texttt{calc} instance to be of 689 | either type \texttt{HDerivF} or \texttt{HDerivG}, whereas this has 690 | been inherited from the parent type \texttt{Taylor} being of type 691 | \texttt{class(Deriv)}, 692 | \begin{lstlisting}[language=Fortran] 693 | type :: Taylor 694 | class(Deriv), allocatable :: calc 695 | contains 696 | \end{lstlisting} 697 | Our abstract type \texttt{Deriv} does not contain a declaration for 698 | the method \texttt{deriv2} that the subroutine needs. Since the 699 | compiler can't know at compile time whether at run-time \texttt{calc} 700 | will be of the right type-extension to contain the \texttt{deriv2} 701 | method, it will refuse to compile subroutine \texttt{hterm2} if the 702 | \texttt{select type} statement is omitted. By coupling two inheritance 703 | hierarchies (based on \texttt{Deriv} and \texttt{Taylor}) together, via 704 | object composition, we have created a (potential) type conflict that 705 | now requires a type check every time we run through the subroutine. If 706 | we had written a real production application along these lines, that 707 | would evaluate the Taylor series for a single scalar $x$ per call, the 708 | type-checking would have completely killed performance! 709 | 710 | Type extension (i.e. sub-classing) has locked us into a 711 | straight-jacket of rigid inheritance hierarchies that denies us the 712 | flexibility to provide our procedures with the right data types, and 713 | has thereby forced us to circumvent the static type system of the 714 | language, in order to get the code to work at all. But this is not the 715 | only problem. The entire code relies on concrete derived types, 716 | i.e. on concrete implementations. Except for type \texttt{Deriv}, it 717 | does not use any abstractions. Should the implementation of some 718 | concrete type need to be changed, all types that depend on it would 719 | need to be checked and possibly changed, too, in order not to break 720 | the program. 721 | 722 | \subsection{Extension by sub-typing and object composition} 723 | 724 | Now, let us go back to the point we were at at the end of 725 | Sect.~\ref{sect:basic_design}. Let us, furthermore, suppose that Fortran 726 | doesn't support sub-classing, and instead supports only object 727 | composition and sub-typing (via the features proposed in 728 | Sect.~\ref{sect:propositions}), akin to Rust or Go. We will again 729 | focus first on the extension of our application to higher order of 730 | accuracy. We still contemplate to introduce this functionality through 731 | four derived types \texttt{DerivF}, \texttt{HDerivF}, \texttt{Taylor}, 732 | and \texttt{HTaylor}. But since we no longer have sub-classing at 733 | our disposal, the only way to make these types cooperate, on solving 734 | the task, is the following 735 | \begin{lstlisting}[language=Fortran] 736 | type :: Taylor 737 | type(DerivF), allocatable :: calc 738 | contains 739 | procedure :: term1 740 | procedure :: evaluate 741 | end type Taylor 742 | 743 | type :: HTaylor 744 | type(HDerivF), allocatable :: calc 745 | contains 746 | procedure :: term1 => hterm1 747 | procedure :: term2 => hterm2 748 | procedure :: evaluate => hevaluate 749 | end type HTaylor 750 | \end{lstlisting} 751 | that is, we need to employ object composition \emph{twice}, once in 752 | \texttt{Taylor} and once in \texttt{HTaylor}. This is already 753 | sufficient to avoid the appearance of a \texttt{select type} statement 754 | in subroutine \texttt{hterm2}, because now our instance of 755 | \texttt{calc} will be of type \texttt{HDerivF}, i.e. of the proper 756 | type for doing high-order calculations. We will, of course, see to 757 | it that it contains the procedure \texttt{deriv2} which calculates the 758 | second order derivative required by the high-order Taylor series term. 759 | 760 | The above code fragment still has a problem, though. It relies on the 761 | concrete types \texttt{DerivF} and \texttt{HDerivF}. Our copy of 762 | ``Fortran 202y Explained'' tells us that we should rely on 763 | abstractions instead of concretions, and depend on polymorphic 764 | variables of abstract interfaces within our derived types, instead. We 765 | therefore think about the functionality that these two concrete types 766 | need to provide to their clients. We already mentioned that 767 | \texttt{HDerivF} will have to provide an implementation of subroutine 768 | \texttt{deriv2}. However, and as it was the case in 769 | Sect.~\ref{sect:sub-classing-example}, \texttt{HDerivF} also needs to 770 | implement procedure \texttt{deriv1}, while \texttt{DerivF} needs to 771 | implement \emph{only} the latter procedure. Hence, we distill the 772 | signatures of these methods into two abstract interfaces 773 | \begin{lstlisting}[language=Fortran] 774 | abstract interface :: IDeriv 775 | subroutine deriv1() 776 | end subroutine deriv1 777 | end interface IDeriv 778 | 779 | abstract interface, extends(IDeriv) :: IHDeriv 780 | subroutine deriv2() 781 | end subroutine deriv2 782 | end interface IHDeriv 783 | \end{lstlisting} 784 | and then make use of these interfaces to modify our code for the 785 | Taylor types as follows 786 | \begin{lstlisting}[language=Fortran] 787 | type :: Taylor 788 | class(IDeriv), allocatable :: calc 789 | contains 790 | procedure :: term1 791 | procedure :: evaluate 792 | end type Taylor 793 | 794 | type :: HTaylor 795 | class(IHDeriv), allocatable :: calc 796 | contains 797 | procedure :: term1 => hterm1 798 | procedure :: term2 => hterm2 799 | procedure :: evaluate => hevaluate 800 | end type HTaylor 801 | \end{lstlisting} 802 | With this single stroke, we have just solved our entire problem, 803 | including the requirement of being able to calculate the Taylor series 804 | of different functions! Moreover, we have accomplished this goal with 805 | ease. The only things that remain to be done are to make 806 | \texttt{DerivF} and \texttt{HDerivF} simply implement the two abstract 807 | interfaces, e.g. as follows 808 | \begin{lstlisting}[language=Fortran, 809 | caption={Implementation of interfaces for derivatives of function $F$.}, 810 | label={listing:implderivf}] 811 | type, implements(IDeriv) :: DerivF 812 | contains 813 | procedure, nopass :: deriv1 => deriv1f 814 | end type DerivF 815 | 816 | type, implements(IHDeriv) :: HDerivF 817 | contains 818 | procedure, nopass :: deriv1 => deriv1f 819 | procedure, nopass :: deriv2 => deriv2f 820 | end type HDerivF 821 | \end{lstlisting} 822 | and to add two more types, \texttt{DerivG}, and \texttt{HDerivG}, in 823 | exactly the same fashion, in order to provide implementations of the 824 | same functionality for function $G$. 825 | 826 | We are finished! Since both \texttt{DerivF} and \texttt{DerivG} now 827 | conform to the abstract \texttt{IDeriv} interface, we can easily swap 828 | instances of either of them into type \texttt{Taylor} at will, in 829 | order to calculate the Taylor series of either $F$ or $G$. Similar 830 | functionality is exhibited by the types that implement the high-order 831 | series. We didn't even have to spend a single thought on using the 832 | strategy pattern for these purposes. We used it naturally without 833 | thinking about it because it was encouraged by the features contained 834 | in the language. 835 | 836 | The complete code, that uses this sub-typing approach, is given in 837 | Appendix~\ref{sect:example2}. It is an almost literal translation of a 838 | Java as well as a Rust code, that were used to test and verify the 839 | presented ideas. The code in Appendix~\ref{sect:example2} is vastly 840 | superior compared to the one that uses sub-classing and is given in 841 | Appendix~\ref{sect:example1}. Both codes have roughly the same number 842 | of lines, but in the one relying on sub-typing there are \emph{no} 843 | \texttt{select type} statements, \emph{no} sub-classing hierarchies, 844 | and \emph{no} dependencies on concrete types. We have inverted all the 845 | dependencies in the algorithm, i.e. the only dependencies are on 846 | abstract interfaces. We have thus attained the highest possible degree 847 | of decoupling. The \emph{only} part of the code that depends on 848 | concrete types is the main client program, whose sole purpose is to 849 | organize the entire process of injecting instances of the correct 850 | concrete types via (Fortran default) constructors into the lower level 851 | code. 852 | 853 | This decoupling of types leads to multiple benefits. For instance (and 854 | provided we have compiled the \texttt{interfaces} module), we gain the 855 | freedom to compile the \texttt{derivs} and \texttt{series} modules in 856 | any order we like, even in parallel if we wish to. We thereby 857 | neutralized the biggest drawback of a module system (like Fortran's) 858 | for separate compilation, namely serialization of the compilation 859 | process. We also avoid re-compilation cascades in case we need to 860 | change the implementation of some concrete type, as long as we leave 861 | its (abstract) interface intact. We are, moreover, free to take any of 862 | the aforementioned modules and reuse it in another application, 863 | without having to drag along numerous dependencies. In summary, using 864 | the new features we have not only accomplished to make the code more 865 | efficient, but also much more flexible, much more maintainable, and 866 | much more reusable. 867 | 868 | \subsection{Combination of the two inheritance types} 869 | 870 | The code given in Appendix~\ref{sect:example2} strictly conforms to 871 | the dependency inversion principle. By relaxing this requirement, and 872 | allowing for the use of both sub-typing and sub-classing, we could 873 | have written the code fragment in Listing~\ref{listing:implderivf} 874 | above somewhat more concise as 875 | \begin{lstlisting}[language=Fortran] 876 | type, implements(IDeriv) :: DerivF 877 | contains 878 | procedure, nopass :: deriv1 => deriv1f 879 | end type DerivF 880 | 881 | type, extends(DerivF), implements(IHDeriv) :: HDerivF 882 | contains 883 | procedure, nopass :: deriv2 => deriv2f 884 | end type HDerivF 885 | \end{lstlisting} 886 | This saves us the labor of providing an implementation for procedure 887 | \texttt{deriv1} within type \texttt{HDerivF}, at the significant cost 888 | of \texttt{HDerivF} depending now on a concretion, i.e. on type 889 | \texttt{DerivF}. In this particular, trivial, example, we have only 890 | saved a single line of code in this way. But had \texttt{DerivF} 891 | contained half a dozen or so methods, which we would have been forced 892 | to implement also in \texttt{HDerivF}, the convenience that the 893 | combination of \texttt{extends} and \texttt{implements} would have 894 | afforded us for reducing the number of code-lines could hardly have 895 | been passed up. 896 | 897 | \subsection{Summary} 898 | 899 | The features proposed in this document would significantly enhance 900 | Fortran's OOP capabilities without affecting backwards compatibility. 901 | Moreover, Fortran compilers that adhere to at least the Fortran 2003 902 | standard could implement them relatively easily, as most of the 903 | functionality required to support them is already present in some form 904 | in such compilers for the support of sub-classing, i.e. the extension 905 | of derived types. 906 | 907 | \bibliographystyle{plain} 908 | \bibliography{Fortran_OOP} 909 | 910 | 911 | \lstdefinestyle{mystyle}{ 912 | % backgroundcolor=\color{backcolour}, 913 | commentstyle=\color{codegreen}, 914 | % keywordstyle=\color{magenta}, 915 | numberstyle=\tiny\color{codegray}, 916 | stringstyle=\color{codepurple}, 917 | basicstyle=\ttfamily\footnotesize, 918 | breakatwhitespace=false, 919 | breaklines=true, 920 | captionpos=b, 921 | keepspaces=true, 922 | numbers=left, 923 | numbersep=5pt, 924 | showspaces=false, 925 | showstringspaces=false, 926 | showtabs=false, 927 | tabsize=2 928 | } 929 | 930 | \lstset{style=mystyle} 931 | 932 | 933 | \begin{appendices} 934 | 935 | \section{Taylor series example based on sub-classing} 936 | \label{sect:example1} 937 | 938 | \begin{lstlisting}[language=Fortran] 939 | module derivs 940 | 941 | type, abstract :: Deriv 942 | contains 943 | procedure(pderiv), deferred, nopass :: deriv1 944 | end type Deriv 945 | 946 | abstract interface 947 | subroutine pderiv() 948 | end subroutine pderiv 949 | end interface 950 | 951 | type, extends(Deriv) :: DerivF 952 | contains 953 | procedure, nopass :: deriv1 => deriv1f 954 | end type DerivF 955 | 956 | type, extends(DerivF) :: HDerivF 957 | contains 958 | procedure, nopass :: deriv2 => deriv2f 959 | end type HDerivF 960 | 961 | type, extends(Deriv) :: DerivG 962 | contains 963 | procedure, nopass :: deriv1 => deriv1g 964 | end type DerivG 965 | 966 | type, extends(DerivG) :: HDerivG 967 | contains 968 | procedure, nopass :: deriv2 => deriv2g 969 | end type HDerivG 970 | 971 | contains 972 | 973 | subroutine deriv1f() 974 | write(*,*) ' 1st derivative of function F!' 975 | end subroutine deriv1f 976 | 977 | subroutine deriv2f() 978 | write(*,*) ' 2nd derivative of function F!' 979 | end subroutine deriv2f 980 | 981 | subroutine deriv1g() 982 | write(*,*) ' 1st derivative of function G!' 983 | end subroutine deriv1g 984 | 985 | subroutine deriv2g() 986 | write(*,*) ' 2nd derivative of function G!' 987 | end subroutine deriv2g 988 | 989 | end module derivs 990 | 991 | module series 992 | 993 | use derivs, only: Deriv, HDerivF, HDerivG 994 | 995 | type :: Taylor 996 | class(Deriv), allocatable :: calc 997 | contains 998 | procedure :: term1 999 | procedure :: evaluate 1000 | end type Taylor 1001 | 1002 | type, extends(Taylor) :: HTaylor 1003 | contains 1004 | procedure :: term2 => hterm2 1005 | procedure :: evaluate => hevaluate 1006 | end type HTaylor 1007 | 1008 | contains 1009 | 1010 | subroutine term1(self) 1011 | class(Taylor), intent(in) :: self 1012 | call self%calc%deriv1() 1013 | end subroutine term1 1014 | 1015 | subroutine evaluate(self) 1016 | class(Taylor), intent(in) :: self 1017 | write(*,*) 'Evaluating Taylor series using' 1018 | call self%term1() 1019 | end subroutine evaluate 1020 | 1021 | subroutine hterm2(self) 1022 | class(HTaylor), intent(in) :: self 1023 | select type ( calc => self%calc ) 1024 | class is ( HDerivF ) 1025 | call calc%deriv2() 1026 | class is ( HDerivG ) 1027 | call calc%deriv2() 1028 | class default 1029 | write(*,*) 'Unknown type!' 1030 | end select 1031 | end subroutine hterm2 1032 | 1033 | subroutine hevaluate(self) 1034 | class(HTaylor), intent(in) :: self 1035 | write(*,*) 'Evaluating Taylor series using' 1036 | call self%term1() 1037 | call self%term2() 1038 | end subroutine hevaluate 1039 | 1040 | end module series 1041 | 1042 | program client 1043 | 1044 | use derivs, only: DerivG, HDerivG, Deriv 1045 | use series, only: Taylor, HTaylor 1046 | 1047 | class(Deriv), allocatable :: derv 1048 | class(Taylor), allocatable :: teval 1049 | 1050 | derv = DerivG() 1051 | teval = Taylor(derv) 1052 | call teval%evaluate() 1053 | 1054 | write(*,*) 1055 | 1056 | derv = HDerivG() 1057 | teval = HTaylor(derv) 1058 | call teval%evaluate() 1059 | 1060 | end program client 1061 | \end{lstlisting} 1062 | 1063 | 1064 | \section{Taylor series example based on sub-typing} 1065 | \label{sect:example2} 1066 | 1067 | \begin{lstlisting}[language=Fortran] 1068 | module interfaces 1069 | 1070 | abstract interface :: IDeriv 1071 | subroutine deriv1() 1072 | end subroutine deriv1 1073 | end interface IDeriv 1074 | 1075 | abstract interface, extends(IDeriv) :: IHDeriv 1076 | subroutine deriv2() 1077 | end subroutine deriv2 1078 | end interface IHDeriv 1079 | 1080 | end module interfaces 1081 | 1082 | module derivs 1083 | 1084 | use interfaces, only: IDeriv, IHDeriv 1085 | 1086 | type, implements(IDeriv) :: DerivF 1087 | contains 1088 | procedure, nopass :: deriv1 => deriv1f 1089 | end type DerivF 1090 | 1091 | type, implements(IHDeriv) :: HDerivF 1092 | contains 1093 | procedure, nopass :: deriv1 => deriv1f 1094 | procedure, nopass :: deriv2 => deriv2f 1095 | end type HDerivF 1096 | 1097 | type, implements(IDeriv) :: DerivG 1098 | contains 1099 | procedure, nopass :: deriv1 => deriv1g 1100 | end type DerivG 1101 | 1102 | type, implements(IHDeriv) :: HDerivG 1103 | contains 1104 | procedure, nopass :: deriv1 => deriv1g 1105 | procedure, nopass :: deriv2 => deriv2g 1106 | end type HDerivG 1107 | 1108 | contains 1109 | 1110 | subroutine deriv1f() 1111 | write(*,*) " 1st derivative of function F!" 1112 | end subroutine deriv1f 1113 | 1114 | subroutine deriv2f() 1115 | write(*,*) " 2nd derivative of function F!" 1116 | end subroutine deriv2f 1117 | 1118 | subroutine deriv1g() 1119 | write(*,*) " 1st derivative of function G!" 1120 | end subroutine deriv1g 1121 | 1122 | subroutine deriv2g() 1123 | write(*,*) " 2nd derivative of function G!" 1124 | end subroutine deriv2g 1125 | 1126 | end module derivs 1127 | 1128 | module series 1129 | 1130 | use interfaces, only: IDeriv, IHDeriv 1131 | 1132 | type :: Taylor 1133 | class(IDeriv), allocatable :: calc 1134 | contains 1135 | procedure :: term1 1136 | procedure :: evaluate 1137 | end type Taylor 1138 | 1139 | type :: HTaylor 1140 | class(IHDeriv), allocatable :: calc 1141 | contains 1142 | procedure :: term1 => hterm1 1143 | procedure :: term2 => hterm2 1144 | procedure :: evaluate => hevaluate 1145 | end type HTaylor 1146 | 1147 | contains 1148 | 1149 | subroutine term1(self) 1150 | class(Taylor), intent(in) :: self 1151 | call self%calc%deriv1() 1152 | end subroutine term1 1153 | 1154 | subroutine evaluate(self) 1155 | class(Taylor), intent(in) :: self 1156 | write(*,*) 'Evaluating Taylor series using' 1157 | call self%term1() 1158 | end subroutine evaluate 1159 | 1160 | subroutine hterm1(self) 1161 | class(HTaylor), intent(in) :: self 1162 | call self%calc%deriv1() 1163 | end subroutine hterm1 1164 | 1165 | subroutine hterm2(self) 1166 | class(HTaylor), intent(in) :: self 1167 | call self%calc%deriv2() 1168 | end subroutine hterm2 1169 | 1170 | subroutine hevaluate(self) 1171 | class(HTaylor), intent(in) :: self 1172 | write(*,*) 'Evaluating Taylor series using' 1173 | call self%term1() 1174 | call self%term2() 1175 | end subroutine hevaluate 1176 | 1177 | end module series 1178 | 1179 | program client 1180 | 1181 | use derivs, only: DerivG, HDerivG 1182 | use series, only: Taylor, HTaylor 1183 | 1184 | type(Taylor), allocatable :: teval 1185 | type(HTaylor), allocatable :: hteval 1186 | 1187 | teval = Taylor( DerivG() ) 1188 | call teval%evaluate() 1189 | 1190 | write(*,*) 1191 | 1192 | hteval = HTaylor( HDerivG() ) 1193 | call hteval%evaluate() 1194 | 1195 | end program client 1196 | \end{lstlisting} 1197 | 1198 | \end{appendices} 1199 | 1200 | \end{document} 1201 | -------------------------------------------------------------------------------- /proposals/traits/20-109.txt: -------------------------------------------------------------------------------- 1 | To: J3 J3/20-109 2 | From: Brad Richardson 3 | Subject: Traits for Types 4 | Date: 2020-February-23 5 | 6 | Proposal for Fortran Standard: 202y (NOT 202x) 7 | 8 | 1. Problem 9 | 10 | Currently, in order to pass a derived type to some library expected a type 11 | derived from it's own abstract type requires the addition of a "wrapper" 12 | type to be used. This requires a cumbersome amount of boiler plate code and 13 | is not conducive to the type of "generic" code one would like to be able to 14 | write. 15 | 16 | This proposal seeks to address the issue that writing "generic" libraries 17 | is either not possible, or places undue burden on its users. The main 18 | benefits are the reduction in repeated logic or boiler plate wrapper types 19 | required for generic libraries, thereby enabling more code reuse and easier 20 | to maintain code bases. 21 | 22 | 2. Proposed Solution 23 | 24 | The solution would require the addition of a few attributes and variable 25 | declaration capabilities. First is the addition of the `trait` attribute to 26 | a type specification. This requires that the type be abstract, and contain 27 | no components. I.e. 28 | 29 | type, abstract, trait :: Show_t 30 | contains 31 | procedure(show_i), deferred :: show 32 | end type Show_t 33 | 34 | abstract interface 35 | function show_i(self) result(string) 36 | class(Show_t), intent(in) :: self 37 | character(len=:), allocatable :: string 38 | end function show_i 39 | end interface 40 | 41 | Second is the addition of the `implements` attribute for a type 42 | specification. The `implements` attribute requires a list of at least one 43 | trait the type is to implement, requiring the type to contain the 44 | procedures with the interfaces defined by the trait(s). I.e. 45 | 46 | type, implements(Show_t) :: Thing_t 47 | character(len=:), allocatable :: the_string 48 | contains 49 | procedure :: show 50 | end type Thing_t 51 | 52 | function show(self) result(string) 53 | class(Thing_t), intent(in) :: self 54 | character(len=:), allocatable :: string 55 | 56 | string = self%the_string 57 | end function show 58 | 59 | Third is the addition of a variable declaration form using `trait`, in a 60 | similar fashion to `class`. The `trait` specification would require a list 61 | of at least one trait, and must either be allocatable, or a dummy argument 62 | of a procedure. In a similar manner that the instantiated value of a class 63 | variable must be of that type, or an extended type, the instantiated value 64 | of a trait variable must be of a type which implements the given trait(s). 65 | Thus, no components of the instantiated value are accessible, only the 66 | procedures defined by the trait(s). 67 | 68 | 3. Backward compatibility 69 | 70 | This addition to the language would not break any existing standard 71 | conforming Fortran program, and thus preserves Fortran's backward 72 | compatibility. 73 | 74 | 4. Further discussion 75 | 76 | Online discussion that led to this proposal can be found at 77 | https://github.com/j3-fortran/fortran_proposals/issues/125 78 | 79 | 80 | --------------------------------------------------------------------------------