F#:消除映射/减少/过滤中的冗余

F#:消除映射/减少/过滤中的冗余,f#,functional-programming,pipe,F#,Functional Programming,Pipe,假设我有一个列表和一套,我想用它们做一些事情: let outA = inA |> List.map(fun x -> x + 1) |> List.filter(fun x -> x > 10) let outB = inB |> Set.map(fun x -> x + 1) |> Set.filter(fun x -> x > 10) 显然,A处理列表,B处理集合。然而,我发现必须一遍又一遍地写列表非常烦人。列表。不仅是冗长、重

假设我有一个列表和一套,我想用它们做一些事情:

let outA = inA |> List.map(fun x -> x + 1) |> List.filter(fun x -> x > 10)
let outB = inB |> Set.map(fun x -> x + 1) |> Set.filter(fun x -> x > 10)
显然,A处理列表,B处理集合。然而,我发现必须一遍又一遍地写
列表非常烦人。
列表。
不仅是冗长、重复的样板文件,没有传达任何信息,而且妨碍了阅读代码的功能,它也是一个事实上的类型注释,我必须跟踪它并与代码的其余部分保持同步

我希望能够做到以下几点:

let outA = inA |> map(fun x -> x + 1) |> filter(fun x -> x > 10)
let outB = inB |> map(fun x -> x + 1) |> filter(fun x -> x > 10)
编译器知道inA是列表,inB是集合,因此所有操作都来自正确的类,因此outA是列表,outB是集合。我可以通过Seq部分实现这一点:

let map(x) =
    Seq.map(x)

let filter(x) =
    Seq.filter(x)
我完全可以写出来。问题是,它将所有内容强制转换为序列,我无法再对它们执行列表/集合操作。同样地

let outA = inA.Select(fun x -> x + 1).Where(fun x -> x > 10)
let outB = inB.Select(fun x -> x + 1).Where(fun x -> x > 10)
也会删除所有这些,但随后将所有内容强制转换为IEnumerable。我通过扩展将所有静态方法转换为实例方法,获得了非常好的结果:

type Microsoft.FSharp.Collections.List<'a> with
    member this.map(f) = this |> List.map(f)
    member this.filter(f) = this |> List.filter(f)

let b = a.map(fun x -> x + 1).filter(fun x -> x > 10)

键入Microsoft.FSharp.Collections.List这不是OO/继承/多态解决的问题。相反,它是“类型类”(一个la Haskell)解决的问题。.NET类型系统无法执行类型类,而F#也没有尝试添加该功能。(您可以使用
内联
执行一些棘手的操作,但它有各种限制。)


我建议你接受它,继续前进。

我同意布赖恩的观点——你会习惯的。正如我在回答前一个问题时所提到的,这有时非常有用,因为您将看到代码的功能(哪一部分的计算是惰性的,哪一部分使用数组来提高效率等等)

此外,有些函数仅适用于某些类型,例如有
List.tail
List.foldBack
,但
IEnumerable
(在
Seq
模块中)没有类似的函数,因为它们会导致错误的代码

在Haskell中,类型类可以描述具有某些函数的类型(如
map
filter
),但我认为它们的伸缩性不太好-要为F#库指定类型类,您将得到一个类型类的层次结构,这些类型类指定类似列表的数据结构,类似于列表的数据结构具有类似于
尾部
折叠
的函数和太多其他约束

除此之外,对于许多简单的列表处理作业,您还可以使用理解,这为您提供了更好的语法(当然,它只对基本内容有用):


在我看来,模块名称使代码更具描述性。只要看看代码,你就会知道它是一个列表/集合。基本上你想要类型类:)Checkout Haskell,或者继续使用限定名。@ebb:但是类型推断的要点是,我不需要太多地描述代码=D。快速注释-你也可以通过更巧妙地处理函数来解决这个问题-不需要先映射然后过滤,你只需要过滤。我现在正在研究类型类。我认为OO/继承/多态性的要点是,您可以给它一个方法名,它会立即知道(根据您输入的“this”)要调用哪个方法(在所有具有该方法的类中),这是我的问题。或者我完全遗漏了什么?从高层次上说,你的描述是正确的,而且OO似乎可以做到这一点。但在细节中,带有子类型的类型不起作用。。。请查看类型类,但也可以查看与OO相关的“方差”(协方差和逆变),以了解OO为什么不能很好地解决这个问题。+1,但我认为在.NET上支持类型类是很好的,只是F#的类型系统没有实现这一点。实际上,我真的不相信类型类能解决这个问题。F#(seq、list、array)中的不同集合都具有略微不同的功能(tail、foldBack、zip等等),其中一些集合只能由一些集合合理地支持。类型类意味着您必须命名每一组受支持的函数,并且必须选择正确的粒度和继承。在Haskell中,
函子
单子
(及相关)的设计表明,正确地实现这一点非常困难。
let outB = seq { for x in inA do 
                   if x > 10 do yield x + 1 }