user655321
user655321

Reputation: 1742

How to use F#-defined types within a C# project

I've implemented F# For Fun And Profit's Designing for Correctness example in an F# DLL, which starts like this:

module Foo =
    type Item = { Price : double; Name : string }

    type EmptyState = NoItems
    type ActiveState = { UnpaidItems : Item list }
    type CompletedState = { PaidItems : Item list; PurchaseDate : System.DateTime }

    type Cart =
        | EmptyCart of EmptyState
        | ActiveCart of ActiveState
        | CompletedCart of CompletedState

    ....

    let addItemToCart cart item =
        match cart with
        | EmptyCart state -> state.Add item
        | ActiveCart state -> state.Add item
        | CompletedCart state ->
            printfn "ERROR: The cart is paid for"
            cart
    ...

    type Cart with
        static member NewCart = Cart.EmptyCart NoItems
        member this.Add = addItemToCart this
        ....

I'm now attempting to create a simple C# console app to use it. My expectation is that I can do something like:

var myCart = Foo.Cart.NewCart(); // implicitly creates an EmptyCart

var item = new Foo.Item(123.45, "something");
myCart.Add(item); // adds a new item, changing to ActiveCart
// do other stuff, like adding more items, deleting them, then completing the cart

However, Add isn't a method:

enter image description here

Intellisense tells me that Add is Microsoft.FSharp.Core.FSharpFunc<Foo.Item,Foo.Cart> Cart.Add.

I'm not sure if I've done something wrong, am missing an annotation, or if I'm misunderstanding the interoperability of C# and F#. Is there, in fact, a way to use this F# code seamlessly in C#?

Upvotes: 2

Views: 114

Answers (1)

TheInnerLight
TheInnerLight

Reputation: 12184

Your member Add is a property, hence from C# you're going to see something which returns FSharpFunc which won't be much help to you. What you need to do is make Add a method by making it take an argument.

type Cart with
    static member NewCart = Cart.EmptyCart NoItems
    member this.Add item = addItemToCart this item

That's only the first part of the answer, you also need to deal with the question of mutability/immutability.

Because you are using immutable data, your addItemToCart function needs to look something like this:

let addItemToCart cart item =
    match cart with
    | EmptyCart state -> ActiveCart {UnpaidItems = [item]}
    | ActiveCart state -> ActiveCart {UnpaidItems = item :: state.UnpaidItems}
    | CompletedCart state ->
        printfn "ERROR: The cart is paid for"
        cart

(I've simplified this a bit from the example on the page you provided so you can easily see what's actually going on.)

Now, the function addItemToCart, when given an empty cart, returns an active cart with one item and when given an active cart with n items, it returns an active carts with n+1 items such that the extra item is prepended to the list. Note that addItemToCart doesn't actually update the state of existing cart, every time you give it a cart as an argument, it gives you a new cart back.

You would then need to use your Cart immutably from C# too.

var myCart = Foo.Cart.NewCart(); // creates an EmptyCart
var item = new Foo.Item(123.45, "something");
var myUpdatedCart = myCart.Add(item); // adds a new item, returning a new ActiveCart

Upvotes: 3

Related Questions