34

I am a bit confused by the Literal keyword and why it is necessary in F#.

Reading the docs, it sounds to me that [<Literal>] is used to define a constant, however I am a bit confused how this constant differs from all other constants in F#..

Values that are intended to be constants can be marked with the Literal attribute. This attribute has the effect of causing a value to be compiled as a constant.

When I think of a constant, I think of something which is immutable....

let x = "a" + "b" //this is a immutable value, its value is constant
[<Literal>]
let y = "a" + "b" //this is also a immutable value, but why is this a special constant?

Is it because the 'normal' F# values are evaluated lazily and the [<Literal>] is not evaluated lazily..? is that what they mean with 'compiled as constant'..? or is there something else to it?

Michelrandahl
  • 3,035
  • 2
  • 24
  • 41

3 Answers3

49

In your example, x is an immutable value that is assigned during runtime (but NOT evaluated lazily), whereas y is assigned during compiletime. As an example,

let myDLL = "foo.dll"

[<DllImport(myDLL, CallingConvention = CallingConvention.Cdecl)>]
extern void HelloWorld()

will not work, because DllImport is an attribute and needs to know the value of myDLL during compilation. However, this will work:

[<Literal>]
let myDLL = "foo.dll"

[<DllImport(myDLL, CallingConvention = CallingConvention.Cdecl)>]
extern void HelloWorld()
John Reynolds
  • 4,751
  • 3
  • 30
  • 42
  • 3
    You might also want to note the importance for .NET interop. Fields decorated `[]` are compiled into IL as constants, which has certain implications for consumers of your code. See http://stackoverflow.com/a/755693/385844, for example. – phoog Sep 08 '14 at 20:56
  • 2
    Please note that according to f# naming conventions, literals should be PascalCase. If you use them for pattern matching, this is more than just convention: https://docs.microsoft.com/sv-se/dotnet/fsharp/language-reference/literals – Guran Sep 18 '18 at 08:19
27

If you come from C# background, you can think of Literal values as const fields, and non-literal ones as readonly fields. The same differences apply.

cynic
  • 5,155
  • 1
  • 24
  • 40
15

I think a better example is what happens in a match.

This doesn't do what you expect:

let t = 3
match q with
|t -> printfn "this always happens"
|_ -> printfn "this never happens" //and produces a compiler warning

on the other hand:

[<Literal>]
let t = 3
match q with
|t -> printfn "q is 3"
|_ -> printfn "q isn't 3"

So here as the Literal is a compile time constant we can use it for pattern matching.

John Palmer
  • 25,190
  • 3
  • 46
  • 67
  • 3
    Unfortunately, this example is case sensitive. To work as described the literal identifier `t` must begin with a capital letter. Using a capital for the non-literal version produces an additional compiler warning, but functions identically. – cadull Feb 23 '15 at 03:56