4

I have the following types

type StatusCode = 
    | OK          = 200
    | NoContent   = 204
    | MovedTemp   = 301
    | MovedPerm   = 302
    | SeeOther    = 303
    | NotModified = 304
    | NotFound    = 404
    | ServerError = 500

[<Literal>]
let NoBodyAllowedStatusCodes = [StatusCode.NoContent; StatusCode.NotModified]

And I'm getting a compile-time error that says:

This is not a valid constant expression or custom attribute value

I can't really figure out what's wrong here.

Overly Excessive
  • 2,095
  • 16
  • 31

1 Answers1

6

In F#, and .NET in general, lists cannot be literals (constant in C#/VB.NET). Only primitive values can, like string, bool, etc. The F# 3.0 specification has the guidelines on what can or cannot be a literal in section 10.2.2:

A value that has the Literal attribute is subject to the following restrictions:

  • It may not be marked mutable or inline.
  • It may not also have the ThreadStatic or ContextStatic attributes.
  • The right-hand side expression must be a literal constant expression that is made up of either:
  • A simple constant expression, with the exception of (), native integer literals, unsigned native integer literals, byte array literals, BigInteger literals, and user-defined numeric literals.

—OR—

  • A reference to another literal.

Depending on what you are trying to do, you could make your list static if the let binding is being used in a class. If it is in a module, I'd just remove the Literal attribute since let bindings are immutable by default, anyway.

vcsjones
  • 138,677
  • 31
  • 291
  • 286
  • The link you gave to MSDN is not exactly relevant. What's listed there is the list of possible types of literals as terms of F# syntax. This is not exactly the same as what can be rendered as a literal in CLI. A CLI literal (or constant) is a value stored in an assembly's metadata and which thus is available to referring assemblies without executing any code. Out of the types listed in your link, you cannot make CLI constants of types `decimal`, `bigint`, `unativeint` or `byte[]`. On the other hand, CLI allows `null`s of any reference type as constants (in F#, `null`s are forbidden). – ach Mar 30 '15 at 15:16
  • @AndreyChernyakhovskiy I edited to include the relevant part from the F# specification instead. Thanks for pointing that out. – vcsjones Mar 30 '15 at 15:22
  • The specification on this is to be found in http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf, §II.22.9. Quite difficult to read, it is, and lacks info on how all this CLI stuff maps on F# syntax (or C# as well). Would be nice to find an easy explanation. – ach Mar 30 '15 at 15:33
  • @AndreyChernyakhovskiy: it's not true that nulls are forbidden in F# (sadly), and you can in fact have null literals. Anyway, lists are certainly not among things you can have literals of. – scrwtp Mar 30 '15 at 21:28
  • @scrwtp, As far as I understand, you cannot make a value of null in F# (at least I failed), you can only compare values that came from foreign code with null. If I'm wrong, it would be nice to see a how-to example. – ach Mar 31 '15 at 06:37
  • 1
    It's possible if you decorate your type with the ``[]`` attribute. – Overly Excessive Mar 31 '15 at 09:17
  • @AndreyChernyakhovskiy: something like this works for instance: `[] let str : string = null`. – scrwtp Mar 31 '15 at 10:39