I am choosing to learn F# for my own enjoyment. I am getting to the point where concepts of F# seem to be pretty easy, but understanding some of the whys and whens is a bit harder.
Before I get into the code and the explanation, let me put my question up front. I am asking where can I find better advice on formatting F# code for readability? Or can someone give me a few guiding tips based off of the example given below?
So what are the best practices for code format and file layout?
Now to the explanation of the code.
I have started practicing coding Kata's in F# just to allow me to flex the language a little. The following program is an implementation of a natural sort. This was my first attempt to solving the problem in a TDD fashion in F#, as such I chose to forgo any framework as I did not want to deal with figuring out how to use any of them and not break the functional paradigm.
So the code below carries a light weight unit test framework.
Here is the code for the natural sort:
namespace Katas open System.Linq module NaturalSortKata = exception InvalidException of string type Comparison = | Equal | Lesser | Greater static member Compare x y = if x = y then Equal elif x > y then Greater else Lesser type ChunckType = | NumberType | StringType | Unknown static member GetType (c : char) = if System.Char.IsDigit(c) then NumberType else StringType member this.Compare other = match other with | ty when ty = this -> Equal | Unknown -> Lesser | NumberType when this = Unknown -> Greater | NumberType -> Lesser | StringType -> Greater let natualCompare (left : string) (right : string) = if left = right then Equal else let fix str = new System.String( str |> List.rev |> List.toArray ) let gatherChunck str = let rec gather str acc = match str with |  -> let (ty, l) = acc (ty, fix(l)) | fistLetter::rest -> match acc with | (ty, _) when ty = Unknown -> let t = ChunckType.GetType(fistLetter) gather rest (t, fistLetter :: ) | (ty, l) when ty = ChunckType.GetType(fistLetter) -> gather rest (ty, fistLetter::l) | (ty, l) -> (ty, fix(l)) gather str (Unknown, ) let rec compare (left : string) (right : string) = if (not (left.Any())) || (not (right.Any())) then match left.Length, right.Length with | llen, rlen when llen = rlen -> Equal | llen, rlen when llen > rlen -> Greater | llen, rlen when llen < rlen -> Lesser | _ -> raise (InvalidException "Bad Data") else let lt, lChunk = left |> Seq.toList |> gatherChunck let rt, rChunk = right |> Seq.toList |> gatherChunck match lt.Compare rt with | Equal -> if lChunk = rChunk then let lVal = left.Replace(lChunk, "") let rVal = right.Replace(rChunk, "") compare lVal rVal else match lt with | NumberType -> Comparison.Compare (System.Int64.Parse(lChunk)) (System.Int64.Parse(rChunk)) | _ -> Comparison.Compare lChunk rChunk | _ -> lt.Compare(rt) compare left right
Here is the code for the tests:
namespace Katas.Testing open Katas.NaturalSortKata module Tests = let test left right expected title= let testRun x = let result = right |> natualCompare left if result = expected then x |> printfn "%d good" true else title + " fails" |> printfn "%d %s" x result |> sprintf "%d Actual: %A" x |> sprintf "%d Expected: %A\r\n%s" x expected |> printfn "%s" false testRun let testRunner tests= let rec runner x result tests = match tests with |  -> result | head::tests -> let current = (head x) && result tests |> runner (x + 1) current tests |> runner 1 true let test01 = "Simple Equality" |> test "one" "one" Equal let test02 = "left < right" |> test "left" "right" Lesser let test03 = "beta > alpha" |> test "beta" "alpha" Greater let test04 = "\"9\" < \"10\"" |> test "9" "10" Lesser let test05 = "\"alpha9\" < \"alpha10\"" |> test "alpha9" "alpha10" Lesser let test06 = "\"alpha9Centary9\" < \"alpha9Centary10\"" |> test "alpha9Centary9" "alpha9Centary10" Lesser let test07 = "\"10\" > \"9\"" |> test "10" "9" Greater let test08 = "\"alpha10\" > \"alpha9\"" |> test "alpha10" "alpha9" Greater let test09 = "\"alpha9Centary9\" < \"alpha9Centary10\"" |> test "alpha9Centary10" "alpha9Centary9" Greater let tests = test01 :: test02 :: test03 :: test04 :: test05 :: test06 :: test07 :: test08 :: test09 :: let runTests = tests |> testRunner |> printfn "%b"
Here is the code that runs it all:
open Katas.NaturalSortKata open Katas.Testing.Tests [<EntryPoint>] let main argv = //Katas.Lockers.showLockerResults 300 runTests let _ = System.Console.ReadKey(true) 1