我有一个关于使函数通用的问题,以便我可以重用该行为。我的情况是,无论数据是float
还是string
,我都要执行相同的操作。我已经包含了一些数据模型,所以你可以看到我在做什么。在底部附近,我在DataSeries
模块中有两个不同的功能,simpleHigh
和preferredHigh
。 simpleHigh
函数完全符合我的要求。您可以在float
或string
两种情况下看到,我使用的是同一组函数。
理想情况下,我可以在preferredHigh
函数中执行我尝试执行的操作。我定义了一个函数,然后将它用于两个条件。编译器抱怨虽然说类型不匹配。我尝试使内部high
函数采用类型参数:
let high<'T> v = (Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value))
但是因为它包含在编译器所说的另一个函数定义中,&#34;显式类型参数只能用于模块或成员绑定&#34;。
我在这里打一场失败的战斗吗?我应该解决这个简单版本吗?我的直觉告诉我,我在这里错过了简单的微妙和简单。我宁愿重用这种行为,如果可能的话,而不是写两次确切的东西。我应该采取不同的方法吗?欢迎所有反馈!
open System
type Observation<'T> = {
ObsDateTime : DateTimeOffset
Value : 'T
}
type Result =
| Float of float
| String of string
type DataSeries =
| Float of Observation<float> seq
| String of Observation<string> seq
module DataSeries =
let summarise
(f: float Observation seq -> float)
(s: string Observation seq -> string)
(ds: DataSeries) =
match ds with
| DataSeries.Float v -> f v |> Result.Float
| DataSeries.String v -> s v |> Result.String
// What works
let simpleHigh ds =
summarise
(Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value))
(Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value))
ds
// What I would rather write
let preferredHigh ds =
let high v = (Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value))
summarise
high
high
ds
答案 0 :(得分:3)
你现在编写preferredHigh
的方式,它不会因完全不同的原因编译:{{1}}是两个参数的函数(一个明确定义high
而另一个明确定义v
从函数组合中),但是你将它传递给summarise
,它需要一个参数的函数。
所以,显而易见的方法是删除额外的参数v
:
let high = (Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value))
但是,由于您已经注意到,这不会编译:编译器根据第一次使用high
确定float Observation seq -> float
的类型,然后拒绝接受它第二种用法,因为它与string Observation seq -> string
不匹配。
这(正如您可能已经知道的)是由于F#值不能是通用的,除非您明确地写它们。阿卡"value restriction"。
但是,函数可以是通用的。即使嵌套函数也可以:即使你不能显式地编写泛型参数,编译器仍会在适当的位置推断嵌套函数的泛型类型。
从这一点来看,解决方案是显而易见的:使high
成为一个函数,而不是一个值。给它一个参数,并且不要忘记将组合函数应用于该参数,以便结果函数不会以两个参数结束(又名"eta-abstraction"):
let high x = (Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value)) x