• Using type variables instead of enums to write cleaner code in Fortran

    From Arash Agan@21:1/5 to All on Wed Dec 8 06:23:25 2021
    I have been trying to write clearer fortran code by using enums but it seems using a type variable instead would be better in terms of code readability.
    you can find the code here: https://github.com/AAGAN/fortran_type_clean_code/blob/main/rang.f95

    Program Rang

    use iso_fortran_env, only : int8, int16, int32, int64

    implicit none

    ! mix and match integer types for demonstration
    integer(int16) :: red
    integer(int32) :: blue
    integer(int64) :: col

    ! create a type with some integer values
    !(probably it's better to put this in a module)
    type colortype
    integer(int8) :: red = 1, blue = 2, veryDarkGreen = 3
    end type colortype
    ! instantiate a variable of type colortype to use in the rest of the program
    type(colortype) :: color

    ! output: 1 2 3
    print *, color%red, color%blue, color%veryDarkGreen

    red = color%red
    if (red .eq. color%red) then
    print *, "it is red"
    end if

    blue = 2
    if (blue .eq. color%blue) then
    print *, "it is blue"
    end if

    col = color%veryDarkGreen
    if (col .eq. color%blue) then
    print *, "it is not blue"
    else if (col .eq. color%veryDarkGreen) then
    print *, "it is very dark green"
    else
    print *, "it is red"
    end if

    End Program Rang

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Gary Scott@21:1/5 to Arash Agan on Wed Dec 8 11:47:37 2021
    On 12/8/2021 8:23 AM, Arash Agan wrote:
    I have been trying to write clearer fortran code by using enums but it seems using a type variable instead would be better in terms of code readability.
    you can find the code here: https://github.com/AAGAN/fortran_type_clean_code/blob/main/rang.f95

    Program Rang

    use iso_fortran_env, only : int8, int16, int32, int64

    implicit none

    ! mix and match integer types for demonstration
    integer(int16) :: red
    integer(int32) :: blue
    integer(int64) :: col

    ! create a type with some integer values
    !(probably it's better to put this in a module)
    type colortype
    integer(int8) :: red = 1, blue = 2, veryDarkGreen = 3
    end type colortype
    ! instantiate a variable of type colortype to use in the rest of the program
    type(colortype) :: color

    ! output: 1 2 3
    print *, color%red, color%blue, color%veryDarkGreen

    red = color%red
    if (red .eq. color%red) then
    print *, "it is red"
    end if

    blue = 2
    if (blue .eq. color%blue) then
    print *, "it is blue"
    end if

    col = color%veryDarkGreen
    if (col .eq. color%blue) then
    print *, "it is not blue"
    else if (col .eq. color%veryDarkGreen) then
    print *, "it is very dark green"
    else
    print *, "it is red"
    end if

    End Program Rang

    True, enums seldom if ever improve code readability or logic.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From FortranFan@21:1/5 to Gary Scott on Wed Dec 8 13:41:56 2021
    On Wednesday, December 8, 2021 at 12:47:39 PM UTC-5, Gary Scott wrote:

    ..
    True, enums seldom if ever improve code readability or logic.

    Well, Fortran does *not* have any meaningful facility with ENUMs. So *no* such assertion can be made in the context of Fortran. Fortran includes some rudimentary support toward ENUMs that was introduced starting Fortran 2003 along with interoperability
    with C but it provides no type safety in actual usage and it doesn't really add much that named constants (PARAMETER attribute) does not provide already.

    With other languages though that do include well-feature ENUMs feature, ENUMs do indeed help greatly while coding and readability and maintenance and also with usage such as with libraries.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From FortranFan@21:1/5 to arash...@gmail.com on Wed Dec 8 13:34:07 2021
    On Wednesday, December 8, 2021 at 9:23:27 AM UTC-5, arash...@gmail.com wrote:

    I have been trying to write clearer fortran code by using enums but it seems using a type variable instead would be better in terms of code readability.
    you can find the code here: https://github.com/AAGAN/fortran_type_clean_code/blob/main/rang.f95 ..

    Did you mean named constants instead of "enums"?

    Or to ask differently, where is your Fortran "code by using enums"?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Gary Scott@21:1/5 to FortranFan on Thu Dec 9 12:03:04 2021
    On 12/8/2021 3:41 PM, FortranFan wrote:
    On Wednesday, December 8, 2021 at 12:47:39 PM UTC-5, Gary Scott wrote:

    ..
    True, enums seldom if ever improve code readability or logic.

    Well, Fortran does *not* have any meaningful facility with ENUMs. So *no* such assertion can be made in the context of Fortran. Fortran includes some rudimentary support toward ENUMs that was introduced starting Fortran 2003 along with
    interoperability with C but it provides no type safety in actual usage and it doesn't really add much that named constants (PARAMETER attribute) does not provide already.

    With other languages though that do include well-feature ENUMs feature, ENUMs do indeed help greatly while coding and readability and maintenance and also with usage such as with libraries.

    I did not say that Fortran DOES have enum. My statement was generic.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From John@21:1/5 to All on Fri Dec 10 10:10:33 2021
    I am not a user of enums, but someone told me they needed an enum, and that the members needed to be constant integers so they could
    be used in a select; and after he described what he wanted I said he could do the following and he said it was what he needed. Not sure if this helps or not
    program demo_enum

    ! pseudo-enum
    type enum_colors
    integer :: RED
    integer :: BLUE
    integer :: GREEN
    end type enum_colors
    type(enum_colors),parameter :: colors=enum_colors(1,2,3)

    integer :: col

    col=colors%red

    select case(col)
    case(colors%red)
    write(*,*)'red'
    case(colors%green)
    write(*,*)'green'
    case(colors%blue)
    write(*,*)'blue'
    end select

    end program demo_enum

    unfortunately values declared in a user-defined type cannot have the PARAMETER attribute, which might make it a bit more elegant.
    It worked with two compilers so I cannot say I know for certain that is standard-conforming but cannot think of a reason why it would not be as long as the values are constant; but at the time I wrote it I was not sure that a user defined type would work
    in the SELECT even if it was a constant, but it did and he says he has used it subsequently with other compilers and not had a problem.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Arash Agan@21:1/5 to John on Fri Dec 10 11:05:08 2021
    Thanks John for sharing your pseudo-enum code.
    Creating the type as a parameter as you suggested makes it even a better "enum".
    I updated my original code on github using your improvement.

    https://github.com/AAGAN/fortran_type_clean_code/blob/main/rang.f95

    Best

    On Friday, December 10, 2021 at 1:18:41 PM UTC-5, John wrote:
    (test1) urbanjs@venus:~$ vi /tmp/tmp.f90
    (test1) urbanjs@venus:~$ nvfortran /tmp/tmp.f90
    (test1) urbanjs@venus:~$ ./a.out
    red
    (test1) urbanjs@venus:~$ ifort /tmp/tmp.f90
    (test1) urbanjs@venus:~$ ./a.out
    red
    (test1) urbanjs@venus:~$ gfortran /tmp/tmp.f90
    (test1) urbanjs@venus:~$ ./a.out
    red


    Worked with at least these three

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From John@21:1/5 to All on Fri Dec 10 10:18:39 2021
    (test1) urbanjs@venus:~$ vi /tmp/tmp.f90
    (test1) urbanjs@venus:~$ nvfortran /tmp/tmp.f90
    (test1) urbanjs@venus:~$ ./a.out
    red
    (test1) urbanjs@venus:~$ ifort /tmp/tmp.f90
    (test1) urbanjs@venus:~$ ./a.out
    red
    (test1) urbanjs@venus:~$ gfortran /tmp/tmp.f90
    (test1) urbanjs@venus:~$ ./a.out
    red


    Worked with at least these three

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From John@21:1/5 to All on Fri Dec 10 16:54:54 2021
    Well, I have a love-hate relationship with another similiar use, because
    I showed someone how to do this and now I have to use it; but consider that with Fortran you are not limited to constant integers, so you can create
    a "Universal Constant" (uc) that has attributes like bit size, units,
    and even a description which is actually not that bad to use (I will
    admit), but the code for the module is a sight to behold. It started
    with this example that I made, where you want a module of constants ...

    But first, before you see the module, you can see it is actually easy
    to use. If I want a "universal constant" in Double Precision for the
    Golden Ratio I just use "uc%dp%golden_precision". That really is not that
    bad. An example program that also shows how ASSOCIATE is often used with
    the module:

    program test_universal_constants
    use M_constants, only : uc
    write(*,*)uc%qp%gamma ! universal constant, quad-precision, gamma >associate (gamma => uc%dp%gamma)
    write(*,*)gamma
    end associate
    end program test_universal_constants

    But even for this "simple" initial example the "hidden" code gets a
    little crazy. Add units and descriptors (as was done) and the code gets interesting to maintain.

    module M_constants
    use, intrinsic :: iso_fortran_env, only : r4=>real32, r8=>real64, r16=>real128 >implicit none
    private

    real(r16),parameter :: &
    pi = 3.141592653589793238462643383279502884197169399375105820974944592307_r16, &
    gamma = 0.577215664901532860606512090082402431042_r16, &
    e = 2.71828182845904523536028747135266249775724709369995_r16, &
    Golden_Ratio = 1.6180339887498948482045868_r16, &
    euler = 0.577215664901532860606512090082402431042_r16, &
    Deg_Per_Rad = 57.2957795130823208767981548_r16, &
    Rad_Per_Deg = 0.01745329251994329576923691_r16

    type r128; real(r16) :: pi,gamma,e,golden_ratio,euler; endtype r128
    type r64; real(r8) :: pi,gamma,e,golden_ratio,euler; endtype r64
    type r32; real(r4) :: pi,gamma,e,golden_ratio,euler; endtype r32

    type rall
    type(r128) :: qp
    type(r64) :: dp
    type(r32) :: sp
    end type rall

    type(rall),parameter,public :: uc=rall( &
    & r128(pi=pi,gamma=gamma,e=e,golden_ratio=golden_ratio,euler=euler), &
    & r64(pi=real(pi,r8),gamma=real(gamma,r8),e=real(e,r8),golden_ratio=real(golden_ratio,r8),euler=real(euler,r8) ), &
    & r32(pi=real(pi,r4),gamma=real(gamma,r4),e=real(e,r4),golden_ratio=real(golden_ratio,r4),euler=real(euler,r4) ) &
    & )
    end module M_constants

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From John@21:1/5 to All on Fri Dec 10 17:25:17 2021
    I forgot to use the most common usage

    real,parameter :: e=uc%sp%e

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From John@21:1/5 to All on Fri Dec 10 18:16:44 2021
    actually, this syntax might be nicer; I guess when I first did the first example and found I could not add the PARAMETER attribute in the TYPE definition I did not try it with just the values specified:


    program demo_enum
    type enum_colors
    integer :: RED=1
    integer :: BLUE=2
    integer :: GREEN=3
    integer :: MAGENTA=4
    integer :: CYAN=5
    end type enum_colors
    type(enum_colors),parameter :: colors=enum_colors()
    write(*,*)colors


    end program demo_enum

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ron Shepard@21:1/5 to John on Sun Dec 12 11:34:55 2021
    On 12/10/21 12:10 PM, John wrote:
    unfortunately values declared in a user-defined type cannot have the PARAMETER attribute, which might make it a bit more elegant.

    Yes, this capability would have been useful since user defined types
    were introduced in f90. Now that defined types can have stuff below the CONTAINS, I think that would be a good place to allow parameters. That
    is, everything below the CONTAINS would be stuff associated with the
    type, and it is not replicated in memory for each entity or array
    element of that type.

    $.02 -Ron Shepard

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From miesiehl@gmail.com@21:1/5 to All on Sun Dec 12 13:18:09 2021
    unfortunately values declared in a user-defined type cannot have the PARAMETER attribute, which might make it a bit more elegant.

    I did ask for this earlier here: https://github.com/j3-fortran/fortran_proposals/issues/110
    The use of the PARAMETER attribute would make things more safe.

    But even without the PARAMETER attribute, we can effectively use integer based enums with the compilers already. Integer based enums are essentially for implementing distributed objects with Coarray Fortran because we can use the values with atomic
    coarrays. I am currently working on a channel implementation using coarrays, code snippets below are taken from that: John’s last syntax example will not work for this, and the enum type component must be declared above CONTAINS yet, of course. The
    enum type itself can even be extended in Fortran, which makes the use of such enum types very flexible and efficient as we may use them differently at multiple levels.

    !************************
    !**** enumeration types: ***
    !************************
    !*** Enum_FragmentedMethodChannel1Status:
    type, extends(OOOPchnl00_ChannelStatus_EnumDef), private :: OOOPfrob03_00_Channel1Status_EnumDef
    ! (values below 10000000 are reserved for the channel itself here)
    !
    ! values for controlling the execution flow:
    integer(kind=OOOGglob_kint) :: InitiateFragmentedMethod3 != 10000000
    integer(kind=OOOGglob_kint) :: ReadyToStartFragmentedMethod3 != 11000000
    integer(kind=OOOGglob_kint) :: StartFragmentedMethod3 != 12000000
    integer(kind=OOOGglob_kint) :: SendCurrentTestValue != 13000000
    integer(kind=OOOGglob_kint) :: AdjustTestValueToMaxTestValue != 14000000
    integer(kind=OOOGglob_kint) :: FinishedFragmentedMethod3 != 15000000
    integer(kind=OOOGglob_kint) :: ExitFragmentedMethod3 != 16000000
    !
    ! always required:
    integer(kind=OOOGglob_kint) :: Enum_MaxValue != 17000000
    end type OOOPfrob03_00_Channel1Status_EnumDef

    !********************
    !*** type definition: ***
    !********************
    type, extends(OOOPfrob03_00_objBaseFragmentedMethod3), abstract, public :: OOOPfrob03_01_objFragmentedMethod3_CE
    private
    !*****
    ! enumeration type members:
    ! (values below 10000000 are reserved for the channel itself):
    type (OOOPfrob03_00_Channel1Status_EnumDef), public :: Enum_Channel1Status &
    = OOOPfrob03_00_Channel1Status_EnumDef (1000000,2000000,3000000, &
    10000000,11000000, 12000000,13000000,14000000,15000000,16000000,17000000)
    !*****
    ! local members:
    !*****
    ! coarray component members: (not recommended if channels are in use)
    !*****
    contains
    private
    procedure, public :: FragmentedMethod3_CE => OOOPfrob03_01_FragmentedMethod3_CE
    end type OOOPfrob03_01_objFragmentedMethod3_CE

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From miesiehl@gmail.com@21:1/5 to All on Thu Dec 16 13:38:01 2021
    John’s last syntax example will not work for this,...

    That was my mistake, the syntax does work (using ifort and gfortran) and it is a huge improvement for such nested/extended enum types:

    !*****************************
    !**** enumeration types: ***
    !*****************************
    type, extends(OOOPchnl00_ChannelStatus_EnumDef), private :: OOOPfrob03_00_Channel1Status_EnumDef
    ! (values below 10000000 are reserved for the channel itself here)
    !
    ! values for controlling the execution flow:
    integer(kind=OOOGglob_kint) :: InitiateFragmentedMethod3 = 10000000
    integer(kind=OOOGglob_kint) :: ReadyToStartFragmentedMethod3 = 11000000
    integer(kind=OOOGglob_kint) :: StartFragmentedMethod3 = 12000000
    integer(kind=OOOGglob_kint) :: SendCurrentTestValue = 13000000
    integer(kind=OOOGglob_kint) :: AdjustTestValueToMaxTestValue = 14000000
    integer(kind=OOOGglob_kint) :: FinishedFragmentedMethod3 = 15000000
    integer(kind=OOOGglob_kint) :: ExitFragmentedMethod3 = 16000000
    !
    ! always required:
    integer(kind=OOOGglob_kint) :: Enum_MaxValue = 17000000
    end type OOOPfrob03_00_Channel1Status_EnumDef
    !

    !*************************
    !*** type definition: ***
    !*************************
    type, extends(OOOPfrob03_00_objBaseFragmentedMethod3), abstract, public :: OOOPfrob03_01_objFragmentedMethod3_CE
    private
    !*****
    ! enumeration type members:
    type (OOOPfrob03_00_Channel1Status_EnumDef), public :: Enum_Channel1Status & ! we can not use PARAMETER here
    = OOOPfrob03_00_Channel1Status_EnumDef ()
    .
    .

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)