Range Over Iterators in Co-array Fortran

Let’s start by translating the Go code provided to Co-Array Fortran. The original example utilized iterators, which are not a native feature in Fortran. However, we can approximate the behavior using arrays and custom loops.

Here is the translation:

Example: Range over Iterators

Starting with version 1.23, Go has added support for iterators, which lets us range over pretty much anything!

Let’s look at the List type from the previous example again. In that example, we had an AllElements method that returned a slice of all elements in the list. With Co-Array Fortran, we can manage this differently - as shown below.

module list_module
    implicit none

    type :: element
        integer :: value
        type(element), pointer :: next => null()
    end type element

    type :: list
        type(element), pointer :: head => null()
        type(element), pointer :: tail => null()
    contains
        procedure :: push
        procedure :: all_elements
    end type list

contains

    subroutine push(lst, v)
        class(list), intent(inout) :: lst
        integer, intent(in) :: v
        type(element), pointer :: new_element

        allocate(new_element)
        new_element%value = v
        new_element%next => null()

        if (.not. associated(lst%tail)) then
            lst%head => new_element
            lst%tail => new_element
        else
            lst%tail%next => new_element
            lst%tail => new_element
        end if
    end subroutine push

    function all_elements(lst) result(elements)
        class(list), intent(in) :: lst
        integer, allocatable :: elements(:)
        type(element), pointer :: current
        integer :: count, i

        count = 0
        current => lst%head
        do while (associated(current))
            count = count + 1
            current => current%next
        end do

        allocate(elements(count))
        current => lst%head
        i = 1
        do while (associated(current))
            elements(i) = current%value
            i = i + 1
            current => current%next
        end do
    end function all_elements

end module list_module

program main
    use list_module
    implicit none

    type(list) :: lst
    integer :: i
    integer, allocatable :: all(:)

    call lst%push(10)
    call lst%push(13)
    call lst%push(23)

    all = lst%all_elements()
    do i = 1, size(all)
        print *, all(i)
    end do
end program main

Our implementation doesn’t use iterators directly but mimics their behavior by manually traversing and collecting elements in a list. This allows iteration over linked list elements in Fortran without native iterator support.

Explanation

The program involves a module list_module that contains a definition for a linked list and its elements. Here’s a brief breakdown of the components:

  1. Type Definitions:

    • element: Represents an element of the linked list containing an integer value and a pointer to the next element.
    • list: Represents the linked list containing pointers to the head and tail elements. It also includes procedures to push elements into the list and retrieve all elements.
  2. Procedures:

    • push: Adds an element to the linked list.
    • all_elements: Returns an array of all elements in the list.
  3. Main Program:

    • In main, we create a list, push some integers into it, and then retrieve and print all elements.

By replacing iterators with array-based traversal, we maintain the functional intent of iterating over elements.

Now that we can manage lists and iterate over elements in Fortran, let’s explore more about handling collections in this language.