Attributes - IS4Code/Sona GitHub Wiki
Attributes offer a way to attach custom information to elements in the code, outside of what could be encoded directly using the concrete syntax. This information may be used by the environment to alter the behaviour of such elements, or it may be retrieved later using reflection, as instances of System.Attribute
.
attribute_group:
('#:' | '#' target ':'?) attribute (',' attribute)* (':#' | '#' | EOL);
attribute:
name argument*;
Attributes use the directive syntax, started either by #:
or the specific attribute target (described below), followed by the name of the attribute and its arguments. Unlike regular code, whitespace is significant ‒ successive arguments must be separated by whitespace, and newline is used (but not required) to end the attribute. In-line syntax without newlines is possible as well, by using :#
or just #
(whichever is more aesthetically pleasing) to end the attribute. Multiple attributes may be specified in a single group, separated by ,
.
There are several options how to make the attribute not end with a newline:
- By escaping the newline with
\
, in which case it is ignored. - By ending the line with
,
(in which case the next attribute is expected). - By using parentheses. Expressions in
(
…)
,[
…]
,{
…}
are parsed using the usual rules, skipping any whitespace.
The argument
may be any unary expression, potentially preceded by a name followed by =
, which assigns the expression to a particular property of the attribute. The expression may also be written in parentheses, which makes using arbitrary binary operators possible.
The following example attaches the System.ComponentModel.DescriptionAttribute
attribute to a function. Like in other languages, the Attribute
suffix is optional.
import System.ComponentModel
#: Description "A special function"
function f() end
F# source
open System.ComponentModel
let rec [<Description("A special function")>] f() = ()
This example shows many alternative ways to express two attributes on a variable:
#item Attr "Value" Additional=2,
AnotherAttr "Value" Additional=2
let a = 1
#item Attr "Value" Additional=2
#item AnotherAttr "Value" Additional=2
let a = 1
#:Attr "Value" Additional=2, AnotherAttr "Value" Additional=2:#let a = 1
F# source
[<Attr("Value", Additional = 2); AnotherAttr("Value", Additional = 2)>]
let a = 1
Global attributes are not attached to any particular element declared in the code, rather they attach to the program as a whole. A global attribute must use the targeted syntax, with target
being one of program
(auto-picks the proper program element to attach to), entry
(the main entry method), assembly
, or module
.
Global attributes may be placed only among top-level statements (but they are not statements themselves). They are not allowed in other locations.
The following example shows the specific locations a global attribute may be attached to.
#program Description "A special program"
#entry Description "A special method"
#assembly Description "A special assembly"
#module Description "A special module"
F# source
[<Description("A special program")>]do()
[<method: Description("A special method")>]do()
[<assembly: Description("A special assembly")>]do()
[<``module``: Description("A special module")>]do()
Local attributes attach to whichever code element they follow, and as such are applicable only when followed by a supported element, such as a variable, function, parameter, type, or similar. The supported targets are item
(auto-picks the target), type
, method
, property
, return
, param
, field
, event
, or constructor
. The short #:
syntax is synonymous with #item
.
If a local attribute is attached to a declaration that may produce multiple applicable elements (such as let
defining two or more variables), it is applied to all of them.
The following example shows several elements that support local attributes.
#field Description "A special variable"
let x = 0
#method Description "A special function"
function f(
#param Description "A special parameter"
x
) end
F# source
[<field: Description("A special variable")>]
let x = 0
let rec [<method: Description("A special function")>]f ([<param: Description("A special parameter")>]x) = ()