F# "do" 语句作为块表达式
F# "do" statements as Block expressions
F# spec 描述 10.2.5 do
模块中的语句 ,在模块级别 do
语句可能具有属性,并且当其表达式的类型未计算为 unit
时,它将产生警告。显然,do
语句在其他位置的行为类似于 Block 表达式 (6.5.1),无论是带括号的还是 begin-end-block,同时仍然声明 unit
类型。
这使得do
语句适用于控制确定性处置表达式的范围(6.6.4):
let d x =
printf "new %s, " x
{ new System.IDisposable with
member __.Dispose() = printf "disposing %s, " x }
let ab() =
use a = d "a"
use b = d "b"
printf "a + b, "
ab(); printfn ""
// prints 'new a, new b, a + b, disposing b, disposing a, '
let aba() =
use a = d "a"
do use b = d "b"
printf "a + b, "
printf "a, "
aba(); printfn ""
// prints 'new a, new b, a + b, disposing b, a, disposing a, '
如果规范中没有明确规定,是否还有其他任何东西可以防止 F# 编译器的维护者破坏这种用法?
我认为您示例中的 aba
函数实际上同时使用了 do
语句和块表达式。 do
的语法是 do <expr>
所以 do
语句的主体是一个单一的表达式。如果我们用括号写同样的东西:
let aba() =
use a = d "a"
do ( use b = d "b"
printf "a + b, " )
printf "a, "
然后确定性处置行为来自块表达式的通常确定性处置行为 - 而不是来自 do
块的某些未记录的 属性。
实际上您根本不需要 do
语句,使用括号就足够了:
let aba() =
use a = d "a"
(
use b = d "b"
printf "a + b, "
)
printf "a, "
aba(); printfn ""
// Prints "new a, new b, a + b, disposing b, a, disposing a, "
这是 Tomas Petricek 的示例减去 do
。括号为它们包含的代码块定义了一个新的范围,因此当您到达右括号时,b
超出范围并被释放。为了证明作用域规则是这样工作的,让我们添加另一个变量定义:
let aba() =
use a = d "a"
(
use b = d "b"
let c = 5
printf "a + b, and c=%d " c // This compiles
)
printf "and now c=%d " c // This does not compile
printf "a, "
aba(); printfn ""
括号内的 c
的第一次使用将编译。括号外的 c
的第二次使用不会编译,因为此时 c
超出范围,您将得到:
error FS0039: The value or constructor 'c' is not defined.
因此,如果您使用 do
表达式来控制 IDisposable
何时超出范围,则仅使用括号即可获得相同的结果。由您决定哪种形式更具可读性。
F# spec 描述 10.2.5 do
模块中的语句 ,在模块级别 do
语句可能具有属性,并且当其表达式的类型未计算为 unit
时,它将产生警告。显然,do
语句在其他位置的行为类似于 Block 表达式 (6.5.1),无论是带括号的还是 begin-end-block,同时仍然声明 unit
类型。
这使得do
语句适用于控制确定性处置表达式的范围(6.6.4):
let d x =
printf "new %s, " x
{ new System.IDisposable with
member __.Dispose() = printf "disposing %s, " x }
let ab() =
use a = d "a"
use b = d "b"
printf "a + b, "
ab(); printfn ""
// prints 'new a, new b, a + b, disposing b, disposing a, '
let aba() =
use a = d "a"
do use b = d "b"
printf "a + b, "
printf "a, "
aba(); printfn ""
// prints 'new a, new b, a + b, disposing b, a, disposing a, '
如果规范中没有明确规定,是否还有其他任何东西可以防止 F# 编译器的维护者破坏这种用法?
我认为您示例中的 aba
函数实际上同时使用了 do
语句和块表达式。 do
的语法是 do <expr>
所以 do
语句的主体是一个单一的表达式。如果我们用括号写同样的东西:
let aba() =
use a = d "a"
do ( use b = d "b"
printf "a + b, " )
printf "a, "
然后确定性处置行为来自块表达式的通常确定性处置行为 - 而不是来自 do
块的某些未记录的 属性。
实际上您根本不需要 do
语句,使用括号就足够了:
let aba() =
use a = d "a"
(
use b = d "b"
printf "a + b, "
)
printf "a, "
aba(); printfn ""
// Prints "new a, new b, a + b, disposing b, a, disposing a, "
这是 Tomas Petricek 的示例减去 do
。括号为它们包含的代码块定义了一个新的范围,因此当您到达右括号时,b
超出范围并被释放。为了证明作用域规则是这样工作的,让我们添加另一个变量定义:
let aba() =
use a = d "a"
(
use b = d "b"
let c = 5
printf "a + b, and c=%d " c // This compiles
)
printf "and now c=%d " c // This does not compile
printf "a, "
aba(); printfn ""
括号内的 c
的第一次使用将编译。括号外的 c
的第二次使用不会编译,因为此时 c
超出范围,您将得到:
error FS0039: The value or constructor 'c' is not defined.
因此,如果您使用 do
表达式来控制 IDisposable
何时超出范围,则仅使用括号即可获得相同的结果。由您决定哪种形式更具可读性。