tags:

views:

259

answers:

3

I am trying to initialize an XmlDocument in F#, without polluting the global namespace - my only functional background comes from LISP, where one can create a new scope using "let". I came up with this:

let doc = 
    let reader = new XmlTextReader("url")
    let doc = new XmlDocument()
    doc.Load(reader)
    doc

I was rather surprised when my first solution didn't work:

let doc = new XmlDocument() in
    let reader = new XmlTextReader("url");
    doc.Load(reader)

print_any reader.ToString // Still in scope!

What is the preferred way to do what I want?

+1  A: 

Your first example seems to be the style adopted by Don Syme and the rest of the F# enthusiasts. Mixing #light and ML-style let .. in doesn't seem like a good idea.

You can read some samples from Expert F# to get a feel for contemporary F# style.

There are also the F# Formatting Conventions written by Don Syme.

Frank Krueger
Indeed, using the let .. in with the #light option is unnecessary, though looking at the specification it's still valid as optional syntax. The fact that reader is still in scope seems vary strange indeed to me. Possible bug, perhaps?
Noldorin
To my eyes, it looks like a bug. But he is mixing a lot in that example: #light, the ';' operator, indentation, and 'in'. F#'s syntax is far from elegant, and not intuitive -- too much baggage left over from merging ML and .NET...
Frank Krueger
+2  A: 

Hi, it seems that when you use "let .. in" you can override the default "#light" setting which is that whitespace is significant, so I guess that's what happens in this case and why you don't get any warning/error in the second case (you added 'in', so the compiler thinks that you want to specify the scope explicitly). It seems a bit weird so it may be a bug. I'm sure Brian from the F# team will answer this doubt soon :-).

Anyway, I think the compiler treats your second example just as (using an easier to compile example):

open System

let ar = new ResizeArray<_>() in
let rnd = new Random();    
ar.Add(rnd.Next())

printfn "%A" (rnd.Next())

You can force it to treat it as you wanted if you add parentheses and write something like this:

let ar = new ResizeArray<_>() in
    (let rnd = new Random() 
     ar.Add(rnd.Next()))

printfn "%A" (rnd.Next())

In general, you can use parentheses to specify scopes in any place of the F# program. For example you can write:

let a = 1
(let a = a + 10
 printfn "%d" a)
printfn "%d" a

This example prints "10" and then "1". Of course, this doesn't seem to be very practical, but it is useful when using the use keyword that behaves like let, but works for IDisposable objects and ensures that the object is disposed when it leaves the scope (this is just like using in C#):

let some = new Some()
(use file = new StreamReader(...)
 let txt = file.ReadToEnd()
 printfn "%s" txt)
doSomething() // continue, 'file' is now closed!

EDIT: I completely forgot to mention the important bit - the first way of writing the code seems more natural to me (and it fully uses the benefits of "simple" #light syntax that F# offers), so I would prefer it :-).

Tomas Petricek
A: 

Part of the problem may be that "let ... in ..." is an expression, not a statement, so using this construct at the top level doesn't really make sense. For instance, what would it mean to say:

let x = 1 in
  x - 5

at the top level? It seems like the compiler should flag any use of "let ... in ..." at the top level as an error.

The other issue is that the XmlDocument API is not written in a functional style (at all), which makes it a bit awkward to use from F#. I'd say your first attempt is about as good as you're going to get, but it would sure be nice if the API allowed something more like:

let doc =
  let reader = new XmlTextReader("url")
  reader.ReadAsDocument()

or if there were a constructor for XmlDocument which took an XmlReader, but since the API is unapologetically imperative, you'll have to make do.

kvb