Enums in F# for strings - enums

I'm working on a legacy database with no systems to which I can modify. Because of type providers I decided to use F#. It's not so simple now that I'm into it. How do I structure a field in my DB that has only the following strings? It's sort of like an ENUM. I've tried the following but haven't quite gotten it.
Attempt 1
type SuspendedDriver = "SI"
type ActiveDriver = "IMPRESO"
type Applicant = "NO"
type TemporaryDriver = "TEMP"
type Uncertain = "NULL"
type DriverStatus =
| SuspendedDriver
| ActiveDriver
| Applicant
| TemporaryDriver
| Uncertain
type Driver = {
status : DriverStatus
}
Error
Error FS0618: Invalid literal in type (FS0618) (ScriptTest)
Attempt 2
type DriverStatus =
| SuspendedDriver = "SI"
| ActiveDriver = "IMPRESO"
| Applicant = "NO"
| TemporaryDriver = "TEMP"
| Uncertain = "NULL"
Error
Error FS0951: Literal enumerations must have type int, uint, int16,
uint16, int64, uint64, byte, sbyte or char (FS0951) (ScriptTest)

As mentioned in the comment, you cannot define an enum that has values erased to strings. Enums can only be integers (of various types).
Using enums in an ugly way
You could use the actual names of the enum fields and then parse them using Enum.Parse, but that's probably not good idea, because the enum cases would be fairly obscure codes. But just for the record, the following works:
type DriverStatus =
| SI = 0
System.Enum.Parse(typeof<DriverStatus>, "SI") :?> DriverStatus
Cleaner parsing into discriminated union
In practice, I would do what Sam recommends and write a function to parse the string from the database. This has the advantage that you'll need to figure out what to do with errors in the database (the value can always be something wrong):
type DriverStatus =
| SuspendedDriver
| ActiveDriver
let parseDriverStatus = function
| "SI" -> SuspendedDriver
| "IMPRESO" -> ActiveDriver
| code -> failwith (sprintf "Wrong driver code! %s" code)
Using the cool Enum SQL provider
Finally, if you happen to be using MS SQL database, then the SQL Command Provider project has a neat feature that lets you automatically import enum-like types from SQL database itself, which might actually be exactly what you need in this case.

Related

Cast integer to enum type with Flags attribute

I have an enum type with the flags attribute:
[<Flags>]
type StatusWord =
| DATAPROCESS = 0b0000000001
| ERRORPRESENT = 0b0000000010
| CHECKSUMERROR = 0b0000000100
| PACKETSIZEERROR = 0b0000001000
| TIMEOUT = 0b0000010000
| DONOTRETRY = 0b1000000000
and during some data initialization, I have a uint16 value I want to convert to the enum's type, StatusWord, so I can compare it's properties:
let value: uint16 = 0b1000001001us
let flags: StatusWord = StatusWord value
but, as you might be able to guess, this code doesn't compile; the conversion isn't available. Likewise, I also can't do explicit casts, eg. value :> StatusWord or value :?> StatusWord. This is a simple task in C#, so I'm having trouble figuring out why I can't do it in F#.
So, two things you have to worry about. One (which I think you already realize) is that your underlying enum type is int32, and your value is uint16, so a conversion will need to happen somewhere. Two, you have to construct the enum type.
StatusWord looks like a constructor (similar to a union case member), but it's not. So here are two ways to do it with your uint16 value, and a third way to do it which is much better for readability, if you can do it that way.
let value = 0b1000001001us
// use F# enum operator
let flags1 = enum<StatusWord> (int value)
// use static Enum class
let flags2 = Enum.Parse(typeof<StatusWord>, string value) :?> StatusWord
// do bitwise stuff, of course now the compiler knows what you're doing
let flags3 = StatusWord.DATAPROCESS ||| StatusWord.PACKETSIZEERROR ||| StatusWord.DONOTRETRY
Because there are multiple ways, I had to refresh my memory, which I did at
https://fsharpforfunandprofit.com/posts/enum-types/
which is highly recommended reading (that article and the rest of that blog - it's how many people learn F#).
To convert a numerical value to an enum in F#, you can use the built-in LanguagePrimitives.EnumOfValue function.
let flags: StatusWord = LanguagePrimitives.EnumOfValue value
In your example, this does not actually work, because the type of value is uint16, but the underlying type of the enum is int (because the values do not have the us suffix). To get that to work, you'll need to either convert uint32 to int, or change the definition of your enum. The following works perfectly:
[<Flags>]
type StatusWord =
| DATAPROCESS = 0b0000000001us
| ERRORPRESENT = 0b0000000010us
| CHECKSUMERROR = 0b0000000100us
| PACKETSIZEERROR = 0b0000001000us
| TIMEOUT = 0b0000010000us
| DONOTRETRY = 0b1000000000us
let value: uint16 = 0b1000001001us
let flags: StatusWord = LanguagePrimitives.EnumOfValue value
EDIT: Jim's answer mentions the enum function. For some reason (I'm not sure why!) this only works with int32 arguments, so using EnumOfValue is probably better if you want to keep the base type of your enum as uint16. If you wanted to keep that as int32, then enum is much nicer option!

Inline records in polymorphic variants?

The ocaml manual chapter 8 "language extensions" describes "inline records" (8.17):
The arguments of sum-type constructors can now be defined using the same syntax as records. Mutable and polymorphic fields are allowed. GADT syntax is supported. Attributes can be specified on individual fields. [...]
I am looking for that with polymorphic variants:
# type a = B of {x:int; mutable y:int} ;;
type a = B of { x : int; mutable y : int; }
# type b = `A of {u:int; mutable v:int} ;;
Line 1, characters 9-10:
Error: Syntax error
But that does not work, so right now I use an explicit auxiliary record type instead...
As I understand it now, this both takes more memory and is somewhat slower.
Can I get this cool feature with polymorphic variants, too?
In the cases of ordinary constructors, the compiler can use the type definition to distinguish between:
type t = A of int * int | B
let f = function
| A (_,y) -> y
| B -> 0
and
type 'a t = A of 'a | B
let f = function
| A (_,y) -> y
| B -> 0
Thus, it is possible to optimize the first
A (_,y) -> y
into "access the second field of the block` while still compiling the second case
A (_,y) -> y
to "access the tuple in the first field of the block, and then access the second field of the block".
For polymorphic variants, it is not possible to rely on the non-existing type definition to distinguish between those two solutions. Consequently, their memory representation must be uniform. This means that polymorphic variants always take one argument, and it is not really useful to label each argument of the constructor when there is only one argument.
This is why inline records cannot be combined with polymorphic variants.

What is the difference between literals and non-literals, other than the fact that non-literals go into the heap?

I am confused by the difference between literals and non-literals (the ones that go on the heap, I do not know what they are called). For example, taking the String type as an example:
We’ve already seen string literals, where a string value is hardcoded
into our program. String literals are convenient, but they aren’t
always suitable for every situation in which you want to use text. One
reason is that they’re immutable. ...
I do not understand the above, as we have already seen an example like this:
let mut a = "a"; // this is String literal here, so sitting on the stack
a = "b";
println!("a is being changed to...{}", a); // this is the same String literal sitting on the stack?
Clearly literals can be mutable in Rust. What is the difference between the two, other than the fact that literals go into the stack, while non-literals go into the heap?
I am trying to understand why I shouldn't just use mutable literals in my code, considering that the stack is faster than the heap.
// a is mutable literal
let mut a = "a";
a = "b";
// b is mutable 'non-literal'
let mut b = String::from("a");
b = String::from("b");
Clearly literals can be mutable in Rust
First, you need to understand what a literal is. Literals are never mutable because they are literally written in the source code and compiled into the final binary. Your program does not change your source code!
An example showing that you cannot modify a literal:
fn main() {
1 += 2;
}
error[E0067]: invalid left-hand side expression
--> src/main.rs:2:5
|
2 | 1 += 2;
| ^ invalid expression for left-hand side
On the other hand, a literal can be copied into a variable and then the variable can be changed, but we still are not mutating the literal 1:
fn main() {
let mut a = 1;
a += 2;
}
To be honest, I don't know what I would call a "non-literal". A literal is a specific type of expression, but there are other types of things in a program besides expressions. It's kind of like saying "cats" and "non-cats" — does that second group include dogs, mushrooms, sand, and/or emotions?
the fact that literals go into the stack, while non-literals go into the heap
Those two qualities aren't really directly related. It's pretty easy to have non-literals on the stack:
fn main() {
let a = 1;
let b = 2;
let c = a + b;
}
All three variables are on the stack, but there is no literal 3 anywhere in the source code.
Right now, Rust doesn't allow for a literal value to have a heap-allocation, but that's a language-specific thing that might change over time. Other languages probably allow it.
In fact, you have to go out of your way in Rust to put something on the heap. Types like Box, Vec, and String all call functions to allocate space on the heap. The only way for your code to use heap memory is if you use these types, other types that use them, or types which allocate heap memory in some other way.
What is the reason we cannot use String literal data-type
There is no String literal — none. The source code "foo" creates a literal of type &'static str. These are drastically different types. Specifically, the Rust language can work in environments where there is no heap; no literal could assume that it's possible to allocate memory.
have to specifically use String::from()
String::from converts from &str to a String; they are two different types and a conversion must be performed.
Clearly, as per the example, in my code, both can be mutable
No, they cannot. It is impossible to start with let mut foo = "a" and modify that "a" to become anything else. You can change what that foo points to:
let mut foo = "a";
foo
+-----------+
|
|
+---v---+
| |
| "a" |
| |
+-------+
foo = "b";
foo
+----------+
|
|
+-------+ +---v---+
| | | |
| "a" | | "b" |
| | | |
+-------+ +-------+
Neither "a" nor "b" ever change, but what foo points to does.
This isn't specific to Rust. Java and C# strings are also immutable, for example, but you can reassign a variable to point to a different immutable string.
See also:
What does the word "literal" mean?
What's the difference in `mut` before a variable name and after the `:`?
What are the differences between Rust's `String` and `str`?

How is the | symbol read in Elm?

Consider the following example code:
-- Update the fields of a record. (It must have the fields already.)
{ person |
name = "George" }
-- Update multiple fields at once, using the current values.
{ particle |
position = particle.position + particle.velocity,
velocity = particle.velocity + particle.acceleration }
Source: Learn Elm in X Minutes
How is one supposed to read | in this example, and in Elm generally?
I'm familiar with it in set-builder notation as "where" / "such that", and in list comprehensions in Haskell it has a very similar purpose, e.g.
[ x*2 | x <- [1..10] ]
is logically equivalent to
source: Learn You A Haskell
(Obviously I'm also familiar with its use as the unary "or" operator in C-like languages)
What about something like type Msg = Increment | Decrement ?
Source: https://guide.elm-lang.org
Or, in this example when discussing Union Types:
type Boolean
= T
| F
| Not Boolean
| And Boolean Boolean
| Or Boolean Boolean
In types I read it as 'or'. In the counter example:
type Msg = Increment | Decrement
I would read it as "a Msg is Increment or Decrement". In a slightly more complex but still common example of the Result type:
type Result error value
= Ok value
| Err error
I would read "a Result is either Ok with a value or Err with an error".
In the example you give of the record update syntax, I would read it as 'with' rather than 'where'. For example:
{ person | name = "George" }
is "the person value with its name field set to "George"" (rather than "where the name = 'George'" which seems to imply that you're filtering based on what values are in person). This one is I think more ambiguous than the type case though.

Map from discriminated union to enum

Currently, I'm trying to teach myself some F# by making an application that consists of a C# GUI layer and an F# business layer. In the GUI layer, the user will at some point have to make a choice by selecting a value that is part of a simple enum, e.g. selecting either of the following:
enum {One, Two, Three}
I have written a function to translate the enum value to an F# discriminated union
type MyValues =
| One
| Two
| Three
Now I have to translate back, and am already tired of the boilerplate code. Is there a generic way to translate my discriminated union to the corresponding enum, and vice versa?
Cheers,
You can also define the enum in F# and avoid doing conversions altogether:
type MyValues =
| One = 0
| Two = 1
| Three = 2
The = <num> bit tells the F# compiler that it should compile the type as a union. When using the type from C#, this will appear as a completely normal enum. The only danger is that someone from C# can call your code with (MyValues)4, which will compile, but it will cause incomplete pattern match exception if you are using match in F#.
Here are generic DU/enum converters.
open Microsoft.FSharp.Reflection
type Union<'U>() =
static member val Cases =
FSharpType.GetUnionCases(typeof<'U>)
|> Array.sortBy (fun case -> case.Tag)
|> Array.map (fun case -> FSharpValue.MakeUnion(case, [||]) :?> 'U)
let ofEnum e =
let i = LanguagePrimitives.EnumToValue e
Union.Cases.[i - 1]
let toEnum u =
let i = Union.Cases |> Array.findIndex ((=) u)
LanguagePrimitives.EnumOfValue (i + 1)
let du : MyValues = ofEnum ConsoleColor.DarkGreen
let enum : ConsoleColor = toEnum Three
It maps the DU tag to the enum underlying value.

Resources