gpt4 book ai didi

f# - 代码生成错误还是我的误解?

转载 作者:行者123 更新时间:2023-12-04 19:05:47 26 4
gpt4 key购买 nike

我看到了我无法从 F# 编译器 (Visual F# 3.1.1.0) 解释的行为——表面上看起来只是具有命名本地和传递临时变量之间的区别实际上会产生行为差异。

我是否不了解 F# 行为,或者这是代码生成错误? (我知道,后者更有可能。)

复制 - 我发现如果不使用 Reactive Extensions 就很难重现,所以这就像我得到的一样简单。请注意 try1try2几乎相同。

open System
open System.Reactive.Linq
open System.Threading

let interval = TimeSpan.FromSeconds(0.5)
let testDuration = TimeSpan.FromSeconds(2.0)

let mkHandler () = // creates a function that closes over state
let count = ref 0
fun _ -> count := !count + 1
printfn "State is now %d" !count

let try1 () =
printfn "try1"
let handler = mkHandler ()
use subscription = Observable.Interval(interval).Subscribe(handler)
Thread.Sleep(testDuration)

let try2 () =
printfn "try2"
// creates handler inline:
use subscription = Observable.Interval(interval).Subscribe(mkHandler ())
Thread.Sleep(testDuration)

[<EntryPoint>]
let main argv =
try1 ()
try2 ()
0

输出 - try1try2函数分别说明了期望和不期望的行为。程序的输出是:
try1
State is now 1
State is now 2
State is now 3
try2
State is now 1
State is now 1
State is now 1

据我了解 try2应该与 try1 的行为相同.如果不是,请解释这个微小的差异应该如何发挥不同的作用。

通过检查反编译器的输出,我确定了以下内容:
mkHandler功能正常;它创建了一个关闭唯一状态的函数。当多次调用时,它会改变该状态。
Subscribe 的相同过载被两个 try1 调用和 try2 : public static IDisposable Subscribe<T>(this IObservable<T> source, Action<T> onNext)
try1 生成的幕后帮助程序代码关闭处理函数并正确调用它:
[CompilationMapping(SourceConstructFlags.Closure)]  
[Serializable]
// subscription@16
internal sealed class subscriptionu004016
{
public FSharpFunc<long, Unit> handler;

public subscriptionu004016(FSharpFunc<long, Unit> handler)
{
}

internal void Invoke(long obj)
{
this.handler.Invoke(obj);
}
}
try2 的幕后帮助代码不会关闭处理函数,而是调用 mkHandler每次调用时的工厂函数;这解释了输出,但不是所需的行为:
[CompilationMapping(SourceConstructFlags.Closure)]
[Serializable]
// subscription@22-1
internal sealed class subscriptionu004022u002d1
{
public subscriptionu004022u002d1()
{
}

internal void Invoke(long obj)
{
Program.mkHandler<long>().Invoke(obj);
}
}

重申我的问题:为什么这两个函数的行为不同?这是代码生成错误吗?以上都不是?

最佳答案

据我所知,您的代码没有任何问题-您所做的事情是有道理的。这似乎是 F# 编译器中的一个细微错误。

我怀疑编译器解析 Subscribe 的方式有问题。方法。您的代码正在创建 F# 函数值,但编译器将其包装到 Action<int64>自动委托(delegate)并使用 Subscribe 的 Rx 版本.但是,它通常不会自动将部分应用的功能转为委托(delegate) - 它似乎只在这种情况下发生。

最简单的解决方法似乎是更改您的 mkHandler函数显式创建委托(delegate),然后一切都按预期工作:

let mkHandler () = // creates a function that closes over state
let count = ref 0
Action<int64>(fun _ ->
count := !count + 1
printfn "State is now %d" !count)

编辑:经过更多调查,我想说这是一个专门发生在 Subscribe 中的错误。 IObservable<T>的方法.由于 F# 自动将事件视为 IObservable<T>值,它对它们进行了一些特殊处理,并添加了 Subscribe方法。如果有 Subscribe扩展在其他地方声明,它发生冲突并且事情破裂。

我能找到的最简单的复制是创建一个 C# 项目:
public static class Extensions {
public static void Subscribe(this IObservable<int> c1, Action<int> f) {
f(1);
f(2);
}
}

然后完全按照你的做法做:
let partial() = 
printfn "called"
fun n -> ()

let a = new Event<int>()
let o = a.Publish.Subscribe(partial())

这会打印两次“调用”,而实际上应该只调用一次。我创建了一个 bug for this issue on the F# bug tracker .

关于f# - 代码生成错误还是我的误解?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25189077/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com