具有变量类型的{F#函数

时间:2018-01-12 17:48:45

标签: generics f# functional-programming

我有一个关于使函数通用的问题,以便我可以重用该行为。我的情况是,无论数据是float还是string,我都要执行相同的操作。我已经包含了一些数据模型,所以你可以看到我在做什么。在底部附近,我在DataSeries模块中有两个不同的功能,simpleHighpreferredHighsimpleHigh函数完全符合我的要求。您可以在floatstring两种情况下看到,我使用的是同一组函数。

理想情况下,我可以在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

1 个答案:

答案 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