【问题标题】:F# - How to defining multiple generic functions togetherF# - 如何一起定义多个泛型函数
【发布时间】:2021-07-31 10:26:37
【问题描述】:

好的:

let em inp=sprintf"<em>%A</em>"inp
let bold inp=sprintf"<b>%A</b>"inp
printfn"%s"<|em"blabla"///<em>blabla</em>

试图一起定义(编译错误):

let em2,bold2=
    let tag a b=sprintf"<%s>%A</%s>"a b a
    (fun inp->tag"em"inp),tag"b"

错误:

值限制。值 'em2' 已被推断为具有泛型类型 val em2 : ('_a -> 字符串 -> 字符串)
要么明确 'em2' 的参数,或者,如果你不打算让它是通用的,添加一个类型注释。F# Compiler(30)

【问题讨论】:

    标签: f#


    【解决方案1】:

    我认为这行不通,因为 F# 编译器不认为元组是“simple immutable value”:

    编译器仅对具有显式参数的完整函数定义和简单的不可变值执行自动泛化。

    这意味着,如果您尝试编译的代码没有被充分限制为特定类型,但也不能泛化,编译器会发出错误。此问题的错误消息将这种对值的自动泛化的限制称为值限制。

    相反,我认为您必须单独定义它们,如下所示:

    let tag a b=sprintf"<%s>%A</%s>"a b a
    let em2 inp=tag"em"inp
    let bold2 b=tag"b"b
    

    如果您希望在这里隐藏tag 的定义,您可以将其设为私有。

    【讨论】:

    • 好吧,我希望我可以在一行中定义那么多 HTML 标签,而且,是的,应该隐藏一些实用功能。
    【解决方案2】:

    我喜欢将逻辑(此处为 HTML 格式)集中在单个 factory 函数中的想法,以执行 DRY 原则。

    除了将tag 工厂函数完全隐藏在闭包中,我们可以将其仅对其他模块隐藏,使其成为private,这通常是足够的封装。经过一些重命名:

    let private inside tag content = // 'a -> '-b -> string
        $"<{tag}>{content}</{tag}>"  // ? F# 5 interpolated string
    

    那么,F#中生成特定函数的常用方法是通过部分应用。由于当前的inside 函数是泛型的,我们不能在不丢失泛型的情况下使用无点表示法(即隐式参数content):

    let em = inside "em" // ⚠️ obj -> string 
    

    我们有两种解决方案:

    1. 有明确的content 参数:let em content = inside "em" content,但不太优雅。
    2. 更改inside函数的签名,使string类型的所有参数。实际上,函数inside 并不关心其参数的类型——它只关心strings,因为它使用ToString() 方法隐式地将它们转换为string,这在调用它时可能会导致严重的意外功能。
    let private inside tag content =    // string -> string -> string
        $"<%s{tag}>%s{content}</{tag}>" // ? %s to indicate parameters are strings
    
    let em     = inside "em"            // string -> string
    let strong = inside "strong"
    

    【讨论】:

    • 应该总是有一个到字符串的转换
    • @a_a : 如果参数tagcontent 只是string,你怎么看解决方案2?这是对您问题的明确回答吗?如果是,请点赞。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多