简体   繁体   English

F#是否具有用于访问词汇范围的语言构造(例如python locals()/ globals())

[英]Does F# have a language construct to access the lexical scope (like python locals()/globals())

When writing tests in F# I am trying to generate useful messages about the state that caused errors. 用F#编写测试时,我试图生成有关导致错误的状态的有用消息。 In python I would include all the locals(), so they are easily accessible in the test trace. 在python中,我将包括所有locals(),因此可以在测试跟踪中轻松访问它们。

Is there a similar construct in F#? F#中是否有类似的构造?

I have been searching the web and the excellent fsharpforfunandprofit site, as well as gone through the list of reserved keywords. 我一直在搜索网络和出色的fsharpforfunandprofit网站,并浏览了保留关键字列表。

Here is some code I would like to be able to do. 这是我想做的一些代码。

[<Property>]
let some_test x =
    let example_inner_helper y =
        let z = y + x
        // Example of the meta-construction i am looking for
        let scope = {| 
            local = {| y = y; z = z |}; 
            capture = {| 
                            local = {| x = x |};
                            capture = {| (* ... *) |};
            |};
        |}
        x = z |@ sprintf "require %A=%A (%A)" x y scope
    example_inner_helper (x+x)

Which would produce the very useful output 这将产生非常有用的输出

 Tests.some_test
   Source: Tests.fs line 105
   Duration: 51 ms

  Message: 
    FsCheck.Xunit.PropertyFailedException : 
    Falsifiable, after 1 test (1 shrink) (StdGen (387696160,296644521)):
    Label of failing property: require 1=2 ({ capture = { capture = {}
                  local = { x = 1 } }
      local = { y = 2
                z = 3 } })
    Original:
    -1
    Shrunk:
    1

However, I am having to explicitly capture the information that a construct like "scope" could automatically provide. 但是,我必须明确捕获诸如“范围”之类的结构可以自动提供的信息。 Resulting in ugly, error prone stuff like 导致丑陋,容易出错的东西,例如

let ``firstAscendingLastDescendingPartE Prop`` (a: Extent<int>) b =
    let validateT ea eb =
        let checkXbeforeY ex ey headx restx =
            match restx with 
                | ValueNone -> // No rest
                    (ex = headx) |@ sprintf "no rest: ex = headx (%A = %A)" ex headx 
                    .&. ((intersectE ex ey) = ValueNone) |@ sprintf "no rest: ex ∩ ey = ∅ (%A ∩ %A)" ex ey
                | ValueSome rex -> // Some rest -> 
                    // headx and restx combines to ex
                    ((expandE headx rex) = ex) |@ sprintf "rest: headx+rex = ex (%A...%A)" headx rex 
                    .&. (exactlyTouchesE headx rex) |@ sprintf "rest: headx ends where rex starts (ex=%A ey=%A headx=%A restx=%A)" ex ey headx restx
                    .&. (exactlyTouchesE headx ey) |@ sprintf "rest: headx ends where ey starts (ex=%A ey=%A headx=%A restx=%A)" ex ey headx restx
....

(Of course, I don't care about the actual type of the "scope" construct) (当然,我不在乎“ scope”构造的实际类型)

Regarding unquote 关于取消报价

I already looked at unquote which is quite cute and looks about right. 我已经看过unquote了,它很可爱,看起来不错。 But it limits the code quite a bit: 但是它限制了代码很多:

  • Error FS3155 A quotation may not involve an assignment to or taking the address of a captured local variable 错误FS3155引用可能不涉及对捕获的局部变量的分配或采用该地址
  • Error FS1230 Inner generic functions are not permitted in quoted expressions. 错误FS1230带引号的表达式中不允许使用内部通用函数。 Consider adding some type constraints until this function is no longer generic. 考虑添加一些类型约束,直到该函数不再通用为止。

I have code that looks a bit like: 我有看起来像的代码:

[<Struct>]
type X<'T when 'T: comparison and 'T: equality> =
    val public first: 'T
    val public last: 'T
    new (first: 'T, last: 'T) = {
        first = 
            (if last <= first then 
                invalidOp (sprintf "first < last required, (%A) is not < (%A)" first last) 
             else first)
        last = last;
    }
    // ..

So I have issues with tests using the two constructs below (causing the above errors). 因此,我在使用以下两种结构的测试中遇到了问题(导致上述错误)。

let quote_limits () = 
    let x first last = X(first, last)
    let validate (x: X<'a>) = x.first < x.last
    let a = X(0, 1)
    <@ a.first = 0 @> |> test
    <@ validate a @> |> test 

The first one i can workaround with functions to access the struct parts, but the limitation on generics is PITA. 我可以使用第一种方法解决访问结构部分的功能,但是泛型的局限性是PITA。

I don't think there is a way to do this, even in principle. 我认为即使在原则上也没有办法做到这一点。 F# code gets compiled to .NET bytecode which is stack based and so local variables do not really (in general) exist in the compiled code. F#代码被编译为基于堆栈的.NET字节码,因此本地变量实际上(通常)并不真正存在于编译后的代码中。 You might be able to get some globals (they are static members) or perhaps use debug information somehow, but I don't think there is a standard way of doing that. 您也许可以获取一些全局变量(它们是静态成员),或者也许以某种方式使用调试信息,但是我不认为有这样做的标准方法。

That said, in your specific case, F# actually has a nice option using the unquote testing framework. 就是说,在您的特定情况下,使用unquote测试框架,F#实际上有一个不错的选择。 This lets you put test code in F# quotations and it then shows how this quotation evaluates. 这使您可以将测试代码放入F#引号中,然后显示该引号的评估方式。 For example: 例如:

let some_test x =
    let example_inner_helper y =
        <@ 
          let z = y + x
          x = z 
        @> |> test
    example_inner_helper (x+x)

some_test 10

Here, I defined the local variable z inside the quotation. 在这里,我在引号内定义了局部变量z When you run the test, unquote will print the individual evaluation steps and you can see the value of z there: 运行测试时,取消引用将打印各个评估步骤,您可以在其中看到z的值:

Test failed:

let z = y + x in x = z
let z = 20 + 10 in x = z
let z = 30 in x = z
false

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM