F#带有可变类型的函数

3
我有一个问题,想要将一个函数变成通用的,以便可以重复使用。我遇到了这样一种情况:无论数据是float还是string,我都希望执行相同的一组操作。我在下面包含了一些数据模型,以便您可以看到我正在做什么。在DataSeries模块中,我有两个不同的函数:simpleHighpreferredHigh。其中simpleHigh函数正好符合我的要求。您可以看到,在两种情况下,即floatstring,我都使用了相同的一组函数。
理想情况下,我可以像在preferredHigh函数中所尝试的那样做。我定义了一个单一的函数,然后将其用于两种条件。但编译器却抱怨说存在类型不匹配。我尝试让内部的high函数接受一个类型参数:
let high<'T> v = (Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value))

但是由于它包含在另一个函数定义中,编译器会说:“显式类型参数只能用于模块或成员绑定”。 我在这里打一场无用之战吗?我应该只满足简单版本吗?我的直觉告诉我,我在这里错过了一些微妙和简单的东西。如果可能,我宁愿重用行为而不是两次编写完全相同的内容。 我应该采取不同的方法吗?欢迎任何反馈!
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个回答

3

你现在编写的preferredHigh方式,无法编译,原因完全不同: high是一个带有两个参数的函数(一个是明确定义的v,另一个来自函数组合),但你将其传递给了期望一个参数的summarise函数。

所以,显而易见的方法是删除额外的参数v

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

然而,这段代码无法编译,因为你已经注意到的原因:编译器根据第一次使用 float Observation seq -> float 来确定 high 的类型,然后拒绝在第二次使用时接受它,因为它与 string Observation seq -> string 不匹配。 (如您可能已经知道的)这是由于在 F# 中值不能是泛型,除非您明确地将它们写成这样。也就是所谓的 "值约束"
然而,函数可以是泛型的,甚至嵌套函数也可以是泛型的。即使您无法明确地编写通用参数,编译器仍会推断出适当的嵌套函数的通用类型。
因此,解决方案很明显:将 high 作为函数而不是值。给它一个参数,并不要忘记将组合函数应用于该参数,以便生成的函数不会具有两个参数(也就是所谓的 "eta抽象")。
let high x = (Seq.sortBy (fun x -> x.Value) >> Seq.last >> (fun x -> x.Value)) x

1
哦,哇,这么简单的事情。谢谢!我不知道为什么我没有注意到high不是一个函数。好吧,我感到有些傻 :) 谢谢! - Matthew Crews

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接