Разделить модули F # на несколько файлов

Можно ли разделить модуль F # на файлы?

В соответствии с книгой, которая у меня есть, но книга, вероятно, устарела (Основы F #)

Ответ 1

По-видимому, нет:

C:\temp\Tim>type 1.fs 2.fs

1.fs


#light
module Module

let sayHello1 = printfn "Hello, "

2.fs


#light
module Module

let sayHello2 = printfn "world!"

C:\temp\Tim>fsc 1.fs 2.fs
Microsoft F# Compiler, (c) Microsoft Corporation, All Rights Reserved
F# Version 1.9.6.2, compiling for .NET Framework Version v2.0.50727

2.fs(2,1): error FS0191: An implementation of the file or module Module has already been given.

Обновление: ошибка изменилась в F # 4.0, теперь она:

error FS0248: два модуля с именем "Module" встречаются в двух частях этой сборки

где Module - полное имя вашей сборки, включая часть пространства имен.

Ответ 2

Расширения типа являются классными, и, надеюсь, они позволят быть перекрестным файлом, хотя и остаются неотъемлемыми. Если вы делаете расширение типа в том же файле, оно компилируется в один класс, а расширение имеет доступ к закрытым членам и так далее. Если вы делаете это в другом файле, это просто "необязательное" расширение, например, методы статического расширения С#. (Хотя спецификации F # говорят по-другому.)

Я был бы удивлен, если это не будет рассмотрено в какой-то момент, если только для поддержки дизайнера. Если внутренние расширения могут быть в любом месте сборки, это было бы неплохо.

Другой вариант, который может быть не таким, каким вы хотите, - это создать тип и модуль, вызвать модуль с тем же именем, а затем добавить к нему флаг ModuleSuffix:

type Foo() = 
    static member Bar = 1

[<CompilationRepresentationAttribute(CompilationRepresentationFlags.ModuleSuffix)>]
module Foo =
    let Baz = 2

printfn "%d %d" Foo.Bar Foo.Baz

Это используется в библиотеках F #, поэтому они могут иметь список типов или что-то еще, а также множество вспомогательных материалов в модуле.

Ответ 3

Как говорит Курт, вы можете добавлять методы расширения к типам, и, таким образом,

// File1.fs
namespace Foo

type Mine() =
    static member f1 () = ()

затем

// File2.fs
type Foo.Mine with
    static member f2() = ()

Foo.Mine.    // both f1 and f2 here

Так как это класс, а не модуль, вы теряете способность делать "открытое Mine" (но получаете возможность перегружать); таким образом, это может быть или не быть приемлемой альтернативой для вас в зависимости от того, что вы делаете.

Ответ 4

Я иногда разбиваю тип на несколько мест, например:

module Foo

type Partial = Bar | BarInt of int

module Bar

type Foo.Partial with
    member x.Extend = 5


let b = Foo.Bar.Extend

где модули Foo и Bar находятся в разных файлах.

Ответ 5

В одном из моих проектов целью было файловые операции Cp и Rm для разделения модулей, но не требуют от пользователя открытия двух пространств имен для обеих задач.

open Xake.FileTasks
...
do! Cp "*/*.exe" "deploy/*.exe"
do! Rm "*/*.exe"

Вот мои модули:

namespace Xake.FileTasks

[<AutoOpen>]
module RmImpl =
    let Rm filemask target =
...

а другой:

namespace Xake.FileTasks

[<AutoOpen>]
module CpImpl =
    let Cp filemask target =
...