Можно ли разделить модуль F # на файлы?
В соответствии с книгой, которая у меня есть, но книга, вероятно, устарела (Основы F #)
Можно ли разделить модуль F # на файлы?
В соответствии с книгой, которая у меня есть, но книга, вероятно, устарела (Основы F #)
По-видимому, нет:
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
- полное имя вашей сборки, включая часть пространства имен.
Расширения типа являются классными, и, надеюсь, они позволят быть перекрестным файлом, хотя и остаются неотъемлемыми. Если вы делаете расширение типа в том же файле, оно компилируется в один класс, а расширение имеет доступ к закрытым членам и так далее. Если вы делаете это в другом файле, это просто "необязательное" расширение, например, методы статического расширения С#. (Хотя спецификации F # говорят по-другому.)
Я был бы удивлен, если это не будет рассмотрено в какой-то момент, если только для поддержки дизайнера. Если внутренние расширения могут быть в любом месте сборки, это было бы неплохо.
Другой вариант, который может быть не таким, каким вы хотите, - это создать тип и модуль, вызвать модуль с тем же именем, а затем добавить к нему флаг ModuleSuffix:
type Foo() =
static member Bar = 1
[<CompilationRepresentationAttribute(CompilationRepresentationFlags.ModuleSuffix)>]
module Foo =
let Baz = 2
printfn "%d %d" Foo.Bar Foo.Baz
Это используется в библиотеках F #, поэтому они могут иметь список типов или что-то еще, а также множество вспомогательных материалов в модуле.
Как говорит Курт, вы можете добавлять методы расширения к типам, и, таким образом,
// 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" (но получаете возможность перегружать); таким образом, это может быть или не быть приемлемой альтернативой для вас в зависимости от того, что вы делаете.
Я иногда разбиваю тип на несколько мест, например:
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 находятся в разных файлах.
В одном из моих проектов целью было файловые операции 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 =
...