F# Quality of Life Questions

廉价感情. 提交于 2019-12-30 04:00:38

问题


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#, it feels so cumbersome and bloated.

But there are still things I think are problematic in F#, and this is what my questions are related to:

  1. There is no auto complete like VS has for C# right ? E.g. inside a function that takes parameter aParameter, if I write aPara no auto complete comes up. Is there a functionality inside VS that can solve this issue and which I am not aware of?

  2. Debugging is tedious to say the least. Since F# supports piping / chaining or whatever you want to call it, I typically try to chain as many things as possible (wherever it makes sense of course). Example:

    correctedData 
    |> List.filter (fun (_, r, _) -> r <= 3) 
    |> Seq.ofList 
    |> Seq.groupBy (fun (_, r, cti) -> (r,cti))                            
    |> Seq.map (fun ((r,cti),xs) -> (r, cti, Seq.length xs)) 
    |> Seq.toList
    

And this is just quarter of my whole chaining done. Whenever I mess something up in these chains, I find it very very hard to debug where it all went wrong.

Am I doing this chaining wrong (abusing it)? From my point of view, nothing intermediary from this chaining makes sense to exist atomically, thus there is no reason to have intermediary values. But because of this semantic point of view, I also lose the power of having intermediary values which help me debug. So then I have to insert them in the code, debug, then remove them again. But this is a wasted effort. Is there any way around this?

Also, debugging a List.map anonymous function inside a chain feels again awkward and hard compared to e.g. a for loop.

I am sure that I am missing something, and that my current way of debugging is probably not optimal - not by a long shot - so any suggestions are welcome.


回答1:


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.




回答2:


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:

  1. see the exact type of x in design time;
  2. set a breakpoint (place cursor at the function body) and see the value at debug time;
  3. Even if forgotten in the code after done, this leaves minimal footprint.

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:

  1. you set a breakpoint just before the debug call, just as described above;
  2. switch Caps Lock on
  3. and Step Over or just let the application run

If you have multiple places where debug call sit, they would not spoil the output.




回答3:


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.



来源:https://stackoverflow.com/questions/31045246/f-quality-of-life-questions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!