F#-如何一起定义多个通用函数



确定:

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->string->string(
使'em2'的参数显式,或者,如果不希望它是泛型的,则添加类型注释。F#编译器(30(

我认为这不会起作用,因为F#编译器不认为元组是一个"简单不可变值":

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

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

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

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格式化(集中在一个工厂函数中的想法,以执行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"

最新更新