Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a concise inline expression for unwrapping a single case discriminated union?

Tags:

f#

I'm working through the basics of F# and am still at the point where I'm not sure what's possible or what's available. I'm thinking there ought to be a better way to do this:

I'm looking at the common demonstration scenario of using this code for added type protection

type CustomerId = CustomerId of int
type OrderId = OrderId of int

At some point I have persistence code that needs to unwrap the integer:

dbcmd.Execute(CID = ???, OID = ???)

Option A: bulky but works

dbcmd.Execute(CID = match CustomerId with (CustomerId cid) -> cid, OID = match OrderId with (OrderId oid) -> oid)

Option B is derived from the answer in Concise pattern match on single case discriminated union in F#

This requires 2 lines and if there are 4 or 5 things to unwrap I start to really dislike the 'distance' between the left and right side of the let statement -- I'm likely to eventually type something out of order

let (CustomerId cid, OrderId oid) = (CustomerId, OrderId)
dbcmd.Execute(CID = cid, OrderId = oid)

Option C: This is probably what I'll prefer if there's nothing better. It is clear but consumes more vertical space than I would have hoped

let (CustomerId cid) = CustomerId
let (OrderId oid) = OrderId
dbcmd.Execute(CID = cid, OrderId = oid)

Option D: This is sort of what I'm hoping exists. This doesn't actually work since this is the syntax for wrapping, not unwrapping, but you get the idea

dbcmd.Execute(CID = (CustomerId id), OID = (OrderId id))

Does concise syntax that resembles Option D exist?

like image 473
Clyde Avatar asked Mar 16 '16 21:03

Clyde


2 Answers

I usually use one of these options, choosing between them pretty much intuitively. Mostly I prefer option 1, but it doesn't work if I need to make the constructor private.

Option 1: specify patterns right in parameter declarations

You can do this, because function parameters don't have to be just plain identifiers, they can be patterns too.

let f (CustomerId cid) (OrderId oid) =
  let cmd = ...
  cmd.Execute( cid, oid )

Option 2: create special accessor functions

type CustomerId = CustomerId of int
  with static member toInt (CustomerId id) = id

cmd.Execute( CustomerId.toInt cid, ... )

Option 2b: same, but with instance member

type CustomerId = CustomerId of int 
  with member this.asInt = match this with (CustomerId id) -> id

cmd.Execute( cid.asInt, ... )
like image 131
Fyodor Soikin Avatar answered Nov 02 '22 11:11

Fyodor Soikin


You could also use a lambda without modifying or extending the type definition:

cid |> fun (CustomerId i) -> i
like image 43
TheQuickBrownFox Avatar answered Nov 02 '22 11:11

TheQuickBrownFox