Functional programming in Fortran

I have read about functional programming languages such as Haskell but not used them. Fortran is an imperative language but does provide some support for functional programming, such as the built-in array operations and the ability to define PURE functions. Subroutines returning multiple variables could be replaced by functions returning derived types. There is a functional-fortran project. In a purely functional language you cannot redefine a variable. Although you can hardly program in Fortran without loops (which implies a changing loop variable), should you try to avoid redefining other variables? For example, if you want uniform random numbers between 0 and c, you could write

real, allocatable :: x(:)
allocate (x(n))
call random_number(x)
x = c*x

or

real, allocatable :: x(:),ran(:)
allocate (ran(n))
call random_number(ran)
x = c*ran
deallocate (ran)

Is it worth it to define more intermediate variables to have fewer reassignments of variables? I may try to program in a style that avoids redefining variables when possible and see if I like the results.

1 Like

It occurred to me that one way to set variables that cannot be changed is with ASSOCIATE, for example

program xassoc_ran
integer, parameter :: n = 10
real :: xraw(n)
call random_number(xraw)
associate (xmult => 100.0)
associate (xscaled => xmult * (xraw - sum(xraw)/n))
   write (*,"(*(f8.4))") xscaled
   ! xscaled = xscaled*5 ! illegal -- gfortran says Error: 'xscaled' at (1) associated to expression cannot be used in a variable definition context (assignment)
end associate
end associate
end program xassoc_ran

Another way, available since Fortran 90, is to define a subroutine where the fixed variables are declared as INTENT(IN) arguments.

I have been pretty pleased with the ability to program in Fortran using a functional style. All of my open source libraries are based heavily on it. If compilers were less buggy wrt associate I could probably get away without ever declaring a local variable, and you wouldn’t really notice it.

2 Likes

I’ll try and come back later and post some examples. Basically, in places where I only assign to a local variable once, if I try change that to an associate, there are a few different compiler errors that I occasionally run in to. The rhyme and reason to when it works and when it doesn’t has so far escaped me.

It would be convenient if each ASSOCIATE did not have to be paired with END ASSOCIATE. In this case the ASSOCIATE block would terminate at the end of the program unit. Then, in Fortran, you would have a simple way to define variables that cannot be changed, and you could use the type inference of ASSOCIATE to avoid declaring many variables. Currently if you use ASSOCIATE for each variable you define, you must clutter the end of the program unit with many END ASSOCIATE statements. For example, it would be nice if the program

implicit none
associate (pi => 3.14)
associate (r => 10.0)
print*,"Radius, circumference =",r,2*pi*r
end associate
end associate
end

were valid without the two END ASSOCIATE statements. In this case, pi and r could be defined in the same ASSOCIATE statement since they are independent, but often the variable you want to define depend on variables that must be defined in previous ASSOCIATE statements.

If people think this idea has merit I can submit it to j3-fortran/ fortran_proposals

@Beliavsky , you will know this is a must-watch video prior to and during every proposal for Fortran!!!: https://youtu.be/euLQOQNVzgY

As stated in your comment, it may appear redundant compared to what Fortran has long provided and you may thus receive the no “associate statement” for you response!!

real, parameter :: pi = 3.14

So I suggest you put together all the use cases along with the needs and requirements, that can help it gain traction.

2 Likes

Unfortunately, Fortran, as many other similar languages, is missing first class functions. And hence also lexical closures. And it cannot be overcome with syntactic sugar and overloading as in older C++.

But Fortran array operations are a good alternatives for applications of functions to lists in various ways.

1 Like

Funny video, thanks. Since Fortran really never deletes anything (the compilers will still support deleted features), most of my ideas should be rejected – I just don’t know which ones :slight_smile: It looks like my A0 edit descriptor suggestion (trim trailing spaces on output), analogous to I0, is going to be in Fortran 202x, but as AT, (T stands for TRIM). So some proposals from the general public are accepted. A0 was initially proposed 14 years ago, so one should be patient :slight_smile:

Making END ASSOCIATE optional or introducing a new construct LET are being discussed in shorthand for immutability #221 in the fortran_proposals list.

You don’t need to make end associate optional to avoid clutter. You can have multiple associations in one line, just by comma delimiting, and therefore only one ‘end associate’:

implicit none

associate (pi => 3.14, r=> 10.0)
print*,"Radius, circumference =",r,2*pi*r

end associate
end

EDIT: It seems I am still in the dark about F18 updates, I thought you couldn’t name associate statements. EDIT2: Again wrong about that!

It has been possible to use a named ASSOCIATE statement since Fortran 2003. Fortran 2003, page 160-161

R817 associate-stmt  is [ associate-construct-name : ] ASSOCIATE

C810 (R820) If the associate-stmt of an associate-construct specifies an
associate-construct-name, the corresponding end-associate-stmt shall
specify the same associate-construct-name.

Strange. I am (again) mistaken! It was the nvfortran compiler I’ve been using for my CUDA Fortran work. It claims to fully support F2003, but perhaps that is not the case.

unless they depend on one another. You cannot do

associate(y => x**2, z => y/2)
...
end associate

you must do

associate(y => x**2)
  associate(z => y/2)
  ...
  end associate
end associate

which is where the verbosity starts to creep in, and the convenience of let would make a difference.

That’s fair! I can see the utility in having that functionality. One could define z in terms of x again though, and I think that would be less cumbersome than adding more associate lines; depends on the problem. If your associations are too complex to do that, perhaps additional local variables is a better choice anyway, to help identify more registers to hold variables.