With the following F# definitions, how can the Model be initialized?
open System
open System.Windows
type ContactDetail = { Id: Guid; Name: string; Content: string; Text: string }
type Internet = { Id: Guid; Name: string; Content: string; Text: string }
type PhoneNumber = { Id: Guid; Name: string; Content: string; Text: string }
type Address = { Id: Guid; Name: string; Content: string; Text: string }
module Testing =
type Details =
| ContactDetail of ContactDetail
| Internet of Internet
| PhoneNumber of PhoneNumber
| Address of Address
let contactDetail : ContactDetail = {Id=Guid.NewGuid(); Name="Contact Detail"; Content="Content for Contact Detail"; Text="here is the contact detail text" }
let internet : Internet = {Id=Guid.NewGuid(); Name="Internet"; Content="Content for Internet"; Text="here is the internet text" }
let phoneNumber : PhoneNumber = {Id=Guid.NewGuid();Name="Phone Number"; Content="Content for phone number"; Text="here is the phone number text" }
let address : Address = {Id=Guid.NewGuid(); Name="Address"; Content="Content for Address"; Text="here is the Address text" }
let details = [ContactDetail contactDetail
Internet internet
PhoneNumber phoneNumber
Address address
]
type DetailsWithId = DetailsWithId of Details * Guid
type Model = {
ClickCount: int
Message: string
Details: DetailsWithId list
}
More specifically, I need Details of the Model to be a list (or seq). Is type DetailsWithId already a list because of type Details?
TIA
As explained in the comment, a value of your Details type represents just a single detail - one of the several possible details listed. A value of type Details list is a list of details (which can possibly be empty, or it can include multiple instances of the same detail type).
If this is not what you need, then it'd be good to find a model that better captures the structure of your domain.
If this is what you want, then I would still change two things about your data types:
First, I'd rethink how you're storing IDs. In your model, you have Id field in the individual records, but then, again, in the DetailsWithId type. I suppose the question is how to automatically copy the ID from the record to the ID in the DetailsWithId type. There is no easy way to do this, but it's better to design your types so that you do not need to do this.
Second, your four record types i.e. ContactDetail, Internet, etc. are all the same. Wouldn't it be easier to use just one type?
I think working with your data would be a lot easier (unless there is something about your domain that you did not document in the question), if you used a type definition like this:
type DetailKind =
| ContactDetail
| Internet
| PhoneNumber
| Address
type Detail =
{ Id: Guid
Name: string
Content: string
Text: string
Kind: DetailKind }
type Details = Detail list
Related
I have 3 state machines that follow the similar model:
- same states
- same calls for each state
only the implementation of these calls change.
All the data is carried in a single record that also contains a DU that has the state and there are also a few other common parameters. Each implementation also adds their own fields to that record.
So I would like to figure out if there is a way to make a record I can extend but that contains a number of common fields that the code is expecting to find. Something similar to an interface for a type, but for a record.
For example:
type State =
| a
| b
type StateMachine1StateObject =
{
State: State
SomeData: ...
}
type StateMachine2StateObject =
{
State: State
SomeOtherKindOfData: ...
}
and I would like to have a single state machine loop (which is really a mailbox processor loop) take all the same messages, and be able to rely on some fields that will always be there in that type, but the records themselves may be different as they contain their own fields.
How can this be structured? It looks like I can't use interfaces with records nor combine multiple records into one.
If you have a lot of data in common then can create for example a record, with a field, that has a generic type.
type State<'a> = {
Name: string
Id: int
Extended: 'a
}
type Person = {
FirstName: string
LastName: string
}
type Computer = {
CPU: string
Ghz: float
}
This way, you can create a state that have different Extended data
// State<Person>
let x = {
Name = "foo"
Id = 1
Extended = {
FirstName = "David"
LastName = "Raab"
}
}
// State<Computer>
let y = {
Name = "bar"
Id = 2
Extended = {
CPU = "AMD"
Ghz = 3.6
}
}
You also could convert from one record to another. Your state machine, only accepts a record with those data it needs. For exmaple, you have a state machine that only needs Name and Id. You create.
type StateA = {
Name: string
Id: int
}
Assume you now have a different state with shared keys.
type StateB = {
Name: string
Id: int
Extra: float
}
Instead of type hackery, generics and so on, the easiest way is just to convert from B to A.
let stateBtoA (record:StateB) : StateA = {
Name = record.Name
Id = record.Id
}
So your state machine only accepts StateA, and you have a bunch of function, and other types must be converted to StateA before passing it to the state machine.
If for some reason your state machine, needs access to the orignal data, you should be able to pass it as a generic paramter to a function call. Like
message (x:StateA) (y:'a) : 'a =
// do something with x
y
message (stateBtoA data) data
So your function could work on the first argument, that is of StateA, and for example returns the second argument y that could be StateB. As long your function does nothing with y. It should be generic.
So say I have an employee type
type employee = {
employee_id: int
name: string
department: int
}
type department = {
department_id: int
department_name: string
}
And I want a second type that includes everything in the employee type, but also everything from the department type (in practice the result of an SQL join).
E.g
type employee_extended = {
employee_id: int
name: string
department: int
department_id: int
department_name: string
}
In reality the tables I have have many more columns so just wondering if there was a shorthand to define the extended type :)
Can assume no duplicate of property names, but if it's possible to handle them might be useful in the future.
If you have some data in two types (employee and department) and want to create a new type that contains information about both the employee and the department, the best way is to define a new record type that contains the two other types as fields:
type Employee = {
EmployeeId: int
Name: string
Department: int
}
type Department = {
DepartmentId: int
Department_name: string
}
// New type containing information about
// an employee and also their department
type EmployeeWithDepartment = {
Employee : Employee
Department : Department
}
I am a newbie. Given that each of these represents different classes in C#:
ContactDetails
Internet
PhoneNumbers
Addresses
How is a "list" created in F# to hold the different concrete types?
All the above types will have a common field of:
Name -- string
Outside of the name, all the concrete types will have different fields and contents.
This "list" type is to be consumed by WFP/XAML.
(I'm thinking an interface for the F# list needs to be used, but I do not know how this is implemented--F# is really really new to me. :)
TIA
Consider using Seq instead of List to aid C# clients:
I would recommend using a sequence (i.e. seq) instead of list for C# consumption.
Hence a sequence in F# equates to IEnumerable in C#. Thus, you will be able to use these items from your Windows app.
Here's how I would implement the requirement:
type ContactDetail = { Name : string; Other:string }
type Internet = { Name : string; Other:string }
type PhoneNumber = { Name : string; Other:string }
type Address = { Name : string; Other:string }
type MyType =
| ContactDetails of ContactDetail seq
| Internet of Internet seq
| PhoneNumbers of PhoneNumber seq
| Addresses of Address seq
let contactDetail : ContactDetail = { Name="some name"; Other="???" }
let contactDetails = ContactDetails [contactDetail]
let internet : Internet = { Name="some name"; Other="???" }
let internets = Internet [internet]
let phoneNumber : PhoneNumber = { Name="some name"; Other="???" }
let PhoneNumbers = PhoneNumbers [phoneNumber]
let myTypes : MyType seq = seq [ contactDetails
internets
PhoneNumbers
]
Excuse me, is this what you want?
F#
module FSharpTest.ListTest
open System
type YourType = Object
type ContactDetails = YourType
type Internet = YourType
type PhoneNumbers = YourType
type Addresses = YourType
type WrapperOfCSharpClass =
| CD of ContactDetails
| I of Internet
| PN of PhoneNumbers
| A of Addresses
let list = [
Unchecked.defaultof<WrapperOfCSharpClass>
CD (new ContactDetails())
I (new Internet())
]
C#
using System;
using FSharpTest;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var fsharplist_item = ListTest.list[0];
if (fsharplist_item.IsPN)
{
Console.WriteLine("I am a phone number");
} else if (fsharplist_item.IsA)
{
Console.WriteLine("I am an address");
}
}
}
}
My intent is to define a module with functions which can operate on all records types which comply with certain assumptions about the keys.
To illustrate, let us have the following code:
> type DBRow = { id: string ; createdAt: System.DateTime } ;;
type DBRow =
{id: string;
createdAt: System.DateTime;}
> let logCreationInfo row = printf "Record %s created at %s " row.id (row.createdAt.ToString()) ;;
val logCreationInfo : row:DBRow -> unit
I would like to change the above logCreationInfo to be able to operate on all records which have id: string and createdAt: System.DateTime (and maybe other things).
Coming from typescript's structural typing, I'd have expected this to be trivial, but I am exploring the possibility that there is a more idiomatic way to handle this in F#.
I had attempted to handle this using interfaces, but even if that could work, since F# supports only explicit interfaces, this will not be suitable for types I don't define myself.
You could use statically resolved type constraints.
let inline logCreationInfo (x : ^t) =
printfn "Record %s created at %s"
(^t : (member id : string) (x))
((^t : (member createdAt : System.DateTime) (x)).ToString())
F# largely uses nominative typing - this is a natural choice in its runtime environment, as this is what Common Type System specification prescribes. Adherence to that set of rules allows F# code to near-seamlessly interoperate with other .NET languages.
It's worth noting that this follows the same reasoning as to why TypeScript uses structural typing. Since that language builds up on top of dynamically typed JavaScript, it's more natural to express object relationships in terms of their structure rather than nominal types - which are a foreign concept in JS.
F# does have a "backdoor" for structural typing through already mentioned SRTPs, but I would suggest using it very sparingly. SRTPs are resolved and the code using them is inlined by the compiler, making for longer compilation times and reduced interoperability with other languages and the .NET platform in general (simply put, you can't refer to that code from other languages or using reflection API, because it's "compiled away").
Usually there are other solutions available. Interfaces were already mentioned, though the example used was a bit contrived - this is simpler:
type IDBRow =
abstract Id: string
abstract CreatedAt: System.DateTime
type Person =
{
id: string
name: string
age: int
createdAt: System.DateTime
}
interface IDBRow with
member this.Id = this.id
member this.CreatedAt = this.createdAt
let logCreationInfo (row: #IDBRow) =
printf "Record %s created at %s" row.Id (string row.CreatedAt)
let x = { id = "1"; name = "Bob"; age = 32; createdAt = DateTime.Now }
logCreationInfo x
Or using composition and a generic type to capture the generic part of what it means to be a DBRow:
type DBRow<'data> =
{
id: string
data: 'data
createdAt: System.DateTime
}
type Person =
{
name: string
age: int
}
let logCreationInfo (row: DBRow<_>) =
printf "Record %s created at %s" row.id (string row.createdAt)
let x = { id = "1"; data = { name = "Bob"; age = 32 }; createdAt = DateTime.Now }
logCreationInfo x
Here's a version with interfaces:
open System
type DBRow1 = {
id: string
createdAt: DateTime
}
type DBRow2 = {
id: string
createdAt: DateTime
address: string
}
/// The types are defined above without an interface
let row1 = {id = "Row1"; createdAt = DateTime.Now}
let row2 = {id = "Row2"; createdAt = DateTime.Now; address = "NYC"}
type IDBRow<'A> =
abstract member Data:(string * DateTime)
// Object expression implements the interface
let Data1 (x:DBRow1) = {
new IDBRow<_> with
member __.Data = (x.id, x.createdAt)
}
let Data2 (x: DBRow2) = {
new IDBRow<_> with
member __.Data = (x.id, x.createdAt)
}
//pass in both the object expression and the record
let getData (ifun: 'a -> IDBRow<'b>) xrec =
(ifun xrec).Data
// You could partially apply the functions: `getData1 = getData Data1`
getData Data1 row1 //("Row1", 2018/02/05 9:24:17)
getData Data2 row2 //("Row2", 2018/02/05 9:24:17)
You can certainly use an interface (an object expression in this case) to tack on another member, .Data, even if you don'T have access to the original type. You would still need to put together one object expression for each type though, so SRTP might be a more "elegant" solution.
I'd like some way to define related records. For example,
type Thing = { field1: string; field2: float }
type ThingRecord = { field1: string; field2: float; id: int; created: DateTime }
or
type UserProfile = { username: string; name: string; address: string }
type NewUserReq = { username: string; name: string; address: string; password: string }
type UserRecord = { username: string; name: string; address: string; encryptedPwd: string; salt: string }
along with a way to convert from one to the other, without needing to write so much boilerplate. Even the first example in full would be:
type Thing =
{ field1: string
field2: float }
with
member this.toThingRecord(id, created) =
{ field1 = this.field1
field2 = this.field2
id = id
created = created } : ThingRecord
and ThingRecord =
{ field1: string
field2: float
id: int
created: DateTime }
with
member this.toThing() =
{ field1 = this.field1
field2 = this.field2 } : Thing
As you get up to field10 etc, it gets to be a liability.
I currently do this in an unsafe (and slow) manner using reflection.
I added a request for with syntax to be extended to record definitions on uservoice, which would fill this need.
But is there perhaps an typesafe way to do this already? Maybe with type providers?
Yes, that's a chink in F#'s otherwise shiny armor. I don't feel there's a universal solution there for easily inheriting or extending a record. No doubt there is an appetite for one - I've counted over a dozen uservoice submissions advocating improvements along these lines - here are a few leading ones, feel free to vote up: 1, 2, 3, 4, 5.
For sure, there are things you can do to work around the problem, and depending on your scenario they might work great for you. But ultimately - they're workarounds and there's something you have to sacrifice:
Speed and type safety when using reflection,
Brevity when you go the type safe way and have full-fledged records with conversion functions between them,
All the syntactic and semantic goodness that records give you for free when you decide to fall back to plain .NET classes and inheritance.
Type providers won't cut it because they're not really a good tool for metaprogramming. That's not what they were designed for. If you try to use them that way, you're bound to hit some limitation.
For one, you can only provide types based on external information. This means that while you could have a type provider that would pull in types from a .NET assembly via reflection and provide some derived types based on that, you can't "introspect" into the assembly you're building. So no way of deriving from a type defined earlier in the same assembly.
I guess you could work around that by structuring your projects around the type provider, but that sounds clunky. And even then, you can't provide record types anyway yet, so best you could do are plain .NET classes.
For a more specific use case of providing some kind of ORM mapping for a database - I imagine you could use type providers just fine. Just not as a generic metaprogramming facility.
Why don't you make them more nested, like the following?
type Thing = { Field1: string; Field2: float }
type ThingRecord = { Thing : Thing; Id: int; Created: DateTime }
or
type UserProfile = { Username: string; Name: string; Address: string }
type NewUserReq = { UserProfile: UserProfile; Password: string }
type UserRecord = { UserProfile: UserProfile; EncryptedPwd: string; Salt: string }
Conversion functions are trivial:
let toThingRecord id created thing = { Thing = thing; Id = id; Created = created }
let toThing thingRecord = thingRecord.Thing
Usage:
> let tr = { Field1 = "Foo"; Field2 = 42. } |> toThingRecord 1337 (DateTime (2016, 6, 24));;
val tr : ThingRecord = {Thing = {Field1 = "Foo";
Field2 = 42.0;};
Id = 1337;
Created = 24.06.2016 00:00:00;}
> tr |> toThing;;
val it : Thing = {Field1 = "Foo";
Field2 = 42.0;}