递归的歧视联盟和地图

时间:2014-03-25 19:56:36

标签: recursion f# functor discriminated-union

我需要一种树和一张地图,所以我这样做:

type 'a grouping = 
    G of ('a * 'a grouping) list
    with 
        member g.map f = 
            let (G gs) = g
            gs |> List.map (fun (s, g) -> f s, g.map f) |> G

但这让我想知道:

  1. map成员是样板文件。在Haskell中,GHC将为我实现fmap... deriving (Functor))。我知道F#没有类型类,但还有其他方法可以避免在F#中自己编写地图吗?
  2. 我可以以某种方式避开行let (G gs) = g
  3. 整个建筑在某种程度上是非惯用的吗?对我来说这看起来很奇怪,但也许这只是因为把成员放在总和类型上对我来说是新的。

1 个答案:

答案 0 :(得分:4)

我认为有一种方法可以自动导出map,但是有emulate type classes in F#的方式,您的代码可以像这样编写:

#r @"FsControl.Core.dll"
#r @"FSharpPlus.dll"

open FSharpPlus
open FsControl.Core.TypeMethods

type 'a grouping = 
    G of ('a * 'a grouping) list
    with
        // Add an instance for Functor 
        static member instance (_:Functor.Map, G gs, _) = fun (f:'b->'c) -> 
            map (fun (s, g) -> f s, map f g) gs |> G

// TEST
let a = G [(1, G [2, G[]] )]
let b = map ((+) 10) a        // G [(11, G [12, G[]] )]

请注意,map确实已超载,您看到的第一个应用程序调用{​​{1}}的实例,第二个应用程序调用{​​{1}}的实例。因此它在Haskell中的行为类似于List<'a>

另请注意,您可以在不创建grouping<'a>

的情况下分解fmap

现在关于什么是惯用的,我想很多人会同意你的解决方案更像F#惯用,但对我来说,也应该开发新的习语,以获得更多的功能并克服当前的语言限制,这就是我考虑的原因使用定义明确惯例的库也是惯用的。

无论如何,我同意@kvb,因为将G gs定义到一个模块中稍微更惯用,在F#+中也使用了约定,所以你有通用的{{1}和特定的let (G gs) = g