记录 "with" 语法中的泛型

Generics in record "with" syntax

如果我有一个带有通用字段的记录,在更改通用字段时有什么方法可以模仿方便的 with 语法吗?

即如果我有

type User<'photo> = // 'photo can be Bitmap or Url
  { first: string; last: string; address: string; phone: string; photo: 'photo }

我希望能够写出类似

的东西
let loadUser(user: User<Url>): User<Bitmap> =
  { user with
    photo = download user.photo }

但看来我必须改写这个。

let loadUser(user: User<Url>): User<Bitmap> =
  { first = user.first
    last = user.last
    address = user.address
    phone = user.phone
    photo = download user.photo }

有没有办法得到第一个语法?

不是直接的,但是你可以将你的 User 变成一个 仿函数 (对于 photo 部分):

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 }

一次并写入(例如):

let loadUser (user : User<Url>) : User<Bitmap> =
    fmap download user

你也可以 重命名 fmapwithPhoto 如果你喜欢 let loadUser = withPhoto download :D

现在 photo 部分是任何一种 data/type 可能有点奇怪,所以我会考虑将这部分重新命名为 value - 但那只是我

遗憾的是,{ with .. } 语法无法处理类型更改时的情况(尽管我认为可以添加,所以请随时在 F# 用户语音页面上打开建议!)

添加 map 函数有效,但另一种选择是在类型上定义 With 方法。这可以为非通用字段采用可选参数。对于通用字段,你不能完全这样做(因为它会统一类型),但你可以重载:

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 }

第一种方法是通用的,returns User 使用另一种类型的照片。第二种方法不会更改照片,因此它是非通用的。然后你可以像这样使用它:

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