Why are parens required around format strings?

If the READ statement had both i and j in the I/O list, then yes. And to your second question, yes also.

1 Like

What’s syntactic sugar and do you think it’s bad in general?
Fortran just represents a different way of expressing a [small] subset of what existing Assembly can do. So why the effort to program compilers at all?
It’s the purpose of the language and the compiler to make programmers’ life easier, isn’t it?

While Fortran’s input/output editing capabilities are very powerful, they also are very complicated. I bet most of the features are barely ever used.
I couldn’t even find a comprehensive overview over all descriptors when I prepared my Fortran course (besides the j3 working draft). I was surprised, that there are so many more descriptors I’ve never heard of (I knew 9 out of over 30).

2 Likes

“Syntactic sugar” is when an existing feature is given new syntax - it doesn’t provide additional functionality, just an alternate spelling. It “sweetens” syntax that some may find unpleasant.
The standard has added such things in the past, notably being able to use [ ] in array constructors instead of (/ /), but in general getting the committee to accept alternate spellings for existing functionality is an uphill road; we’d much rather spend our efforts on bringing new capabilities to programmers.

I don’t know what you’ve been using as a reference for the language - most people refer to the language reference manual for commercial compilers, which are comprehensive, and most are available for free reference online.

I agree that Fortran’s format processing is complex, and we keep adding features to it (AT and LZ formats, for example), but you can generally get by with just a small subset, and that subset is what these f-strings would mimic. G0.d is incredibly useful and simple.

1 Like

I created issues for the two features being discussed here:

It seems that most people agree the ideas are good, but there is no agreement on the priority, as well as whether it is worth doing at all given an almost equivalent (although more verbose/complex) existing feature.

2 Likes

Most of the syntax was created when Fortran had no allocatable variables and so most everything was of fixed length,and most text was not in variables but in the Format statements themselves. AT, and * repeaters and such address some of that, but a pet peeve is that * does not have a way to just consume a single variable, so given an arbitrarily sized array that I would like to print as
[ r1, r2, r3, r4 … ] I cannot use "(‘[’,*(g0:,‘,’),‘]’)’ or I never get to the closing bracket. So I have to build the format in a string using the correct size N instead of * or used non-advancing I/O to built the string. Something like f-strings makes that far more intuitive to construct and can be done on one like using something like the M_overload module does. Some compilers have the VPE extension that among other things lets you specify the repeat counters but if not a full-blown f-strings implementation something like the * repeat but that applies only to the array it started on would make things like this easier, where otherwise the use of * on anything accept the last part of a format statement basically causes the rest of the format to be ignored.

And speaking of unused features and Fortran I/O being a bit like an embedded language of it’s own it is not obvious to new users how different the output can be in different programming environments. Many are surprised the first time they use their second compiler by it. How much white space is between values, what delimiters are used and whether strings are quoted or not and why column 1 is blank are old-hat to old-timers but always confuse newcomers. Let alone in-line comments and repeat counts; and that does not even touch on NAMELIST groups. When I enter something like
10,20,5*,4*1.0d0 / next case
as input to a program and anyone new to Fortran is watching I inevitably spend the next five minutes explaining list-directed I/O is much more powerful than they think but again, sort of its own little language.

2 Likes

I think nonadvancing i/o solves the original problem in an ideal way. Rewriting your example as

character(*), parameter :: cfmt = "(*(g0:,‘,’))"
!...
write(*,cfmt, advance='no') '['
write(*,cfmt, advance='no') array
write(*,cfmt) ']'

seems straightforward and much simpler than either building a custom format string just for that one write statement or arguably even than the <n> extension syntax.

Yes. F-strings are much like a series of simple non-advancing I/o statements but much less verbose.

The biggest issue is that you cannot use it on internal writes to build a string. You can do that with an internal write and concatenation or use the result of the first write as a string in a second write but if you write more than a few items you start creating little functions or end up with a dozen write statements.

Some of the nicer solutions use things like TL1 which a new user is unlikely to know exists. But yes. Non-advancing I/O is probably most used when a single format does not suffice or requires a very complex format if done with advancing io. That is how I picture f-strings… as a compact syntax for writing a series of non-advancing writes, actually.

The VPE extension was nice in a case like this, where “(‘[’,(g0,‘,’),TL1,‘]’)” would work where N=size(array). But anything other than a format specifying the digits of precision gets cumbersome. I wonder if TL was on the list of the nine known format descriptors mentioned above, but I do not see it used in places were it looks like it should be; maybe a little Fortran tutorial for the Fortran Wiki or fortran-lang is in order on descriptors.

Maybe something like %re and %im for complex values is just needed for all intrinsics that converts them to a string is the most Fortranik? a%f(‘g0.3’),b%f(‘f5.2’), … which ironically requires a set of parens but is Fortran-ish and OOP-like keeping the information close together so I am not looking back and forth from a FORMAT statement and a write statement. Some might like that better than f(a,‘g0.3’).

RE f-string, the biggest difference for me (if available) is that it is an expression rather than a statement, so that it can be used directly as an actual argument, for example, to pass a filename with numerical data embedded in via {...}. In my codes I have been doing this with my custom to_str() function (maybe similar to fmt() as mentioned above), but I think the code would become much simpler and readable if it can be done with f-string (also no need to prepare such to_stri()). Another point is that f-string can be used directly in another expression (e.g., a ternary/conditional expression), which likely makes the code even shorter (than declaring several temporary variables and using several statements for making IF constructs).

EDIT: Another feature I find useful is this one (which I typically use for echo-ing input values).

>>> long_var_name = 2.0
>>> print( f"{long_var_name = }, {long_var_name**2 = :.6f} " )
long_var_name = 2.0, long_var_name**2 = 4.000000

For another examples, string interpolation (wiki)

1 Like