If I've got a record with a generic field, is there any way to mimic the convenient with syntax when changing the generic?
i.e. if I have
type User<'photo> = // 'photo can be Bitmap or Url
{ first: string; last: string; address: string; phone: string; photo: 'photo }
I want to be able to write something like
let loadUser(user: User<Url>): User<Bitmap> =
{ user with
photo = download user.photo }
But it looks like I've got to write this instead.
let loadUser(user: User<Url>): User<Bitmap> =
{ first = user.first
last = user.last
address = user.address
phone = user.phone
photo = download user.photo }
Is there a way to get the first syntax?
not directly but you can make your User into a functor (for the photo part):
let fmap (f : 'a -> 'b) (user: User<'a>): User<'b> =
{ first = user.first
last = user.last
address = user.address
phone = user.phone
photo = f user.photo }
once and write (for example):
let loadUser (user : User<Url>) : User<Bitmap> =
fmap download user
also you could rename fmap into withPhoto if you prefer let loadUser = withPhoto download :D
now it might be a bit strange that the photo part is any kind of data/type so I would think about renamig this part just into value - but that's just me
Sadly the { with .. } syntax does not handle the case when the type changes (though I think this could be added, so feel free to open a suggestion at the F# user voice page!)
Adding map function works, but another alternative is to define With method on the type. This can take optional parameters for non-generic fields. For the generic field, you cannot quite do this (because it would unify the types), but you can have an overload:
type User<'TPhoto> =
{ Name : string
Photo : 'TPhoto }
member x.With(photo, ?name) =
{ Name = defaultArg name x.Name; Photo = photo }
member x.With(?name) =
{ Name = defaultArg name x.Name; Photo = x.Photo }
The first method is generic and returns User with a photo of another type. The second method does not change the photo and so it is non-generic. You can then use it like this:
let t = { Name = "Tomas"; Photo = 0 }
t.With(photo="img") // Change type of photo
t.With(name="Tomáš") // Change just the name
t.With(photo="img", name="Test") // Change everything
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.
Is there any way to force parsing of only non-empty string fields of a record type in F# using Newtonsoft.Json?
#r """Newtonsoft.Json.dll"""
open Newtonsoft.Json
type Customer = {
Name: string
Email: string
ContactPhoneNo: string
}
// one or more fields can be empty
let customer = {
Name = ""
Email = "ca#gmail.com"
ContactPhoneNo = "+123456789"
}
let serializedCustomer =
JsonConvert.SerializeObject(customer)
// this parses correctly with the Name field set as ""
// But as the name field is empty, it should not parse it
let deserializedCustomer =
JsonConvert.DeserializeObject<Customer>(serializedCustomer)
You might want to consider using Newtonsoft's schema support for this, which is in a separate package called Newtonsoft.Json.Schema. You can specify many different kinds of constraints using annotations. For example, to disallow blank names, you can use MinLength:
open System.ComponentModel.DataAnnotations
type Customer = {
[<MinLength(1)>]
Name: string
Email: string
ContactPhoneNo: string
}
Once you've annotated your type, you can generate a schema:
let generator = JSchemaGenerator()
let schema = generator.Generate(typeof<Customer>)
Then use it to validate the serialized JSON:
let jsonCustomer = JObject.Parse(serializedCustomer)
let isValid = jsonCustomer.IsValid(schema)
If you want to skip the overhead of first loading JSON into a JObject in order to validate it, you can use a JSchemaValidatingReader instead:
use strReader = new System.IO.StringReader(serializedCustomer)
use txtReader = new JsonTextReader(strReader)
use vldReader = new JSchemaValidatingReader(txtReader, Schema = schema)
let messages = ResizeArray()
vldReader.ValidationEventHandler.Add(fun args -> messages.Add(args.Message))
let serializer = JsonSerializer()
let deserializedCustomer = serializer.Deserialize<Customer>(vldReader)
printfn "%A" deserializedCustomer
let isValid = (messages.Count = 0)
printfn "%A" isValid
See this documentation for details.
You could implement a custom JsonConverter that converts values of type string, but throws an exception when the string is empty:
let nonEmptyStringConverter =
{ new JsonConverter() with
override x.CanConvert(objectType) = objectType = typeof<string>
override x.WriteJson(writer, value, serializer) =
JValue(value :?> string).WriteTo(writer)
override x.ReadJson(reader, objectType, existingValue, serializer) =
let jt = JToken.Load(reader)
if jt.Type = JTokenType.String then
let str = jt.Value<string>()
if String.IsNullOrEmpty str then failwith "Empty string"
box str
else failwith "Expected a string" }
If you pass this to DeserializeObject, then it will throw an exception in your example:
let serializedCustomer =
JsonConvert.SerializeObject(customer)
let deserializedCustomer =
JsonConvert.DeserializeObject<Customer>(serializedCustomer, nonEmptyStringConverter)
One caveat is that this will apply to all string values in the type and I expect you may want to allow empty values for some. A better approach would be to define a custom type and define a convertor only for this type, e.g. using:
type NonEmptyString = NE of string
type Customer =
{ Name : NonEmptyString
Email: NonEmptyString
ContactPhoneNo: string }
In Common.fs:
namespace Bug
module Common =
type SourceEntity = {
id : int
link : string
}
type ReleaseEntity = {
id : int
notes : string
}
In Release.fs
namespace Bug
open System
open Common
module Release =
let cache = new Collections.Generic.Dictionary<int, ReleaseEntity>()
let AddToCache(entity) =
cache.Add(entity.id, entity)
()
let AddRec() =
let entity : ReleaseEntity = {
id = 1
notes = "Notes"
}
AddToCache(entity)
In Source.fs
namespace Bug
open System
open Common
module Source =
let Cache = new Collections.Generic.Dictionary<int, SourceEntity>()
let AddToCache(entity) =
Cache.Add(entity.id, entity) <<=== E R R O R
()
let AddRec() =
let ent : SourceEntity = {
id = 1
releases = "Releases"
}
AddToCache(ent) <<=== E R R O R
Files included in above order in the Visual Studio project.
Error reported in Source.fs:
Error FS0001 This expression was expected to have type
'SourceEntity'
but here has type
'ReleaseEntity'
If the order of the two types in Common.fs are reversed, the error is reported in Release.fs where expected type is ReleaseEntity but has type SourceEntity.
Any ideas why this error is happening?
It's a clash (and shadowing) of record field names.
When you write entity.id in the body of Bug.Source.AddToCache, the compiler uses the fact that you're accessing the .id field to infer the type of entity. Which records have a field named id? Well, those two records do, but the compiler has to pick one. How? Easy: the last one takes precedence. This is called "shadowing".
In order to disambiguate the choice, just add a type annotation:
let AddToCache(entity) =
Cache.Add(entity.id, entity)
()
Wait, but why doesn't the compiler use the type of Cache.Add to infer the type of entity?
Well, this is just a limitation (or a feature?) of F#. The compilation is single-pass, type interference proceeds top down, left to right, without doublebacks. This allows the compiler to be very fast and very predictable (looking at you, Haskell).
But in this case it means that by the time the compiler sees that entity is used as parameter in Cache.Add, it has already decided what its type must be.
When you get a type error, try to think about how the compiler came to infer that particular type.
Here:
let AddToCache(entity) =
cache.Add(entity.id, entity)
()
Could the compiler know which type has would have an id field in entity?
If you had typed in
let entity = { id = 1; link = "" }
the compiler would infer that this is SourceEntity because only SourceEntity has those particular record fields. In cache.Add(entity.id, entity), the compiler has no other constraints to go by, other than it has to have an id field, so it picks the last matching type - and that is why you get the error.
If you refactor the common id field to
namespace Bug
module Common =
type SourceEntity = {
source_id : int
link : string
}
type ReleaseEntity = {
release_id : int
notes : string
}
you will find that the error disappears.
Solutions
All of the solutions involve constraining it to a known type.
The simplest is to add a type annotation:
let AddToCache(entity: SourceEntity) =
Another is to deconstruct it explicitly:
let { SourceEntity.id = id } = entity
Cache.Add(id, entity)
Another is to coerce the type - this isn't relevant here, but it may come to be useful down the road:
Cache.Add((entity :> SourceEntity).id, entity)
I'd recommend this article from F# for fun and profit on type inference for a nice explanation of the process.
P.S.
You actually only needed that one type annotation.
The rest can be inferred :)
module Source =
let Cache = new Collections.Generic.Dictionary<_, _>()
let AddToCache (entity: SourceEntity) =
Cache.Add(entity.id, entity)
()
let AddRec () =
let ent = {
id = 1
link = ""
}
AddToCache(ent)
I've been learning iOS development for the past three weeks, I'm currently following a course on Udemy so far so good.
However I'm following one of the lectures whereby we build an Instagram Clone.
The instructor is using three arrays which are as follows:
var usernames = [""] // Stores all usernames
var userIds = [""] // Stores all Id's of the given usernames
var isFollowing = [false] // Stores where or not you're following that user
To me trying to keep track of what userId goes with what username using two arrays is basically an accident waiting to happen so I decided to set off and find a more feasible approach. I reverted back to my .Net days and decided to create a list so I went and created a class as follows:
class Users{
var Username : NSString = ""
var UserId : NSString = ""
var Following : Bool = false
}
Now inside my ViewController I make a call to Parse which returns me a list of users and I'm basically trying to loop through the response, and add them to the list class as shown here:
var t = [Users]() // After googling the web, this seems to be the syntax for a list declaration ?
let u = Users()
for object in users{
if let o = object as? PFUser {
u.Username = o.username!
u.UserId = o.objectId!
u.Following = o.IsFollowing!
self.t.append(u)
}
}
print(self.t)
Now when I print this to the console I see the following:
ParseStarterProject_Swift.Users
As I have one user at present, however when I try to loop through T and display the username in the console it doesn't display anything.
for x in t {
print(x.Username)
}
Your basic intuition is correct, it's better to have an array of custom objects, not multiple arrays.
Regarding making it more Swifty, consider your Users type. You might want something like:
struct User {
let username: String
let userId: String
let following: Bool
}
Note,
property names should start with lowercase letter;
Users should probably be called User, as it represents a single user;
we don't generally initialize values to default values like that, but rather specify them in the initializer;
we probably use String not NSString;
if a property cannot change, you'd use let, not var;
properties begin with lower case letters;
Then you can do something like:
var t = [User]()
for object in users {
if let o = object as? PFUser {
t.append(User(username: o.username!, userId: o.objectId!, following: o.IsFollowing!)
}
}
print(t)
Clearly, with all of those ! forced unwrapping operators, you'd want to be confident that those fields were populated for all of those properties.
Using struct is nice because (a) it's a value type; (b) you get the initializer for free; and (c) you can just print them. If you really wanted User to be a reference type (a class), you'd do something like:
class User {
let username: String
let userId: String
let following: Bool
init(username: String, userId: String, following: Bool) {
self.username = username
self.userId = userId
self.following = following
}
}
And if you wanted to be able to just print them, you'd define it to conform to CustomStringConvertible:
extension User: CustomStringConvertible {
var description: String { return "<User; username = \(username); userId = \(userId); following = \(following)>" }
}
With the class, you can feel free to change that description computed property to show it in whatever format you want, but it illustrates the idea.
You are correct in considering that keeping track of what userId goes with what username using two arrays is dangerous, you in the correct direction with your approach.
First, I would just like to suggest that you use correct naming convention:
Classes should be singular (except in very specific cases).
Variable/property names should begin with lowercase.
This would mean that your user class should look like this:
class User {
var username : NSString = ""
var userId : NSString = ""
var following : Bool = false
}
I will keep your existing naming use for the next part. The main problem with your code is that the variable "u" is a object which you create only once and then modify it. You should be creating a new "Users" object for each user instead of modifying the original. If you don't do this you will just have an array with the same user multiple times. This is how your code would look now:
var t = [Users]()
for object in users {
if let o = object as? PFUser {
let u = Users()
u.Username = o.username!
u.UserId = o.objectId!
u.Following = o.IsFollowing!
self.t.append(u)
}
}
print(self.t)
Next you mention that when you print to console you see the text: ParseStarterProject_Swift.Users, that is because Swift does not automatically print a pretty text with the content of your object. In order for it to print something more detailed, your "Users" object would need to implement the CustomStringConvertible. You can see a more detailed answer about that here: how-can-i-change-the-textual-representation-displayed-for-a-type-in-swif.
Lastly, you mention that when you loop trough "t" and display the username in the console it does not display anything. This is caused by one of two things:
Because there are no users being returned from parse, so the "t" array is actually empty. Try print(t.count) to see how many objects are in the array.
Because your "Users" object declares an empty string "" as the default username and the username is not being set correctly when getting the data from the parse. Which means that it IS actually printing something, just that it is an empty string. Try defining a different default value like var username : NSString = "Undefined" to see if it prints something.
Good luck learning swift!
Can somebody give me an example of how to make inserting data into an F# record more flexible?
I often see examples using records like this:
type Employee = {mutable name:string; mutable id:string}
let data =
[{name = "Thomas";id = "000"};
{name = "Johny";id = "001"};
{name = "Lucky";id = "002"};
{name = "Don";id = "003"}
]
Can't we start with no data at all and insert the data into the record later?
(What I mean is without declaration of the value of the data like in the example, so for example: the program is running and asking us to insert the data)
Can we doing something like this with record?
If you're talking about specifying values of a record as they become available, then you need to make fields of the record option so that you can represent the fact that value is missing. I'll use immutable records, because this is more common in functional style:
type Employee = { Name:option<string>; ID:option<string> }
Now you can create a record with only ID and add name when the user enters it:
let empty = { Name = None; ID = Some 123 }
let name = // read name from user
let full = { empty with Name = name }
If you're talking about adding items to a list as they become available, then you have several options. The direct one is to write a recursive function that repeatedly reads record and builds a list:
let rec readData i records =
let name = // read name from user
if name <> "" then
// Create new record and add it to our list
let itm = { Name = name; ID = string i }
readData (i + 1) (itm::records)
else
// Return records that we collected so far in the right order
records |> List.rev
Alternatively, you can also use sequence expressions (see for example free Chapter 12 (PDF) of Real-World Functional Programming). If you user interaction involves waiting for events (e.g. mouse click), then you can still use this style, but you'd need to wrap everything in asynchronous workflow and use Async.AwaitEvent.
Are you saw you often saw an example like that?
I'd say that it is not very idiomatic in F# to use mutable records.
Immutability is a rather large subject
to explain in one answer here, but
briefly: immutability means that the
objects you create never change:
they stay the way they were at
creation. In the immutable world, when
you want to 'change' something, you
create a new one, and throw away the
old one.
Anyway, if I understand your question correctly, you are actually talking about mutating data, not the record. So, you could have:
let data = []
let data = {name = "Thomas";id = "000"} :: data
let data = {{name = "Johny";id = "001"} :: data
But in this case, you aren't really 'changing' data, you are just creating a new list each time and pointing data at it.