I started coding in F# about 2 months ago.
I am greatly enjoying this programming language. I come from a C# background, and every time I need to revert back to C#,
1.There is no auto complete like VS has for C# right
There is auto-complete for F#. It is not triggered automatically when you start typing though. If you're in Visual Studio and type aPara
and then hit Ctrl+Space, it should be auto-completed to aParameter
if it is in the scope. Similarly, you can do this in top-level scope to see available types and namespaces. Auto-completion also get triggered automatically when you type .
2.Debugging is tedious to say the least
I'd agree with this - debugging pipelines (especially with lazy sequences) is tricky. This is a bit confusing even when you're in C#, but C# does surprisingly good job on this one. There are two ways to deal with this:
Use F# Interactive more. I write most of my code in an F# Script file first, where you can run your partially complete solutions and see results immediately. For me, this pretty much replaces debugging, because by the time my code is complete, I know it works.
You can define a function tap
that materializes the data in the pipeline and lets you see what is going through the pipe. I don't use this very much, but I know some people like it:
let tap data =
let materialized = List.ofSeq data
materialized
Then you can use it in your pipeline:
correctedData
|> List.filter (fun (_, r, _) -> r <= 3)
|> tap
|> Seq.groupBy (fun (_, r, cti) -> (r,cti))
|> tap
|> Seq.map (fun ((r,cti),xs) -> (r, cti, Seq.length xs))
|> Seq.toList
This adds some noise to the pipeline, but you can remove it again once you're done with debugging.
On the debugging |> pipelines issue - try to have a personal coding standard that you don't have more than three, or at most four, lines in such a pipeline. When they get longer than that, refactor. This is what I do and it helps a lot.
The question of improving debugging experience with F# has many aspects so it deserves a big article. So I'm afraid the question will be closed.
Nevertheless, here are two neat tricks I'm using. I must note that I'm also a big fan of pipeline approach and so I'm facing exactly the same problems.
Know your types.
Having a value threaded through a chain of many transformations may quickly lead to difficulty remembering exact types at each step. The trick is:
value
|> transformation1
|> fun x -> x
|> transformation2
This lets you:
x
in design time;Conditionally dump the values to console.
Having complicated lambdas, breakpoints may be of little help. Here's yet another trick, related to the one described in @Tomas' answer: write a small function, like this:
let inline debug x =
#if DEBUG
if System.Console.CapsLock then
printfn "%A" x
// obviously, it must not be necessarily printf;
// it can be System.Diagnostics.Debug.WriteLine()
// or any other logger tool that exists in the project.
#endif
x
The usage code looks like this:
value
|> transformation1
|> fun x -> x
|> debug
|> transformation2
The idea is that:
debug
call, just as described above;If you have multiple places where debug
call sit, they would not spoil the output.