views:

115

answers:

4

Is there any way to get Visual Studio debugger to display the contents of an F# seq expression?

Visual Studio knows about IEnumerable<T> objects and gives you a results view inside the watch window. If you look at one of these in the watch window you get a mess of private fields.

Some potential alternatives:

  • fsi.exe does a nice job of printing sequences, but it doesn't interact with the debugger
  • Find a way to call Seq.toArray from inside the debugger. I can't find the right syntax to invoke this from, say, the Immediate window.
  • Write a vizualiser. I don't know if it's possible to attach vizualisers to Microsoft types.
  • Something else...?

Edit: Further investigation reveals that F#'s seq objects implement IEnumerable<T> just fine -- they appear in the watch window as such -- but for some reason the results view doesn't appear.

However, F# seq objects don't seem to be plain IEnumerables; instead, they look to be closures coming from the functions inside the Seq module. (F# seq objects look to be instances created using { new IEnumerable with ... }.)

+2  A: 

You could force the evaluation by putting the seq into a list. Assuming q is a seq<int>, this works in the watch window:

new System.Collections.Generic.List<int>(q)

BTW: seq is an alias for IEnumerable. As defined in prim-types.fsi:

/// <summary>An abbreviation for the CLI type <c>System.Collections.Generic.IEnumerable&lt;_&gt;</c></summary>
type seq<'T> = IEnumerable<'T>
Mauricio Scheffer
Verbose, but it works! Thanks.
Tim Robinson
+2  A: 

Well, the reason they appear as closure is because that is what they are : seq aren't evaluated until you actually use them in code. You can have an infinite seq, and that would take quite a long time to show in the debugger. And since it's not impossible that the evaluation has some side effect, evaluating them right a the creation can cause problem that show only in the debugger or in real code but not in both.

If you know that there is no problem with evaluting it, you can put it in a list or another collection that is not lazy evaluated and then check if it is correct here.

Laurent Bourgault-Roy
But a plain IEnumerable, in any other .NET language, is lazy too. That's why, when you have a C# sequence, Visual Studio makes you open the results view by hand. I'm not sure why F# needs closures here.
Tim Robinson
Well, if I read this question correctly http://stackoverflow.com/questions/2049806/debugging-an-ienumerable-method , Lazy IEnumerable shouldn't show either. My guess at this point is that the debugger is intelligent enough to detect when an IEnumerable is not lazy evaluated (if it is for example a list) and then show it in the debugger.
Laurent Bourgault-Roy
+3  A: 

Test environment:

let get s = 
    for x in s do // breakpoint is set here
        printfn "%A" s

get <| seq {for i in 1..10 -> i}

This should do the job in Quickwatch window:

Microsoft.FSharp.Collections.SeqModule.ToArray(s)

Or you can add this line to ensure that System.Core is loaded (and System.Linq.Enumerable is accessible)

let s : System.Linq.Expressions.Expression = Unchecked.defaultof<_>

after that you can use this syntax in QuickWatch

System.Linq.Enumerable.ToList(s)
desco
+7  A: 

One way to do this is to use the Results view node which was added in Visual Studio 2008. If System.Core.dll is loaded into the process, any type which implements IEnumerable<T> will get an extra node when expanded named Results. Expanding that node will enumerate the IEnumerable<T> and display the results.

The F# type seq is just an alias for IEnumerable<T> so it gets the same benefit.

results view

By default you don't see this in F# because it doesn't make use of any types in System.Core. But you can force this DLL into the process wit the following line.

// Force System.Core into the process
let x = typeof<System.Linq.Enumerable>
JaredPar
Just out of curiosity, what happens when an IEnumerable is an infinite list?
Gabe
That's brilliant. You don't need to modify your code. It works if you type this into the immediate window: `System.Reflection.Assembly.Load("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL")`
Tim Robinson
@Gabe, At that point expanding the Result view will essentially hang until the call to the debugger times out (assuming the IEnumerable is 100% managed code). If it's native you could end up with a Stack overflow, deadlock, out of memory ... These are the reasons the results view doesn't expand inline by default.
JaredPar
I just tried it - "Function evaluation timed out"
Tim Robinson