Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show a seq in the watch window

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

like image 296
Tim Robinson Avatar asked Aug 18 '10 12:08

Tim Robinson


4 Answers

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>
like image 79
JaredPar Avatar answered Oct 09 '22 12:10

JaredPar


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)
like image 23
desco Avatar answered Oct 09 '22 12:10

desco


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>
like image 2
Mauricio Scheffer Avatar answered Oct 09 '22 10:10

Mauricio Scheffer


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.

like image 2
Laurent Bourgault-Roy Avatar answered Oct 09 '22 10:10

Laurent Bourgault-Roy