6

I have a following F# enum

type DataType = AUCTION|TRANSACTION

I would like to use DataType as a parameter to a function, so that the values of the parameter is restricted to string AUCTION and TRANSACTION,

is that possible to convert the items in this enum to string, or is there a better way to contraint the value of a parameter to a set of string?

tesla1060
  • 2,621
  • 6
  • 31
  • 43
  • Have you looked for other SO questions or outside SO with the keywords `F# constrain parameter DU`? If not please do so. If any of them look close but do not work please add them to the question and explain why they do not work. That is what I am doing right now. This smells of a duplicate question. – Guy Coder Feb 28 '16 at 14:04
  • 3
    Note that `DataType` is not an enum... it is a discriminated union. And thus you should use pattern matching. – Ringil Feb 28 '16 at 14:13
  • Does this work for you? [F# type constraints on enums](http://stackoverflow.com/questions/15115050/f-type-constraints-on-enums) Also see: [F# – Enums vs Discriminated Unions](http://theburningmonk.com/2011/10/fsharp-enums-vs-discriminated-unions/) – Guy Coder Feb 28 '16 at 14:22
  • Or this: [Working around incomplete pattern matching on enums](http://stackoverflow.com/questions/6075322/working-around-incomplete-pattern-matching-on-enums/6128810#6128810) – Guy Coder Feb 28 '16 at 14:35
  • Also see: [How do I write an enumeration in F# without explicitly assigning number literals?](http://stackoverflow.com/questions/7083598/how-do-i-write-an-enumeration-in-f-without-explicitly-assigning-number-literals) – Guy Coder Feb 28 '16 at 14:39
  • You can use the `string` function to convert values to string values. – Mark Seemann Feb 28 '16 at 15:28

1 Answers1

14

First of all, as several people have mentioned in the comments, the type you have defined is not an Enumeration, it's a Discriminated Union.

Enumerations are effectively just a label given to an integer and, in F#, are declared using this syntax:

type DataType = 
    |Auction = 1
    |Transaction = 2

Using this syntax, you've got a relationship between the value and the associated integer, you can use the integer to get the value of an Enumeration, e.g.

let transaction = enum<DataType>(2) // Transaction

Note that there is nothing stopping you from saying enum<DataType>(3537), even though we haven't defined that case.

For more details on Enumerations, see: https://msdn.microsoft.com/en-us/library/dd233216.aspx


Discriminated Unions are much more flexible than Enumerations. Let's take a look at yours:

type DataType = 
    |Auction
    |Transaction

This version is now actually a Standard .NET class with two case identifiers: Auction and Transaction. You can think of Auction and Transaction as two type constructors for DataType.

Discriminated Unions are not restricted to just simple cases, you could store additional data, e.g.

type DataType = 
    /// An auction with a list of bids
    |Auction of Bid list  
    /// A transaction with some price in GBP
    |Transaction of decimal<GBP>

With Disciminated Unions, there is no implicit relationships with integers, if we want to construct a particular case, we have to use the appropriate case identifier.

e.g. let auction = Auction (bidlist)

For more details on Discriminated Unions, see: https://msdn.microsoft.com/en-us/library/dd233226.aspx


In both cases, converting to a specific string for each case can be achieved using pattern matching.

For the Discriminated Union:

let datatypeToString datatype =
    match datatype with
    |Auction -> "AUCTION"
    |Transaction -> "TRANSACTION"

And for the Enumeration:

let datatypeToString datatype =
    match datatype with
    |DataType.Auction -> "AUCTION"
    |DataType.Transaction -> "TRANSACTION"

Notice that when you use Enumerations, F# will give you a compiler warning telling you that pattern matches cases aren't complete. This is because Enumerations are just ints and there are many ints besides just 1 and 2, this means that the match cases aren't exhaustive.

I therefore recommend you stick to Discriminated Unions and keep your exhaustive pattern matching.


P.S. If you want to go in the other direction, from string to DataType, I recommend using a tryCreateDataType function which would look something like this:

let tryCreateDataType str =
    match str with
    |"AUCTION" -> Some Auction
    |"TRANSACTION" -> Some Transaction
    |_ -> None

This returns an Option, so it will allow you to safely match against the function being successful or it failing due to an invalid string.

TheInnerLight
  • 12,034
  • 1
  • 29
  • 52