F#, namespaces, modules, fs and fsx

2019-01-23 23:25发布

问题:

I'm aware of other questions about modules and namespaces in F#, but they're not helping me right now.

I've got a project with

Utilities.fs

namespace Company.Project.Namespace
module Utilities = 
     //stuff here

Functions.fs

namespace Company.Project.Namespace
open Utilities

module Functions = 
     //stuff here

And I'm trying to test them in an fsx:

#load "Utilities.fs"
#load "Functions.fs"

which gives me error FS0039: The namespace or module 'Utilities' is not defined when I try to send it to FSI with Alt-Enter.

I've tried adding same namespace at the top of the script file, but it doesn't like that.

What's weird is that the background compiler doesn't shout at me.

This seems to work, but is it the right approch?

#load "Utilities.fs"
open Company.Project.Namespace
#load "Functions.fs"

Is there a 'reference' FSharp project somewhere, which contains examples of how to integrate all this stuff: namespaces, modules, classes, script files, tests etc.?

回答1:

I'm not an expert with FSI, but some experimentation suggests that namespaces are only supported by #load declarations (not via typical interactions - sending a namespace declaration group to VFSI via Alt-Enter does not work), and that different interactions contribute different 'instances'. For example, with the code file

namespace Foo

type Bar() =
    member this.Qux() = printfn "hi"

namespace Other

type Whatever() = class end

namespace Foo

module M =
    let bar = new Bar()
    bar.Qux()

if I #load it more than once I get e.g.

> [Loading C:\Program.fs]
hi

namespace FSI_0002.Foo
  type Bar =
    class
      new : unit -> Bar
      member Qux : unit -> unit
    end
namespace FSI_0002.Other
  type Whatever =
    class
      new : unit -> Whatever
    end
namespace FSI_0002.Foo
  val bar : Bar

> #load @"C:\Program.fs";;
> [Loading C:\Program.fs]
hi

namespace FSI_0003.Foo
  type Bar =
    class
      new : unit -> Bar
      member Qux : unit -> unit
    end
namespace FSI_0003.Other
  type Whatever =
    class
      new : unit -> Whatever
    end
namespace FSI_0003.Foo
  val bar : Bar

> new Foo.Bar();;
> val it : Foo.Bar = FSI_0003.Foo.Bar

Note that it seems the FSI_0003.Foo.Bar shadowed the FSI_0002 version.

So I'm thinking the part of the F# spec that says

Within a namespace declaration group, the namespace itself is implicitly opened if any preceding namespace declaration groups or referenced assemblies contribute to this namespace, e.g.

namespace MyCompany.MyLibrary 

   module Values1 = 
      let x = 1

namespace MyCompany.MyLibrary 

   // Implicit open of MyCompany.MyLibrary bringing Values1 into scope

   module Values2 = 
      let x = Values1.x

However this only opens the namespace as constituted by preceding namespace declaration groups.

Does not interact with FSI, given FSI's limited understanding of namespaces. Specifically, I expect that the 'second #load' from your example opens e.g. FSI_000N+1's version of the namespace, whereas the prior code was in FSI_000N. Which maybe-explains why the explicit open interaction fixes it; you bring the existing, unshadowed FSI_000N stuff up to the top level before trying to (implicitly) reference it later.



回答2:

I'm relatively new at this too, but this is what works for me when I'm testing in an fsx file:

#if INTERACTIVE
#r @"C:\Program Files\FSharpPowerPack-2.0.0.0\bin\FParsec.dll"
#r @"C:\Program Files\FSharpPowerPack-2.0.0.0\bin\FParsecCS.dll"
#endif

open FParsec.Primitives  
open FParsec.CharParsers

followed by my code that uses these libraries.