Inline source code - IS4Code/Sona GitHub Wiki
With the use of the #inline
and #endinline
directives, it is possible to write F# code directly in the source. This option is available only in privileged code, as direct F# may bypass safety restrictions on the generated CIL.
The directive is usable only in place of a single statement, expression, type, or pattern. It must not be surrounded by any operators, otherwise it must be in parentheses.
inline_source:
'#inline "F#"'
code
'#endinline' ('return' | 'throw')?;
The #inline
token is followed by a string indicating the language of the inline code, which must be "F#"
. The F# code starts immediately after the string, without requiring a newline. In order for it to be recognized, the #endinline
keyword must be located at the beginning of a line, must not be followed by a non-whitespace character, and must not be a part of any of the following:
- a comment,
- a string,
- a string interpolation.
In other words, the #endline
directive is recognized per standard F# syntax rules. When used as a statement, it may optionally be followed by either return
or throw
, indicating the syntactic category of the statement ‒ return
if the code is supposed to be returning a value (from its last expression), or throw
if the code should be regarded as never-returning.
F# code within the inline source section must be properly formatted per F#'s indentation rules. To aid this, there are several transformations performed on the code when it becomes a part of the output:
- The code that immediately follows the
#inline
directive is placed on a single line, with the first token properly indented to a position where normal code would otherwise be generated. Any whitespace that precedes the token is ignored. - Likewise, each following line that starts with a non-whitespace token is indented to the same position. Of these, the first line sets the input indentation that must be respected by all further lines ‒ any line starting with a less indented token is invalid.
- Comments (both
//…
and(*…*)
) are treated as whitespace, but are copied directly to the output (unless they form indentation). Newlines in block comments are treated as line separators, per F#'s rules. Newlines in strings are not considered line separators. - Directives are never indented.
It should be noted that the F# code should correspond to a self-contained code element. Code that syntactically affects other parts of the source or depends on them is not supported.
Invalid code example
#inline "F#"
let f() = begin
printfn "First line in F#"
#endinline
printfn("Second line outside F#")
#inline "F#"
printfn "Last line in F#"
end
#endinline
This code attempts to begin a function in one code snippet and end it in another. This is not possible, however, as the indentation maintained by the first F# snippet does not extend to the statement that prints "Second line outside F#"
, which thus does not get indented at all. Worse even, the last statement starts indented, but the end
following it is 2 character below its level, making the code invalid.
F# source may be used as a body of a single function, to embed pre-existing code or use code that would not be normally expressible. The code may freely access variables created prior, and can reference variables created in the F# source.
function f()
let arr = [1, 2, 3]
#inline "F#"
use ptr = fixed &arr[0]
#endinline
Console.WriteLine(NativePtr.toNativeInt(ptr))
end
F# source
let rec f() =
let arr = [| 1;2;3 |]
use ptr = fixed &arr[0]
Console.WriteLine(NativePtr.toNativeInt(ptr))
()
Regardless of the indentation of the input, the meaningful lines of F# code are aligned with the other statements.