2

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

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. 这整个结构在某种程度上是非惯用的吗?这对我来说看起来很奇怪,但也许这只是因为将成员放在 sum 类型上对我来说是新的。
4

1 回答 1

4

我不认为有一种方法可以自动派生map,但是有一种方法可以在 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它确实重载,您看到的第一个应用程序调用实例,List<'a>第二个应用程序调用实例grouping<'a>。所以它的行为就像fmap在 Haskell 中一样。

还要注意这种方式你可以分解G gs而不创建let (G gs) = g

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

无论如何,我同意@kvb 的观点,因为在F#+map中也使用了该约定,因此将其定义到模块中会更加惯用,因此您拥有通用的和特定的mapModuleX.map

于 2014-03-26T07:40:03.187 回答