Skip to content

Conversation

@Thorium
Copy link
Contributor

@Thorium Thorium commented Nov 3, 2025

Added RFC-1334-elif:

F# RFC FS-1334 - Add #elif Preprocessor Directive

Summary

Add #elif to F# conditional compilation so multiple mutually exclusive branches can be written linearly (#if ... #elif ... #elif ... #else ... #endif) instead of nesting or repeating #if blocks. Aligns F# with C# and other languages; improves readability.

Motivation

Without #elif, developers either:

  • Nest #if inside #else (deep indentation, harder to scan), or
  • Write separate #if blocks plus a final negated catch-all (#if !A && !B && !C), which is verbose and error-prone.
  • C# parity

#elif removes redundancy, reduces logical mistakes, and eases cross-language sharing.

Design

Grammar change:

#if <cond>
  group
#elif <cond>
  group
... (0+ more #elif)
#else
  group
#endif

First matching condition’s group is included; at most one group is compiled. #elif must follow an #if and precede an optional #else. No change to symbol definition semantics.

Example #1

#if WIN64
let path = "/library/x64/runtime.dll"
#elif WIN86
let path = "/library/x86/runtime.dll"
#elif MAC
let path = "/library/iOS/runtime-osx.dll"
#else
let path = "/library/unix/runtime.dll"
#endif

Example #2

module test =
 // Should evaluate to x = 3
 let x = 
#if false
   1
#elif false
   2     
#elif (true || false)
   3
#elif true
   4
#else
   5
#endif

Spec Changes

Update conditional compilation section to:

  • Add { #elif pp-expression group } to grammar.
  • Document ordering and “first true wins” semantics.
  • State errors: #elif after #else, #elif without #if, missing condition.

Drawbacks

Small parser/editor updates (fantomas, etc); possibility of long chains (already possible).

Alternatives

Keep nesting; use multiple guarded blocks; introduce more complex compile-time constructs (unnecessary).

Prior Art

VB.NET, C/C++, C#, Swift, Objective-C all support #elif (or equivalent). Aligns F# with ecosystem norms.

Compatibility

Non-breaking. Older compilers will error on #elif. No IL or FSharp.Core changes.

Diagnostics

Errors for:

  • #elif without preceding #if
  • #elif after #else
  • Missing condition
  • Unbalanced directives

Tooling

Add keyword highlighting (or darken un-used branch), folding across entire block, completion for #elif. No runtime tooling differences.

Performance

Negligible; linear evaluation of conditions. No impact on generated code beyond selected branch.

Scaling

Typical branch count ≤ 8; acceptable upper bound ≈ 50; linear processing.

Migration Example

Before:

#if A
let mode = 1
#else
#if B
let mode = 2
#else
let mode = 3
#endif
#endif

After:

#if A
let mode = 1
#elif B
let mode = 2
#else
let mode = 3
#endif

Copy link
Contributor

@Martin521 Martin521 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please give the file a .md extensions.
Under tooling please add that tools like Fantomas must update their logic that evaluates the #if conditions.
Otherwise this looks good to me.
Thanks!

@T-Gro T-Gro merged commit 69c586f into fsharp:main Nov 10, 2025
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

Successfully merging this pull request may close these issues.

3 participants