【问题标题】:Static casting between types for Generic nested records通用嵌套记录的类型之间的静态转换
【发布时间】:2019-10-05 06:28:49
【问题描述】:

带有泛型类型参数的嵌套 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

如何静态和/或 懒惰地转换成seq&lt;Family&lt;int&gt;&gt;? 在我的真实案例中,我有一个 DiffSharp D 类型,可以使用 D |&gt; floatfloat(D) 转换为浮点数。

【问题讨论】:

    标签: casting f# static type-conversion lazy-evaluation


    【解决方案1】:

    没有什么神奇的方法可以转换类型的内部,你必须自己编写。

    对于 F# 和函数式编程来说,通常(我个人也推荐它)为简单的数据转换编写小函数,然后将它们组装在一起是惯用的:

    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 }
                 )
            }
        )
    

    【讨论】:

    • 感谢您的回答!您是否建议将您的第一组函数作为静态或非静态成员放入类型中?例如,这两个将起作用:(1) let t1 = people |&gt; Seq.map (Person&lt;Foo&gt;.Convert1 int),但是对于被忽略的 来说很难看。 (2) let t2 = people |&gt; Seq.map (fun p -&gt; p.Convert2 int)(3) 我想这不可能完成这项工作Seq.map(Person&lt;int&gt;.Convert)?
    • 不要使用静态成员。将该函数放入名为Person 的模块中。此外,此类函数的惯用名称是 map,而不是 Convert
    【解决方案2】:

    看起来你要求的是协方差,即这应该编译

    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/

    我曾经认为 F# 中无法使用多态镜头,因为缺少更高级别的类型。但是,那里有一个隐藏的宝石: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
    

    【讨论】:

      猜你喜欢
      • 2011-10-28
      • 2016-08-31
      • 2021-10-23
      • 2021-12-24
      • 1970-01-01
      • 1970-01-01
      • 2019-02-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多