因为computation expressions can be parameterized,你可能首先想到尝试这样的事情:
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构造函数中x < y表达式的y上,有一个警告:
此构造导致代码比类型注释所指示的更通用。类型变量 'a 已被限制为类型“'b”。
在let! x = [1 .. 10] 表达式中的数字1 上有一个警告:
此构造导致代码比类型注释所指示的更通用。类型变量'b 已被限制为类型“int”。
因此,在这两个约束之间,计算表达式的返回类型('b list)已被约束为 int list,但您的表达式改为返回 int * int list。在考虑了类型约束之后,您可能会得出这样的结论:这是行不通的。 但有一种方法可以让它发挥作用。关键是要意识到'b 类型将成为计算表达式的输出,在本例中,实际上是 元组 em> 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 的任何类型,但我传入ints,它们是值类型,因此不实现任何接口。这导致了错误
此表达式的类型应为“System.IComparable”,但此处的类型为“int”。
但是,将谓词的两个参数都声明为 int 就成功了。