通用嵌套记录类型之间的静态转换
Static casting between types for Generic nested records
具有泛型类型参数的嵌套 F# 记录,我如何在嵌套结构中的类型之间进行静态转换,相当于遍历和执行 'T |> 'K
,例如float |> int
?
目前我正在天真地遍历嵌套记录并使用 from:float |> to:int
或等效的 int(from)
显式转换类型。然而,这并不是很美好。
type Person<'T> = {Id : int; Value : 'T}
type Family<'T> = {Id : 'T; People : seq<Person<'T>>}
let fam1 = {Id = 1.0; People = [{Id = 1.1; Value = 2.9}; {Id = 1.2; Value = 4.4}]} : Family<float>
let fam2 = {Id = 2.0; People = [{Id = 2.1; Value = 3.9}; {Id = 2.2; Value = 5.4}]} : Family<float>
let partyFloat = seq{ yield fam1; yield fam2}
// In general, how to do this from a type T to a type K where conversion using T |> K will work
let partyInt : seq<Family<int>> = partyFloat
如何静态and/or
懒惰地转换为 seq<Family<int>>
?
在我的真实世界案例中,我有一个 DiffSharp D
类型,可以使用 D |> float
或 float(D)
.
转换为浮点数
没有神奇的方法来转换类型的内部,您必须自己编写。
一般来说,F# 和函数式编程(我个人也推荐)编写用于简单数据转换的小函数,然后 assemble 将它们放在一起是惯用的:
let mapPerson f p = { Id = p.Id; Value = f p.Value }
let mapFamily f fm = { Id = f fm.Id; People = Seq.map (mapPerson f) fm.People }
let mapParty f = Seq.map (mapFamily f)
let partyInt = mapParty int partyFloat
但是你当然可以一次搞定:
let partyInt =
partyFloat
|> Seq.map (fun fm ->
{ Id = int fm.Id
People =
fm.People
|> Seq.map (fun p ->
{ Id = p.Id; Value = int p.Value }
)
}
)
看起来你要求的是协方差,即这应该编译
let vs : obj list = ["1"; "2"]
F# 不支持协变(或逆变),而且可能永远不会。然而,C# 确实如此,因此您可以编写类似这样的东西
using System.Collections.Generic;
interface IPerson<out T>
{
int Id { get; }
T Value { get; }
}
interface IFamily<out T>
{
int Id { get; }
IEnumerable<IPerson<T>> Members { get; }
}
static class Program
{
static IFamily<string> CreateFamily()
{
return null;
}
static void Main(string[] args)
{
IFamily<string> familyOfString = CreateFamily();
IFamily<object> familyOfObject = familyOfString;
}
}
然而,有一种功能模式可以帮助我们称为多态透镜。
(图片来自reddit帖子:https://www.reddit.com/r/haskell/comments/2qjnho/learning_curves_for_different_programming/)
我曾经认为由于缺少 higher-rank 类型,在 F# 中不可能使用多态透镜。然而,那里有一个隐藏的 gem:http://www.fssnip.net/7Pk
Vesa Karvonen(IIRC,他也是 hopac 的幕后黑手,所以他很酷)使用一些非常有趣的技巧在 F# 中实现多态透镜。
然后我们可以相当容易地映射不可变结构的内部值。
let input : Family<int> =
{
Id = 1
Members = [{ Id = 10; Value = 123}; { Id = 11; Value = 456}]
}
printfn "%A" input
let output : Family<string> =
input
|> over Family.membersL (overAll Person.valueL ((+) 1 >> string))
printfn "%A" output
完整源代码
// ----------------------------------------------------------------------------
// The code below taken from: http://www.fssnip.net/7Pk
// by Vesa+Karvonen - http://www.fssnip.net/authors/Vesa+Karvonen
// ----------------------------------------------------------------------------
type LensFunctor<'a> =
| Over of 'a
| View
member t.map a2b =
match t with
| Over a -> Over (a2b a)
| View -> View
type Lens<'s,'t,'a,'b> = ('a -> LensFunctor<'b>) -> 's -> LensFunctor<'t>
module Lens =
let view l s =
let r = ref Unchecked.defaultof<_>
s |> l (fun a -> r := a; View) |> ignore
!r
let over l f =
l (f >> Over) >> function Over t -> t | _ -> failwith "Impossible"
let set l b = over l <| fun _ -> b
let (>->) a b = a << b
let lens get set = fun f s ->
(get s |> f : LensFunctor<_>).map (fun f -> set f s)
let fstL f = lens fst (fun x (_, y) -> (x, y)) f
let sndL f = lens snd (fun y (x, _) -> (x, y)) f
// ----------------------------------------------------------------------------
// The code above taken from: http://www.fssnip.net/7Pk
// by Vesa+Karvonen - http://www.fssnip.net/authors/Vesa+Karvonen
// ----------------------------------------------------------------------------
let overAll l f = List.map (over l f)
open Lens
type Person<'T> = { Id : int; Value : 'T }
module Person =
let idS i p = { p with Id = i }
let valueS v { Id = i } = { Id = i; Value = v }
let idL f = lens (fun {Id = i } -> i) idS f
let valueL f = lens (fun {Value = v } -> v) valueS f
type Family<'T> = { Id : int; Members : Person<'T> list }
module Family =
let idS i f = { f with Id = i }
let membersS m { Id = i } = { Id = i; Members = m }
let idL f = lens (fun {Id = i } -> i) idS f
let membersL f = lens (fun {Members = m } -> m) membersS f
[<EntryPoint>]
let main argv =
let input =
{
Id = 1
Members = [{ Id = 10; Value = 123}; { Id = 11; Value = 456}]
}
printfn "%A" input
let output =
input
|> over Family.membersL (overAll Person.valueL ((+) 1 >> string))
printfn "%A" output
0
具有泛型类型参数的嵌套 F# 记录,我如何在嵌套结构中的类型之间进行静态转换,相当于遍历和执行 'T |> 'K
,例如float |> int
?
目前我正在天真地遍历嵌套记录并使用 from:float |> to:int
或等效的 int(from)
显式转换类型。然而,这并不是很美好。
type Person<'T> = {Id : int; Value : 'T}
type Family<'T> = {Id : 'T; People : seq<Person<'T>>}
let fam1 = {Id = 1.0; People = [{Id = 1.1; Value = 2.9}; {Id = 1.2; Value = 4.4}]} : Family<float>
let fam2 = {Id = 2.0; People = [{Id = 2.1; Value = 3.9}; {Id = 2.2; Value = 5.4}]} : Family<float>
let partyFloat = seq{ yield fam1; yield fam2}
// In general, how to do this from a type T to a type K where conversion using T |> K will work
let partyInt : seq<Family<int>> = partyFloat
如何静态and/or
懒惰地转换为 seq<Family<int>>
?
在我的真实世界案例中,我有一个 DiffSharp D
类型,可以使用 D |> float
或 float(D)
.
没有神奇的方法来转换类型的内部,您必须自己编写。
一般来说,F# 和函数式编程(我个人也推荐)编写用于简单数据转换的小函数,然后 assemble 将它们放在一起是惯用的:
let mapPerson f p = { Id = p.Id; Value = f p.Value }
let mapFamily f fm = { Id = f fm.Id; People = Seq.map (mapPerson f) fm.People }
let mapParty f = Seq.map (mapFamily f)
let partyInt = mapParty int partyFloat
但是你当然可以一次搞定:
let partyInt =
partyFloat
|> Seq.map (fun fm ->
{ Id = int fm.Id
People =
fm.People
|> Seq.map (fun p ->
{ Id = p.Id; Value = int p.Value }
)
}
)
看起来你要求的是协方差,即这应该编译
let vs : obj list = ["1"; "2"]
F# 不支持协变(或逆变),而且可能永远不会。然而,C# 确实如此,因此您可以编写类似这样的东西
using System.Collections.Generic;
interface IPerson<out T>
{
int Id { get; }
T Value { get; }
}
interface IFamily<out T>
{
int Id { get; }
IEnumerable<IPerson<T>> Members { get; }
}
static class Program
{
static IFamily<string> CreateFamily()
{
return null;
}
static void Main(string[] args)
{
IFamily<string> familyOfString = CreateFamily();
IFamily<object> familyOfObject = familyOfString;
}
}
然而,有一种功能模式可以帮助我们称为多态透镜。
我曾经认为由于缺少 higher-rank 类型,在 F# 中不可能使用多态透镜。然而,那里有一个隐藏的 gem:http://www.fssnip.net/7Pk
Vesa Karvonen(IIRC,他也是 hopac 的幕后黑手,所以他很酷)使用一些非常有趣的技巧在 F# 中实现多态透镜。
然后我们可以相当容易地映射不可变结构的内部值。
let input : Family<int> =
{
Id = 1
Members = [{ Id = 10; Value = 123}; { Id = 11; Value = 456}]
}
printfn "%A" input
let output : Family<string> =
input
|> over Family.membersL (overAll Person.valueL ((+) 1 >> string))
printfn "%A" output
完整源代码
// ----------------------------------------------------------------------------
// The code below taken from: http://www.fssnip.net/7Pk
// by Vesa+Karvonen - http://www.fssnip.net/authors/Vesa+Karvonen
// ----------------------------------------------------------------------------
type LensFunctor<'a> =
| Over of 'a
| View
member t.map a2b =
match t with
| Over a -> Over (a2b a)
| View -> View
type Lens<'s,'t,'a,'b> = ('a -> LensFunctor<'b>) -> 's -> LensFunctor<'t>
module Lens =
let view l s =
let r = ref Unchecked.defaultof<_>
s |> l (fun a -> r := a; View) |> ignore
!r
let over l f =
l (f >> Over) >> function Over t -> t | _ -> failwith "Impossible"
let set l b = over l <| fun _ -> b
let (>->) a b = a << b
let lens get set = fun f s ->
(get s |> f : LensFunctor<_>).map (fun f -> set f s)
let fstL f = lens fst (fun x (_, y) -> (x, y)) f
let sndL f = lens snd (fun y (x, _) -> (x, y)) f
// ----------------------------------------------------------------------------
// The code above taken from: http://www.fssnip.net/7Pk
// by Vesa+Karvonen - http://www.fssnip.net/authors/Vesa+Karvonen
// ----------------------------------------------------------------------------
let overAll l f = List.map (over l f)
open Lens
type Person<'T> = { Id : int; Value : 'T }
module Person =
let idS i p = { p with Id = i }
let valueS v { Id = i } = { Id = i; Value = v }
let idL f = lens (fun {Id = i } -> i) idS f
let valueL f = lens (fun {Value = v } -> v) valueS f
type Family<'T> = { Id : int; Members : Person<'T> list }
module Family =
let idS i f = { f with Id = i }
let membersS m { Id = i } = { Id = i; Members = m }
let idL f = lens (fun {Id = i } -> i) idS f
let membersL f = lens (fun {Members = m } -> m) membersS f
[<EntryPoint>]
let main argv =
let input =
{
Id = 1
Members = [{ Id = 10; Value = 123}; { Id = 11; Value = 456}]
}
printfn "%A" input
let output =
input
|> over Family.membersL (overAll Person.valueL ((+) 1 >> string))
printfn "%A" output
0