Reputation: 1742
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:
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
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