由于计算表达式可以参数化,您可能首先考虑尝试这样的事情:
let filterAndCollect (pred : 'a -> 'b -> bool) (f : 'a -> 'b list) (m : 'a list) =
let f' a = [ for b in f a do if pred a b then yield b ]
List.collect f' m
type FilteringListMonad(pred) =
member o.Bind( (m:'a list), (f: 'a -> 'b list) ) = filterAndCollect pred f m
member o.Return(x) = [x]
let filteredList = FilteringListMonad(fun x y -> x < y)
let test2 =
filteredList {
let! x = [1 .. 10]
let! y = [2 .. 2 .. 20]
return (x,y)
}
但是,由于元组上的类型错误而失败(x,y)
:
此表达式应具有类型“ int
”,但此处具有类型“ 'a * 'b
”
还有两个编译器警告:在FilteringListMonad 构造函数y
中的表达式上,有一个警告:x < y
这种构造导致代码不像类型注释所指示的那样通用。类型变量'a
已被限制为类型“ 'b
”。
1
在表达式中的数字上let! x = [1 .. 10]
,有一个警告:
这种构造导致代码不像类型注释所指示的那样通用。类型变量'b
已被限制为类型“ int
”。
因此,在这两个约束之间,计算表达式 (a 'b list
) 的返回类型已被约束为int list
,但您的表达式正在返回 a int * int list
。在考虑了类型约束之后,您可能会得出这样的结论:这是行不通的。但有一种方法可以让它发挥作用。关键是要意识到'b
,在这个例子中,作为计算表达式输出的类型实际上是tuple int * int
,所以你重写谓词函数实际上只是采用该'b
类型,然后一切正常:
let filterAndCollect (pred : 'b -> bool) (f : 'a -> 'b list) (m : 'a list) =
let f' a = [ for b in f a do if pred b then yield b ]
List.collect f' m
type FilteringListMonad(pred) =
member o.Bind( (m:'a list), (f: 'a -> 'b list) ) = filterAndCollect pred f m
member o.Return(x) = [x]
let filteredList = FilteringListMonad(fun (x:int,y:int) -> x < y)
let test2 =
filteredList {
let! x = [ 1 .. 10]
let! y = [2 .. 2 .. 20]
return (x,y)
}
请注意,我还必须指定谓词函数输入的类型。没有它,F# 将它们概括为“实现的任何类型System.IComparable
,但我传入int
s,它们是值类型,因此不实现任何接口。这导致了错误
此表达式应具有类型“ System.IComparable
”,但此处具有类型“ int
”。
但是,将这两个参数都声明为谓词就像int
诀窍一样。