Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding F# type constructors

I am trying to create a complex type and need, at construction time, to perform some actions. So I started writing my code:

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 *)
   }

Well when trying to place a for or something else from an assignment the compiler goes mad. I assume the following: The syntax for new follows the one of Computational Expressions, or better, the new is a computational expression (I imply this because of the curly brackets and the semicolon from one oinstruction to another one). And in this case, for constructor computetional expressions, it is possible to palce assignments only.

So please could you answer me:

1) Is my deduction correct? (regarding computetional expressions and the constructor for types).

2) What should I do whether I need to place an articulated set of instructions to be executed in the constructor???? Well you know, sometimes it is necessary to perform an action at construction time and it might involve everything from a cycle to all possibilities.

But the compiler gets mad anyway...

Thanks to kvd I understood that I have the possibility to do the following:

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";
      }

Well I am sorry, but this does not help me because F# compiler tells me this:

Object constructors cannot directly use try/with and try/finally prior to the initialization of the object. This includes constructors such as 'for x in ...' that may elaborate to uses of these constructs. This is a limitation imposed by Common IL.

OK, if the problem is doing something prior to the object initalization, and it sounds correct, then let's do this after:

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

Again frustrated by failure:

The value or constructor 'myvar' is not defined.

What should I do???? It seems that after, it does not recognize elements in my class, it seems correct because it would require an indentifier as for when declaring members using self or this... here it does not have a self-reference and, correctly, tells me: "You're trying to get something I cannot give you!!!!!!"

like image 502
Andry Avatar asked Feb 15 '11 20:02

Andry


2 Answers

  1. No, your inference is not correct. The curly braces are more like a record construction expression, which can only contain field assignments. You cannot do anything other than assign values to each field within the braces.

  2. You can put statements before the field assignments (that is, before the opening brace) as normal. If you then wish to execute other statements afterward, you need to use the then keyword:

    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
    

Edit

Regarding your new question, you can use new (ctorpar:AnotherType) as this = ... and then this.myvar <- 10.

like image 76
kvb Avatar answered Sep 28 '22 12:09

kvb


The answer from kvb is great and gives you all the information you asked for :-).

I would just like to recommend to use the implicit syntax where primary constructor is written immediately following the name of the type. This makes so many things a lot easier:

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
like image 27
Tomas Petricek Avatar answered Sep 28 '22 13:09

Tomas Petricek