Skip to content

optval and module dependencies #574

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

Open
ivan-pi opened this issue Nov 24, 2021 · 8 comments
Open

optval and module dependencies #574

ivan-pi opened this issue Nov 24, 2021 · 8 comments

Comments

@ivan-pi
Copy link
Member

ivan-pi commented Nov 24, 2021

So there have been a few discussions about optval before:

The former thread established the performance implications of sprinkling optval here and there are likely negligible. The latter thread established that merge cannot be used as a replacement due to it's behavior interacting with the present function, and also being unsuitable for variables of type character(:), allocatable. However, I think one comment from @zoziha is worthy of more attention:

Because merge is a standard function, we can reduce optval module dependencies by using it(merge). It is convenient to replace them with the conditional expression syntax (F202X) in the future.

In my opinion the stdlib modules should be as independent as reasonably possible. This enables users to simply copy the sources of the modules they want to use, without having to climb up the module dependency tree. Now I realize this is a non-issue for those using stdlib via fpm, but some users might prefer to preserve their current build methods.

For source files that require the preprocessor anyways, it would be straightforward to provide a preprocessor version of optval instead:

#:def optval(lhs,opt,default)
  ${lhs}$ = ${default}$
  if (present(${opt}$)) ${lhs}$ = ${opt}$
#:enddef

An alternative variation is

#:def optval(lhs,opt,default)
  if (present(${opt}$)) then
    ${lhs}$ = ${opt}$
  else
    ${lhs}$ = ${default}$
  end if
#:enddef

however this one can result in some false positive warning about non-initialized arguments.

[Edit: hopefully the macros are correct now...]

@milancurcic
Copy link
Member

I like the idea of using the pre-processor for internal use of optval

#:def optval(lhs,opt,default)
  lhs = default
  if (present(opt)) lhs = opt
#enddef

Would this not require the variables to be called exactly lhs, opt, and default? Or does fypp know that this is a function call and to substitute variables?

Now I realize this is a non-issue for those using stdlib via fpm,

Eventually fpm should tree-shake, i.e. build only the modules used by the project and not the whole stdlib, so this may in the future have an effect on fpm build as well.

@jvdp1
Copy link
Member

jvdp1 commented Nov 24, 2021

good point @ivan-pi
I use a similar approach in my own code with fypp:

#:def myfunction(var2,var4,var3)
(var1(${var2}$)#{if var3 != '0' }#+${var3}$#{endif}#)*var5#{if var4 != '0' }#+${var4}$#{endif}#
#:enddef

where var2..4 can be names of variables or integer (e.g.,0)
It is called in the code as:

result = $(myfunction( myvar1, myvar2, 0)}$

this would be available for all files if stored in common.fypp of stdlib

@ivan-pi
Copy link
Member Author

ivan-pi commented Nov 24, 2021

Forgive me, I only glanced quickly through the fypp docs. I think the following macro is correct:

#:def optval(lhs,opt,default)
  ${lhs}$ = ${default}$
  if (present(${opt}$)) ${lhs}$ = ${opt}$
#:enddef

and can be called as follows:

subroutine demo(left)
  integer, intent(in), optional :: left
  integer :: l 

  @:optval(l, left, 1)

end subroutine

producing the output

$ fypp test_optval.fypp 


subroutine demo(left)
  integer, intent(in), optional :: left
  integer :: l 

  l = 1
  if (present(left)) l = left

end subroutine

The downside is the assignment is hidden in the macro which makes it a bit cryptic.

@milancurcic
Copy link
Member

I see. And it can't be used in expressions, correct?

@jvdp1
Copy link
Member

jvdp1 commented Nov 24, 2021

I see. And it can't be used in expressions, correct?

Not in the form proposed by @ivan-pi because the macro will be replaced by the two lines before compilation.
However, I cannot think about a way to do that, without conditional expressions or intrinsics.

@ivan-pi
Copy link
Member Author

ivan-pi commented Nov 24, 2021

Correct, an expression implies having a function or using the F202X syntax:

l = present(opt) ? opt : default

This can in principle be wrapped in a macro:

#:def optval(opt,default)
present(${opt}$)) ? ${opt}$ : ${default}$
#:enddef

which can be used as an expression using a similar syntax to the one Jeremie showed:

l = @{optval(left, 1)}@

but IMO it doesn't feel worth the effort.

I'm not sure if fypp macros can be overloaded, however you could pull both versions off in the same macro by using either the positional or keyword arguments that fypp supports.

@jvdp1
Copy link
Member

jvdp1 commented Nov 24, 2021

On the other side, are we not proposing a shortcut for a function (optval) we find useful but that we don't want to use because the compilers is not able to inline it properly by default? This might give a bad image of optval to the stdlib users (and to some extends, also to the committee)

@ivan-pi
Copy link
Member Author

ivan-pi commented Nov 24, 2021

Personally I'm not worried too much about the performance penalty. As long as not used in a performance-critical context I think it's handy. On the other hand with the new F202X conditional syntax I think optval becomes superfluous.

The reason I wanted to suggest a preprocessor alternative was merely as a means of reducing inter-dependencies between modules, allowing users to "pick-n-play". You could argue that downloading three modules (kinds, optval, selection) instead of two (kinds, selection) is not a big difference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants