Generics记录“with”语法

时间:2015-05-22 14:07:06

标签: f# functor

如果我有一个带有通用字段的记录,有没有办法在更改泛型时模仿方便的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 }

有没有办法获得第一种语法?

2 个答案:

答案 0 :(得分:5)

不是直接但您可以将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

如果您愿意fmap,也可以 withPhoto重命名为let loadUser = withPhoto download:D

现在可能有点奇怪photo部分是任何类型的数据/类型所以我会考虑将此部分重新命名为value - 但这只是我

答案 1 :(得分:4)

遗憾的是{ 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 }

第一种方法是通用的,并返回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