F# - 如何一起定义多个泛型函数
F# - How to defining multiple generic functions together
确定:
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"
错误:
Value restriction. The value 'em2' has been inferred to have generic type
val em2 : ('_a -> string -> string)
Either make the arguments to 'em2' explicit or, if you do not intend for it to be generic, add a type annotation.F# Compiler(30)
我认为这行不通,因为 F# 编译器不会将元组视为 "simple immutable value":
The compiler performs automatic generalization only on complete function definitions that have explicit arguments, and on simple immutable values.
This means that the compiler issues an error if you try to compile code that is not sufficiently constrained to be a specific type, but is also not generalizable. The error message for this problem refers to this restriction on automatic generalization for values as the value restriction.
相反,我认为您必须单独定义它们,如下所示:
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 格式化)集中在单个 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
我们有 2 个解决方案:
- 具有明确的
content
参数:let em content = inside "em" content
但不够优雅。
- 更改
inside
函数的签名并使所有参数类型为string
。事实上,函数 inside
并不关心其参数的类型 - 它只关心 string
s 因为它使用 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"
确定:
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"
错误:
Value restriction. The value 'em2' has been inferred to have generic type val em2 : ('_a -> string -> string)
Either make the arguments to 'em2' explicit or, if you do not intend for it to be generic, add a type annotation.F# Compiler(30)
我认为这行不通,因为 F# 编译器不会将元组视为 "simple immutable value":
The compiler performs automatic generalization only on complete function definitions that have explicit arguments, and on simple immutable values.
This means that the compiler issues an error if you try to compile code that is not sufficiently constrained to be a specific type, but is also not generalizable. The error message for this problem refers to this restriction on automatic generalization for values as the value restriction.
相反,我认为您必须单独定义它们,如下所示:
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 格式化)集中在单个 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
我们有 2 个解决方案:
- 具有明确的
content
参数:let em content = inside "em" content
但不够优雅。 - 更改
inside
函数的签名并使所有参数类型为string
。事实上,函数inside
并不关心其参数的类型 - 它只关心string
s 因为它使用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"