Defining Modules VS.NET vs F# Interactive

2019-06-16 16:52发布

问题:

I have written this code which compiles and works perfectly in VS.NET 2010

module ConfigHandler
open System
open System.Xml
open System.Configuration

let GetConnectionString (key : string) =
  ConfigurationManager.ConnectionStrings.Item(key).ConnectionString

however when I do a control + A and Alt + Enter to send this to FSI I get an error

ConfigHandler.fs(2,1): error FS0010: Unexpected start of structured construct in definition. Expected '=' or other token.

OK.

So I change my code to

module ConfigHandler =
  open System
  open System.Xml
  open System.Configuration

  let GetConnectionString (key : string) =
    ConfigurationManager.ConnectionStrings.Item(key).ConnectionString

Now Control + A, Alt + Enter is successful and I FSI nicely tells me

module ConfigHandler = begin val GetConnectionString : string -> string end

However now If I try to compile my code in VS.NET 2010, I get an error message

Files in libraries or multiple-file applications must begin with a namespace or module declaration, e.g. 'namespace SomeNamespace.SubNamespace' or 'module SomeNamespace.SomeModule'

How can I have both? Ability to compile in VS.NET and the ability to send modules to FSI?

回答1:

There is a tiny -- but crucial -- difference between your two snippets of code which is to blame here.

F# has two ways to declare a module. The first, a "top-level module", is declared like this:

module MyModule
// ... code goes here

The other way to declare a module is as a "local module", like so:

module MyModule =
    // ... code goes here

The main differences between the "top-level" and "local" declarations are that the local declaration is followed by an = sign and the code within a "local" module must be indented.

The reason you get the ConfigHandler.fs(2,1): error FS0010: Unexpected start of structured construct in definition. Expected '=' or other token. message for the first snippet is that you can't declare top-level modules in fsi.

When you added the = sign to your module definition, it changed from a top-level module to a local module. From there, you got the error Files in libraries or multiple-file applications must begin with a namespace or module declaration, e.g. 'namespace SomeNamespace.SubNamespace' or 'module SomeNamespace.SomeModule' because local modules must be nested within a top-level module or a namespace. fsi doesn't allow you to define namespaces (or top-level modules), so if you want to copy-paste the entire file into fsi the only way it'll work is if you use the compilation directives as @pad mentioned. Otherwise, you can simply copy-paste the local module definitions (without the containing namespace) into fsi and they should work as expected.

Reference: Modules (F#) on MSDN



回答2:

The common solution is to keep the first example and create a fsx file which references the module:

#load "ConfigHandler.fs"

You have advantage of loading multiple modules and writing plumbing code for experiment.

If you really want to load ConfigHandler.fs directly to F# Interactive, you can use INTERACTIVE symbol and compiler directives:

#if INTERACTIVE
#else
module ConfigHandler
#endif

which works for both fsi and fsc.