理解 F# 类型构造函数

10

我尝试创建一个复杂类型,在构建时需要执行一些操作。所以我开始编写我的代码:

type public MyType =
   val private myvar: int
   val private myvar2: string
   (* ...Some val declarations... *)
   new () = {
      (* The default ctor makes something *)
   }
   new (ctorpar: AnotherType) = {
      myvar = 1;
      myvar2 = "Hello";
      (* Other assignments in the form var = val *)
      (* Here I would like to start a cycle in order *)
      for i in ctorpar.array do
         (* Do something *) (* ERROR *)
   }

当我试图从赋值中放置一个for或其他东西时,编译器会发疯。

我假设以下内容:new的语法遵循计算表达式的语法,或者更好的说法是new是一种计算表达式(因为有花括号和分号从一个指令到另一个指令)。在这种情况下,对于构造函数计算表达式,只能放置赋值。

所以请问:

1)我的推断正确吗?(关于计算表达式和类型构造函数)。

2)如果我需要在构造函数中放置一组详细的指令该怎么办?你知道,有时在构造时执行某个操作是必要的,可能涉及从循环到所有可能性的所有内容。

但无论如何,编译器都会出错...

感谢kvd,我了解到我有以下选择:

type public MyType =
   val private myvar: int
   val private myvar2: string
   (* ...Some val declarations... *)
   new () = {
      (* The default ctor makes something *)
   }
   new (ctorpar: AnotherType) = 
      for i in ctorpar.ACollection do
         (* Something *)
      {
      myvar = 1;
      myvar2 = "Hello";
      }

抱歉,这不会对我有所帮助,因为F#编译器告诉我:

  

对象构造函数在对象初始化之前不能直接使用try/with和try/finally语句。这包括像“for x in ...”这样的构造函数,这些构造函数可能会演变成对这些构造的使用。这是Common IL强制执行的限制。

好的,如果问题是在对象初始化之前做某些事情,并且听起来正确,那么让我们在此之后进行:

type public MyType =
   val mutable private myvar: int
   val mutable private myvar2: string
   (* ...Some val declarations... *)
   new () = {
      (* The default ctor makes something *)
   }
   new (ctorpar: AnotherType) = 
      {
      myvar = 1;
      myvar2 = "Hello";
      }
      then
      for i in ctorpar.ACollection do
         (* Something *)
         myvar <- 10

我又失败了,非常沮丧:

值或构造函数'myvar'未定义。

我该怎么办?似乎之后它无法识别我的类中的元素,这是正确的,因为当使用self或this声明成员时,需要一个标识符... 在这里它没有自引用,并且正确地告诉我:“你在试图得到我无法给你的东西!!!!”

2个回答

16
  1. 不,你的推断是不正确的。这些花括号更像是记录构造表达式,只能包含字段赋值。你不能在花括号内进行除赋值之外的任何操作。

  2. 你可以像平常一样在字段赋值之前(也就是在左花括号之前)放置语句。如果您希望在之后执行其他语句,则需要使用 then 关键字:

  3. type public MyType =
      val private myvar: int
      val private myvar2: string
    
      new () = 
        for i in 1 .. 10 do
          printfn "Before field assignments %i" i
        { myvar = 1; myvar2 = "test" } 
        then 
          for i in 1 .. 10 do
            printfn "After field assignments %i" i
    

编辑

关于你的新问题,你可以使用new (ctorpar:AnotherType) as this = ...然后this.myvar <- 10


kvb非常感谢您,您真正为我澄清了一切,不仅回答了我刚才提出的问题,甚至更多。现在我终于明白了。非常感谢您。 - Andry
“then”关键字在文档中出现在哪里?它确实有效,但我之前好像没有见过它。 - Ramon Snir
1
@Ramon - 请查看http://msdn.microsoft.com/en-us/library/dd233192.aspx中的“在构造函数中执行副作用”部分。 - kvb
@Andry - 看看我的编辑。这是我之前链接的MSDN页面中“构造函数中的自标识符”部分所涵盖的内容。 - kvb

12

kvb的答案很好,给了你所需要的所有信息 :-).

我只想推荐使用隐式语法,其中主构造函数紧跟类型名称写在一起。这样做可以使许多事情变得更加容易:

type MyType(n:int, s:string) =
  do 
    // Run before fields initialized (they aren't accessible here)
    for i in 1 .. 10 do
      printfn "Before field assignments %i" i
  // Initialize fields (accessible from now on)
  let myvar = n
  let myvar2 = s
  do // Run after fields initialized - you can use them here
     for i in 1 .. 10 do
       printfn "After field assignments %i" i

  // You can add multiple constructors too:
  new() = MyType(0, "hi")
  member x.Foo = 0

2
实际上,请参见http://lorgonblog.wordpress.com/2009/02/13/the-basic-syntax-of-f-classes-interfaces-and-members/。 - Brian
Tomas Petricek和@kvb,像往常一样,在所有与F#相关的事情上,你们两个都是巨大的帮助。 - Onorio Catenacci

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接