Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A Long Term Goal For Preprocessing #308

Open
everythingfunctional opened this issue Dec 15, 2020 · 6 comments
Open

A Long Term Goal For Preprocessing #308

everythingfunctional opened this issue Dec 15, 2020 · 6 comments
Labels
specification Issue regarding fpm manifest and model

Comments

@everythingfunctional
Copy link
Member

A killer functionality that would be nice to have as part of fpm, would be the ability to use Fortran code to generate Fortran code as part of preprocessing a source file.

Rust has intelligent macros, which provide a similar capability. I don't remember if it's part of stack or the Haskell language itself, but they have a directive which can call a tool, which might be built as part of the current package, to generate some source code. For example, I created a testing framework in Haskell (here) similar to my vegetables framework, where the "main" test program only contains the following line:

{-# OPTIONS_GHC -F -pgmF hedge-trimmer #-}

It would be awesome if my test driver program could contain just a similar line.

@ivan-pi
Copy link
Member

ivan-pi commented Dec 16, 2020

I can't find the thread right now, but I recall reading the minutes from a standards committee meeting (perhaps related to generics?), where they decided not to pursue intelligent macros as part of the language. This means that any potential preprocessing facilities we invent would be specific to fpm.

Do you have any other motivations for such meta-programming facilities apart from unit testing? Is what you have in mind similar in functionality to Julia's @test macro? I think D also has a similar unit test capability, e.g. if you look into the D ascii module you find code like this:

/++
    Params: c = The character to test.
    Returns: Whether `c` is a letter or a number (0 .. 9, a .. z, A .. Z).
  +/
bool isAlphaNum(dchar c) @safe pure nothrow @nogc
{
    return c <= 'z' && c >= '0' && (c <= '9' || c >= 'a' || (c >= 'A' && c <= 'Z'));
}
///
@safe pure nothrow @nogc unittest
{
    assert( isAlphaNum('A'));
    assert( isAlphaNum('1'));
    assert(!isAlphaNum('#'));
    // N.B.: does not return true for non-ASCII Unicode alphanumerics:
    assert(!isAlphaNum('á'));
}

I was thinking in the past of prototyping something like this as a demonstration of using symengine.f90. The idea was you could do your symbolic manipulation in Fortran, and then "print" the resulting symbolic expressions into Fortran commands. Building upon my example from Discourse, I envisioned something like:

real :: aw, daw_dX, daw_dT

$symengine

! Initialize symbols
call symbols(X, T, a1, a2, b1, b2, 'X T a1 a2 b1 b2')

! Define symbolic expressions
a = a1 + a2*T
b = b1 + b2*T
aw = (X/a)**(1/b)/(1 + (X/a)**(1/b)) 

! Find symbolic derivatives with respect to X and T
daw_dX = sp.diff(aw,X)
daw_dT = sp.diff(aw,T)

call fcode(cse([aw, daw_dX, daw_dT]), ['aw','daw_dX','daw_dT'])

$end symengine

which would get compiled and linked with symengine in a first pass, and executed to produce the following Fortran source output:

block
    real :: x0, x1, x2, x3, x4, x5, x6, x7
    x0 = T*a2 + a1
    x1 = 1d0/x0
    x2 = X*x1
    x3 = T*b2 + b1
    x4 = 1d0/x3
    x5 = x2**x4
    x6 = x5 + 1
    x7 = x5/x6**2
    aw = x5/x6
    daw_dX = x4*x7/X
    daw_dT = -x1*x7*(a2*x3 + b2*x0*log(x2))/x3**2
end block

I am worried this development cycle is kind of convoluted, and there are many points of failure. If you make errors in your symbolic manipulation section, how are they communicated back to you? This kind of meta-programming is perhaps suitable in something like a Jupyter notebook, but so far I am not convinced it can work in static text files. Since my Fortran source code only needs to be developed once, what is the benefit versus doing my symbolic manipulation in Python or MATLAB and just copying the output to my Fortran source directly?

@ivan-pi
Copy link
Member

ivan-pi commented Dec 16, 2020

The preprocessor thread over at j3-fortran contains multiple interesting views from compiler developers, those involved in the standards committee and also users. The prevailing thought seemed to be that it is best to avoid it.

@everythingfunctional
Copy link
Member Author

Hi @ivan-pi , I caught wind of some of the discussions around intelligent macros, and fully understand why they aren't a good idea for the standard. It's basically just a step way too far in complexity for compilers to implement (at least right now, and maybe ever). I also think they're a pretty difficult feature for programmers to use. Using a well designed macro wouldn't be that hard, but writing one sure would be.

My use case was primarily for the purposes of unit testing, but I could see it being useful for other sorts of code generation. I wouldn't necessarily expect the code that does the generation to be inline though; more like a simple call to an external program whose outputs get substituted in.

I was mostly just throwing the idea out the to see what other solutions there might be and get the wheels turning. I don't expect to solve this one any time soon.

@certik
Copy link
Member

certik commented Dec 17, 2020

Yes, I asked @everythingfunctional to open an issue for this, as he mentioned this at our last Fortran call.

It's a broader issue of: do we want to use fpm as a vehicle to extend Fortran, or to provide default pre-processing (for all compilers, for example using fypp), etc.

And for now I recommend to stick to standard Fortran. We can still deliver most of our goals with that.

In general, I would like to see Fortran being used without pre-processing.

If it turns out there is no other way, we can (in the future) make fpm do all kinds of default pre-processing.

@interkosmos
Copy link
Member

C preprocessor macros are probably the most common, and they are supported (at least) by GNU Fortran, IFORT, and LLVM. The only problem is that the different implementations do not predefine the same macros. It should therefore be the task if fpm to set common macros (e.g., operating system identifier) if preprocessing/conditional compilation is selected.

@arjenmarkus
Copy link
Member

arjenmarkus commented Jan 27, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
specification Issue regarding fpm manifest and model
Projects
None yet
Development

No branches or pull requests

6 participants