Reputation: 10781
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?
Upvotes: 3
Views: 210
Reputation: 243106
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
Upvotes: 4
Reputation: 52290
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
Upvotes: 5