Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to expose the type as friendly as possible to both C# and F#?

Tags:

f#

For example, if I have written a module in F#

module Lib

type A =
    member this.x1 x = ...

let helpa x = ...
let helpb x = ...

type B =
    member this.y1 x = ...

let helpc x = ...

typeA with
    member this.x2 x = ...
typeB with
    member this.y2 x = ...

It works well in F# by open Lib, However, if I want to consume it in C# (where I am only interested in types and member functions in Lib), each time I create a type I have to new Lib.A(...). It becomes rather annoying there is no way to omit the module names. Calling a static method like Lib.A.C() is even more of a hassle.

Then I try to replace module with namespace, each time I introduce some helper functions I have to create a new module with a new name. Occasionally I can manage to rearrange all helper functions into 1 module, but that would result in less readable code somehow.

What would be a better structure for this?

Wish I had: Using * = Lib.* for C#.

like image 612
colinfang Avatar asked Jan 22 '13 16:01

colinfang


2 Answers

F# offers more flexibility than C# here, so I would expose it to C# in the standard way, i.e., enclose types in a namespace. Something like this, I think, offers the best of both worlds:

namespace Lib

type A =
    member this.x1 x = ()

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module A =
  let helpa x = ()
  let helpb x = ()

type B =
    member this.y1 x = ()

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module B =
  let helpb x = ()

type A with
    member this.x2 x = ()
type B with
    member this.y2 x = ()

The F# collections follow a similar design. You can use the [<AutoOpen>] and [<RequireQualifiedAccess>] attributes to further control how the modules are used from F#.

like image 80
Daniel Avatar answered Oct 13 '22 18:10

Daniel


I think you already mentioned the best option in your answer - define the file with namespace declaration at the top (this way, you can write just using Lib in C#) and then place all helper functions in modules.

Helper functions that are clearly associated with some type (e.g. with A) could be placed into a module named A (similarly to F# functions in the List module that are associated with the List<'T> type).

This is a bit more work, because you need to mark the module with a special attribute (to avoid name clash), but it will be easy to use from both F# and C# (and I think having nice use is more important than saving a few keystrokes when building the library):

namespace Lib

// Declaration of the 'A' type and helper functions in 'A' module 
type A() =
  member this.x1 x = 10

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module A = 
  let helpa (x:A) = x.x1
  let helpb (x:A) = x.x1

// Declaration of the 'B' type and helper functions in 'B' module 
type B() =
  member this.y1 x = 10

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module B = 
  let helpc (x:B) = x.y1

// Member augmentations for easy use from C#
type A with
    member this.x2 x = A.helpa this
type B with
    member this.y2 x = B.helpc this
like image 33
Tomas Petricek Avatar answered Oct 13 '22 20:10

Tomas Petricek