Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Let bindings support punctuation enclosed in double backticks, but types don't?

Using F# in Visual Studio 2012, this code compiles:

let ``foo.bar`` = 5

But this code does not:

type ``foo.bar`` = class end

Invalid namespace, module, type or union case name

According to section 3.4 of the F# language specification:

Any sequence of characters that is enclosed in double-backtick marks (````),
excluding newlines, tabs, and double-backtick pairs themselves, is treated
as an identifier.

token ident =
    | ident-text
    | `` [^ '\n' '\r' '\t']+ | [^ '\n' '\r' '\t'] ``

Section 5 defines type as:

type := 
    ( type )
    type -> type       -- function type
    type * ... * type  -- tuple type
    typar              -- variable type
    long-ident         -- named type, such as int
    long-ident<types> -- named type, such as list<int>
    long-ident< >      -- named type, such as IEnumerable< >
    type long-ident    -- named type, such as int list
    type[ , ... , ]    -- array type
    type lazy          -- lazy type
    type typar-defns   -- type with constraints
    typar :> type      -- variable type with subtype constraint
    #type              -- anonymous type with subtype constraint

... and Section 4.2 defines long-ident as:

long-ident :=  ident '.' ... '.' ident

As far as I can tell from the spec, types are named with long-idents, and long-idents can be idents. Since idents support double-backtick-quoted punctuation, it therefore seems like types should too.

So am I misreading the spec? Or is this a compiler bug?

like image 852
Marty Avatar asked Oct 20 '12 05:10

Marty


1 Answers

It definitely looks like the specification is not synchronized with the actual implementation, so there is a bug on one side or the other.

When you use identifier in double backticks, the compiler treats it as a name and simply generates type (or member) with the name you specified in backticks. It does not do any name mangling to make sure that the identifier is valid type/member name.

This means that it is not too surprising that you cannot use identifiers that would clash with some standard meaning in the compiled code. In your example, it is dot, but here are a few other examples:

type ``Foo.Bar``() =  // Dot is not allowed because it represents namespace
    member x.Bar = 0

type ``Foo`1``() =    // Single backtick is used to compile generic types
    member x.Bar = 0

type ``Foo+Bar``() =  // + is used in the name of a nested type
    member x.Bar = 0

The above examples are not allowed as type names (because they clash with some standard meaning), but you can use them in let-bindings, because there are no such restrictions on variable names:

let ``foo`1`` = 0
let ``foo.bar`` = 2
let ``foo+bar`` = 1

This is definitely something that should be explained in the documentation & the specification, but I hope this helps to clarify what is going on.

like image 87
Tomas Petricek Avatar answered Oct 21 '22 02:10

Tomas Petricek