2

If I have a module of name MyModule defined in an .fsx script and referenced from within another .fsx script; is it possible to determine at runtime if the module defines a variable foo?

I am trying to implement something like this:

let fooWithDefault = 
    let cfgType:Type = typedefof<MyModule>
    let propOpt = 
        cfgType.GetProperties()
        |> Seq.tryFind( fun p -> p.Name = "foo")

    match propOpt with
    | Some foo -> foo.GetValue(null).ToString()
    | None -> "My Default Value for f"

The above attempt fails with the error:

The type 'MyModule' is not defined

5
  • I think there will be two problems with this, MyModule should be a class, not a module (which is probably not a type), and in an fsx file FSI will prepend an artifical namespace if you use #load. If you use a class in a dll or an .fs file, it might work. Commented Sep 5, 2016 at 9:33
  • I'm pretty sure a module would compile to a type: The docs here: learn.microsoft.com/en-us/dotnet/articles/fsharp/… suggest "It is implemented as a common language runtime (CLR) class that has only static members." Don't know about how FSI deals with it though. Any way to know or determine which artificial namespace it prepends? Commented Sep 5, 2016 at 9:41
  • 2
    It certainly does compile to a type but actually getting a module's type requires some shenanigans. This answer shows the usual way of doing that if you really need to: stackoverflow.com/a/14706890/5438433 Commented Sep 5, 2016 at 9:54
  • 1
    Thanks folks - Having given it a bit more thought, I can work around it with Assembly.GetExecutingAssembly().GetTypes() |> Seq.find (fun t -> t.Name = "MyModule") - risk of a name collision is negligible and there are no perf worries in my use case Commented Sep 5, 2016 at 9:57
  • you're correct that a module is like a static class for CLR purposes. If you run/load the code that creates the type you'll see somethinge like: namespace FSI_0005 prepended. Commented Sep 5, 2016 at 9:57

1 Answer 1

1

One possible (somewhat kludgy) approach is to enumerate all the types in the executing assembly:

let fooWithDefault = 
    let cfgType = 
        Assembly.GetExecutingAssembly().GetTypes()
        |> Seq.find(fun t -> t.Name = "MyModule")

    let propOpt = 
        cfgType.GetProperties()
        |> Seq.tryFind( fun p -> p.Name = "foo")

    match propOpt with
    | Some foo -> foo.GetValue(null).ToString()
    | None -> "My Default Value for f"

If using this approach we'd need to be aware that there is a risk of a name collision if we have 2 entities named 'MyModule'. Also, enumerating all the types in the executing assembly is surely sub-optimal.

With that said it seems to work ok for the limited test-cases I have attempted

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.