8

(I'm using the word "workflow" - not in the sense of async workflows - but rather in the "git workflow" sense, that is, how you use it as part of your development)

Having played around with F# for a while, I've started developing my first F# app. I'm from c#/vb. Having watched various demos/talks - rightly or wrongly- I've started off using fsi as the main development "engine" and work up stuff within that area. If I hit a problem which I need to debug, I tend to break out the problematic function into smaller bits and check those work to try and debug the problem.

However, In order to keep the amount of code manageable in fsi, once I am happy with what I have done, I the move it into a .fs and #load the .fs back into fsi. As the app gets bigger, this can begin to feel a bit clunky since when I need to refactor, I end up having to bring back in content from the fs file change it run stuff to get something working again, before pushing the code back out into the .fs file. Further this style isn't really a test first approach and so I am not getting the benefit of building a set of tests. (I can also miss the ability to set breakpoints/step the code which, istm in certain situations e.g. recursion, can be quicker for diagnosing errors than breaking out parts of a function - though maybe this is available in VS11 and I'm not setup right) .. so I think I'm perhaps not doing things optimally or else not thinking about things in the right way.

I was wondering if others could offer how they develop apps. Do you primarily use fsi or do you start with tdd. Should the tdd approach be the primary dev vehicle and FSI used more selectively to aid in the, say, implementation of more complex algorithms, data exploration etc etc

I have looked at this question and obviously it helpfully points to various tdd frameworks for F#, but I'd still be interested to find out the workflow of seasoned F# developers.

Many thx

S

Community
  • 1
  • 1
Simon Woods
  • 2,223
  • 4
  • 27
  • 34
  • Have you tried [script files](http://blogs.msdn.com/b/carlnol/archive/2011/08/09/creating-f-fsx-script-files-from-fs-source-files.aspx?Redirected=true)? It seems like a good opportunity to add some tests along the way – Artem Koshelev Apr 01 '13 at 08:39
  • Thx. Any chance you could expand on that a bit wrt to tdd? I've been using them simply as a "freer" way of getting stuff executed in fsi. I'm obviously missing something! – Simon Woods Apr 01 '13 at 09:11
  • The REPL is no substitute for proper tests. They rather complement each other. https://twitter.com/missingfaktor/status/314452078682595328 – Mauricio Scheffer Apr 01 '13 at 16:06

2 Answers2

12

I think you're on the right track.

Development process is a matter of taste. I'll share my approach anyway.

  • Start by a few fs files. Each file represents a module, which consists of a group of functions closely related to each other. It doesn't have to be precise from beginning; you often move stuffs between modules.
  • Create a few fsx files for quick testing once skeleton of the modules is ready.
  • Create a test project and set up NuGet packages. I often use NUnit and FsUnit together.
  • Whenever fsx scripting gives correct results, move them to test cases. Do this repeatedly.
  • Include a Program.fs into the main project and compile to executable in order to debug if needed.

In general, F# REPL is the main development engine. It gives me instant feedbacks and allows incremental changes, which are very helpful in prototyping. In F#, TDD is less critical since bug rate is much lower than in other languages. And I don't test everything, just focus on main functionalities and ensure a high test coverage. Using testdriven.net add-in or Visual Studio 2012 Premium and Ultimate can give you useful statistics on test coverage.

Using F# REPL and TDD, I almost never have to use debugging. Whenever there is a wrong behaviour, I stop and think. Since your codes don't have side effects, you can reason on them easily. In many times reasoning and a few printing commands can give me the right answer.

You can use TDD in F# REPL with Unquote and FsCheck. The former offers testing via quotations, which is quite impressive. The latter uses random testing approach which is attractive in handling corner cases of your codes. I find it really useful when your programs have to satisfy certain properties. However, it may take time to learn to use these frameworks properly.

pad
  • 41,040
  • 7
  • 92
  • 166
3

pad gave a great answer that is very practical and useful for a person new to F#. I will give a different means so that others don't think there is only one way F#'ers do it.

Note: If you are very new to programming, then stick with pad's answer, it is much better for a new programmer.

In the Object Oriented world one thinks with objects and in such languages I would start with writing objects down on paper and working with various diagrams such as use-case, state transition, sequence diagram, etc., until I felt I had enough to start creating objects in C# cs files, fleshing out the objects with methods, properties, events, etc.

In the functional world I typically start with abstract concepts and convert them into discriminated unions (DU) in an F# fs file, skipping the use of a REPL, i.e. F# Interactive, and then start adding a few functions. After I have a few functions I set up a test project using NUnit and FsUnit via NuGet. Since the DU are abstract, the test cases are typically harder to write, so I create printers for the DU and then insert them into the test case where I capture result output from the printer in the NUnit tool, for cut and paste back into the test case making changes as necessary. See these for examples of why I don't write them from scratch by hand.

Once I have the abstract DU done, I then can move onto the code to convert the human/concrete form into the abstract DU and convert the abstract DU into human/concrete form. In some cases these would be parsers and pretty printers.

The main point I am trying to make is that I don't focus on the tools I use but on the abstract concept of the problem and bring the tools in when needed.

I will note that I also program in PROLOG and there I do start with the REPL and move the code to a store once the logic works. So I am not opposed to using a REPL, it's just a different way of approaching the problem.

EDIT

Per a request by Ken for an example.

See: Discriminated Unions (F#) and look for the section
Using Discriminated Unions Instead of Object Hierarchies

So instead of a base type of shape with inherited types of Circle, EquilateralTriangle, Square and Rectangle one would create a discriminated Union as noted:

type Shape =
| Circle of float
| EquilateralTriangle of double
| Square of double
| Rectangle of double * double

As your question would make for a much better independent question and get answers with much better detail than I can give, I would suggest you ask it.

Also if you search for info on this also search with the following substitutions for discriminated union (DU):

Community
  • 1
  • 1
Guy Coder
  • 24,501
  • 8
  • 71
  • 136
  • Thx vm. There's a bit to digest there - especially the examples! Am I right in thinking that you create DUs at roughly the same point that you'd start creating object hierarchies if working in C#, for example, or are you saying something slightly different? Also, have you an example of what you mean when you say "create printers for the DU" - StructuredFormatDisplayAttribute? – Simon Woods Apr 05 '13 at 15:11
  • 1
    "Am I right in thinking that you create DUs at roughly the same point that you'd start creating object hierarchies if working in C#," Yes that is basically correct. With OO you think objects first, but with functional you think functions first, but functions need some types before you can write them and so for anything beyond the basics you will probably need a DU. Note: It is possible to do projects that don't need a DU because it is all functions of basic types, so don't think a DU is always needed, just like you may not need a object in an OO language to solve a problem. – Guy Coder Apr 05 '13 at 15:37
  • 1
    "Also, have you an example of what you mean when you say "create printers for the DU". That's actually a very good question that you should ask here on SO. I will point you to some here, but seriously ask as others can give more info than this. See: [fol.fs](https://github.com/jack-pappas/fsharp-logic-examples/blob/master/FSharpx.Books.AutomatedReasoning/fol.fs) You also need to know about AddPrinter. See: [fol.fsx](https://github.com/jack-pappas/fsharp-logic-examples/blob/master/Examples/fol.fsx) – Guy Coder Apr 05 '13 at 15:49
  • 1
    Also See: [intro.fs](https://github.com/jack-pappas/fsharp-logic-examples/blob/master/FSharpx.Books.AutomatedReasoning/intro.fs) and [intro.fsx](https://github.com/jack-pappas/fsharp-logic-examples/blob/master/Examples/intro.fsx) for some simplier printers. You should also reset F# interactive, print some DU values, then run AddPrinter statement and print again to see the difference it makes. – Guy Coder Apr 05 '13 at 16:05
  • Could you provide an example for your answer? (I don't see how DUs help your early design stages). – MasterMastic Feb 03 '14 at 15:17