Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Splitting F# module in multiple parts in the same file

Tags:

f#

I have the following working F# source file

namespace Namspa1

[<AutoOpen>]
module M1 = 
    let [<Literal>] constant1 = "Hello, "

type Greeter(name) = 
    member x.Greet() = sprintf "%s%s" constant1 name

module M2 =
    let greet name = Greeter(name).Greet()

This works but what I want is define the function greet in the same module M1 where constant1 is defined. In other words, using only one file I want to obtain

Namspa1.M1.constant1
Namspa1.Greeter
Namspa1.M1.greet //not Namspa1.M2.greet
  • constant1 is a value, so must be inside a module
  • Greater uses constant1 so must be later in the file; I want it to be in the namespace, not inside a module, so it is non indented
  • Finally greet is a function, so it must be in a module and I want to use the same module M1. It also uses Greeter, so it must be located after the type definition.

If I try to change the greet function definition as

module M1 =
    let greet name = Greeter(name).Greet()

I get an Duplicate definition error for M1.

How do I do it?

EDIT

It's been suggested that using attribute CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) fixes the Duplicate definition error, which is true so thanks for this.

However my request is about extending M1. I want to be able to use M1.greet as if the function was defined in M1. For example, if I try to use the definitions externally (e.g. another source file), I can use M2.greet, so I want to use M1.greet, which is not possible with the attribute

like image 292
Franco Tiveron Avatar asked Sep 11 '25 22:09

Franco Tiveron


2 Answers

This is possible by setting the CompilationRepresentation attribute to CompilationRepresentationFlags.ModuleSuffix. Extending a module like this is not an intended use case, so the second module definition needs to be moved to a separate source file.

First file:

namespace Namspa1

[<AutoOpen>]
module M1 = 
    let [<Literal>] constant1 = "Hello, "

type Greeter(name) = 
    member x.Greet() = sprintf "%s%s" constant1 name

Second file:

namespace Namspa1

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module M1 =
    let greet name = Greeter(name).Greet()

Technically, it generates a module named M1Module, which will affect access from other .NET languages.

like image 159
cadull Avatar answered Sep 15 '25 00:09

cadull


What you want to achieve is possible by using a recursive namespace. Notice the rec keyword when declaring the namespace.

namespace rec Namspa1

[<AutoOpen>]
module M1 =
    let [<Literal>] constant1 = "Hello, "
    let greet name = Greeter(name).Greet()

type Greeter(name) =
    member x.Greet() = sprintf "%s%s" constant1 name