简体   繁体   English

F#具有变量类型的函数

[英]F# Function with variable types

I have a question on making a function generic so that I can reuse the behavior. 我有一个关于使函数通用的问题,以便我可以重用该行为。 I have a situation where I want to perform the same set of actions whether the data is a float or a string . 我有一种情况,我想执行相同的操作集,无论数据是float还是string I have included a bit of the data model so you can see what I am doing. 我已经包含了一些数据模型,所以你可以看到我在做什么。 Near the bottom I have two different functions in the DataSeries module, simpleHigh and preferredHigh . 靠近底部我有两个不同的功能DataSeries模块, simpleHighpreferredHigh The simpleHigh function does exactly what I want. simpleHigh函数完全符合我的要求。 You can see that in both cases, float or string , I am using the same set of functions. 您可以看到在两种情况下, floatstring ,我使用相同的函数集。

Ideally I could do what I attempt to do in the preferredHigh function. 理想情况下,我可以在preferredHigh函数中执行我尝试执行的操作。 I define a single function and then use it for both conditions. 我定义了一个函数,然后将它用于两个条件。 The compiler complains though saying there is a type mismatch. 编译器抱怨虽然说类型不匹配。 I tried making the inner high function take a type argument: 我尝试使内部high函数采用类型参数:

let high<'T> v = (Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value))

But because it is contained inside another function definition the compiler says, "Explicit type parameters may only be used on module or member bindings". 但是因为它包含在另一个函数定义中,编译器说,“显式类型参数只能用于模块或成员绑定”。

Am I fighting a loosing battle here? 我在这里打一场失败的战斗吗? Should I just settle with the simple version? 我应该解决这个简单版本吗? My instinct tells me I am missing simple subtle and simple here. 我的直觉告诉我,我在这里错过了简单的微妙和简单。 I would rather reuse the behavior if possible instead of writing the exact some thing twice. 我宁愿重用这种行为,如果可能的话,而不是写两次确切的东西。 Should I take a different approach? 我应该采取不同的方法吗? All feedback welcome! 欢迎所有反馈!

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

The way you have preferredHigh written right now, it doesn't compile for an entirely different reason: high is a function of two arguments (one explicitly defined v and another coming from the function composition), but you're passing it to summarise , which expects a function of one argument. preferredHigh的方式现在很高兴,它不会因为一个完全不同的原因而编译: high是两个参数的函数(一个明确定义v ,另一个来自函数组合),但是你要传递它来summarise ,期望一个参数的功能。

So, the obvious way would be to remove the extra argument v : 所以,显而易见的方法是删除额外的参数v

let high = (Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value))

However, this wouldn't compile for the reason that you have already noted: the compiler determines the type of high based on the first usage as float Observation seq -> float , and then refuses to accept it in the second usage, because it doesn't match string Observation seq -> string . 但是,由于您已经注意到,这不会编译:编译器根据第一次使用确定high类型为float Observation seq -> float ,然后在第二次使用时拒绝接受它,因为它没有匹配string Observation seq -> string

This (as you probably already know) is due to the fact that in F# values cannot be generic, unless you write them as such explicitly. 这(你可能已经知道)是由于F#值不能是通用的,除非你明确地写它们。 Aka "value restriction" . 阿卡“价值限制”

However, functions can be generic. 但是,功能可以是通用的。 Even nested functions can: even though you can't write the generic parameters explicitly, the compiler would still infer a generic type for a nested function where appropriate. 即使嵌套函数也可以:即使您无法显式地编写泛型参数,编译器仍会在适当的位置推断嵌套函数的泛型类型。

From this, the solution is obvious: make high a function, not a value. 从这一点来看,解决方案显而易见: high功能,而不是价值。 Give it an argument, and don't forget to apply the composed function to that argument, so that the resulting function doesn't end up with two arguments (aka "eta-abstraction" ): 给它一个参数,并且不要忘记将组合函数应用于该参数,以便结果函数不会以两个参数结尾(也称为“eta-abstraction” ):

let high x = (Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value)) x

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM