Reputation: 6422
After reading F# Component Design Guidelines, I don't see any comment whether I should declare module & its type to have the same name.
Usually my projects don't have any cyclic dependency, so I don't need to make a new module (e.g. InfrastructureTypes
or DomainTypes
) to put every types in one place.
For example, if I have a record type System
and a bunch of its functions, should I put everything inside one module file? This is my attempt:
// System.fs
module System
type rec System =
{ name : string
children : System list }
let init () = { name = ""; children = [] }
let addChild system child =
{ system with system.children = child :: system.children }
let removeChild system child =
let rec removeChild children acc child =
match children with
| c :: children ->
if c <> child then removeChild children (c :: acc) child
else removeChild children acc child
| [] -> List.rev acc
let children = removeChild system.Children [] child
{ system with system.children = children }
Upvotes: 9
Views: 1325
Reputation: 10624
It is a common approach in the F# core library itself to have a module and type with the same name at the same level (as opposed to having the type inside the module like in your example). For example, there is a List<'a>
type and a List
module that contains the function that contains functions for working with it. Similarly with Option
, Set
, Result
etc.
If you already had a type with a given name, there is an attribute you can add to to allow creating a module with the same name without a compiler error: [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
However, since F# 4.1, this is implicit. So defining a module with the same name as the type just works, and the word Module
will be at the end of the module name when the code is compiled.
type System =
{ name : string
children : System list }
module System =
let init () = { name = ""; children = [] }
I think all this implies that this can be a valid pattern to use in F#. Particularly when you have a good abstraction. You might want to look up "abstract data types" as I think they apply here.
Looking at the examples I've given above, this is normally more of a library-level pattern, but I've heard the argument that it can be applied to good effect in application code too. I can imagine that it could help you to think about and enforce abstraction boundaries and keep code organised.
Upvotes: 10