diff --git a/Why use F#/origin/assets/img/ShoppingCart.png b/Why use F#/origin/assets/img/ShoppingCart.png new file mode 100644 index 0000000..365ef8a Binary files /dev/null and b/Why use F#/origin/assets/img/ShoppingCart.png differ diff --git a/Why use F#/origin/assets/img/four-concepts2.png b/Why use F#/origin/assets/img/four-concepts2.png new file mode 100644 index 0000000..9f383c3 Binary files /dev/null and b/Why use F#/origin/assets/img/four-concepts2.png differ diff --git a/Why use F#/origin/assets/img/glyphicons/glyphicons_030_pencil.png b/Why use F#/origin/assets/img/glyphicons/glyphicons_030_pencil.png new file mode 100644 index 0000000..24d4fa3 Binary files /dev/null and b/Why use F#/origin/assets/img/glyphicons/glyphicons_030_pencil.png differ diff --git a/Why use F#/origin/assets/img/glyphicons/glyphicons_054_clock.png b/Why use F#/origin/assets/img/glyphicons/glyphicons_054_clock.png new file mode 100644 index 0000000..2f89c16 Binary files /dev/null and b/Why use F#/origin/assets/img/glyphicons/glyphicons_054_clock.png differ diff --git a/Why use F#/origin/assets/img/glyphicons/glyphicons_150_check.png b/Why use F#/origin/assets/img/glyphicons/glyphicons_150_check.png new file mode 100644 index 0000000..8d33317 Binary files /dev/null and b/Why use F#/origin/assets/img/glyphicons/glyphicons_150_check.png differ diff --git a/Why use F#/origin/assets/img/glyphicons/glyphicons_280_settings.png b/Why use F#/origin/assets/img/glyphicons/glyphicons_280_settings.png new file mode 100644 index 0000000..d24b947 Binary files /dev/null and b/Why use F#/origin/assets/img/glyphicons/glyphicons_280_settings.png differ diff --git a/Why use F#/origin/assets/img/glyphicons/glyphicons_343_thumbs_up.png b/Why use F#/origin/assets/img/glyphicons/glyphicons_343_thumbs_up.png new file mode 100644 index 0000000..eaf67fc Binary files /dev/null and b/Why use F#/origin/assets/img/glyphicons/glyphicons_343_thumbs_up.png differ diff --git a/Why use F#/origin/posts/completeness-anything-csharp-can-do.md b/Why use F#/origin/posts/completeness-anything-csharp-can-do.md new file mode 100644 index 0000000..61f4b62 --- /dev/null +++ b/Why use F#/origin/posts/completeness-anything-csharp-can-do.md @@ -0,0 +1,240 @@ +--- +layout: post +title: "Anything C# can do..." +description: "A whirlwind tour of object-oriented code in F#" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 29 +categories: [Completeness] +--- + +As should be apparent, you should generally try to prefer functional-style code over object-oriented code in F#, but in some situations, you may need all the features of a fully fledged OO language ? classes, inheritance, virtual methods, etc. + +So just to conclude this section, here is a whirlwind tour of the F# versions of these features. + +Some of these will be dealt with in much more depth in a later series on .NET integration. But I won't cover some of the more obscure ones, as you can read about them in the MSDN documentation if you ever need them. + +## Classes and interfaces ## + +First, here are some examples of an interface, an abstract class, and a concrete class that inherits from the abstract class. + +```fsharp +// interface +type IEnumerator<'a> = + abstract member Current : 'a + abstract MoveNext : unit -> bool + +// abstract base class with virtual methods +[] +type Shape() = + //readonly properties + abstract member Width : int with get + abstract member Height : int with get + //non-virtual method + member this.BoundingArea = this.Height * this.Width + //virtual method with base implementation + abstract member Print : unit -> unit + default this.Print () = printfn "I'm a shape" + +// concrete class that inherits from base class and overrides +type Rectangle(x:int, y:int) = + inherit Shape() + override this.Width = x + override this.Height = y + override this.Print () = printfn "I'm a Rectangle" + +//test +let r = Rectangle(2,3) +printfn "The width is %i" r.Width +printfn "The area is %i" r.BoundingArea +r.Print() +``` + +Classes can have multiple constructors, mutable properties, and so on. + +```fsharp +type Circle(rad:int) = + inherit Shape() + + //mutable field + let mutable radius = rad + + //property overrides + override this.Width = radius * 2 + override this.Height = radius * 2 + + //alternate constructor with default radius + new() = Circle(10) + + //property with get and set + member this.Radius + with get() = radius + and set(value) = radius <- value + +// test constructors +let c1 = Circle() // parameterless ctor +printfn "The width is %i" c1.Width +let c2 = Circle(2) // main ctor +printfn "The width is %i" c2.Width + +// test mutable property +c2.Radius <- 3 +printfn "The width is %i" c2.Width +``` + +## Generics ## + +F# supports generics and all the associated constraints. + +```fsharp +// standard generics +type KeyValuePair<'a,'b>(key:'a, value: 'b) = + member this.Key = key + member this.Value = value + +// generics with constraints +type Container<'a,'b + when 'a : equality + and 'b :> System.Collections.ICollection> + (name:'a, values:'b) = + member this.Name = name + member this.Values = values +``` + +## Structs ## + +F# supports not just classes, but the .NET struct types as well, which can help to boost performance in certain cases. + +```fsharp + +type Point2D = + struct + val X: float + val Y: float + new(x: float, y: float) = { X = x; Y = y } + end + +//test +let p = Point2D() // zero initialized +let p2 = Point2D(2.0,3.0) // explicitly initialized +``` + +## Exceptions ## + +F# can create exception classes, raise them and catch them. + +```fsharp +// create a new Exception class +exception MyError of string + +try + let e = MyError("Oops!") + raise e +with + | MyError msg -> + printfn "The exception error was %s" msg + | _ -> + printfn "Some other exception" +``` + +## Extension methods ## + +Just as in C#, F# can extend existing classes with extension methods. + +```fsharp +type System.String with + member this.StartsWithA = this.StartsWith "A" + +//test +let s = "Alice" +printfn "'%s' starts with an 'A' = %A" s s.StartsWithA + +type System.Int32 with + member this.IsEven = this % 2 = 0 + +//test +let i = 20 +if i.IsEven then printfn "'%i' is even" i +``` + +## Parameter arrays ## + +Just like C#'s variable length "params" keyword, this allows a variable length list of arguments to be converted to a single array parameter. + +```fsharp +open System +type MyConsole() = + member this.WriteLine([] args: Object[]) = + for arg in args do + printfn "%A" arg + +let cons = new MyConsole() +cons.WriteLine("abc", 42, 3.14, true) +``` + +## Events ## + +F# classes can have events, and the events can be triggered and responded to. + +```fsharp +type MyButton() = + let clickEvent = new Event<_>() + + [] + member this.OnClick = clickEvent.Publish + + member this.TestEvent(arg) = + clickEvent.Trigger(this, arg) + +// test +let myButton = new MyButton() +myButton.OnClick.Add(fun (sender, arg) -> + printfn "Click event with arg=%O" arg) + +myButton.TestEvent("Hello World!") +``` + +## Delegates ## + +F# can do delegates. + +```fsharp +// delegates +type MyDelegate = delegate of int -> int +let f = MyDelegate (fun x -> x * x) +let result = f.Invoke(5) +``` + +## Enums ## + +F# supports CLI enums types, which look similar to the "union" types, but are actually different behind the scenes. + +```fsharp +// enums +type Color = | Red=1 | Green=2 | Blue=3 + +let color1 = Color.Red // simple assignment +let color2:Color = enum 2 // cast from int +// created from parsing a string +let color3 = System.Enum.Parse(typeof,"Green") :?> Color // :?> is a downcast + +[] +type FileAccess = | Read=1 | Write=2 | Execute=4 +let fileaccess = FileAccess.Read ||| FileAccess.Write +``` + +## Working with the standard user interface ## + +Finally, F# can work with the WinForms and WPF user interface libraries, just like C#. + +Here is a trivial example of opening a form and handling a click event. + +```fsharp +open System.Windows.Forms + +let form = new Form(Width= 400, Height = 300, Visible = true, Text = "Hello World") +form.TopMost <- true +form.Click.Add (fun args-> printfn "the form was clicked") +form.Show() +``` + diff --git a/Why use F#/origin/posts/completeness-intro.md b/Why use F#/origin/posts/completeness-intro.md new file mode 100644 index 0000000..1912b42 --- /dev/null +++ b/Why use F#/origin/posts/completeness-intro.md @@ -0,0 +1,22 @@ +--- +layout: post +title: "Completeness" +description: "F# is part of the whole .NET ecosystem" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 27 +categories: [Completeness] +--- + +In this final set of posts, we will look at some other aspects of F# under the theme of "completeness". + +Programming languages coming from the academic world tend to focus on elegance and purity over real-world usefulness, while more mainstream business languages such as C# and Java are valued precisely because they are pragmatic; they can work in a wide array of situations and have extensive tools and libraries to meet almost every need. In other words, to be useful in the enterprise, a language needs to be *complete*, not just well-designed. + +F# is unusual in that it successfully bridges both worlds. Although all the examples so far have focused on F# as an elegant functional language, it does support an object-oriented paradigm as well, and can integrate easily with other .NET languages and tools. As a result, F# is not a isolated island, but benefits from being part of the whole .NET ecosystem. + +The other aspects that make F# "complete" are being an official .NET language (with all the support and documentation that that entails) and being designed to work in Visual Studio (which provides a nice editor with IntelliSense support, a debugger, and so on). These benefits should be obvious and won't be discussed here. + +So, in this last section, we'll focus on two particular areas: + +* **Seamless interoperation with .NET libraries**. Obviously, there can be a mismatch between the functional approach of F# and the imperative approach that is designed into the base libraries. We'll look at some of the features of F# that make this integration easier. +* **Full support for classes and other C# style code**. F# is designed as a hybrid functional/OO language, so it can do almost everything that C# can do as well. We'll have a quick tour of the syntax for these other features. diff --git a/Why use F#/origin/posts/completeness-seamless-dotnet-interop.md b/Why use F#/origin/posts/completeness-seamless-dotnet-interop.md new file mode 100644 index 0000000..5e6ded4 --- /dev/null +++ b/Why use F#/origin/posts/completeness-seamless-dotnet-interop.md @@ -0,0 +1,243 @@ +--- +layout: post +title: "Seamless interoperation with .NET libraries" +description: "Some convenient features for working with .NET libraries" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 28 +categories: [Completeness] +--- + + +We have already seen many examples of F#'s use with the .NET libraries, such as using `System.Net.WebRequest` and `System.Text.RegularExpressions`. And the integration was indeed seamless. + +For more complex requirements, F# natively supports .NET classes, interfaces, and structures, so the interop is still very straightforward. For example, you can write an `ISomething` interface in C# and have the implementation be done in F#. + +But not only can F# call into existing .NET code, it can also expose almost any .NET API back to other languages. For example, you can write classes and methods in F# and expose them to C#, VB or COM. You can even do the above example backwards -- define an `ISomething` interface in F# and have the implementation be done in C#! The benefit of all this is that you don't have to discard any of your existing code base; you can start using F# for some things while retaining C# or VB for others, and pick the best tool for the job. + +In addition to the tight integration though, there are a number of nice features in F# that often make working with .NET libraries more convenient than C# in some ways. Here are some of my favorites: + +* You can use `TryParse` and `TryGetValue` without passing an "out" parameter. +* You can resolve method overloads by using argument names, which also helps with type inference. +* You can use "active patterns" to convert .NET APIs into more friendly code. +* You can dynamically create objects from an interface such as `IDisposable` without creating a concrete class. +* You can mix and match "pure" F# objects with existing .NET APIs + +## TryParse and TryGetValue ## + +The `TryParse` and `TryGetValue` functions for values and dictionaries are frequently used to avoid extra exception handling. But the C# syntax is a bit clunky. Using them from F# is more elegant because F# will automatically convert the function into a tuple where the first element is the function return value and the second is the "out" parameter. + +```fsharp +//using an Int32 +let (i1success,i1) = System.Int32.TryParse("123"); +if i1success then printfn "parsed as %i" i1 else printfn "parse failed" + +let (i2success,i2) = System.Int32.TryParse("hello"); +if i2success then printfn "parsed as %i" i2 else printfn "parse failed" + +//using a DateTime +let (d1success,d1) = System.DateTime.TryParse("1/1/1980"); +let (d2success,d2) = System.DateTime.TryParse("hello"); + +//using a dictionary +let dict = new System.Collections.Generic.Dictionary(); +dict.Add("a","hello") +let (e1success,e1) = dict.TryGetValue("a"); +let (e2success,e2) = dict.TryGetValue("b"); +``` + +## Named arguments to help type inference + +In C# (and .NET in general), you can have overloaded methods with many different parameters. F# can have trouble with this. For example, here is an attempt to create a `StreamReader`: + +```fsharp +let createReader fileName = new System.IO.StreamReader(fileName) +// error FS0041: A unique overload for method 'StreamReader' +// could not be determined +``` + +The problem is that F# does not know if the argument is supposed to be a string or a stream. You could explicitly specify the type of the argument, but that is not the F# way! + +Instead, a nice workaround is enabled by the fact that in F#, when calling methods in .NET libraries, you can specify named arguments. + +```fsharp +let createReader2 fileName = new System.IO.StreamReader(path=fileName) +``` + +In many cases, such as the one above, just using the argument name is enough to resolve the type issue. And using explicit argument names can often help to make the code more legible anyway. + +## Active patterns for .NET functions ## + +There are many situations where you want to use pattern matching against .NET types, but the native libraries do not support this. Earlier, we briefly touched on the F# feature called "active patterns" which allows you to dynamically create choices to match on. This can be very for useful .NET integration. + +A common case is that a .NET library class has a number of mutually exclusive `isSomething`, `isSomethingElse` methods, which have to be tested with horrible looking cascading if-else statements. Active patterns can hide all the ugly testing, letting the rest of your code use a more natural approach. + +For example, here's the code to test for various `isXXX` methods for `System.Char`. + +```fsharp +let (|Digit|Letter|Whitespace|Other|) ch = + if System.Char.IsDigit(ch) then Digit + else if System.Char.IsLetter(ch) then Letter + else if System.Char.IsWhiteSpace(ch) then Whitespace + else Other +``` + +Once the choices are defined, the normal code can be straightforward: + +```fsharp +let printChar ch = + match ch with + | Digit -> printfn "%c is a Digit" ch + | Letter -> printfn "%c is a Letter" ch + | Whitespace -> printfn "%c is a Whitespace" ch + | _ -> printfn "%c is something else" ch + +// print a list +['a';'b';'1';' ';'-';'c'] |> List.iter printChar +``` + +Another common case is when you have to parse text or error codes to determine the type of an exception or result. Here's an example that uses an active pattern to parse the error number associated with `SqlExceptions`, making them more palatable. + +First, set up the active pattern matching on the error number: + +```fsharp +open System.Data.SqlClient + +let (|ConstraintException|ForeignKeyException|Other|) (ex:SqlException) = + if ex.Number = 2601 then ConstraintException + else if ex.Number = 2627 then ConstraintException + else if ex.Number = 547 then ForeignKeyException + else Other +``` + +Now we can use these patterns when processing SQL commands: + +```fsharp +let executeNonQuery (sqlCommmand:SqlCommand) = + try + let result = sqlCommmand.ExecuteNonQuery() + // handle success + with + | :?SqlException as sqlException -> // if a SqlException + match sqlException with // nice pattern matching + | ConstraintException -> // handle constraint error + | ForeignKeyException -> // handle FK error + | _ -> reraise() // don't handle any other cases + // all non SqlExceptions are thrown normally +``` + +## Creating objects directly from an interface ## + +F# has another useful feature called "object expressions". This is the ability to directly create objects from an interface or abstract class without having to define a concrete class first. + +In the example below, we create some objects that implement `IDisposable` using a `makeResource` helper function. + +```fsharp +// create a new object that implements IDisposable +let makeResource name = + { new System.IDisposable + with member this.Dispose() = printfn "%s disposed" name } + +let useAndDisposeResources = + use r1 = makeResource "first resource" + printfn "using first resource" + for i in [1..3] do + let resourceName = sprintf "\tinner resource %d" i + use temp = makeResource resourceName + printfn "\tdo something with %s" resourceName + use r2 = makeResource "second resource" + printfn "using second resource" + printfn "done." +``` + +The example also demonstrates how the "`use`" keyword automatically disposes a resource when it goes out of scope. Here is the output: + + + using first resource + do something with inner resource 1 + inner resource 1 disposed + do something with inner resource 2 + inner resource 2 disposed + do something with inner resource 3 + inner resource 3 disposed + using second resource + done. + second resource disposed + first resource disposed + +## Mixing .NET interfaces with pure F# types ## + +The ability to create instances of an interface on the fly means that it is easy to mix and match interfaces from existing APIs with pure F# types. + +For example, say that you have a preexisting API which uses the `IAnimal` interface, as shown below. + +```fsharp +type IAnimal = + abstract member MakeNoise : unit -> string + +let showTheNoiseAnAnimalMakes (animal:IAnimal) = + animal.MakeNoise() |> printfn "Making noise %s" +``` + +But we want to have all the benefits of pattern matching, etc, so we have created pure F# types for cats and dogs instead of classes. + +```fsharp +type Cat = Felix | Socks +type Dog = Butch | Lassie +``` + +But using this pure F# approach means that that we cannot pass the cats and dogs to the `showTheNoiseAnAnimalMakes` function directly. + +However, we don't have to create new sets of concrete classes just to implement `IAnimal`. Instead, we can dynamically create the `IAnimal` interface by extending the pure F# types. + +```fsharp +// now mixin the interface with the F# types +type Cat with + member this.AsAnimal = + { new IAnimal + with member a.MakeNoise() = "Meow" } + +type Dog with + member this.AsAnimal = + { new IAnimal + with member a.MakeNoise() = "Woof" } +``` + +Here is some test code: + +```fsharp +let dog = Lassie +showTheNoiseAnAnimalMakes (dog.AsAnimal) + +let cat = Felix +showTheNoiseAnAnimalMakes (cat.AsAnimal) +``` + +This approach gives us the best of both worlds. Pure F# types internally, but the ability to convert them into interfaces as needed to interface with libraries. + +## Using reflection to examine F# types ## + +F# gets the benefit of the .NET reflection system, which means that you can do all sorts of interesting things that are not directly available to you using the syntax of the language itself. The `Microsoft.FSharp.Reflection` namespace has a number of functions that are designed to help specifically with F# types. + +For example, here is a way to print out the fields in a record type, and the choices in a union type. + +```fsharp +open System.Reflection +open Microsoft.FSharp.Reflection + +// create a record type... +type Account = {Id: int; Name: string} + +// ... and show the fields +let fields = + FSharpType.GetRecordFields(typeof) + |> Array.map (fun propInfo -> propInfo.Name, propInfo.PropertyType.Name) + +// create a union type... +type Choices = | A of int | B of string + +// ... and show the choices +let choices = + FSharpType.GetUnionCases(typeof) + |> Array.map (fun choiceInfo -> choiceInfo.Name) +``` diff --git a/Why use F#/origin/posts/conciseness-extracting-boilerplate.md b/Why use F#/origin/posts/conciseness-extracting-boilerplate.md new file mode 100644 index 0000000..28ca61b --- /dev/null +++ b/Why use F#/origin/posts/conciseness-extracting-boilerplate.md @@ -0,0 +1,310 @@ +--- +layout: post +title: "Using functions to extract boilerplate code" +description: "The functional approach to the DRY principle" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 10 +categories: [Conciseness, Functions, Folds] +--- + +In the very first example in this series, we saw a simple function that calculated the sum of squares, implemented in both F# and C#. +Now let's say we want some new functions which are similar, such as: + +* Calculating the product of all the numbers up to N +* Counting the sum of odd numbers up to N +* The alternating sum of the numbers up to N + +Obviously, all these requirements are similar, but how would you extract any common functionality? + +Let's start with some straightforward implementations in C# first: + +```csharp +public static int Product(int n) +{ + int product = 1; + for (int i = 1; i <= n; i++) + { + product *= i; + } + return product; +} + +public static int SumOfOdds(int n) +{ + int sum = 0; + for (int i = 1; i <= n; i++) + { + if (i % 2 != 0) { sum += i; } + } + return sum; +} + +public static int AlternatingSum(int n) +{ + int sum = 0; + bool isNeg = true; + for (int i = 1; i <= n; i++) + { + if (isNeg) + { + sum -= i; + isNeg = false; + } + else + { + sum += i; + isNeg = true; + } + } + return sum; +} +``` + +What do all these implementations have in common? The looping logic! As programmers, we are told to remember the DRY principle ("don't repeat yourself"), yet here we have repeated almost exactly the same loop logic each time. Let's see if we can extract just the differences between these three methods: + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunctionInitial valueInner loop logic
Productproduct=1Multiply the i'th value with the running total
SumOfOddssum=0Add the i'th value to the running total if not even
AlternatingSumint sum = 0
bool isNeg = true
Use the isNeg flag to decide whether to add or subtract, and flip the flag for the next pass.
+ +Is there a way to strip the duplicate code and focus on the just the setup and inner loop logic? Yes there is. Here are the same three functions in F#: + +```fsharp +let product n = + let initialValue = 1 + let action productSoFar x = productSoFar * x + [1..n] |> List.fold action initialValue + +//test +product 10 + +let sumOfOdds n = + let initialValue = 0 + let action sumSoFar x = if x%2=0 then sumSoFar else sumSoFar+x + [1..n] |> List.fold action initialValue + +//test +sumOfOdds 10 + +let alternatingSum n = + let initialValue = (true,0) + let action (isNeg,sumSoFar) x = if isNeg then (false,sumSoFar-x) + else (true ,sumSoFar+x) + [1..n] |> List.fold action initialValue |> snd + +//test +alternatingSum 100 +``` + +All three of these functions have the same pattern: + +1. Set up the initial value +2. Set up an action function that will be performed on each element inside the loop. +3. Call the library function `List.fold`. This is a powerful, general purpose function which starts with the initial value and then runs the action function for each element in the list in turn. + +The action function always has two parameters: a running total (or state) and the list element to act on (called "x" in the above examples). + +In the last function, `alternatingSum`, you will notice that it used a tuple (pair of values) for the initial value and the result of the action. This is because both the running total and the `isNeg` flag must be passed to the next iteration of the loop -- there are no "global" values that can be used. The final result of the fold is also a tuple so we have to use the "snd" (second) function to extract the final total that we want. + +By using `List.fold` and avoiding any loop logic at all, the F# code gains a number of benefits: + +* **The key program logic is emphasized and made explicit**. The important differences between the functions become very clear, while the commonalities are pushed to the background. +* **The boilerplate loop code has been eliminated**, and as a result the code is more condensed than the C# version (4-5 lines of F# code vs. at least 9 lines of C# code) +* **There can never be a error in the loop logic** (such as off-by-one) because that logic is not exposed to us. + +By the way, the sum of squares example could also be written using `fold` as well: + +```fsharp +let sumOfSquaresWithFold n = + let initialValue = 0 + let action sumSoFar x = sumSoFar + (x*x) + [1..n] |> List.fold action initialValue + +//test +sumOfSquaresWithFold 100 +``` + +## "Fold" in C# ## + +Can you use the "fold" approach in C#? Yes. LINQ does have an equivalent to `fold`, called `Aggregate`. And here is the C# code rewritten to use it: + +```csharp +public static int ProductWithAggregate(int n) +{ + var initialValue = 1; + Func action = (productSoFar, x) => + productSoFar * x; + return Enumerable.Range(1, n) + .Aggregate(initialValue, action); +} + +public static int SumOfOddsWithAggregate(int n) +{ + var initialValue = 0; + Func action = (sumSoFar, x) => + (x % 2 == 0) ? sumSoFar : sumSoFar + x; + return Enumerable.Range(1, n) + .Aggregate(initialValue, action); +} + +public static int AlternatingSumsWithAggregate(int n) +{ + var initialValue = Tuple.Create(true, 0); + Func, int, Tuple> action = + (t, x) => t.Item1 + ? Tuple.Create(false, t.Item2 - x) + : Tuple.Create(true, t.Item2 + x); + return Enumerable.Range(1, n) + .Aggregate(initialValue, action) + .Item2; +} +``` + +Well, in some sense these implementations are simpler and safer than the original C# versions, but all the extra noise from the generic types makes this approach much less elegant than the equivalent code in F#. You can see why most C# programmers prefer to stick with explicit loops. + +## A more relevant example ## + +A slightly more relevant example that crops up frequently in the real world is how to get the "maximum" element of a list when the elements are classes or structs. +The LINQ method 'max' only returns the maximum value, not the whole element that contains the maximum value. + +Here's a solution using an explicit loop: + +```csharp +public class NameAndSize +{ + public string Name; + public int Size; +} + +public static NameAndSize MaxNameAndSize(IList list) +{ + if (list.Count() == 0) + { + return default(NameAndSize); + } + + var maxSoFar = list[0]; + foreach (var item in list) + { + if (item.Size > maxSoFar.Size) + { + maxSoFar = item; + } + } + return maxSoFar; +} + +``` + +Doing this in LINQ seems hard to do efficiently (that is, in one pass), and has come up as a [Stack Overflow question](http://stackoverflow.com/questions/1101841/linq-how-to-perform-max-on-a-property-of-all-objects-in-a-collection-and-ret). Jon Skeet event wrote an [article about it](http://codeblog.jonskeet.uk/2005/10/02/a-short-case-study-in-linq-efficiency/). + +Again, fold to the rescue! + +And here's the C# code using `Aggregate`: + +```csharp +public class NameAndSize +{ + public string Name; + public int Size; +} + +public static NameAndSize MaxNameAndSize(IList list) +{ + if (!list.Any()) + { + return default(NameAndSize); + } + + var initialValue = list[0]; + Func action = + (maxSoFar, x) => x.Size > maxSoFar.Size ? x : maxSoFar; + return list.Aggregate(initialValue, action); +} +``` + +Note that this C# version returns null for an empty list. That seems dangerous -- so what should happen instead? Throwing an exception? That doesn't seem right either. + +Here's the F# code using fold: + +```fsharp +type NameAndSize= {Name:string;Size:int} + +let maxNameAndSize list = + + let innerMaxNameAndSize initialValue rest = + let action maxSoFar x = if maxSoFar.Size < x.Size then x else maxSoFar + rest |> List.fold action initialValue + + // handle empty lists + match list with + | [] -> + None + | first::rest -> + let max = innerMaxNameAndSize first rest + Some max +``` + +The F# code has two parts: + +* the `innerMaxNameAndSize` function is similar to what we have seen before. +* the second bit, `match list with`, branches on whether the list is empty or not. +With an empty list, it returns a `None`, and in the non-empty case, it returns a `Some`. +Doing this guarantees that the caller of the function has to handle both cases. + +And a test: + +```fsharp +//test +let list = [ + {Name="Alice"; Size=10} + {Name="Bob"; Size=1} + {Name="Carol"; Size=12} + {Name="David"; Size=5} + ] +maxNameAndSize list +maxNameAndSize [] +``` + +Actually, I didn't need to write this at all, because F# already has a `maxBy` function! + +```fsharp +// use the built in function +list |> List.maxBy (fun item -> item.Size) +[] |> List.maxBy (fun item -> item.Size) +``` + +But as you can see, it doesn't handle empty lists well. Here's a version that wraps the `maxBy` safely. + +```fsharp +let maxNameAndSize list = + match list with + | [] -> + None + | _ -> + let max = list |> List.maxBy (fun item -> item.Size) + Some max +``` diff --git a/Why use F#/origin/posts/conciseness-functions-as-building-blocks.md b/Why use F#/origin/posts/conciseness-functions-as-building-blocks.md new file mode 100644 index 0000000..751d90a --- /dev/null +++ b/Why use F#/origin/posts/conciseness-functions-as-building-blocks.md @@ -0,0 +1,251 @@ +--- +layout: post +title: "Using functions as building blocks" +description: "Function composition and mini-languages make code more readable" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 11 +categories: [Conciseness, Functions] +--- + +A well-known principle of good design is to create a set of basic operations and then combine these building blocks in various ways to build up more complex behaviors. In object-oriented languages, this goal gives rise to a number of implementation approaches such as "fluent interfaces", "strategy pattern", "decorator pattern", and so on. In F#, they are all done the same way, via function composition. + +Let's start with a simple example using integers. Say that we have created some basic functions to do arithmetic: + +```fsharp +// building blocks +let add2 x = x + 2 +let mult3 x = x * 3 +let square x = x * x + +// test +[1..10] |> List.map add2 |> printfn "%A" +[1..10] |> List.map mult3 |> printfn "%A" +[1..10] |> List.map square |> printfn "%A" +``` + +Now we want to create new functions that build on these: + +```fsharp +// new composed functions +let add2ThenMult3 = add2 >> mult3 +let mult3ThenSquare = mult3 >> square +``` + +The "`>>`" operator is the composition operator. It means: do the first function, and then do the second. + +Note how concise this way of combining functions is. There are no parameters, types or other irrelevant noise. + +To be sure, the examples could also have been written less concisely and more explicitly as: + +```fsharp +let add2ThenMult3 x = mult3 (add2 x) +let mult3ThenSquare x = square (mult3 x) +``` + +But this more explicit style is also a bit more cluttered: + +* In the explicit style, the x parameter and the parentheses must be added, even though they don't add to the meaning of the code. +* And in the explicit style, the functions are written back-to-front from the order they are applied. In my example of `add2ThenMult3` I want to add 2 first, and then multiply. The `add2 >> mult3` syntax makes this visually clearer than `mult3(add2 x)`. + +Now let's test these compositions: + +```fsharp +// test +add2ThenMult3 5 +mult3ThenSquare 5 +[1..10] |> List.map add2ThenMult3 |> printfn "%A" +[1..10] |> List.map mult3ThenSquare |> printfn "%A" +``` + +## Extending existing functions + +Now say that we want to decorate these existing functions with some logging behavior. We can compose these as well, to make a new function with the logging built in. + +```fsharp +// helper functions; +let logMsg msg x = printf "%s%i" msg x; x //without linefeed +let logMsgN msg x = printfn "%s%i" msg x; x //with linefeed + +// new composed function with new improved logging! +let mult3ThenSquareLogged = + logMsg "before=" + >> mult3 + >> logMsg " after mult3=" + >> square + >> logMsgN " result=" + +// test +mult3ThenSquareLogged 5 +[1..10] |> List.map mult3ThenSquareLogged //apply to a whole list +``` + +Our new function, `mult3ThenSquareLogged`, has an ugly name, but it is easy to use and nicely hides the complexity of the functions that went into it. You can see that if you define your building block functions well, this composition of functions can be a powerful way to get new functionality. + +But wait, there's more! Functions are first class entities in F#, and can be acted on by any other F# code. Here is an example of using the composition operator to collapse a list of functions into a single operation. + +```fsharp +let listOfFunctions = [ + mult3; + square; + add2; + logMsgN "result="; + ] + +// compose all functions in the list into a single one +let allFunctions = List.reduce (>>) listOfFunctions + +//test +allFunctions 5 +``` + +## Mini languages + +Domain-specific languages (DSLs) are well recognized as a technique to create more readable and concise code. The functional approach is very well suited for this. + +If you need to, you can go the route of having a full "external" DSL with its own lexer, parser, and so on, and there are various toolsets for F# that make this quite straightforward. + +But in many cases, it is easier to stay within the syntax of F#, and just design a set of "verbs" and "nouns" that encapsulate the behavior we want. + +The ability to create new types concisely and then match against them makes it very easy to set up fluent interfaces quickly. For example, here is a little function that calculates dates using a simple vocabulary. Note that two new enum-style types are defined just for this one function. + +```fsharp +// set up the vocabulary +type DateScale = Hour | Hours | Day | Days | Week | Weeks +type DateDirection = Ago | Hence + +// define a function that matches on the vocabulary +let getDate interval scale direction = + let absHours = match scale with + | Hour | Hours -> 1 * interval + | Day | Days -> 24 * interval + | Week | Weeks -> 24 * 7 * interval + let signedHours = match direction with + | Ago -> -1 * absHours + | Hence -> absHours + System.DateTime.Now.AddHours(float signedHours) + +// test some examples +let example1 = getDate 5 Days Ago +let example2 = getDate 1 Hour Hence + +// the C# equivalent would probably be more like this: +// getDate().Interval(5).Days().Ago() +// getDate().Interval(1).Hour().Hence() +``` + +The example above only has one "verb", using lots of types for the "nouns". + +The following example demonstrates how you might build the functional equivalent of a fluent interface with many "verbs". + +Say that we are creating a drawing program with various shapes. Each shape has a color, size, label and action to be performed when clicked, and we want a fluent interface to configure each shape. + +Here is an example of what a simple method chain for a fluent interface in C# might look like: + +```fsharp +FluentShape.Default + .SetColor("red") + .SetLabel("box") + .OnClick( s => Console.Write("clicked") ); +``` + +Now the concept of "fluent interfaces" and "method chaining" is really only relevant for object-oriented design. In a functional language like F#, the nearest equivalent would be the use of the pipeline operator to chain a set of functions together. + +Let's start with the underlying Shape type: + +```fsharp +// create an underlying type +type FluentShape = { + label : string; + color : string; + onClick : FluentShape->FluentShape // a function type + } +``` + +We'll add some basic functions: + +```fsharp +let defaultShape = + {label=""; color=""; onClick=fun shape->shape} + +let click shape = + shape.onClick shape + +let display shape = + printfn "My label=%s and my color=%s" shape.label shape.color + shape //return same shape +``` + +For "method chaining" to work, every function should return an object that can be used next in the chain. So you will see that the "`display`" function returns the shape, rather than nothing. + +Next we create some helper functions which we expose as the "mini-language", and will be used as building blocks by the users of the language. + +```fsharp +let setLabel label shape = + {shape with FluentShape.label = label} + +let setColor color shape = + {shape with FluentShape.color = color} + +//add a click action to what is already there +let appendClickAction action shape = + {shape with FluentShape.onClick = shape.onClick >> action} +``` + +Notice that `appendClickAction` takes a function as a parameter and composes it with the existing click action. As you start getting deeper into the functional approach to reuse, you start seeing many more "higher order functions" like this, that is, functions that act on other functions. Combining functions like this is one of the keys to understanding the functional way of programming. + +Now as a user of this "mini-language", I can compose the base helper functions into more complex functions of my own, creating my own function library. (In C# this kind of thing might be done using extension methods.) + +```fsharp +// Compose two "base" functions to make a compound function. +let setRedBox = setColor "red" >> setLabel "box" + +// Create another function by composing with previous function. +// It overrides the color value but leaves the label alone. +let setBlueBox = setRedBox >> setColor "blue" + +// Make a special case of appendClickAction +let changeColorOnClick color = appendClickAction (setColor color) +``` + +I can then combine these functions together to create objects with the desired behavior. + +```fsharp +//setup some test values +let redBox = defaultShape |> setRedBox +let blueBox = defaultShape |> setBlueBox + +// create a shape that changes color when clicked +redBox + |> display + |> changeColorOnClick "green" + |> click + |> display // new version after the click + +// create a shape that changes label and color when clicked +blueBox + |> display + |> appendClickAction (setLabel "box2" >> setColor "green") + |> click + |> display // new version after the click +``` + +In the second case, I actually pass two functions to `appendClickAction`, but I compose them into one first. This kind of thing is trivial to do with a well structured functional library, but it is quite hard to do in C# without having lambdas within lambdas. + +Here is a more complex example. We will create a function "`showRainbow`" that, for each color in the rainbow, sets the color and displays the shape. + +```fsharp +let rainbow = + ["red";"orange";"yellow";"green";"blue";"indigo";"violet"] + +let showRainbow = + let setColorAndDisplay color = setColor color >> display + rainbow + |> List.map setColorAndDisplay + |> List.reduce (>>) + +// test the showRainbow function +defaultShape |> showRainbow +``` + +Notice that the functions are getting more complex, but the amount of code is still quite small. One reason for this is that the function parameters can often be ignored when doing function composition, which reduces visual clutter. For example, the "`showRainbow`" function does take a shape as a parameter, but it is not explicitly shown! This elision of parameters is called "point-free" style and will be discussed further in the ["thinking functionally"](../series/thinking-functionally.md) series diff --git a/Why use F#/origin/posts/conciseness-intro.md b/Why use F#/origin/posts/conciseness-intro.md new file mode 100644 index 0000000..ebfd793 --- /dev/null +++ b/Why use F#/origin/posts/conciseness-intro.md @@ -0,0 +1,29 @@ +--- +layout: post +title: "Conciseness" +description: "Why is conciseness important?" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 7 +categories: [Conciseness] +--- + +After having seen some simple code, we will now move on to demonstrating the major themes (conciseness, convenience, correctness, concurrency and completeness), filtered through the concepts of types, functions and pattern matching. + +With the next few posts, we'll examine the features of F# that aid conciseness and readability. + +An important goal for most mainstream programming languages is a good balance of readability and conciseness. Too much conciseness can result in hard-to-understand or obfuscated code (APL anyone?), while too much verbosity can easily swamp the underlying meaning. Ideally, we want a high signal-to-noise ratio, where every word and character in the code contributes to the meaning of the code, and there is minimal boilerplate. + +Why is conciseness important? Here are a few reasons: + +* **A concise language tends to be more declarative**, saying *what* the code should do rather than *how* to do it. That is, declarative code is more focused on the high-level logic rather than the +nuts and bolts of the implementation. +* **It is easier to reason about correctness** if there are fewer lines of code to reason about! +* And of course, **you can see more code on a screen** at a time. This might seem trivial, but the more you can see, the more you can grasp as well. + +As you have seen, compared with C#, F# is generally much more concise. This is due to features such as: + +* **Type inference** and **low overhead type definitions**. One of the major reasons for F#'s conciseness and readability is its type system. F# makes it very easy to create new types as you need them. They don't cause visual clutter either in their definition or in use, and the type inference system means that you can use them freely without getting distracted by complex type syntax. +* **Using functions to extract boilerplate code**. The DRY principle ("don't repeat yourself") is a core principle of good design in functional languages as well as object-oriented languages. In F# it is extremely easy to extract repetitive code into common utility functions, which allows you to focus on the important stuff. +* **Composing complex code from simple functions** and **creating mini-languages**. The functional approach makes it easy to create a set of basic operations and then combine these building blocks in various ways to build up more complex behaviors. In this way, even the most complex code is still very concise and readable. +* **Pattern matching**. We've seen pattern matching as a glorified switch statement, but in fact it is much more general, as it can compare expressions in a number of ways, matching on values, conditions, and types, and then assign or extract values, all at the same time. diff --git a/Why use F#/origin/posts/conciseness-pattern-matching.md b/Why use F#/origin/posts/conciseness-pattern-matching.md new file mode 100644 index 0000000..0b490e1 --- /dev/null +++ b/Why use F#/origin/posts/conciseness-pattern-matching.md @@ -0,0 +1,65 @@ +--- +layout: post +title: "Pattern matching for conciseness" +description: "Pattern matching can match and bind in a single step" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 12 +categories: [Conciseness, Patterns] +--- + +So far we have seen the pattern matching logic in the `match..with` expression, where it seems to be just a switch/case statement. But in fact pattern matching is much more general ? it can compare expressions in a number of ways, matching on values, conditions, and types, and then assign or extract values, all at the same time. + +Pattern matching will be discussed in depth in later posts, but to start with, here is a little taster of one way that it aids conciseness. +We'll look at the way pattern matching is used for binding values to expressions (the functional equivalent of assigning to variables). + +In the following examples, we are binding to the internal members of tuples and lists directly: + +```fsharp +//matching tuples directly +let firstPart, secondPart, _ = (1,2,3) // underscore means ignore + +//matching lists directly +let elem1::elem2::rest = [1..10] // ignore the warning for now + +//matching lists inside a match..with +let listMatcher aList = + match aList with + | [] -> printfn "the list is empty" + | [firstElement] -> printfn "the list has one element %A " firstElement + | [first; second] -> printfn "list is %A and %A" first second + | _ -> printfn "the list has more than two elements" + +listMatcher [1;2;3;4] +listMatcher [1;2] +listMatcher [1] +listMatcher [] +``` + +You can also bind values to the inside of complex structures such as records. In the following example, we will create an "`Address`" type, and then a "`Customer`" type which contains an address. Next, we will create a customer value, and then match various properties against it. + +```fsharp +// create some types +type Address = { Street: string; City: string; } +type Customer = { ID: int; Name: string; Address: Address} + +// create a customer +let customer1 = { ID = 1; Name = "Bob"; + Address = {Street="123 Main"; City="NY" } } + +// extract name only +let { Name=name1 } = customer1 +printfn "The customer is called %s" name1 + +// extract name and id +let { ID=id2; Name=name2; } = customer1 +printfn "The customer called %s has id %i" name2 id2 + +// extract name and address +let { Name=name3; Address={Street=street3} } = customer1 +printfn "The customer is called %s and lives on %s" name3 street3 +``` + +In the last example, note how we could reach right into the `Address` substructure and pull out the street as well as the customer name. + +This ability to process a nested structure, extract only the fields you want, and assign them to values, all in a single step, is very useful. It removes quite a bit of coding drudgery, and is another factor in the conciseness of typical F# code. diff --git a/Why use F#/origin/posts/conciseness-type-definitions.md b/Why use F#/origin/posts/conciseness-type-definitions.md new file mode 100644 index 0000000..3afaa18 --- /dev/null +++ b/Why use F#/origin/posts/conciseness-type-definitions.md @@ -0,0 +1,105 @@ +--- +layout: post +title: "Low overhead type definitions" +description: "No penalty for making new types" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 9 +categories: [Conciseness,Types] +--- + +In C#, there is a disincentive for creating new types ? the lack of type inference means you need to explicitly specify types in most places, resulting in brittleness and more visual clutter. As a result, there is always a temptation to create monolithic classes rather than modularizing them. + +In F# there is no penalty for making new types, so it is quite common to have hundreds if not thousands of them. Every time you need to define a structure, you can create a special type, rather than reusing (and overloading) existing types such as strings and lists. + +This means that your programs will be more type-safe, more self documenting, and more maintainable (because when the types change you will immediately get compile-time errors rather than runtime errors). + +Here are some examples of one-liner types in F#: + +```fsharp +open System + +// some "record" types +type Person = {FirstName:string; LastName:string; Dob:DateTime} +type Coord = {Lat:float; Long:float} + +// some "union" (choice) types +type TimePeriod = Hour | Day | Week | Year +type Temperature = C of int | F of int +type Appointment = OneTime of DateTime + | Recurring of DateTime list +``` + + +## F# types and domain driven design + +The conciseness of the type system in F# is particularly useful when doing domain driven design (DDD). In DDD, for each real world entity and value object, you ideally want to have a corresponding type. This can mean creating hundreds of "little" types, which can be tedious in C#. + +Furthermore, "value" objects in DDD should have structural equality, meaning that two objects containing the same data should always be equal. In C# this can mean more tedium in overriding `IEquatable`, but in F#, you get this for free by default. + +To show how easy it is to create DDD types in F#, here are some example types that might be created for a simple "customer" domain. + +```fsharp +type PersonalName = {FirstName:string; LastName:string} + +// Addresses +type StreetAddress = {Line1:string; Line2:string; Line3:string } + +type ZipCode = ZipCode of string +type StateAbbrev = StateAbbrev of string +type ZipAndState = {State:StateAbbrev; Zip:ZipCode } +type USAddress = {Street:StreetAddress; Region:ZipAndState} + +type UKPostCode = PostCode of string +type UKAddress = {Street:StreetAddress; Region:UKPostCode} + +type InternationalAddress = { + Street:StreetAddress; Region:string; CountryName:string} + +// choice type -- must be one of these three specific types +type Address = USAddress | UKAddress | InternationalAddress + +// Email +type Email = Email of string + +// Phone +type CountryPrefix = Prefix of int +type Phone = {CountryPrefix:CountryPrefix; LocalNumber:string} + +type Contact = + { + PersonalName: PersonalName; + // "option" means it might be missing + Address: Address option; + Email: Email option; + Phone: Phone option; + } + +// Put it all together into a CustomerAccount type +type CustomerAccountId = AccountId of string +type CustomerType = Prospect | Active | Inactive + +// override equality and deny comparison +[] +type CustomerAccount = + { + CustomerAccountId: CustomerAccountId; + CustomerType: CustomerType; + ContactInfo: Contact; + } + + override this.Equals(other) = + match other with + | :? CustomerAccount as otherCust -> + (this.CustomerAccountId = otherCust.CustomerAccountId) + | _ -> false + + override this.GetHashCode() = hash this.CustomerAccountId +``` + +This code fragment contains 17 type definitions in just a few lines, but with minimal complexity. How many lines of C# code would you need to do the same thing? + +Obviously, this is a simplified version with just the basic types ? in a real system, constraints and other methods would be added. But note how easy it is to create lots of DDD value objects, especially wrapper types for strings, such as "`ZipCode`" and "`Email`". By using these wrapper types, we can enforce certain constraints at creation time, and also ensure that these types don't get confused with unconstrained strings in normal code. The only "entity" type is the `CustomerAccount`, which is clearly indicated as having special treatment for equality and comparison. + +For a more in-depth discussion, see the series called ["Domain driven design in F#"](../series/domain-driven-design-in-fsharp.md). + diff --git a/Why use F#/origin/posts/conciseness-type-inference.md b/Why use F#/origin/posts/conciseness-type-inference.md new file mode 100644 index 0000000..e2a89db --- /dev/null +++ b/Why use F#/origin/posts/conciseness-type-inference.md @@ -0,0 +1,71 @@ +--- +layout: post +title: "Type inference" +description: "How to avoid getting distracted by complex type syntax" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 8 +categories: [Conciseness,Types] +--- + + +As you have already seen, F# uses a technique called "type inference" to greatly reduce the number of type annotations that need to be explicitly specified in normal code. And even when types do need to be specified, the syntax is less longwinded compared to C#. + +To see this, here are some C# methods that wrap two standard LINQ functions. The implementations are trivial, but the method signatures are extremely complex: + +```csharp +public IEnumerable Where( + IEnumerable source, + Func predicate + ) +{ + //use the standard LINQ implementation + return source.Where(predicate); +} + +public IEnumerable> GroupBy( + IEnumerable source, + Func keySelector + ) +{ + //use the standard LINQ implementation + return source.GroupBy(keySelector); +} +``` + +And here are the exact F# equivalents, showing that no type annotations are needed at all! + +```fsharp +let Where source predicate = + //use the standard F# implementation + Seq.filter predicate source + +let GroupBy source keySelector = + //use the standard F# implementation + Seq.groupBy keySelector source +``` + +
+You might notice that the standard F# implementations for "filter" and "groupBy" have the parameters in exactly the opposite order from the LINQ implementations used in C#. The "source" parameter is placed last, rather than first. There is a reason for this, which will be explained in the thinking functionally series. +
+ +The type inference algorithm is excellent at gathering information from many sources to determine the types. In the following example, it correctly deduces that the `list` value is a list of strings. + +```fsharp +let i = 1 +let s = "hello" +let tuple = s,i // pack into tuple +let s2,i2 = tuple // unpack +let list = [s2] // type is string list +``` + +And in this example, it correctly deduces that the `sumLengths` function takes a list of strings and returns an int. + +```fsharp +let sumLengths strList = + strList |> List.map String.length |> List.sum + +// function type is: string list -> int +``` + + diff --git a/Why use F#/origin/posts/concurrency-actor-model.md b/Why use F#/origin/posts/concurrency-actor-model.md new file mode 100644 index 0000000..f23aa0f --- /dev/null +++ b/Why use F#/origin/posts/concurrency-actor-model.md @@ -0,0 +1,410 @@ +--- +layout: post +title: "Messages and Agents" +description: "Making it easier to think about concurrency" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 25 +categories: [Concurrency] +--- + +In this post, we'll look at the message-based (or actor-based) approach to concurrency. + +In this approach, when one task wants to communicate with another, it sends it a message, rather than contacting it directly. The messages are put on a queue, and the receiving task (known as an "actor" or "agent") pulls the messages off the queue one at a time to process them. + +This message-based approach has been applied to many situations, from low-level network sockets (built on TCP/IP) to enterprise wide application integration systems (for example MSMQ or IBM WebSphere MQ). + +From a software design point of view, a message-based approach has a number of benefits: + +* You can manage shared data and resources without locks. +* You can easily follow the "single responsibility principle", because each agent can be designed to do only one thing. +* It encourages a "pipeline" model of programming with "producers" sending messages to decoupled "consumers", which has additional benefits: + * The queue acts as a buffer, eliminating waiting on the client side. + * It is straightforward to scale up one side or the other of the queue as needed in order to maximize throughput. + * Errors can be handled gracefully, because the decoupling means that agents can be created and destroyed without affecting their clients. + +From a practical developer's point of view, what I find most appealing about the message-based approach is that when writing the code for any given actor, you don't have to hurt your brain by thinking about concurrency. The message queue forces a "serialization" of operations that otherwise might occur concurrently. And this in turn makes it much easier to think about (and write code for) the logic for processing a message, because you can be sure that your code will be isolated from other events that might interrupt your flow. + +With these advantages, it is not surprising that when a team inside Ericsson wanted to design a programming language for writing highly-concurrent telephony applications, they created one with a message-based approach, namely Erlang. Erlang has now become the poster child for the whole topic, and has created a lot of interest in implementing the same approach in other languages. + +## How F# implements a message-based approach ## + +F# has a built-in agent class called `MailboxProcessor`. These agents are very lightweight compared with threads - you can instantiate tens of thousands of them at the same time. + +These are similar to the agents in Erlang, but unlike the Erlang ones, they do *not* work across process boundaries, only in the same process. +And unlike a heavyweight queueing system such as MSMQ, the messages are not persistent. If your app crashes, the messages are lost. + +But these are minor issues, and can be worked around. In a future series, I will go into alternative implementations of message queues. The fundamental approach is the same in all cases. + +Let's see a simple agent implementation in F#: + +```fsharp + +#nowarn "40" +let printerAgent = MailboxProcessor.Start(fun inbox-> + + // the message processing function + let rec messageLoop = async{ + + // read a message + let! msg = inbox.Receive() + + // process a message + printfn "message is: %s" msg + + // loop to top + return! messageLoop + } + + // start the loop + messageLoop + ) + +``` + +The `MailboxProcessor.Start` function takes a simple function parameter. That function loops forever, reading messages from the queue (or "inbox") and processing them. + +*Note: I have added the #nowarn "40" pragma to avoid the warning "FS0040", which can be safely ignored in this case.* + +Here's the example in use: + +```fsharp +// test it +printerAgent.Post "hello" +printerAgent.Post "hello again" +printerAgent.Post "hello a third time" +``` + +In the rest of this post we'll look at two slightly more useful examples: + +* Managing shared state without locks +* Serialized and buffered access to shared IO + +In both of these cases, a message based approach to concurrency is elegant, efficient, and easy to program. + +## Managing shared state ## + +Let's look at the shared state problem first. + +A common scenario is that you have some state that needs to be accessed and changed by multiple concurrent tasks or threads. +We'll use a very simple case, and say that the requirements are: + +* A shared "counter" and "sum" that can be incremented by multiple tasks concurrently. +* Changes to the counter and sum must be atomic -- we must guarantee that they will both be updated at the same time. + +### The locking approach to shared state ### + +Using locks or mutexes is a common solution for these requirements, so let's write some code using a lock, and see how it performs. + +First let's write a static `LockedCounter` class that protects the state with locks. + +```fsharp +open System +open System.Threading +open System.Diagnostics + +// a utility function +type Utility() = + static let rand = new Random() + + static member RandomSleep() = + let ms = rand.Next(1,10) + Thread.Sleep ms + +// an implementation of a shared counter using locks +type LockedCounter () = + + static let _lock = new Object() + + static let mutable count = 0 + static let mutable sum = 0 + + static let updateState i = + // increment the counters and... + sum <- sum + i + count <- count + 1 + printfn "Count is: %i. Sum is: %i" count sum + + // ...emulate a short delay + Utility.RandomSleep() + + + // public interface to hide the state + static member Add i = + // see how long a client has to wait + let stopwatch = new Stopwatch() + stopwatch.Start() + + // start lock. Same as C# lock{...} + lock _lock (fun () -> + + // see how long the wait was + stopwatch.Stop() + printfn "Client waited %i" stopwatch.ElapsedMilliseconds + + // do the core logic + updateState i + ) + // release lock +``` + +Some notes on this code: + +* This code is written using a very imperative approach, with mutable variables and locks +* The public `Add` method has explicit `Monitor.Enter` and `Monitor.Exit` expressions to get and release the lock. This is the same as the `lock{...}` statement in C#. +* We've also added a stopwatch to measure how long a client has to wait to get the lock. +* The core "business logic" is the `updateState` method, which not only updates the state, but adds a small random wait as well to emulate the time taken to do the processing. + +Let's test it in isolation: + +```fsharp +// test in isolation +LockedCounter.Add 4 +LockedCounter.Add 5 +``` + +Next, we'll create a task that will try to access the counter: + +```fsharp +let makeCountingTask addFunction taskId = async { + let name = sprintf "Task%i" taskId + for i in [1..3] do + addFunction i + } + +// test in isolation +let task = makeCountingTask LockedCounter.Add 1 +Async.RunSynchronously task +``` + +In this case, when there is no contention at all, the wait times are all 0. + +But what happens when we create 10 child tasks that all try to access the counter at once: + +```fsharp +let lockedExample5 = + [1..10] + |> List.map (fun i -> makeCountingTask LockedCounter.Add i) + |> Async.Parallel + |> Async.RunSynchronously + |> ignore +``` + +Oh dear! Most tasks are now waiting quite a while. If two tasks want to update the state at the same time, one must wait for the other's work to complete before it can do its own work, which affects performance. + +And if we add more and more tasks, the contention will increase, and the tasks will spend more and more time waiting rather than working. + +### The message-based approach to shared state ### + +Let's see how a message queue might help us. Here's the message based version: + +```fsharp +type MessageBasedCounter () = + + static let updateState (count,sum) msg = + + // increment the counters and... + let newSum = sum + msg + let newCount = count + 1 + printfn "Count is: %i. Sum is: %i" newCount newSum + + // ...emulate a short delay + Utility.RandomSleep() + + // return the new state + (newCount,newSum) + + // create the agent + static let agent = MailboxProcessor.Start(fun inbox -> + + // the message processing function + let rec messageLoop oldState = async{ + + // read a message + let! msg = inbox.Receive() + + // do the core logic + let newState = updateState oldState msg + + // loop to top + return! messageLoop newState + } + + // start the loop + messageLoop (0,0) + ) + + // public interface to hide the implementation + static member Add i = agent.Post i +``` + +Some notes on this code: + +* The core "business logic" is again in the `updateState` method, which has almost the same implementation as the earlier example, except the state is immutable, so that a new state is created and returned to the main loop. +* The agent reads messages (simple ints in this case) and then calls `updateState` method +* The public method `Add` posts a message to the agent, rather than calling the `updateState` method directly +* This code is written in a more functional way; there are no mutable variables and no locks anywhere. In fact, there is no code dealing with concurrency at all! +The code only has to focus on the business logic, and is consequently much easier to understand. + +Let's test it in isolation: + +```fsharp +// test in isolation +MessageBasedCounter.Add 4 +MessageBasedCounter.Add 5 +``` + +Next, we'll reuse a task we defined earlier, but calling `MessageBasedCounter.Add` instead: + +```fsharp +let task = makeCountingTask MessageBasedCounter.Add 1 +Async.RunSynchronously task +``` + +Finally let's create 5 child tasks that try to access the counter at once. + +```fsharp +let messageExample5 = + [1..5] + |> List.map (fun i -> makeCountingTask MessageBasedCounter.Add i) + |> Async.Parallel + |> Async.RunSynchronously + |> ignore +``` + +We can't measure the waiting time for the clients, because there is none! + +## Shared IO ## + +A similar concurrency problem occurs when accessing a shared IO resource such as a file: + +* If the IO is slow, the clients can spend a lot of time waiting, even without locks. +* If multiple threads write to the resource at the same time, you can get corrupted data. + +Both problems can be solved by using asynchronous calls combined with buffering -- exactly what a message queue does. + +In this next example, we'll consider the example of a logging service that many clients will write to concurrently. +(In this trivial case, we'll just write directly to the Console.) + +We'll first look at an implementation without concurrency control, and then at an implementation that uses message queues to serialize all requests. + +### IO without serialization ### + +In order to make the corruption very obvious and repeatable, let's first create a "slow" console that writes each individual character in the log message +and pauses for a millisecond between each character. During that millisecond, another thread could be writing as well, causing an undesirable +interleaving of messages. + +```fsharp +let slowConsoleWrite msg = + msg |> String.iter (fun ch-> + System.Threading.Thread.Sleep(1) + System.Console.Write ch + ) + +// test in isolation +slowConsoleWrite "abc" +``` + +Next, we will create a simple task that loops a few times, writing its name each time to the logger: + +```fsharp +let makeTask logger taskId = async { + let name = sprintf "Task%i" taskId + for i in [1..3] do + let msg = sprintf "-%s:Loop%i-" name i + logger msg + } + +// test in isolation +let task = makeTask slowConsoleWrite 1 +Async.RunSynchronously task +``` + + +Next, we write a logging class that encapsulates access to the slow console. It has no locking or serialization, and is basically not thread-safe: + +```fsharp +type UnserializedLogger() = + // interface + member this.Log msg = slowConsoleWrite msg + +// test in isolation +let unserializedLogger = UnserializedLogger() +unserializedLogger.Log "hello" +``` + +Now let's combine all these into a real example. We will create five child tasks and run them in parallel, all trying to write to the slow console. + +```fsharp +let unserializedExample = + let logger = new UnserializedLogger() + [1..5] + |> List.map (fun i -> makeTask logger.Log i) + |> Async.Parallel + |> Async.RunSynchronously + |> ignore +``` + +Ouch! The output is very garbled! + +### Serialized IO with messages ### + +So what happens when we replace `UnserializedLogger` with a `SerializedLogger` class that encapsulates a message queue. + +The agent inside `SerializedLogger` simply reads a message from its input queue and writes it to the slow console. Again there is no code dealing with concurrency and no locks are used. + +```fsharp +type SerializedLogger() = + + // create the mailbox processor + let agent = MailboxProcessor.Start(fun inbox -> + + // the message processing function + let rec messageLoop () = async{ + + // read a message + let! msg = inbox.Receive() + + // write it to the log + slowConsoleWrite msg + + // loop to top + return! messageLoop () + } + + // start the loop + messageLoop () + ) + + // public interface + member this.Log msg = agent.Post msg + +// test in isolation +let serializedLogger = SerializedLogger() +serializedLogger.Log "hello" +``` + +So now we can repeat the earlier unserialized example but using the `SerializedLogger` instead. Again, we create five child tasks and run them in parallel: + +```fsharp +let serializedExample = + let logger = new SerializedLogger() + [1..5] + |> List.map (fun i -> makeTask logger.Log i) + |> Async.Parallel + |> Async.RunSynchronously + |> ignore +``` + +What a difference! This time the output is perfect. + + +## Summary ## + +There is much more to say about this message based approach. In a future series, I hope to go into much more detail, including discussion of topics such as: + +* alternative implementations of message queues with MSMQ and TPL Dataflow. +* cancellation and out of band messages. +* error handling and retries, and handling exceptions in general. +* how to scale up and down by creating or removing child agents. +* avoiding buffer overruns and detecting starvation or inactivity. diff --git a/Why use F#/origin/posts/concurrency-async-and-parallel.md b/Why use F#/origin/posts/concurrency-async-and-parallel.md new file mode 100644 index 0000000..3291a1c --- /dev/null +++ b/Why use F#/origin/posts/concurrency-async-and-parallel.md @@ -0,0 +1,454 @@ +--- +layout: post +title: "Asynchronous programming" +description: "Encapsulating a background task with the Async class" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 24 +categories: [Concurrency] +--- + +In this post we'll have a look at a few ways to write asynchronous code in F#, and a very brief example of parallelism as well. + +## Traditional asynchronous programming ## + +As noted in the previous post, F# can directly use all the usual .NET suspects, such as `Thread` `AutoResetEvent`, `BackgroundWorker` and `IAsyncResult`. + +Let's see a simple example where we wait for a timer event to go off: + +```fsharp +open System + +let userTimerWithCallback = + // create an event to wait on + let event = new System.Threading.AutoResetEvent(false) + + // create a timer and add an event handler that will signal the event + let timer = new System.Timers.Timer(2000.0) + timer.Elapsed.Add (fun _ -> event.Set() |> ignore ) + + //start + printfn "Waiting for timer at %O" DateTime.Now.TimeOfDay + timer.Start() + + // keep working + printfn "Doing something useful while waiting for event" + + // block on the timer via the AutoResetEvent + event.WaitOne() |> ignore + + //done + printfn "Timer ticked at %O" DateTime.Now.TimeOfDay +``` + +This shows the use of `AutoResetEvent` as a synchronization mechanism. + +* A lambda is registered with the `Timer.Elapsed` event, and when the event is triggered, the AutoResetEvent is signalled. +* The main thread starts the timer, does something else while waiting, and then blocks until the event is triggered. +* Finally, the main thread continues, about 2 seconds later. + +The code above is reasonably straightforward, but does require you to instantiate an AutoResetEvent, +and could be buggy if the lambda is defined incorrectly. + +## Introducing asynchronous workflows ## + +F# has a built-in construct called "asynchronous workflows" which makes async code much easier to write. +These workflows are objects that encapsulate a background task, and provide a number of useful operations to manage them. + +Here's the previous example rewritten to use one: + +```fsharp +open System +//open Microsoft.FSharp.Control // Async.* is in this module. + +let userTimerWithAsync = + + // create a timer and associated async event + let timer = new System.Timers.Timer(2000.0) + let timerEvent = Async.AwaitEvent (timer.Elapsed) |> Async.Ignore + + // start + printfn "Waiting for timer at %O" DateTime.Now.TimeOfDay + timer.Start() + + // keep working + printfn "Doing something useful while waiting for event" + + // block on the timer event now by waiting for the async to complete + Async.RunSynchronously timerEvent + + // done + printfn "Timer ticked at %O" DateTime.Now.TimeOfDay +``` + +Here are the changes: + +* the `AutoResetEvent` and lambda have disappeared, and are replaced by `let timerEvent = Control.Async.AwaitEvent (timer.Elapsed)`, which creates an `async` object directly from the event, without needing a lambda. The `ignore` is added to ignore the result. +* the `event.WaitOne()` has been replaced by `Async.RunSynchronously timerEvent` which blocks on the async object until it has completed. + +That's it. Both simpler and easier to understand. + +The async workflows can also be used with `IAsyncResult`, begin/end pairs, and other standard .NET methods. + +For example, here's how you might do an async file write by wrapping the `IAsyncResult` generated from `BeginWrite`. + +```fsharp +let fileWriteWithAsync = + + // create a stream to write to + use stream = new System.IO.FileStream("test.txt",System.IO.FileMode.Create) + + // start + printfn "Starting async write" + let asyncResult = stream.BeginWrite(Array.empty,0,0,null,null) + + // create an async wrapper around an IAsyncResult + let async = Async.AwaitIAsyncResult(asyncResult) |> Async.Ignore + + // keep working + printfn "Doing something useful while waiting for write to complete" + + // block on the timer now by waiting for the async to complete + Async.RunSynchronously async + + // done + printfn "Async write completed" +``` + +## Creating and nesting asynchronous workflows ## + +Asynchronous workflows can also be created manually. +A new workflow is created using the `async` keyword and curly braces. +The braces contain a set of expressions to be executed in the background. + +This simple workflow just sleeps for 2 seconds. + +```fsharp +let sleepWorkflow = async{ + printfn "Starting sleep workflow at %O" DateTime.Now.TimeOfDay + do! Async.Sleep 2000 + printfn "Finished sleep workflow at %O" DateTime.Now.TimeOfDay + } + +Async.RunSynchronously sleepWorkflow +``` + +*Note: the code `do! Async.Sleep 2000` is similar to `Thread.Sleep` but designed to work with asynchronous workflows.* + +Workflows can contain *other* async workflows nested inside them. +Within the braces, the nested workflows can be blocked on by using the `let!` syntax. + +```fsharp +let nestedWorkflow = async{ + + printfn "Starting parent" + let! childWorkflow = Async.StartChild sleepWorkflow + + // give the child a chance and then keep working + do! Async.Sleep 100 + printfn "Doing something useful while waiting " + + // block on the child + let! result = childWorkflow + + // done + printfn "Finished parent" + } + +// run the whole workflow +Async.RunSynchronously nestedWorkflow +``` + + +## Cancelling workflows ## + +One very convenient thing about async workflows is that they support a built-in cancellation mechanism. No special code is needed. + +Consider a simple task that prints numbers from 1 to 100: + +```fsharp +let testLoop = async { + for i in [1..100] do + // do something + printf "%i before.." i + + // sleep a bit + do! Async.Sleep 10 + printfn "..after" + } +``` + +We can test it in the usual way: + +```fsharp +Async.RunSynchronously testLoop +``` + +Now let's say we want to cancel this task half way through. What would be the best way of doing it? + +In C#, we would have to create flags to pass in and then check them frequently, but in F# this technique is built in, using the `CancellationToken` class. + +Here an example of how we might cancel the task: + +```fsharp +open System +open System.Threading + +// create a cancellation source +let cancellationSource = new CancellationTokenSource() + +// start the task, but this time pass in a cancellation token +Async.Start (testLoop,cancellationSource.Token) + +// wait a bit +Thread.Sleep(200) + +// cancel after 200ms +cancellationSource.Cancel() +``` + +In F#, any nested async call will check the cancellation token automatically! + +In this case it was the line: + +```fsharp +do! Async.Sleep(10) +``` + +As you can see from the output, this line is where the cancellation happened. + +## Composing workflows in series and parallel ## + +Another useful thing about async workflows is that they can be easily combined in various ways: both in series and in parallel. + +Let's again create a simple workflow that just sleeps for a given time: + +```fsharp +// create a workflow to sleep for a time +let sleepWorkflowMs ms = async { + printfn "%i ms workflow started" ms + do! Async.Sleep ms + printfn "%i ms workflow finished" ms + } +``` + +Here's a version that combines two of these in series: + +```fsharp +let workflowInSeries = async { + let! sleep1 = sleepWorkflowMs 1000 + printfn "Finished one" + let! sleep2 = sleepWorkflowMs 2000 + printfn "Finished two" + } + +#time +Async.RunSynchronously workflowInSeries +#time +``` + +And here's a version that combines two of these in parallel: + +```fsharp +// Create them +let sleep1 = sleepWorkflowMs 1000 +let sleep2 = sleepWorkflowMs 2000 + +// run them in parallel +#time +[sleep1; sleep2] + |> Async.Parallel + |> Async.RunSynchronously +#time +``` + +
+Note: The #time command toggles the timer on and off. It only works in the interactive window, so this example must be sent to the interactive window in order to work corrrectly. +
+ +We're using the `#time` option to show the total elapsed time, which, because they run in parallel, is 2 secs. If they ran in series instead, it would take 3 seconds. + +Also you might see that the output is garbled sometimes because both tasks are writing to the console at the same time! + +This last sample is a classic example of a "fork/join" approach, where a number of a child tasks are spawned and then the parent waits for them +all to finish. As you can see, F# makes this very easy! + +## Example: an async web downloader ## + +In this more realistic example, we'll see how easy it is to convert some existing code from a non-asynchronous style to an asynchronous style, +and the corresponding performance increase that can be achieved. + +So here is a simple URL downloader, very similar to the one we saw at the start of the series: + +```fsharp +open System.Net +open System +open System.IO + +let fetchUrl url = + let req = WebRequest.Create(Uri(url)) + use resp = req.GetResponse() + use stream = resp.GetResponseStream() + use reader = new IO.StreamReader(stream) + let html = reader.ReadToEnd() + printfn "finished downloading %s" url +``` + +And here is some code to time it: + +```fsharp +// a list of sites to fetch +let sites = ["http://www.bing.com"; + "http://www.google.com"; + "http://www.microsoft.com"; + "http://www.amazon.com"; + "http://www.yahoo.com"] + +#time // turn interactive timer on +sites // start with the list of sites +|> List.map fetchUrl // loop through each site and download +#time // turn timer off +``` + +Make a note of the time taken, and let's if we can improve on it! + +Obviously the example above is inefficient -- only one web site at a time is visited. The program would be faster if we could visit them all at the same time. + +So how would we convert this to a concurrent algorithm? The logic would be something like: + +* Create a task for each web page we are downloading, and then for each task, the download logic would be something like: + * Start downloading a page from a website. While that is going on, pause and let other tasks have a turn. + * When the download is finished, wake up and continue on with the task +* Finally, start all the tasks up and let them go at it! + +Unfortunately, this is quite hard to do in a standard C-like language. In C# for example, you have to create a callback for when an async task completes. Managing these callbacks is painful and creates a lot of extra support code that gets in the way of understanding the logic. There are some elegant solutions to this, but in general, the signal to noise ratio for concurrent programming in C# is very high*. + +* As of the time of this writing. Future versions of C# will have the `await` keyword, which is similar to what F# has now. + +But as you can guess, F# makes this easy. Here is the concurrent F# version of the downloader code: + +```fsharp +open Microsoft.FSharp.Control.CommonExtensions + // adds AsyncGetResponse + +// Fetch the contents of a web page asynchronously +let fetchUrlAsync url = + async { + let req = WebRequest.Create(Uri(url)) + use! resp = req.AsyncGetResponse() // new keyword "use!" + use stream = resp.GetResponseStream() + use reader = new IO.StreamReader(stream) + let html = reader.ReadToEnd() + printfn "finished downloading %s" url + } +``` + +Note that the new code looks almost exactly the same as the original. There are only a few minor changes. + +* The change from "`use resp = `" to "`use! resp =`" is exactly the change that we talked about above -- while the async operation is going on, let other tasks have a turn. +* We also used the extension method `AsyncGetResponse` defined in the `CommonExtensions` namespace. This returns an async workflow that we can nest inside the main workflow. +* In addition the whole set of steps is contained in the "`async {...}`" wrapper which turns it into a block that can be run asynchronously. + +And here is a timed download using the async version. + +```fsharp +// a list of sites to fetch +let sites = ["http://www.bing.com"; + "http://www.google.com"; + "http://www.microsoft.com"; + "http://www.amazon.com"; + "http://www.yahoo.com"] + +#time // turn interactive timer on +sites +|> List.map fetchUrlAsync // make a list of async tasks +|> Async.Parallel // set up the tasks to run in parallel +|> Async.RunSynchronously // start them off +#time // turn timer off +``` + + +The way this works is: + +* `fetchUrlAsync` is applied to each site. It does not immediately start the download, but returns an async workflow for running later. +* To set up all the tasks to run at the same time we use the `Async.Parallel` function +* Finally we call `Async.RunSynchronously` to start all the tasks, and wait for them all to stop. + +If you try out this code yourself, you will see that the async version is much faster than the sync version. Not bad for a few minor code changes! Most importantly, the underlying logic is still very clear and is not cluttered up with noise. + + +## Example: a parallel computation ## + +To finish up, let's have another quick look at a parallel computation again. + +Before we start, I should warn you that the example code below is just to demonstrate the basic principles. +Benchmarks from "toy" versions of parallelization like this are not meaningful, because any kind of real concurrent code has so many dependencies. + +And also be aware that parallelization is rarely the best way to speed up your code. Your time is almost always better spent on improving your algorithms. +I'll bet my serial version of quicksort against your parallel version of bubblesort any day! +(For more details on how to improve performance, see the [optimization series](../series/optimization.md)) + +Anyway, with that caveat, let's create a little task that chews up some CPU. We'll test this serially and in parallel. + +```fsharp +let childTask() = + // chew up some CPU. + for i in [1..1000] do + for i in [1..1000] do + do "Hello".Contains("H") |> ignore + // we don't care about the answer! + +// Test the child task on its own. +// Adjust the upper bounds as needed +// to make this run in about 0.2 sec +#time +childTask() +#time +``` + +Adjust the upper bounds of the loops as needed to make this run in about 0.2 seconds. + +Now let's combine a bunch of these into a single serial task (using composition), and test it with the timer: + +```fsharp +let parentTask = + childTask + |> List.replicate 20 + |> List.reduce (>>) + +//test +#time +parentTask() +#time +``` + +This should take about 4 seconds. + +Now in order to make the `childTask` parallelizable, we have to wrap it inside an `async`: + +```fsharp +let asyncChildTask = async { return childTask() } +``` + +And to combine a bunch of asyncs into a single parallel task, we use `Async.Parallel`. + +Let's test this and compare the timings: + +```fsharp +let asyncParentTask = + asyncChildTask + |> List.replicate 20 + |> Async.Parallel + +//test +#time +asyncParentTask +|> Async.RunSynchronously +#time +``` + +On a dual-core machine, the parallel version is about 50% faster. It will get faster in proportion to the number of cores or CPUs, of course, but sublinearly. Four cores will be faster than one core, but not four times faster. + +On the other hand, as with the async web download example, a few minor code changes can make a big difference, while still leaving the code easy to read and understand. So in cases where parallelism will genuinely help, it is nice to know that it is easy to arrange. + + diff --git a/Why use F#/origin/posts/concurrency-intro.md b/Why use F#/origin/posts/concurrency-intro.md new file mode 100644 index 0000000..2cbd802 --- /dev/null +++ b/Why use F#/origin/posts/concurrency-intro.md @@ -0,0 +1,48 @@ +--- +layout: post +title: "Concurrency" +description: "The next major revolution in how we write software?" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 23 +categories: [Concurrency] +--- + + +We hear a lot about concurrency nowadays, how important it is, and how it is ["the next major revolution in how we write software"](http://www.gotw.ca/publications/concurrency-ddj.htm). + +So what do we actually mean by "concurrency" and how can F# help? + +The simplest definition of concurrency is just "several things happening at once, and maybe interacting with each other". It seems a trivial definition, but the key point is that most computer programs (and languages) are designed to work serially, on one thing at a time, and are not well-equipped to handle concurrency. + +And even if computer programs are written to handle concurrency, there is an even more serious problem: our brains do not do well when thinking about concurrency. It is commonly acknowledged that writing code that handles concurrency is extremely hard. Or I should say, writing concurrent code that is *correct* is extremely hard! It's very easy to write concurrent code that is buggy; there might be race conditions, or operations might not be atomic, or tasks might be starved or blocked unnecessarily, and these issues are hard to find by looking at the code or using a debugger. + +Before talking about the specifics of F#, let's try to classify some of the common types of concurrency scenarios that we have to deal with as developers: + +* **"Concurrent Multitasking"**. This is when we have a number of concurrent tasks (e.g. processes or threads) within our direct control, and we want them to communicate with each other and share data safely. +* **"Asynchronous" programming**. This is when we initiate a conversation with a separate system outside our direct control, and then wait for it to get back to us. Common cases of this are when talking to the filesystem, a database, or the network. These situations are typically I/O bound, so you want to do something else useful while you are waiting. These types of tasks are often *non-deterministic* as well, meaning that running the same program twice might give a different result. +* **"Parallel" programming**. This is when we have a single task that we want to split into independant subtasks, and then run the subtasks in parallel, ideally using all the cores or CPUs that are available. These situations are typically CPU bound. Unlike the async tasks, parallelism is typically *deterministic*, so running the same program twice will give the same result. +* **"Reactive" programming**. This is when we do not initiate tasks ourselves, but are focused on listening for events which we then process as fast as possible. This situation occurs when designing servers, and when working with a user interface. + +Of course, these are vague definitions and overlap in practice. In general, though, for all these cases, the actual implementations that address these scenarios tend to use two distinct approaches: + +* If there are lots of different tasks that need to share state or resources without waiting, then use a "buffered asynchronous" design. +* If there are lots of identical tasks that do not need to share state, then use parallel tasks using "fork/join" or "divide and conquer" approaches. + +## F# tools for concurrent programming ## + +F# offers a number of different approaches to writing concurrent code: + +* For multitasking and asynchronous problems, F# can directly use all the usual .NET suspects, such as `Thread` +`AutoResetEvent`, `BackgroundWorker` and `IAsyncResult`. But it also offers a much simpler model for all types of async IO and background task management, called "asynchronous workflows". +We will look at these in the next post. + +* An alternative approach for asynchronous problems is to use message queues and the ["actor model"](http://en.wikipedia.org/wiki/Actor_model) (this is the "buffered asynchronous" design mentioned above). F# has a built in implementation of the actor model called `MailboxProcessor`. + I am a big proponent of designing with actors and message queues, as it decouples the various components and allows you to think serially about each one. + +* For true CPU parallelism, F# has convenient library code that builds on the asynchronous workflows mentioned above, and it can also use the .NET [Task Parallel Library](http://msdn.microsoft.com/en-us/library/dd460717.aspx). + +* Finally, the functional approach to event handling and reactive programming is quite different from the traditional approach. The functional approach treats events as "streams" which can be filtered, +split, and combined in much the the same way that LINQ handles collections. F# has built in support for this model, as well as for the standard event-driven model. + + diff --git a/Why use F#/origin/posts/concurrency-reactive.md b/Why use F#/origin/posts/concurrency-reactive.md new file mode 100644 index 0000000..91611c8 --- /dev/null +++ b/Why use F#/origin/posts/concurrency-reactive.md @@ -0,0 +1,437 @@ +--- +layout: post +title: "Functional Reactive Programming" +description: "Turning events into streams" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 26 +categories: [Concurrency] +--- + +Events are everywhere. Almost every program has to handle events, whether it be button clicks in the user interface, listening to sockets in a server, or even a system shutdown notification. + +And events are the basis of one of the most common OO design patterns: the "Observer" pattern. + +But as we know, event handling, like concurrency in general, can be tricky to implement. Simple event logic is straightforward, but what about logic like "do something if two events happen in a row but do something different if only one event happens" or "do something if two events happen at roughly the same time". And how easy is it to combine these requirements in other, more complex ways? + +Even you can successfully implement these requirements, the code tends to be spaghetti like and hard to understand, even with the best intentions. + +Is there a approach that can make event handling easier? + +We saw in the previous post on message queues that one of the advantages of that approach was that the requests were "serialized" making it conceptually easier to deal with. + +There is a similar approach that can be used with events. The idea is to turn a series of events into an "event stream". +Event streams then become quite like IEnumerables, and so the obvious next step +is to treat them in much the the same way that LINQ handles collections, so that they can be filtered, mapped, split and combined. + +F# has built in support for this model, as well as for the more tradition approach. + +## A simple event stream ## + +Let's start with a simple example to compare the two approaches. We'll implement the classic event handler approach first. + +First, we define a utility function that will: + +* create a timer +* register a handler for the `Elapsed` event +* run the timer for five seconds and then stop it + +Here's the code: + +```fsharp +open System +open System.Threading + +/// create a timer and register an event handler, +/// then run the timer for five seconds +let createTimer timerInterval eventHandler = + // setup a timer + let timer = new System.Timers.Timer(float timerInterval) + timer.AutoReset <- true + + // add an event handler + timer.Elapsed.Add eventHandler + + // return an async task + async { + // start timer... + timer.Start() + // ...run for five seconds... + do! Async.Sleep 5000 + // ... and stop + timer.Stop() + } +``` + +Now test it interactively: + +```fsharp +// create a handler. The event args are ignored +let basicHandler _ = printfn "tick %A" DateTime.Now + +// register the handler +let basicTimer1 = createTimer 1000 basicHandler + +// run the task now +Async.RunSynchronously basicTimer1 +``` + +Now let's create a similar utility method to create a timer, but this time it will return an "observable" as well, which is the stream of events. + +```fsharp +let createTimerAndObservable timerInterval = + // setup a timer + let timer = new System.Timers.Timer(float timerInterval) + timer.AutoReset <- true + + // events are automatically IObservable + let observable = timer.Elapsed + + // return an async task + let task = async { + timer.Start() + do! Async.Sleep 5000 + timer.Stop() + } + + // return a async task and the observable + (task,observable) +``` + +And again test it interactively: + +```fsharp +// create the timer and the corresponding observable +let basicTimer2 , timerEventStream = createTimerAndObservable 1000 + +// register that everytime something happens on the +// event stream, print the time. +timerEventStream +|> Observable.subscribe (fun _ -> printfn "tick %A" DateTime.Now) + +// run the task now +Async.RunSynchronously basicTimer2 +``` + +The difference is that instead of registering a handler directly with an event, +we are "subscribing" to an event stream. Subtly different, and important. + +## Counting events ## + +In this next example, we'll have a slightly more complex requirement: + + Create a timer that ticks every 500ms. + At each tick, print the number of ticks so far and the current time. + +To do this in a classic imperative way, we would probably create a class with a mutable counter, as below: + +```fsharp +type ImperativeTimerCount() = + + let mutable count = 0 + + // the event handler. The event args are ignored + member this.handleEvent _ = + count <- count + 1 + printfn "timer ticked with count %i" count +``` + +We can reuse the utility functions we created earlier to test it: + +```fsharp +// create a handler class +let handler = new ImperativeTimerCount() + +// register the handler method +let timerCount1 = createTimer 500 handler.handleEvent + +// run the task now +Async.RunSynchronously timerCount1 +``` + +Let's see how we would do this same thing in a functional way: + +```fsharp +// create the timer and the corresponding observable +let timerCount2, timerEventStream = createTimerAndObservable 500 + +// set up the transformations on the event stream +timerEventStream +|> Observable.scan (fun count _ -> count + 1) 0 +|> Observable.subscribe (fun count -> printfn "timer ticked with count %i" count) + +// run the task now +Async.RunSynchronously timerCount2 +``` + +Here we see how you can build up layers of event transformations, just as you do with list transformations in LINQ. + +The first transformation is `scan`, which accumulates state for each event. It is roughly equivalent to the `List.fold` function that we have seen used with lists. +In this case, the accumulated state is just a counter. + +And then, for each event, the count is printed out. + +Note that in this functional approach, we didn't have any mutable state, and we didn't need to create any special classes. + +## Merging multiple event streams ## + +For a final example, we'll look at merging multiple event streams. + +Let's make a requirement based on the well-known "FizzBuzz" problem: + + Create two timers, called '3' and '5'. The '3' timer ticks every 300ms and the '5' timer ticks + every 500ms. + + Handle the events as follows: + a) for all events, print the id of the time and the time + b) when a tick is simultaneous with a previous tick, print 'FizzBuzz' + otherwise: + c) when the '3' timer ticks on its own, print 'Fizz' + d) when the '5' timer ticks on its own, print 'Buzz' + +First let's create some code that both implementations can use. + +We'll want a generic event type that captures the timer id and the time of the tick. + +```fsharp +type FizzBuzzEvent = {label:int; time: DateTime} +``` + +And then we need a utility function to see if two events are simultaneous. We'll be generous and allow a time difference of up to 50ms. + +```fsharp +let areSimultaneous (earlierEvent,laterEvent) = + let {label=_;time=t1} = earlierEvent + let {label=_;time=t2} = laterEvent + t2.Subtract(t1).Milliseconds < 50 +``` + +In the imperative design, we'll need to keep track of the previous event, so we can compare them. +And we'll need special case code for the first time, when the previous event doesn't exist + +```fsharp +type ImperativeFizzBuzzHandler() = + + let mutable previousEvent: FizzBuzzEvent option = None + + let printEvent thisEvent = + let {label=id; time=t} = thisEvent + printf "[%i] %i.%03i " id t.Second t.Millisecond + let simultaneous = previousEvent.IsSome && areSimultaneous (previousEvent.Value,thisEvent) + if simultaneous then printfn "FizzBuzz" + elif id = 3 then printfn "Fizz" + elif id = 5 then printfn "Buzz" + + member this.handleEvent3 eventArgs = + let event = {label=3; time=DateTime.Now} + printEvent event + previousEvent <- Some event + + member this.handleEvent5 eventArgs = + let event = {label=5; time=DateTime.Now} + printEvent event + previousEvent <- Some event +``` + +Now the code is beginning to get ugly fast! Already we have mutable state, complex conditional logic, and special cases, just for such a simple requirement. + +Let's test it: + +```fsharp +// create the class +let handler = new ImperativeFizzBuzzHandler() + +// create the two timers and register the two handlers +let timer3 = createTimer 300 handler.handleEvent3 +let timer5 = createTimer 500 handler.handleEvent5 + +// run the two timers at the same time +[timer3;timer5] +|> Async.Parallel +|> Async.RunSynchronously +``` + +It does work, but are you sure the code is not buggy? Are you likely to accidentally break something if you change it? + +The problem with this imperative code is that it has a lot of noise that obscures the the requirements. + +Can the functional version do better? Let's see! + +First, we create *two* event streams, one for each timer: + +```fsharp +let timer3, timerEventStream3 = createTimerAndObservable 300 +let timer5, timerEventStream5 = createTimerAndObservable 500 +``` + +Next, we convert each event on the "raw" event streams into our FizzBuzz event type: + +```fsharp +// convert the time events into FizzBuzz events with the appropriate id +let eventStream3 = + timerEventStream3 + |> Observable.map (fun _ -> {label=3; time=DateTime.Now}) + +let eventStream5 = + timerEventStream5 + |> Observable.map (fun _ -> {label=5; time=DateTime.Now}) +``` + +Now, to see if two events are simultaneous, we need to compare them from the two different streams somehow. + +It's actually easier than it sounds, because we can: + +* combine the two streams into a single stream: +* then create pairs of sequential events +* then test the pairs to see if they are simultaneous +* then split the input stream into two new output streams based on that test + +Here's the actual code to do this: + +```fsharp +// combine the two streams +let combinedStream = + Observable.merge eventStream3 eventStream5 + +// make pairs of events +let pairwiseStream = + combinedStream |> Observable.pairwise + +// split the stream based on whether the pairs are simultaneous +let simultaneousStream, nonSimultaneousStream = + pairwiseStream |> Observable.partition areSimultaneous +``` + + +Finally, we can split the `nonSimultaneousStream` again, based on the event id: + +```fsharp +// split the non-simultaneous stream based on the id +let fizzStream, buzzStream = + nonSimultaneousStream + // convert pair of events to the first event + |> Observable.map (fun (ev1,_) -> ev1) + // split on whether the event id is three + |> Observable.partition (fun {label=id} -> id=3) +``` + +Let's review so far. We have started with the two original event streams and from them created four new ones: + +* `combinedStream` contains all the events +* `simultaneousStream` contains only the simultaneous events +* `fizzStream` contains only the non-simultaneous events with id=3 +* `buzzStream` contains only the non-simultaneous events with id=5 + +Now all we need to do is attach behavior to each stream: + +```fsharp +//print events from the combinedStream +combinedStream +|> Observable.subscribe (fun {label=id;time=t} -> + printf "[%i] %i.%03i " id t.Second t.Millisecond) + +//print events from the simultaneous stream +simultaneousStream +|> Observable.subscribe (fun _ -> printfn "FizzBuzz") + +//print events from the nonSimultaneous streams +fizzStream +|> Observable.subscribe (fun _ -> printfn "Fizz") + +buzzStream +|> Observable.subscribe (fun _ -> printfn "Buzz") +``` + +Let's test it: + +```fsharp +// run the two timers at the same time +[timer3;timer5] +|> Async.Parallel +|> Async.RunSynchronously +``` + +Here's all the code in one complete set: + +```fsharp +// create the event streams and raw observables +let timer3, timerEventStream3 = createTimerAndObservable 300 +let timer5, timerEventStream5 = createTimerAndObservable 500 + +// convert the time events into FizzBuzz events with the appropriate id +let eventStream3 = timerEventStream3 + |> Observable.map (fun _ -> {label=3; time=DateTime.Now}) +let eventStream5 = timerEventStream5 + |> Observable.map (fun _ -> {label=5; time=DateTime.Now}) + +// combine the two streams +let combinedStream = + Observable.merge eventStream3 eventStream5 + +// make pairs of events +let pairwiseStream = + combinedStream |> Observable.pairwise + +// split the stream based on whether the pairs are simultaneous +let simultaneousStream, nonSimultaneousStream = + pairwiseStream |> Observable.partition areSimultaneous + +// split the non-simultaneous stream based on the id +let fizzStream, buzzStream = + nonSimultaneousStream + // convert pair of events to the first event + |> Observable.map (fun (ev1,_) -> ev1) + // split on whether the event id is three + |> Observable.partition (fun {label=id} -> id=3) + +//print events from the combinedStream +combinedStream +|> Observable.subscribe (fun {label=id;time=t} -> + printf "[%i] %i.%03i " id t.Second t.Millisecond) + +//print events from the simultaneous stream +simultaneousStream +|> Observable.subscribe (fun _ -> printfn "FizzBuzz") + +//print events from the nonSimultaneous streams +fizzStream +|> Observable.subscribe (fun _ -> printfn "Fizz") + +buzzStream +|> Observable.subscribe (fun _ -> printfn "Buzz") + +// run the two timers at the same time +[timer3;timer5] +|> Async.Parallel +|> Async.RunSynchronously +``` + +The code might seem a bit long winded, but this kind of incremental, step-wise approach is very clear and self-documenting. + +Some of the benefits of this style are: + +* I can see that it meets the requirements just by looking at it, without even running it. Not so with the imperative version. +* From a design point of view, each final "output" stream follows the single responsibility principle -- it only does one thing -- so it is very easy to +associate behavior with it. +* This code has no conditionals, no mutable state, no edge cases. It would be easy to maintain or change, I hope. +* It is easy to debug. For example, I could easily "tap" the output of the `simultaneousStream` to see if it +contains what I think it contains: + +```fsharp +// debugging code +//simultaneousStream |> Observable.subscribe (fun e -> printfn "sim %A" e) +//nonSimultaneousStream |> Observable.subscribe (fun e -> printfn "non-sim %A" e) +``` + +This would be much harder in the imperative version. + +## Summary ## + +Functional Reactive Programming (known as FRP) is a big topic, and we've only just touched on it here. I hope this introduction has given you a glimpse of the usefulness of this way of doing things. + +If you want to learn more, see the documentation for the F# [Observable module](http://msdn.microsoft.com/en-us/library/ee370313), which has the basic transformations used above. +And there is also the [Reactive Extensions (Rx)](http://msdn.microsoft.com/en-us/library/hh242985%28v=vs.103%29) library which shipped as part of .NET 4. That contains many other additional transformations. + + + + diff --git a/Why use F#/origin/posts/convenience-active-patterns.md b/Why use F#/origin/posts/convenience-active-patterns.md new file mode 100644 index 0000000..6dfa1a8 --- /dev/null +++ b/Why use F#/origin/posts/convenience-active-patterns.md @@ -0,0 +1,95 @@ +--- +layout: post +title: "Active patterns" +description: "Dynamic patterns for powerful matching" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 17 +categories: [Convenience, Patterns] +--- + +F# has a special type of pattern matching called "active patterns" where the pattern can be parsed or detected dynamically. As with normal patterns, the matching and output are combined into a single step from the caller's point of view. + +Here is an example of using active patterns to parse a string into an int or bool. + +```fsharp +// create an active pattern +let (|Int|_|) str = + match System.Int32.TryParse(str) with + | (true,int) -> Some(int) + | _ -> None + +// create an active pattern +let (|Bool|_|) str = + match System.Boolean.TryParse(str) with + | (true,bool) -> Some(bool) + | _ -> None +``` + +
+You don't need to worry about the complex syntax used to define the active pattern right now ? this is just an example so that you can see how they are used. +
+ +Once these patterns have been set up, they can be used as part of a normal "`match..with`" expression. + +```fsharp +// create a function to call the patterns +let testParse str = + match str with + | Int i -> printfn "The value is an int '%i'" i + | Bool b -> printfn "The value is a bool '%b'" b + | _ -> printfn "The value '%s' is something else" str + +// test +testParse "12" +testParse "true" +testParse "abc" +``` + +You can see that from the caller's point of view, the matching with an `Int` or `Bool` is transparent, even though there is parsing going on behind the scenes. + +A similar example is to use active patterns with regular expressions in order to both match on a regex pattern and return the matched value in a single step. + +
// create an active pattern
+open System.Text.RegularExpressions
+let (|FirstRegexGroup|_|) pattern input =
+   let m = Regex.Match(input,pattern) 
+   if (m.Success) then Some m.Groups.[1].Value else None  
+
+
+ +Again, once this pattern has been set up, it can be used transparently as part of a normal match expression. + +```fsharp +// create a function to call the pattern +let testRegex str = + match str with + | FirstRegexGroup "http://(.*?)/(.*)" host -> + printfn "The value is a url and the host is %s" host + | FirstRegexGroup ".*?@(.*)" host -> + printfn "The value is an email and the host is %s" host + | _ -> printfn "The value '%s' is something else" str + +// test +testRegex "http://google.com/test" +testRegex "alice@hotmail.com" +``` + +And for fun, here's one more: the well-known [FizzBuzz challenge](http://www.codinghorror.com/blog/2007/02/why-cant-programmers-program.html) written using active patterns. + +```fsharp +// setup the active patterns +let (|MultOf3|_|) i = if i % 3 = 0 then Some MultOf3 else None +let (|MultOf5|_|) i = if i % 5 = 0 then Some MultOf5 else None + +// the main function +let fizzBuzz i = + match i with + | MultOf3 & MultOf5 -> printf "FizzBuzz, " + | MultOf3 -> printf "Fizz, " + | MultOf5 -> printf "Buzz, " + | _ -> printf "%i, " i + +// test +[1..20] |> List.iter fizzBuzz +``` diff --git a/Why use F#/origin/posts/convenience-functions-as-interfaces.md b/Why use F#/origin/posts/convenience-functions-as-interfaces.md new file mode 100644 index 0000000..d688074 --- /dev/null +++ b/Why use F#/origin/posts/convenience-functions-as-interfaces.md @@ -0,0 +1,155 @@ +--- +layout: post +title: "Functions as interfaces" +description: "OO design patterns can be trivial when functions are used" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 15 +categories: [Convenience, Functions] +--- + + +An important aspect of functional programming is that, in a sense, all functions are "interfaces", meaning that many of the roles that interfaces play in object-oriented design are implicit in the way that functions work. + +In fact, one of the critical design maxims, "program to an interface, not an implementation", is something you get for free in F#. + +To see how this works, let's compare the same design pattern in C# and F#. For example, in C# we might want to use the "decorator pattern" to enhance some core code. + +Let's say that we have a calculator interface: + +```csharp +interface ICalculator +{ + int Calculate(int input); +} +``` + +And then a specific implementation: + +```csharp +class AddingCalculator: ICalculator +{ + public int Calculate(int input) { return input + 1; } +} +``` + +And then if we want to add logging, we can wrap the core calculator implementation inside a logging wrapper. + +```csharp +class LoggingCalculator: ICalculator +{ + ICalculator _innerCalculator; + + LoggingCalculator(ICalculator innerCalculator) + { + _innerCalculator = innerCalculator; + } + + public int Calculate(int input) + { + Console.WriteLine("input is {0}", input); + var result = _innerCalculator.Calculate(input); + Console.WriteLine("result is {0}", result); + return result; + } +} +``` + +So far, so straightforward. But note that, for this to work, we must have defined an interface for the classes. If there had been no `ICalculator` interface, it would be necessary to retrofit the existing code. + +And here is where F# shines. In F#, you can do the same thing without having to define the interface first. Any function can be transparently swapped for any other function as long as the signatures are the same. + +Here is the equivalent F# code. + +```fsharp +let addingCalculator input = input + 1 + +let loggingCalculator innerCalculator input = + printfn "input is %A" input + let result = innerCalculator input + printfn "result is %A" result + result +``` + +In other words, the signature of the function *is* the interface. + +## Generic wrappers + +Even nicer is that by default, the F# logging code can be made completely generic so that it will work for *any* function at all. Here are some examples: + +```fsharp +let add1 input = input + 1 +let times2 input = input * 2 + +let genericLogger anyFunc input = + printfn "input is %A" input //log the input + let result = anyFunc input //evaluate the function + printfn "result is %A" result //log the result + result //return the result + +let add1WithLogging = genericLogger add1 +let times2WithLogging = genericLogger times2 +``` + +The new "wrapped" functions can be used anywhere the original functions could be used ? no one can tell the difference! + +```fsharp +// test +add1WithLogging 3 +times2WithLogging 3 + +[1..5] |> List.map add1WithLogging +``` + +Exactly the same generic wrapper approach can be used for other things. For example, here is a generic wrapper for timing a function. + +```fsharp +let genericTimer anyFunc input = + let stopwatch = System.Diagnostics.Stopwatch() + stopwatch.Start() + let result = anyFunc input //evaluate the function + printfn "elapsed ms is %A" stopwatch.ElapsedMilliseconds + result + +let add1WithTimer = genericTimer add1WithLogging + +// test +add1WithTimer 3 +``` + +The ability to do this kind of generic wrapping is one of the great conveniences of the function-oriented approach. You can take any function and create a similar function based on it. As long as the new function has exactly the same inputs and outputs as the original function, the new can be substituted for the original anywhere. Some more examples: + +* It is easy to write a generic caching wrapper for a slow function, so that the value is only calculated once. +* It is also easy to write a generic "lazy" wrapper for a function, so that the inner function is only called when a result is needed + +## The strategy pattern + +We can apply this same approach to another common design pattern, the "strategy pattern." + +Let's use the familiar example of inheritance: an `Animal` superclass with `Cat` and `Dog` subclasses, each of which overrides a `MakeNoise()` method to make different noises. + +In a true functional design, there are no subclasses, but instead the `Animal` class would have a `NoiseMaking` function that would be passed in with the constructor. This approach is exactly the same as the "strategy" pattern in OO design. + +```fsharp +type Animal(noiseMakingStrategy) = + member this.MakeNoise = + noiseMakingStrategy() |> printfn "Making noise %s" + +// now create a cat +let meowing() = "Meow" +let cat = Animal(meowing) +cat.MakeNoise + +// .. and a dog +let woofOrBark() = if (System.DateTime.Now.Second % 2 = 0) + then "Woof" else "Bark" +let dog = Animal(woofOrBark) +dog.MakeNoise +dog.MakeNoise //try again a second later +``` + +Note that again, we do not have to define any kind of `INoiseMakingStrategy` interface first. Any function with the right signature will work. +As a consequence, in the functional model, the standard .NET "strategy" interfaces such as `IComparer`, `IFormatProvider`, and `IServiceProvider` become irrelevant. + +Many other design patterns can be simplified in the same way. + diff --git a/Why use F#/origin/posts/convenience-intro.md b/Why use F#/origin/posts/convenience-intro.md new file mode 100644 index 0000000..1642733 --- /dev/null +++ b/Why use F#/origin/posts/convenience-intro.md @@ -0,0 +1,16 @@ +--- +layout: post +title: "Convenience" +description: "Features that reduce programming drudgery and boilerplate code" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 13 +categories: [Convenience] +--- + +In the next set of posts, we will explore a few more features of F# that I have grouped under the theme of "convenience". These features do not necessarily result in more concise code, but they do remove much of the drudgery and boilerplate code that would be needed in C#. + +* **Useful "out-of-the-box" behavior for types**. Most types that you create will immediately have some useful behavior, such as immutability and built-in equality ? functionality that has to be explicitly coded for in C#. +* **All functions are "interfaces"**, meaning that many of the roles that interfaces play in object-oriented design are implicit in the way that functions work. And similarly, many object-oriented design patterns are unnecessary or trivial within a functional paradigm. +* **Partial application**. Complicated functions with many parameters can have some of the parameters fixed or "baked in" and yet leave other parameters open. +* **Active patterns**. Active patterns are a special kind of pattern where the pattern can be matched or detected dynamically, rather than statically. They are great for simplifying frequently used parsing and grouping behaviors. diff --git a/Why use F#/origin/posts/convenience-partial-application.md b/Why use F#/origin/posts/convenience-partial-application.md new file mode 100644 index 0000000..e7377f4 --- /dev/null +++ b/Why use F#/origin/posts/convenience-partial-application.md @@ -0,0 +1,146 @@ +--- +layout: post +title: "Partial Application" +description: "How to fix some of a function's parameters" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 16 +categories: [Convenience, Functions, Partial Application] +--- + +A particularly convenient feature of F# is that complicated functions with many parameters can have some of the parameters fixed or "baked in" and yet leave other parameters open. In this post, we'll take a quick look at how this might be used in practice. + +Let's start with a very simple example of how this works. We'll start with a trivial function: + +```fsharp +// define a adding function +let add x y = x + y + +// normal use +let z = add 1 2 +``` + +But we can do something strange as well ? we can call the function with only one parameter! + +```fsharp +let add42 = add 42 +``` + +The result is a new function that has the "42" baked in, and now takes only one parameter instead of two! This technique is called "partial application", and it means that, for any function, you can "fix" some of the parameters and leave other ones open to be filled in later. + +```fsharp +// use the new function +add42 2 +add42 3 +``` + +With that under our belt, let's revisit the generic logger that we saw earlier: + +```fsharp +let genericLogger anyFunc input = + printfn "input is %A" input //log the input + let result = anyFunc input //evaluate the function + printfn "result is %A" result //log the result + result //return the result +``` + +Unfortunately, I have hard-coded the logging operations. Ideally, I'd like to make this more generic so that I can choose how logging is done. + +Of course, F# being a functional programming language, we will do this by passing functions around. + +In this case we would pass "before" and "after" callback functions to the library function, like this: + +```fsharp +let genericLogger before after anyFunc input = + before input //callback for custom behavior + let result = anyFunc input //evaluate the function + after result //callback for custom behavior + result //return the result +``` + +You can see that the logging function now has four parameters. The "before" and "after" actions are passed in as explicit parameters as well as the function and its input. To use this in practice, we just define the functions and pass them in to the library function along with the final int parameter: + +```fsharp +let add1 input = input + 1 + +// reuse case 1 +genericLogger + (fun x -> printf "before=%i. " x) // function to call before + (fun x -> printfn " after=%i." x) // function to call after + add1 // main function + 2 // parameter + +// reuse case 2 +genericLogger + (fun x -> printf "started with=%i " x) // different callback + (fun x -> printfn " ended with=%i" x) + add1 // main function + 2 // parameter +``` + +This is a lot more flexible. I don't have to create a new function every time I want to change the behavior -- I can define the behavior on the fly. + +But you might be thinking that this is a bit ugly. A library function might expose a number of callback functions and it would be inconvenient to have to pass the same functions in over and over. + +Luckily, we know the solution for this. We can use partial application to fix some of the parameters. So in this case, let's define a new function which fixes the `before` and `after` functions, as well as the `add1` function, but leaves the final parameter open. + +```fsharp +// define a reusable function with the "callback" functions fixed +let add1WithConsoleLogging = + genericLogger + (fun x -> printf "input=%i. " x) + (fun x -> printfn " result=%i" x) + add1 + // last parameter NOT defined here yet! +``` + +The new "wrapper" function is called with just an int now, so the code is much cleaner. As in the earlier example, it can be used anywhere the original `add1` function could be used without any changes. + +```fsharp +add1WithConsoleLogging 2 +add1WithConsoleLogging 3 +add1WithConsoleLogging 4 +[1..5] |> List.map add1WithConsoleLogging +``` + +## The functional approach in C# ## + +In a classical object-oriented approach, we would probably have used inheritance to do this kind of thing. For instance, we might have had an abstract `LoggerBase` class, with virtual methods for "`before`" and "`after`" and the function to execute. And then to implement a particular kind of behavior, we would have created a new subclass and overridden the virtual methods as needed. + +But classical style inheritance is now becoming frowned upon in object-oriented design, and composition of objects is much preferred. And indeed, in "modern" C#, we would probably write the code in the same way as F#, either by using events or by passing functions in. + +Here's the F# code translated into C# (note that I had to specify the types for each Action) + +```csharp +public class GenericLoggerHelper +{ + public TResult GenericLogger( + Action before, + Action after, + Func aFunc, + TInput input) + { + before(input); //callback for custom behavior + var result = aFunc(input); //do the function + after(result); //callback for custom behavior + return result; + } +} +``` + +And here it is in use: + +```csharp +[NUnit.Framework.Test] +public void TestGenericLogger() +{ + var sut = new GenericLoggerHelper(); + sut.GenericLogger( + x => Console.Write("input={0}. ", x), + x => Console.WriteLine(" result={0}", x), + x => x + 1, + 3); +} +``` + +In C#, this style of programming is required when using the LINQ libraries, but many developers have not embraced it fully to make their own code more generic and adaptable. And it's not helped by the ugly `Action<>` and `Func<>` type declarations that are required. But it can certainly make the code much more reusable. diff --git a/Why use F#/origin/posts/convenience-types.md b/Why use F#/origin/posts/convenience-types.md new file mode 100644 index 0000000..bfc8fac --- /dev/null +++ b/Why use F#/origin/posts/convenience-types.md @@ -0,0 +1,159 @@ +--- +layout: post +title: 'Out-of-the-box behavior for types' +description: "Immutability and built-in equality with no coding" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 14 +categories: [Convenience, Types] +--- + +One nice thing about F# is that most types immediately have some useful "out-of-the-box" behavior such as immutability and built-in equality, functionality that often has to be explicitly coded for in C#. + +By "most" F# types, I mean the core "structural" types such as tuples, records, unions, options, lists, etc. Classes and some other types have been added to help with .NET integration, but lose some of the power of the structural types. + +This built-in functionality for these core types includes: + +* Immutability +* Pretty printing when debugging +* Equality +* Comparisons + +Each of these is addressed below. + +## F# types have built-in immutability + +In C# and Java, it is has become good practice to create immutable classes whenever possible. In F#, you get this for free. + +Here is an immutable type in F#: +```fsharp +type PersonalName = {FirstName:string; LastName:string} +``` + +And here is how the same type is typically coded in C#: + +```csharp +class ImmutablePersonalName +{ + public ImmutablePersonalName(string firstName, string lastName) + { + this.FirstName = firstName; + this.LastName = lastName; + } + + public string FirstName { get; private set; } + public string LastName { get; private set; } +} +``` + +That's 10 lines to do the same thing as 1 line of F#. + +## Most F# types have built-in pretty printing + +In F#, you don't have to override `ToString()` for most types -- you get pretty printing for free! + +You have probably already seen this when running the earlier examples. Here is another simple example: + +```fsharp +type USAddress = + {Street:string; City:string; State:string; Zip:string} +type UKAddress = + {Street:string; Town:string; PostCode:string} +type Address = US of USAddress | UK of UKAddress +type Person = + {Name:string; Address:Address} + +let alice = { + Name="Alice"; + Address=US {Street="123 Main";City="LA";State="CA";Zip="91201"}} +let bob = { + Name="Bob"; + Address=UK {Street="221b Baker St";Town="London";PostCode="NW1 6XE"}} + +printfn "Alice is %A" alice +printfn "Bob is %A" bob +``` + +The output is: + +```fsharp +Alice is {Name = "Alice"; + Address = US {Street = "123 Main"; + City = "LA"; + State = "CA"; + Zip = "91201";};} +``` + +## Most F# types have built-in structural equality + +In C#, you often have to implement the `IEquatable` interface so that you can test for equality between objects. This is needed when using objects for Dictionary keys, for example. + +In F#, you get this for free with most F# types. For example, using the `PersonalName` type from above, we can compare two names straight away. + +```fsharp +type PersonalName = {FirstName:string; LastName:string} +let alice1 = {FirstName="Alice"; LastName="Adams"} +let alice2 = {FirstName="Alice"; LastName="Adams"} +let bob1 = {FirstName="Bob"; LastName="Bishop"} + +//test +printfn "alice1=alice2 is %A" (alice1=alice2) +printfn "alice1=bob1 is %A" (alice1=bob1) +``` + + +## Most F# types are automatically comparable + +In C#, you often have to implement the `IComparable` interface so that you can sort objects. + +Again, in F#, you get this for free with most F# types. For example, here is a simple definition of a deck of cards. + +```fsharp + +type Suit = Club | Diamond | Spade | Heart +type Rank = Two | Three | Four | Five | Six | Seven | Eight + | Nine | Ten | Jack | Queen | King | Ace +``` + + +We can write a function to test the comparison logic: + +```fsharp +let compareCard card1 card2 = + if card1 < card2 + then printfn "%A is greater than %A" card2 card1 + else printfn "%A is greater than %A" card1 card2 +``` + +And let's see how it works: + +```fsharp +let aceHearts = Heart, Ace +let twoHearts = Heart, Two +let aceSpades = Spade, Ace + +compareCard aceHearts twoHearts +compareCard twoHearts aceSpades +``` + +Note that the Ace of Hearts is automatically greater than the Two of Hearts, because the "Ace" rank value comes after the "Two" rank value. + +But also note that the Two of Hearts is automatically greater than the Ace of Spades, because the Suit part is compared first, and the "Heart" suit value comes after the "Spade" value. + +Here's an example of a hand of cards: + +```fsharp +let hand = [ Club,Ace; Heart,Three; Heart,Ace; + Spade,Jack; Diamond,Two; Diamond,Ace ] + +//instant sorting! +List.sort hand |> printfn "sorted hand is (low to high) %A" +``` + +And as a side benefit, you get min and max for free too! + +```fsharp +List.max hand |> printfn "high card is %A" +List.min hand |> printfn "low card is %A" +``` + diff --git a/Why use F#/origin/posts/correctness-exhaustive-pattern-matching.md b/Why use F#/origin/posts/correctness-exhaustive-pattern-matching.md new file mode 100644 index 0000000..65cc68b --- /dev/null +++ b/Why use F#/origin/posts/correctness-exhaustive-pattern-matching.md @@ -0,0 +1,340 @@ +--- +layout: post +title: "Exhaustive pattern matching" +description: "A powerful technique to ensure correctness" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 20 +categories: [Correctness, Patterns] +--- + +We briefly noted earlier that when pattern matching there is a requirement to match all possible cases. This turns out be a very powerful technique to ensure correctness. + +Let's compare some C# to F# again. Here's some C# code that uses a switch statement to handle different types of state. + +```csharp +enum State { New, Draft, Published, Inactive, Discontinued } +void HandleState(State state) +{ + switch (state) + { + case State.Inactive: // code for Inactive + break; + case State.Draft: // code for Draft + break; + case State.New: // code for New + break; + case State.Discontinued: // code for Discontinued + break; + } +} +``` + +This code will compile, but there is an obvious bug! The compiler couldn't see it ? can you? If you can, and you fixed it, would it stay fixed if I added another `State` to the list? + +Here's the F# equivalent: + +```fsharp +type State = New | Draft | Published | Inactive | Discontinued +let handleState state = + match state with + | Inactive -> () // code for Inactive + | Draft -> () // code for Draft + | New -> () // code for New + | Discontinued -> () // code for Discontinued +``` + +Now try running this code. What does the compiler tell you? + +The fact that exhaustive matching is always done means that certain common errors will be detected by the compiler immediately: + +* A missing case (often caused when a new choice has been added due to changed requirements or refactoring). +* An impossible case (when an existing choice has been removed). +* A redundant case that could never be reached (the case has been subsumed in a previous case -- this can sometimes be non-obvious). + +Now let's look at some real examples of how exhaustive matching can help you write correct code. + +## Avoiding nulls with the Option type ## + +We'll start with an extremely common scenario where the caller should always check for an invalid case, namely testing for nulls. A typical C# program is littered with code like this: + +```csharp +if (myObject != null) +{ + // do something +} +``` + +Unfortunately, this test is not required by the compiler. All it takes is for one piece of code to forget to do this, and the program can crash. +Over the years, a huge amount of programming effort has been devoted to handling nulls ? the invention of nulls has even been called a [billion dollar mistake](http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare)! + +In pure F#, nulls cannot exist accidentally. A string or object must always be assigned to something at creation, and is immutable thereafter. + +However, there are many situations where the *design intent* is to distinguish between valid and invalid values, +and you require the caller to handle both cases. + +In C#, this can be managed in certain situations by using nullable value types (such as `Nullable`) to make the design decision clear. +When a nullable is encountered the compiler will force you to be aware of it. You can then test the validity of the value before using it. +But nullables do not work for standard classes (i.e. reference types), and it is easy to accidentally bypass the tests too and just call `Value` directly. + +In F# there is a similar but more powerful concept to convey the design intent: the generic wrapper type called `Option`, with two choices: `Some` or `None`. +The `Some` choice wraps a valid value, and `None` represents a missing value. + +Here's an example where `Some` is returned if a file exists, but a missing file returns `None`. + +```fsharp +let getFileInfo filePath = + let fi = new System.IO.FileInfo(filePath) + if fi.Exists then Some(fi) else None + +let goodFileName = "good.txt" +let badFileName = "bad.txt" + +let goodFileInfo = getFileInfo goodFileName // Some(fileinfo) +let badFileInfo = getFileInfo badFileName // None +``` + +If we want to do anything with these values, we must always handle both possible cases. + +```fsharp +match goodFileInfo with + | Some fileInfo -> + printfn "the file %s exists" fileInfo.FullName + | None -> + printfn "the file doesn't exist" + +match badFileInfo with + | Some fileInfo -> + printfn "the file %s exists" fileInfo.FullName + | None -> + printfn "the file doesn't exist" +``` + +We have no choice about this. Not handling a case is a compile-time error, not a run-time error. +By avoiding nulls and by using `Option` types in this way, F# completely eliminates a large class of null reference exceptions. + +Caveat: F# does allow you to access the value without testing, just like C#, but that is considered extremely bad practice. + + +## Exhaustive pattern matching for edge cases ## + +Here's some C# code that creates a list by averaging pairs of numbers from an input list: + +```csharp +public IList MovingAverages(IList list) +{ + var averages = new List(); + for (int i = 0; i < list.Count; i++) + { + var avg = (list[i] + list[i+1]) / 2; + averages.Add(avg); + } + return averages; +} +``` + +It compiles correctly, but it actually has a couple of issues. Can you find them quickly? If you're lucky, your unit tests will find them for you, assuming you have thought of all the edge cases. + +Now let's try the same thing in F#: + +```fsharp +let rec movingAverages list = + match list with + // if input is empty, return an empty list + | [] -> [] + // otherwise process pairs of items from the input + | x::y::rest -> + let avg = (x+y)/2.0 + //build the result by recursing the rest of the list + avg :: movingAverages (y::rest) +``` + +This code also has a bug. But unlike C#, this code will not even compile until I fix it. The compiler will tell me that I haven't handled the case when I have a single item in my list. +Not only has it found a bug, it has revealed a gap in the requirements: what should happen when there is only one item? + +Here's the fixed up version: + +```fsharp +let rec movingAverages list = + match list with + // if input is empty, return an empty list + | [] -> [] + // otherwise process pairs of items from the input + | x::y::rest -> + let avg = (x+y)/2.0 + //build the result by recursing the rest of the list + avg :: movingAverages (y::rest) + // for one item, return an empty list + | [_] -> [] + +// test +movingAverages [1.0] +movingAverages [1.0; 2.0] +movingAverages [1.0; 2.0; 3.0] +``` + +As an additional benefit, the F# code is also much more self-documenting. It explicitly describes the consequences of each case. +In the C# code, it is not at all obvious what happens if a list is empty or only has one item. You would have to read the code carefully to find out. + +## Exhaustive pattern matching as an error handling technique ## + +The fact that all choices must be matched can also be used as a useful alternative to throwing exceptions. For example consider the following common scenario: + +* There is a utility function in the lowest tier of your app that opens a file and performs an arbitrary operation on it (that you pass in as a callback function) +* The result is then passed back up through to tiers to the top level. +* A client calls the top level code, and the result is processed and any error handling done. + +In a procedural or OO language, propagating and handling exceptions across layers of code is a common problem. Top level functions are not easily able to tell the difference between an exception that they should recover from (`FileNotFound` say) vs. an exception that they needn't handle (`OutOfMemory` say). In Java, there has been an attempt to do this with checked exceptions, but with mixed results. + +In the functional world, a common technique is to create a new structure to hold both the good and bad possibilities, rather than throwing an exception if the file is missing. + +```fsharp +// define a "union" of two different alternatives +type Result<'a, 'b> = + | Success of 'a // 'a means generic type. The actual type + // will be determined when it is used. + | Failure of 'b // generic failure type as well + +// define all possible errors +type FileErrorReason = + | FileNotFound of string + | UnauthorizedAccess of string * System.Exception + +// define a low level function in the bottom layer +let performActionOnFile action filePath = + try + //open file, do the action and return the result + use sr = new System.IO.StreamReader(filePath:string) + let result = action sr //do the action to the reader + sr.Close() + Success (result) // return a Success + with // catch some exceptions and convert them to errors + | :? System.IO.FileNotFoundException as ex + -> Failure (FileNotFound filePath) + | :? System.Security.SecurityException as ex + -> Failure (UnauthorizedAccess (filePath,ex)) + // other exceptions are unhandled +``` + +The code demonstrates how `performActionOnFile` returns a `Result` object which has two alternatives: `Success` and `Failure`. The `Failure` alternative in turn has two alternatives as well: `FileNotFound` and `UnauthorizedAccess`. + +Now the intermediate layers can call each other, passing around the result type without worrying what its structure is, as long as they don't access it: + +```fsharp +// a function in the middle layer +let middleLayerDo action filePath = + let fileResult = performActionOnFile action filePath + // do some stuff + fileResult //return + +// a function in the top layer +let topLayerDo action filePath = + let fileResult = middleLayerDo action filePath + // do some stuff + fileResult //return +``` + +Because of type inference, the middle and top layers do not need to specify the exact types returned. If the lower layer changes the type definition at all, the intermediate layers will not be affected. + +Obviously at some point, a client of the top layer does want to access the result. And here is where the requirement to match all patterns is enforced. The client must handle the case with a `Failure` or else the compiler will complain. And furthermore, when handling the `Failure` branch, it must handle the possible reasons as well. In other words, special case handling of this sort can be enforced at compile time, not at runtime! And in addition the possible reasons are explicitly documented by examining the reason type. + +Here is an example of a client function that accesses the top layer: + +```fsharp +/// get the first line of the file +let printFirstLineOfFile filePath = + let fileResult = topLayerDo (fun fs->fs.ReadLine()) filePath + + match fileResult with + | Success result -> + // note type-safe string printing with %s + printfn "first line is: '%s'" result + | Failure reason -> + match reason with // must match EVERY reason + | FileNotFound file -> + printfn "File not found: %s" file + | UnauthorizedAccess (file,_) -> + printfn "You do not have access to the file: %s" file +``` + + +You can see that this code must explicitly handle the `Success` and `Failure` cases, and then for the failure case, it explicitly handles the different reasons. If you want to see what happens if it does not handle one of the cases, try commenting out the line that handles `UnauthorizedAccess` and see what the compiler says. + +Now it is not required that you always handle all possible cases explicitly. In the example below, the function uses the underscore wildcard to treat all the failure reasons as one. This can considered bad practice if we want to get the benefits of the strictness, but at least it is clearly done. + +```fsharp +/// get the length of the text in the file +let printLengthOfFile filePath = + let fileResult = + topLayerDo (fun fs->fs.ReadToEnd().Length) filePath + + match fileResult with + | Success result -> + // note type-safe int printing with %i + printfn "length is: %i" result + | Failure _ -> + printfn "An error happened but I don't want to be specific" +``` + +Now let's see all this code work in practice with some interactive tests. + +First set up a good file and a bad file. + +```fsharp +/// write some text to a file +let writeSomeText filePath someText = + use writer = new System.IO.StreamWriter(filePath:string) + writer.WriteLine(someText:string) + writer.Close() + +let goodFileName = "good.txt" +let badFileName = "bad.txt" + +writeSomeText goodFileName "hello" +``` + +And now test interactively: + +```fsharp +printFirstLineOfFile goodFileName +printLengthOfFile goodFileName + +printFirstLineOfFile badFileName +printLengthOfFile badFileName +``` + +I think you can see that this approach is very attractive: + +* Functions return error types for each expected case (such as `FileNotFound`), but the handling of these types does not need to make the calling code ugly. +* Functions continue to throw exceptions for unexpected cases (such as `OutOfMemory`), which will generally be caught and logged at the top level of the program. + +This technique is simple and convenient. Similar (and more generic) approaches are standard in functional programming. + +It is feasible to use this approach in C# too, but it is normally impractical, due to the lack of union types and the lack of type inference (we would have to specify generic types everywhere). + +## Exhaustive pattern matching as a change management tool ## + +Finally, exhaustive pattern matching is a valuable tool for ensuring that code stays correct as requirements change, or during refactoring. + +Let's say that the requirements change and we need to handle a third type of error: "Indeterminate". To implement this new requirement, change the first `Result` type as follows, and re-evaluate all the code. What happens? + +```fsharp +type Result<'a, 'b> = + | Success of 'a + | Failure of 'b + | Indeterminate +``` + +Or sometimes a requirements change will remove a possible choice. To emulate this, change the first `Result` type to eliminate all but one of the choices. + +```fsharp +type Result<'a> = + | Success of 'a +``` + +Now re-evaluate the rest of the code. What happens now? + +This is very powerful! When we adjust the choices, we immediately know all the places which need to be fixed to handle the change. This is another example of the power of statically checked type errors. It is often said about functional languages like F# that "if it compiles, it must be correct". + + + diff --git a/Why use F#/origin/posts/correctness-immutability.md b/Why use F#/origin/posts/correctness-immutability.md new file mode 100644 index 0000000..7613d34 --- /dev/null +++ b/Why use F#/origin/posts/correctness-immutability.md @@ -0,0 +1,141 @@ +--- +layout: post +title: "Immutability" +description: "Making your code predictable" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 19 +categories: [Correctness, Immutability] +--- + +To see why immutability is important, let's start with a small example. + +Here's some simple C# code that processes a list of numbers. + +```csharp +public List MakeList() +{ + return new List {1,2,3,4,5,6,7,8,9,10}; +} + +public List OddNumbers(List list) +{ + // some code +} + +public List EvenNumbers(List list) +{ + // some code +} +``` + +Now let me test it: + +```csharp +public void Test() +{ + var odds = OddNumbers(MakeList()); + var evens = EvenNumbers(MakeList()); + // assert odds = 1,3,5,7,9 -- OK! + // assert evens = 2,4,6,8,10 -- OK! +} +``` + +Everything works great, and the test passes, but I notice that I am creating the list twice ? surely I should refactor this out? So I do the refactoring, and here's the new improved version: + +```csharp +public void RefactoredTest() +{ + var list = MakeList(); + var odds = OddNumbers(list); + var evens = EvenNumbers(list); + // assert odds = 1,3,5,7,9 -- OK! + // assert evens = 2,4,6,8,10 -- FAIL! +} +``` + +But now the test suddenly fails! Why would a refactoring break the test? Can you tell just by looking at the code? + +The answer is, of course, that the list is mutable, and it is probable that the `OddNumbers` function is making destructive changes to the list as part of its filtering logic. Of course, in order to be sure, we would have to examine the code inside the `OddNumbers` function. + +In other words, when I call the `OddNumbers` function, I am unintentionally creating undesirable side effects. + +Is there a way to ensure that this cannot happen? Yes -- if the functions had used `IEnumerable` instead: + +```csharp +public IEnumerable MakeList() {} +public List OddNumbers(IEnumerable list) {} +public List EvenNumbers(IEnumerable list) {} +``` + +In this case we can be confident that calling the `OddNumbers` function could not possibly have any effect on the list, and `EvenNumbers` would work correctly. What's more, we can know this *just by looking at the signatures*, without having to examine the internals of the functions. And if you try to make one of the functions misbehave by assigning to the list then you will get an error straight away, at compile time. + +So `IEnumerable` can help in this case, but what if I had used a type such as `IEnumerable` instead of `IEnumerable`? Could I still be as confident that the functions wouldn't have unintentional side effects? + +## Reasons why immutability is important ## + +The example above shows why immutability is helpful. In fact, this is just the tip of the iceberg. There are a number of reasons why immutability is important: + +* Immutable data makes the code predictable +* Immutable data is easier to work with +* Immutable data forces you to use a "transformational" approach + +First, immutability makes the code **predictable**. If data is immutable, there can be no side-effects. If there are no side-effects, it is much, much, easier to reason about the correctness of the code. + +And when you have two functions that work on immutable data, you don't have to worry about which order to call them in, or whether one function will mess with the input of the other function. And you have peace of mind when passing data around (for example, you don't have to worry about using an object as a key in a hashtable and having its hash code change). + +In fact, immutability is a good idea for the same reasons that global variables are a bad idea: data should be kept as local as possible and side-effects should be avoided. + +Second, immutability is **easier to work with**. If data is immutable, many common tasks become much easier. Code is easier to write and easier to maintain. Fewer unit tests are needed (you only have to check that a function works in isolation), and mocking is much easier. Concurrency is much simpler, as you don't have to worry about using locks to avoid update conflicts (because there are no updates). + +Finally, using immutability by default means that you start thinking differently about programming. You tend to think about **transforming** the data rather than mutating it in place. + +SQL queries and LINQ queries are good examples of this "transformational" approach. In both cases, you always transform the original data through various functions (selects, filters, sorts) rather than modifying the original data. + +When a program is designed using a transformation approach, the result tends to be more elegant, more modular, and more scalable. And as it happens, the transformation approach is also a perfect fit with a function-oriented paradigm. + +## How F# does immutability ## + +We saw earlier that immutable values and types are the default in F#: + +```fsharp +// immutable list +let list = [1;2;3;4] + +type PersonalName = {FirstName:string; LastName:string} +// immutable person +let john = {FirstName="John"; LastName="Doe"} +``` + +Because of this, F# has a number of tricks to make life easier and to optimize the underlying code. + +First, since you can't modify a data structure, you must copy it when you want to change it. F# makes it easy to copy another data structure with only the changes you want: + +```fsharp +let alice = {john with FirstName="Alice"} +``` + +And complex data structures are implemented as linked lists or similar, so that common parts of the structure are shared. + +```fsharp +// create an immutable list +let list1 = [1;2;3;4] + +// prepend to make a new list +let list2 = 0::list1 + +// get the last 4 of the second list +let list3 = list2.Tail + +// the two lists are the identical object in memory! +System.Object.ReferenceEquals(list1,list3) +``` + +This technique ensures that, while you might appear to have hundreds of copies of a list in your code, they are all sharing the same memory behind the scenes. + +## Mutable data ## + +F# is not dogmatic about immutability; it does support mutable data with the `mutable` keyword. But turning on mutability is an explicit decision, a deviation from the default, and it is generally only needed for special cases such as optimization, caching, etc, or when dealing with the .NET libraries. + +In practice, a serious application is bound to have some mutable state if it deals with messy world of user interfaces, databases, networks and so on. But F# encourages the minimization of such mutable state. You can generally still design your core business logic to use immutable data, with all the corresponding benefits. + diff --git a/Why use F#/origin/posts/correctness-intro.md b/Why use F#/origin/posts/correctness-intro.md new file mode 100644 index 0000000..12d423e --- /dev/null +++ b/Why use F#/origin/posts/correctness-intro.md @@ -0,0 +1,28 @@ +--- +layout: post +title: "Correctness" +description: "How to write 'compile time unit tests'" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 18 +categories: [Correctness] +--- + +As a programmer, you are constantly judging the code that you and others write. In an ideal world, you should be able to look at a piece of code and easily understand exactly what it does; and of course, being concise, clear and readable is a major factor in this. + +But more importantly, you have to be able to convince yourself that the code *does what it is supposed to do*. As you program, you are constantly reasoning about code correctness, and the little compiler in your brain is checking the code for errors and possible mistakes. + +So how can a programming language help you with this? + +A modern imperative language like C# provides many ways that you are already familiar with: type checking, scoping and naming rules, access modifiers and so on. And, in recent versions, static code analysis and code contracts. + +All these techniques mean that the compiler can take on a lot of the burden of checking for correctness. If you make a mistake, the compiler will warn you. + +But F# has some additional features that can have a huge impact on ensuring correctness. The next few posts will be devoted to four of them: + +* **Immutability**, which enables code to behave much more predictably. +* **Exhaustive pattern matching**, which traps many common errors at compile time. +* **A strict type system**, which is your friend, not your enemy. You can use the static type checking almost as an instant "compile time unit test". +* **An expressive type system** that can help you "make illegal states unrepresentable"* . We'll see how to design a real-world example that demonstrates this. + +* Thanks to Yaron Minsky at Jane Street for this phrase. diff --git a/Why use F#/origin/posts/correctness-type-checking.md b/Why use F#/origin/posts/correctness-type-checking.md new file mode 100644 index 0000000..ccfc809 --- /dev/null +++ b/Why use F#/origin/posts/correctness-type-checking.md @@ -0,0 +1,174 @@ +--- +layout: post +title: "Using the type system to ensure correct code" +description: "In F# the type system is your friend, not your enemy" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 21 +categories: [Correctness, Types] +--- + +You are familiar with static type checking through languages such as C# and Java. In these languages, the type checking is straightforward but rather crude, and can be seen as an annoyance compared with the freedom of dynamic languages such as Python and Ruby. + +But in F# the type system is your friend, not your enemy. You can use static type checking almost as an instant unit test ? making sure that your code is correct at compile time. + +In the earlier posts we have already seen some of the things that you can do with the type system in F#: + +* The types and their associated functions provide an abstraction to model the problem domain. Because creating types is so easy, there is rarely an excuse to avoid designing them as needed for a given problem, and unlike C# classes it is hard to create "kitchen-sink" types that do everything. +* Well defined types aid in maintenance. Since F# uses type inference, you can normally rename or restructure types easily without using a refactoring tool. And if the type is changed in an incompatible way, this will almost certainly create compile-time errors that aid in tracking down any problems. +* Well named types provide instant documentation about their roles in the program (and this documentation can never be out of date). + +In this post and the next we will focus on using the type system as an aid to writing correct code. I will demonstrate that you can create designs such that, if your code actually compiles, it will almost certainly work as designed. + +## Using standard type checking ## + +In C#, you use the compile-time checks to validate your code without even thinking about it. For example, would you give up `List` for a plain `List`? Or give up `Nullable` and be forced to used `object` with casting? Probably not. + +But what if you could have even more fine-grained types? You could have even better compile-time checks. And this is exactly what F# offers. + +The F# type checker is not that much stricter than the C# type checker. But because it is so easy to create new types without clutter, you can represent the domain better, and, as a useful side-effect, avoid many common errors. + +Here is a simple example: + +```fsharp +//define a "safe" email address type +type EmailAddress = EmailAddress of string + +//define a function that uses it +let sendEmail (EmailAddress email) = + printfn "sent an email to %s" email + +//try to send one +let aliceEmail = EmailAddress "alice@example.com" +sendEmail aliceEmail + +//try to send a plain string +sendEmail "bob@example.com" //error +``` + +By wrapping the email address in a special type, we ensure that normal strings cannot be used as arguments to email specific functions. (In practice, we would also hide the constructor of the `EmailAddress` type as well, to ensure that only valid values could be created in the first place.) + +There is nothing here that couldn't be done in C#, but it would be quite a lot of work to create a new value type just for this one purpose, so in C#, it is easy to be lazy and just pass strings around. + +## Additional type safety features in F# ## + +Before moving on to the major topic of "designing for correctness", let's see a few of the other minor, but cool, ways that F# is type-safe. + +### Type-safe formatting with printf ### + +Here is a minor feature that demonstrates one of the ways that F# is more type-safe than C#, and how the F# compiler can catch errors that would only be detected at runtime in C#. + +Try evaluating the following and look at the errors generated: + +```fsharp +let printingExample = + printf "an int %i" 2 // ok + printf "an int %i" 2.0 // wrong type + printf "an int %i" "hello" // wrong type + printf "an int %i" // missing param + + printf "a string %s" "hello" // ok + printf "a string %s" 2 // wrong type + printf "a string %s" // missing param + printf "a string %s" "he" "lo" // too many params + + printf "an int %i and string %s" 2 "hello" // ok + printf "an int %i and string %s" "hello" 2 // wrong type + printf "an int %i and string %s" 2 // missing param +``` + +Unlike C#, the compiler analyses the format string and determines what the number and types of the arguments are supposed to be. + +This can be used to constrain the types of parameters without explicitly having to specify them. So for example, in the code below, the compiler can deduce the types of the arguments automatically. + +```fsharp +let printAString x = printf "%s" x +let printAnInt x = printf "%i" x + +// the result is: +// val printAString : string -> unit //takes a string parameter +// val printAnInt : int -> unit //takes an int parameter +``` + + +### Units of measure ### + +F# has the ability to define units of measure and associate them with floats. The unit of measure is then "attached" to the float as a type and prevents mixing different types. This is another feature that can be very handy if you need it. + +```fsharp +// define some measures +[] +type cm + +[] +type inches + +[] +type feet = + // add a conversion function + static member toInches(feet : float) : float = + feet * 12.0 + +// define some values +let meter = 100.0 +let yard = 3.0 + +//convert to different measure +let yardInInches = feet.toInches(yard) + +// can't mix and match! +yard + meter + +// now define some currencies +[] +type GBP + +[] +type USD + +let gbp10 = 10.0 +let usd10 = 10.0 +gbp10 + gbp10 // allowed: same currency +gbp10 + usd10 // not allowed: different currency +gbp10 + 1.0 // not allowed: didn't specify a currency +gbp10 + 1.0<_> // allowed using wildcard +``` + +### Type-safe equality ### + +One final example. In C# any class can be equated with any other class (using reference equality by default). In general, this is a bad idea! For example, you shouldn't really be able to compare a string with a person at all. + +Here is some C# code which is perfectly valid and compiles fine: + +```csharp +using System; +var obj = new Object(); +var ex = new Exception(); +var b = (obj == ex); +``` + +If we write the identical code in F#, we get a compile-time error: + +```fsharp +open System +let obj = new Object() +let ex = new Exception() +let b = (obj = ex) +``` + +Chances are, if you are testing equality between two different types, you are doing something wrong. + +In F#, you can even stop a type being compared at all! This is not as silly as it seems. For some types, there may not be a useful default, or you may want to force equality to be based on a specific field rather than the object as whole. + +Here is an example of this: + +```fsharp +// deny comparison +[] +type CustomerAccount = {CustomerAccountId: int} + +let x = {CustomerAccountId = 1} + +x = x // error! +x.CustomerAccountId = x.CustomerAccountId // no error +``` diff --git a/Why use F#/origin/posts/designing-for-correctness.md b/Why use F#/origin/posts/designing-for-correctness.md new file mode 100644 index 0000000..36eda0e --- /dev/null +++ b/Why use F#/origin/posts/designing-for-correctness.md @@ -0,0 +1,648 @@ +--- +layout: post +title: "Worked example: Designing for correctness" +description: "How to make illegal states unrepresentable" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 22 +categories: [Correctness, Types, Worked Examples] +--- + +In this post, we'll see how you can design for correctness (or at least, for the requirements as you currently understand them), by which I mean that a client of a well designed model will not be able to put the system into an illegal state ? a state that doesn't meet the requirements. You literally cannot create incorrect code because the compiler will not let you. + +For this to work, we do have to spend some time up front thinking about design and making an effort to encode the requirements into the types that you use. +If you just use strings or lists for all your data structures, you will not get any benefit from the type checking. + +We'll use a simple example. Let's say that you are designing an e-commerce site which has a shopping cart and you are given the following requirements. + +* You can only pay for a cart once. +* Once a cart is paid for, you cannot change the items in it. +* Empty carts cannot be paid for. + +## A bad design in C# ## + +In C#, we might think that this is simple enough and dive straight into coding. Here is a straightforward implementation in C# that seems OK at first glance. + +```csharp +public class NaiveShoppingCart +{ + private List items; + private decimal paidAmount; + + public NaiveShoppingCart() + { + this.items = new List(); + this.paidAmount = 0; + } + + /// Is cart paid for? + public bool IsPaidFor { get { return this.paidAmount > 0; } } + + /// Readonly list of items + public IEnumerable Items { get {return this.items; } } + + /// add item only if not paid for + public void AddItem(TItem item) + { + if (!this.IsPaidFor) + { + this.items.Add(item); + } + } + + /// remove item only if not paid for + public void RemoveItem(TItem item) + { + if (!this.IsPaidFor) + { + this.items.Remove(item); + } + } + + /// pay for the cart + public void Pay(decimal amount) + { + if (!this.IsPaidFor) + { + this.paidAmount = amount; + } + } +} +``` + +Unfortunately, it's actually a pretty bad design: + +* One of the requirements is not even met. Can you see which one? +* It has a major design flaw, and a number of minor ones. Can you see what they are? + +So many problems in such a short piece of code! + +What would happen if we had even more complicated requirements and the code was thousands of lines long? For example, the fragment that is repeated everywhere: + +```csharp +if (!this.IsPaidFor) { do something } +``` + +looks like it will be quite brittle if requirements change in some methods but not others. + +Before you read the next section, think for a minute how you might better implement the requirements above in C#, with these additional requirements: + +* If you try to do something that is not allowed in the requirements, you will get a *compile time error*, not a run time error. For example, you must create a design such that you cannot even call the `RemoveItem` method from an empty cart. +* The contents of the cart in any state should be immutable. The benefit of this is that if I am in the middle of paying for a cart, the cart contents can't change even if some other process is adding or removing items at the same time. + +## A correct design in F# ## + +Let's step back and see if we can come up with a better design. Looking at these requirements, it's obvious that we have a simple state machine with three states and some state transitions: + +* A Shopping Cart can be Empty, Active or PaidFor +* When you add an item to an Empty cart, it becomes Active +* When you remove the last item from an Active cart, it becomes Empty +* When you pay for an Active cart, it becomes PaidFor + +And now we can add the business rules to this model: + +* You can add an item only to carts that are Empty or Active +* You can remove an item only from carts that are Active +* You can only pay for carts that are Active + +Here is the state diagram: + +![Shopping Cart](../assets/img/ShoppingCart.png) + +It's worth noting that these kinds of state-oriented models are very common in business systems. Product development, customer relationship management, order processing, and other workflows can often be modeled this way. + +Now we have the design, we can reproduce it in F#: + +```fsharp +type CartItem = string // placeholder for a more complicated type + +type EmptyState = NoItems // don't use empty list! We want to + // force clients to handle this as a + // separate case. E.g. "you have no + // items in your cart" + +type ActiveState = { UnpaidItems : CartItem list; } +type PaidForState = { PaidItems : CartItem list; + Payment : decimal} + +type Cart = + | Empty of EmptyState + | Active of ActiveState + | PaidFor of PaidForState +``` + +We create a type for each state, and `Cart` type that is a choice of any one of the states. I have given everything a distinct name (e.g. `PaidItems` and `UnpaidItems` rather than just `Items`) because this helps the inference engine and makes the code more self documenting. + +
+

This is a much longer example than the earlier ones! Don't worry too much about the F# syntax right now, but I hope that you can get the gist of the code, and see how it fits into the overall design.

+

Also, do paste the snippets into a script file and evaluate them for yourself as they come up.

+
+ +Next we can create the operations for each state. The main thing to note is each operation will always take one of the States as input and return a new Cart. That is, you start off with a particular known state, but you return a `Cart` which is a wrapper for a choice of three possible states. + +```fsharp +// ============================= +// operations on empty state +// ============================= + +let addToEmptyState item = + // returns a new Active Cart + Cart.Active {UnpaidItems=[item]} + +// ============================= +// operations on active state +// ============================= + +let addToActiveState state itemToAdd = + let newList = itemToAdd :: state.UnpaidItems + Cart.Active {state with UnpaidItems=newList } + +let removeFromActiveState state itemToRemove = + let newList = state.UnpaidItems + |> List.filter (fun i -> i<>itemToRemove) + + match newList with + | [] -> Cart.Empty NoItems + | _ -> Cart.Active {state with UnpaidItems=newList} + +let payForActiveState state amount = + // returns a new PaidFor Cart + Cart.PaidFor {PaidItems=state.UnpaidItems; Payment=amount} +``` + +Next, we attach the operations to the states as methods + +```fsharp +type EmptyState with + member this.Add = addToEmptyState + +type ActiveState with + member this.Add = addToActiveState this + member this.Remove = removeFromActiveState this + member this.Pay = payForActiveState this +``` + +And we can create some cart level helper methods as well. At the cart level, we have to explicitly handle each possibility for the internal state with a `match..with` expression. + +```fsharp +let addItemToCart cart item = + match cart with + | Empty state -> state.Add item + | Active state -> state.Add item + | PaidFor state -> + printfn "ERROR: The cart is paid for" + cart + +let removeItemFromCart cart item = + match cart with + | Empty state -> + printfn "ERROR: The cart is empty" + cart // return the cart + | Active state -> + state.Remove item + | PaidFor state -> + printfn "ERROR: The cart is paid for" + cart // return the cart + +let displayCart cart = + match cart with + | Empty state -> + printfn "The cart is empty" // can't do state.Items + | Active state -> + printfn "The cart contains %A unpaid items" + state.UnpaidItems + | PaidFor state -> + printfn "The cart contains %A paid items. Amount paid: %f" + state.PaidItems state.Payment + +type Cart with + static member NewCart = Cart.Empty NoItems + member this.Add = addItemToCart this + member this.Remove = removeItemFromCart this + member this.Display = displayCart this +``` + +## Testing the design ## + +Let's exercise this code now: + +```fsharp +let emptyCart = Cart.NewCart +printf "emptyCart="; emptyCart.Display + +let cartA = emptyCart.Add "A" +printf "cartA="; cartA.Display +``` + +We now have an active cart with one item in it. Note that "`cartA`" is a completely different object from "`emptyCart`" and is in a different state. + +Let's keep going: + +```fsharp +let cartAB = cartA.Add "B" +printf "cartAB="; cartAB.Display + +let cartB = cartAB.Remove "A" +printf "cartB="; cartB.Display + +let emptyCart2 = cartB.Remove "B" +printf "emptyCart2="; emptyCart2.Display +``` + +So far, so good. Again, all these are distinct objects in different states, + +Let's test the requirement that you cannot remove items from an empty cart: + +```fsharp +let emptyCart3 = emptyCart2.Remove "B" //error +printf "emptyCart3="; emptyCart3.Display +``` + +An error ? just what we want! + +Now say that we want to pay for a cart. We didn't create this method at the Cart level, because we didn't want to tell the client how to handle all the cases. This method only exists for the Active state, so the client will have to explicitly handle each case and only call the `Pay` method when an Active state is matched. + +First we'll try to pay for cartA. + +```fsharp +// try to pay for cartA +let cartAPaid = + match cartA with + | Empty _ | PaidFor _ -> cartA + | Active state -> state.Pay 100m +printf "cartAPaid="; cartAPaid.Display +``` + +The result was a paid cart. + +Now we'll try to pay for the emptyCart. + +```fsharp +// try to pay for emptyCart +let emptyCartPaid = + match emptyCart with + | Empty _ | PaidFor _ -> emptyCart + | Active state -> state.Pay 100m +printf "emptyCartPaid="; emptyCartPaid.Display +``` + +Nothing happens. The cart is empty, so the Active branch is not called. We might want to raise an error or log a message in the other branches, but no matter what we do we cannot accidentally call the `Pay` method on an empty cart, because that state does not have a method to call! + +The same thing happens if we accidentally try to pay for a cart that is already paid. + +```fsharp +// try to pay for cartAB +let cartABPaid = + match cartAB with + | Empty _ | PaidFor _ -> cartAB // return the same cart + | Active state -> state.Pay 100m + +// try to pay for cartAB again +let cartABPaidAgain = + match cartABPaid with + | Empty _ | PaidFor _ -> cartABPaid // return the same cart + | Active state -> state.Pay 100m +``` + +You might argue that the client code above might not be representative of code in the real world ? it is well-behaved and already dealing with the requirements. + +So what happens if we have badly written or malicious client code that tries to force payment: + +```fsharp +match cartABPaid with +| Empty state -> state.Pay 100m +| PaidFor state -> state.Pay 100m +| Active state -> state.Pay 100m +``` + +If we try to force it like this, we will get compile errors. There is no way the client can create code that does not meet the requirements. + +## Summary ## + +We have designed a simple shopping cart model which has many benefits over the C# design. + +* It maps to the requirements quite clearly. It is impossible for a client of this API to call code that doesn't meet the requirements. +* Using states means that the number of possible code paths is much smaller than the C# version, so there will be many fewer unit tests to write. +* Each function is simple enough to probably work the first time, as, unlike the C# version, there are no conditionals anywhere. + +
+

Analysis of the original C# code

+ +

+Now that you have seen the F# code, we can revisit the original C# code with fresh eyes. In case you were wondering, here are my thoughts as to what is wrong with the C# shopping cart example as designed. +

+ +

+Requirement not met: An empty cart can still be paid for. +

+ +

+Major design flaw: Overloading the payment amount to be a signal for IsPaidFor means that a zero paid amount can never lock down the cart. Are you sure it would never be possible to have a cart which is paid for but free of charge? The requirements are not clear, but what if this did become a requirement later? How much code would have to be changed? +

+ +

+Minor design flaws: What should happen when trying to remove an item from an empty cart? And what should happen when attempting to pay for a cart that is already paid for? Should we throw exceptions in these cases, or just silently ignore them? And does it make sense that a client should be able to enumerate the items in an empty cart? And this is not thread safe as designed; so what happens if a secondary thread adds an item to the cart while a payment is being made on the main thread? +

+ +

+That's quite a lot of things to worry about. +

+ +

+The nice thing about the F# design is none of these problems can even exist. So designing this way not only ensures correct code, but it also really reduces the cognitive effort to ensure that the design is bullet proof in the first place. +

+ +

+Compile time checking: The original C# design mixes up all the states and transitions in a single class, which makes it very error prone. A better approach would be to create separate state classes (with a common base class say) which reduces complexity, but still, the lack of a built in "union" type means that you cannot statically verify that the code is correct. There are ways of doing "union" types in C#, but it is not idiomatic at all, while in F# it is commonplace. +

+ + +
+ +## Appendix: C# code for a correct solution + +When faced with these requirements in C#, you might immediately think -- just create an interface! + +But it is not as easy as you might think. I have written a follow up post on this to explain why: [The shopping cart example in C#](../csharp/union-types-in-csharp.html). + +If you are interested to see what the C# code for a solution looks like, here it is below. This code meets the requirements above and guarantees correctness at *compile time*, as desired. + +The key thing to note is that, because C# doesn't have union types, the implementation uses a ["fold" function](../posts/match-expression.md#folds), +a function that has three function parameters, one for each state. To use the cart, the caller passes a set of three lambdas in, and the (hidden) state determines what happens. + +```csharp +var paidCart = cartA.Do( + // lambda for Empty state + state => cartA, + // lambda for Active state + state => state.Pay(100), + // lambda for Paid state + state => cartA); +``` + +This approach means that the caller can never call the "wrong" function, such as "Pay" for the Empty state, because the parameter to the lambda will not support it. Try it and see! + +```csharp +using System; +using System.Collections.Generic; +using System.Linq; + +namespace WhyUseFsharp +{ + + public class ShoppingCart + { + + #region ShoppingCart State classes + + /// + /// Represents the Empty state + /// + public class EmptyState + { + public ShoppingCart Add(TItem item) + { + var newItems = new[] { item }; + var newState = new ActiveState(newItems); + return FromState(newState); + } + } + + /// + /// Represents the Active state + /// + public class ActiveState + { + public ActiveState(IEnumerable items) + { + Items = items; + } + + public IEnumerable Items { get; private set; } + + public ShoppingCart Add(TItem item) + { + var newItems = new List(Items) {item}; + var newState = new ActiveState(newItems); + return FromState(newState); + } + + public ShoppingCart Remove(TItem item) + { + var newItems = new List(Items); + newItems.Remove(item); + if (newItems.Count > 0) + { + var newState = new ActiveState(newItems); + return FromState(newState); + } + else + { + var newState = new EmptyState(); + return FromState(newState); + } + } + + public ShoppingCart Pay(decimal amount) + { + var newState = new PaidForState(Items, amount); + return FromState(newState); + } + + + } + + /// + /// Represents the Paid state + /// + public class PaidForState + { + public PaidForState(IEnumerable items, decimal amount) + { + Items = items.ToList(); + Amount = amount; + } + + public IEnumerable Items { get; private set; } + public decimal Amount { get; private set; } + } + + #endregion ShoppingCart State classes + + //==================================== + // Execute of shopping cart proper + //==================================== + + private enum Tag { Empty, Active, PaidFor } + private readonly Tag _tag = Tag.Empty; + private readonly object _state; //has to be a generic object + + /// + /// Private ctor. Use FromState instead + /// + private ShoppingCart(Tag tagValue, object state) + { + _state = state; + _tag = tagValue; + } + + public static ShoppingCart FromState(EmptyState state) + { + return new ShoppingCart(Tag.Empty, state); + } + + public static ShoppingCart FromState(ActiveState state) + { + return new ShoppingCart(Tag.Active, state); + } + + public static ShoppingCart FromState(PaidForState state) + { + return new ShoppingCart(Tag.PaidFor, state); + } + + /// + /// Create a new empty cart + /// + public static ShoppingCart NewCart() + { + var newState = new EmptyState(); + return FromState(newState); + } + + /// + /// Call a function for each case of the state + /// + /// + /// Forcing the caller to pass a function for each possible case means that all cases are handled at all times. + /// + public TResult Do( + Func emptyFn, + Func activeFn, + Func paidForyFn + ) + { + switch (_tag) + { + case Tag.Empty: + return emptyFn(_state as EmptyState); + case Tag.Active: + return activeFn(_state as ActiveState); + case Tag.PaidFor: + return paidForyFn(_state as PaidForState); + default: + throw new InvalidOperationException(string.Format("Tag {0} not recognized", _tag)); + } + } + + /// + /// Do an action without a return value + /// + public void Do( + Action emptyFn, + Action activeFn, + Action paidForyFn + ) + { + //convert the Actions into Funcs by returning a dummy value + Do( + state => { emptyFn(state); return 0; }, + state => { activeFn(state); return 0; }, + state => { paidForyFn(state); return 0; } + ); + } + + + + } + + /// + /// Extension methods for my own personal library + /// + public static class ShoppingCartExtension + { + /// + /// Helper method to Add + /// + public static ShoppingCart Add(this ShoppingCart cart, TItem item) + { + return cart.Do( + state => state.Add(item), //empty case + state => state.Add(item), //active case + state => { Console.WriteLine("ERROR: The cart is paid for and items cannot be added"); return cart; } //paid for case + ); + } + + /// + /// Helper method to Remove + /// + public static ShoppingCart Remove(this ShoppingCart cart, TItem item) + { + return cart.Do( + state => { Console.WriteLine("ERROR: The cart is empty and items cannot be removed"); return cart; }, //empty case + state => state.Remove(item), //active case + state => { Console.WriteLine("ERROR: The cart is paid for and items cannot be removed"); return cart; } //paid for case + ); + } + + /// + /// Helper method to Display + /// + public static void Display(this ShoppingCart cart) + { + cart.Do( + state => Console.WriteLine("The cart is empty"), + state => Console.WriteLine("The active cart contains {0} items", state.Items.Count()), + state => Console.WriteLine("The paid cart contains {0} items. Amount paid {1}", state.Items.Count(), state.Amount) + ); + } + } + + [NUnit.Framework.TestFixture] + public class CorrectShoppingCartTest + { + [NUnit.Framework.Test] + public void TestCart() + { + var emptyCart = ShoppingCart.NewCart(); + emptyCart.Display(); + + var cartA = emptyCart.Add("A"); //one item + cartA.Display(); + + var cartAb = cartA.Add("B"); //two items + cartAb.Display(); + + var cartB = cartAb.Remove("A"); //one item + cartB.Display(); + + var emptyCart2 = cartB.Remove("B"); //empty + emptyCart2.Display(); + + Console.WriteLine("Removing from emptyCart"); + emptyCart.Remove("B"); //error + + + // try to pay for cartA + Console.WriteLine("paying for cartA"); + var paidCart = cartA.Do( + state => cartA, + state => state.Pay(100), + state => cartA); + paidCart.Display(); + + Console.WriteLine("Adding to paidCart"); + paidCart.Add("C"); + + // try to pay for emptyCart + Console.WriteLine("paying for emptyCart"); + var emptyCartPaid = emptyCart.Do( + state => emptyCart, + state => state.Pay(100), + state => emptyCart); + emptyCartPaid.Display(); + } + } +} + +``` \ No newline at end of file diff --git a/Why use F#/origin/posts/fsharp-in-60-seconds.md b/Why use F#/origin/posts/fsharp-in-60-seconds.md new file mode 100644 index 0000000..930dc47 --- /dev/null +++ b/Why use F#/origin/posts/fsharp-in-60-seconds.md @@ -0,0 +1,156 @@ +--- +layout: post +title: "F# syntax in 60 seconds" +description: "A very quick overview on how to read F# code" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 2 +--- + +Here is a very quick overview on how to read F# code for newcomers unfamiliar with the syntax. + +It is obviously not very detailed but should be enough so that you can read and get the gist of the upcoming examples in this series. Don't worry if you don't understand all of it, as I will give more detailed explanations when we get to the actual code examples. + +The two major differences between F# syntax and a standard C-like syntax are: + +* Curly braces are not used to delimit blocks of code. Instead, indentation is used (Python is similar this way). +* Whitespace is used to separate parameters rather than commas. + +Some people find the F# syntax off-putting. If you are one of them, consider this quote: + +> "Optimising your notation to not confuse people in the first 10 minutes of seeing it but to hinder readability ever after is a really bad mistake." +> (David MacIver, via [a post about Scala syntax](http://rickyclarkson.blogspot.co.uk/2008/01/in-defence-of-0l-in-scala.html)). + +Personally, I think that the F# syntax is very clear and straightforward when you get used to it. In many ways, it is simpler than the C# syntax, with fewer keywords and special cases. + +The example code below is a simple F# script that demonstrates most of the concepts that you need on a regular basis. + +I would encourage you to test this code interactively and play with it a bit! Either: + +* Type this into a F# script file (with .fsx extension) +and send it to the interactive window. See the ["installing and using F#"](../installing-and-using/index.md) page for details. +* Alternatively, try running this code in the interactive window. Remember to always use `;;` at the end to tell +the interpreter that you are done entering and ready to evaluate. + + +```fsharp +// single line comments use a double slash +(* multi line comments use (* . . . *) pair + +-end of multi line comment- *) + +// ======== "Variables" (but not really) ========== +// The "let" keyword defines an (immutable) value +let myInt = 5 +let myFloat = 3.14 +let myString = "hello" //note that no types needed + +// ======== Lists ============ +let twoToFive = [2;3;4;5] // Square brackets create a list with + // semicolon delimiters. +let oneToFive = 1 :: twoToFive // :: creates list with new 1st element +// The result is [1;2;3;4;5] +let zeroToFive = [0;1] @ twoToFive // @ concats two lists + +// IMPORTANT: commas are never used as delimiters, only semicolons! + +// ======== Functions ======== +// The "let" keyword also defines a named function. +let square x = x * x // Note that no parens are used. +square 3 // Now run the function. Again, no parens. + +let add x y = x + y // don't use add (x,y)! It means something + // completely different. +add 2 3 // Now run the function. + +// to define a multiline function, just use indents. No semicolons needed. +let evens list = + let isEven x = x%2 = 0 // Define "isEven" as an inner ("nested") function + List.filter isEven list // List.filter is a library function + // with two parameters: a boolean function + // and a list to work on + +evens oneToFive // Now run the function + +// You can use parens to clarify precedence. In this example, +// do "map" first, with two args, then do "sum" on the result. +// Without the parens, "List.map" would be passed as an arg to List.sum +let sumOfSquaresTo100 = + List.sum ( List.map square [1..100] ) + +// You can pipe the output of one operation to the next using "|>" +// Here is the same sumOfSquares function written using pipes +let sumOfSquaresTo100piped = + [1..100] |> List.map square |> List.sum // "square" was defined earlier + +// you can define lambdas (anonymous functions) using the "fun" keyword +let sumOfSquaresTo100withFun = + [1..100] |> List.map (fun x->x*x) |> List.sum + +// In F# returns are implicit -- no "return" needed. A function always +// returns the value of the last expression used. + +// ======== Pattern Matching ======== +// Match..with.. is a supercharged case/switch statement. +let simplePatternMatch = + let x = "a" + match x with + | "a" -> printfn "x is a" + | "b" -> printfn "x is b" + | _ -> printfn "x is something else" // underscore matches anything + +// Some(..) and None are roughly analogous to Nullable wrappers +let validValue = Some(99) +let invalidValue = None + +// In this example, match..with matches the "Some" and the "None", +// and also unpacks the value in the "Some" at the same time. +let optionPatternMatch input = + match input with + | Some i -> printfn "input is an int=%d" i + | None -> printfn "input is missing" + +optionPatternMatch validValue +optionPatternMatch invalidValue + +// ========= Complex Data Types ========= + +// Tuple types are pairs, triples, etc. Tuples use commas. +let twoTuple = 1,2 +let threeTuple = "a",2,true + +// Record types have named fields. Semicolons are separators. +type Person = {First:string; Last:string} +let person1 = {First="john"; Last="Doe"} + +// Union types have choices. Vertical bars are separators. +type Temp = + | DegreesC of float + | DegreesF of float +let temp = DegreesF 98.6 + +// Types can be combined recursively in complex ways. +// E.g. here is a union type that contains a list of the same type: +type Employee = + | Worker of Person + | Manager of Employee list +let jdoe = {First="John";Last="Doe"} +let worker = Worker jdoe + +// ========= Printing ========= +// The printf/printfn functions are similar to the +// Console.Write/WriteLine functions in C#. +printfn "Printing an int %i, a float %f, a bool %b" 1 2.0 true +printfn "A string %s, and something generic %A" "hello" [1;2;3;4] + +// all complex types have pretty printing built in +printfn "twoTuple=%A,\nPerson=%A,\nTemp=%A,\nEmployee=%A" + twoTuple person1 temp worker + +// There are also sprintf/sprintfn functions for formatting data +// into a string, similar to String.Format. + + +``` + +And with that, let's start by comparing some simple F# code with the equivalent C# code. \ No newline at end of file diff --git a/Why use F#/origin/posts/fvsc-download.md b/Why use F#/origin/posts/fvsc-download.md new file mode 100644 index 0000000..f54b79c --- /dev/null +++ b/Why use F#/origin/posts/fvsc-download.md @@ -0,0 +1,143 @@ +--- +layout: post +title: "Comparing F# with C#: Downloading a web page" +description: "In which we see that F# excels at callbacks, and we are introduced to the 'use' keyword" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 5 +categories: [F# vs C#] +--- + +In this example, we will compare the F# and C# code for downloading a web page, with a callback to process the text stream. + +We'll start with a straightforward F# implementation. + +```fsharp +// "open" brings a .NET namespace into visibility +open System.Net +open System +open System.IO + +// Fetch the contents of a web page +let fetchUrl callback url = + let req = WebRequest.Create(Uri(url)) + use resp = req.GetResponse() + use stream = resp.GetResponseStream() + use reader = new IO.StreamReader(stream) + callback reader url +``` + +Let's go through this code: + +* The use of "open" at the top allows us to write "WebRequest" rather than "System.Net.WebRequest". It is similar to a "`using System.Net`" header in C#. +* Next, we define the `fetchUrl` function, which takes two arguments, a callback to process the stream, and the url to fetch. +* We next wrap the url string in a Uri. F# has strict type-checking, so if instead we had written: +`let req = WebRequest.Create(url)` +the compiler would have complained that it didn't know which version of `WebRequest.Create` to use. +* When declaring the `response`, `stream` and `reader` values, the "`use`" keyword is used instead of "`let`". This can only be used in conjunction with classes that implement `IDisposable`. + It tells the compiler to automatically dispose of the resource when it goes out of scope. This is equivalent to the C# "`using`" keyword. +* The last line calls the callback function with the StreamReader and url as parameters. Note that the type of the callback does not have to be specified anywhere. + +Now here is the equivalent C# implementation. + +```csharp +class WebPageDownloader +{ + public TResult FetchUrl( + string url, + Func callback) + { + var req = WebRequest.Create(url); + using (var resp = req.GetResponse()) + { + using (var stream = resp.GetResponseStream()) + { + using (var reader = new StreamReader(stream)) + { + return callback(url, reader); + } + } + } + } +} +``` + +As usual, the C# version has more 'noise'. + +* There are ten lines just for curly braces, and there is the visual complexity of 5 levels of nesting* +* All the parameter types have to be explicitly declared, and the generic `TResult` type has to be repeated three times. + +* It's true that in this particular example, when all the `using` statements are adjacent, the [extra braces and indenting can be removed](https://stackoverflow.com/questions/1329739/nested-using-statements-in-c-sharp), +but in the more general case they are needed. + +## Testing the code + +Back in F# land, we can now test the code interactively: + +```fsharp +let myCallback (reader:IO.StreamReader) url = + let html = reader.ReadToEnd() + let html1000 = html.Substring(0,1000) + printfn "Downloaded %s. First 1000 is %s" url html1000 + html // return all the html + +//test +let google = fetchUrl myCallback "http://google.com" +``` + +Finally, we have to resort to a type declaration for the reader parameter (`reader:IO.StreamReader`). This is required because the F# compiler cannot determine the type of the "reader" parameter automatically. + +A very useful feature of F# is that you can "bake in" parameters in a function so that they don't have to be passed in every time. This is why the `url` parameter was placed *last* rather than first, as in the C# version. +The callback can be setup once, while the url varies from call to call. + +```fsharp +// build a function with the callback "baked in" +let fetchUrl2 = fetchUrl myCallback + +// test +let google = fetchUrl2 "http://www.google.com" +let bbc = fetchUrl2 "http://news.bbc.co.uk" + +// test with a list of sites +let sites = ["http://www.bing.com"; + "http://www.google.com"; + "http://www.yahoo.com"] + +// process each site in the list +sites |> List.map fetchUrl2 +``` + +The last line (using `List.map`) shows how the new function can be easily used in conjunction with list processing functions to download a whole list at once. + +Here is the equivalent C# test code: + +```csharp +[Test] +public void TestFetchUrlWithCallback() +{ + Func myCallback = (url, reader) => + { + var html = reader.ReadToEnd(); + var html1000 = html.Substring(0, 1000); + Console.WriteLine( + "Downloaded {0}. First 1000 is {1}", url, + html1000); + return html; + }; + + var downloader = new WebPageDownloader(); + var google = downloader.FetchUrl("http://www.google.com", + myCallback); + + // test with a list of sites + var sites = new List { + "http://www.bing.com", + "http://www.google.com", + "http://www.yahoo.com"}; + + // process each site in the list + sites.ForEach(site => downloader.FetchUrl(site, myCallback)); +} +``` + +Again, the code is a bit noisier than the F# code, with many explicit type references. More importantly, the C# code doesn't easily allow you to bake in some of the parameters in a function, so the callback must be explicitly referenced every time. diff --git a/Why use F#/origin/posts/fvsc-quicksort.md b/Why use F#/origin/posts/fvsc-quicksort.md new file mode 100644 index 0000000..3a2057e --- /dev/null +++ b/Why use F#/origin/posts/fvsc-quicksort.md @@ -0,0 +1,212 @@ +--- +layout: post +title: "Comparing F# with C#: Sorting" +description: "In which we see that F# is more declarative than C#, and we are introduced to pattern matching." +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 4 +categories: [F# vs C#] +--- + +In this next example, we will implement a quicksort-like algorithm for sorting lists and compare an F# implementation to a C# implementation. + +Here is the logic for a simplified quicksort-like algorithm: + +
+If the list is empty, there is nothing to do.
+Otherwise: 
+  1. Take the first element of the list
+  2. Find all elements in the rest of the list that 
+      are less than the first element, and sort them. 
+  3. Find all elements in the rest of the list that 
+      are >= than the first element, and sort them
+  4. Combine the three parts together to get the final result: 
+      (sorted smaller elements + firstElement + 
+       sorted larger elements)
+
+ +Note that this is a simplified algorithm and is not optimized (and it does not sort in place, like a true quicksort); we want to focus on clarity for now. + +Here is the code in F#: + +```fsharp +let rec quicksort list = + match list with + | [] -> // If the list is empty + [] // return an empty list + | firstElem::otherElements -> // If the list is not empty + let smallerElements = // extract the smaller ones + otherElements + |> List.filter (fun e -> e < firstElem) + |> quicksort // and sort them + let largerElements = // extract the large ones + otherElements + |> List.filter (fun e -> e >= firstElem) + |> quicksort // and sort them + // Combine the 3 parts into a new list and return it + List.concat [smallerElements; [firstElem]; largerElements] + +//test +printfn "%A" (quicksort [1;5;23;18;9;1;3]) +``` + +Again note that this is not an optimized implementation, but is designed to mirror the algorithm closely. + +Let's go through this code: + +* There are no type declarations anywhere. This function will work on any list that has comparable items (which is almost all F# types, because they automatically have a default comparison function). +* The whole function is recursive -- this is signaled to the compiler using the `rec` keyword in "`let rec quicksort list =`". +* The `match..with` is sort of like a switch/case statement. Each branch to test is signaled with a vertical bar, like so: + +```fsharp +match x with +| caseA -> something +| caseB -> somethingElse +``` +* The "`match`" with `[]` matches an empty list, and returns an empty list. +* The "`match`" with `firstElem::otherElements` does two things. + * First, it only matches a non-empty list. + * Second, it creates two new values automatically. One for the first element called "`firstElem`", and one for the rest of the list, called "`otherElements`". + In C# terms, this is like having a "switch" statement that not only branches, but does variable declaration and assignment *at the same time*. +* The `->` is sort of like a lambda (`=>`) in C#. The equivalent C# lambda would look something like `(firstElem, otherElements) => do something`. +* The "`smallerElements`" section takes the rest of the list, filters it against the first element using an inline lambda expression with the "`<`" operator and then pipes the result into the quicksort function recursively. +* The "`largerElements`" line does the same thing, except using the "`>=`" operator +* Finally the resulting list is constructed using the list concatenation function "`List.concat`". For this to work, the first element needs to be put into a list, which is what the square brackets are for. +* Again note there is no "return" keyword; the last value will be returned. In the "`[]`" branch the return value is the empty list, and in the main branch, it is the newly constructed list. + +For comparison here is an old-style C# implementation (without using LINQ). + +```csharp +public class QuickSortHelper +{ + public static List QuickSort(List values) + where T : IComparable + { + if (values.Count == 0) + { + return new List(); + } + + //get the first element + T firstElement = values[0]; + + //get the smaller and larger elements + var smallerElements = new List(); + var largerElements = new List(); + for (int i = 1; i < values.Count; i++) // i starts at 1 + { // not 0! + var elem = values[i]; + if (elem.CompareTo(firstElement) < 0) + { + smallerElements.Add(elem); + } + else + { + largerElements.Add(elem); + } + } + + //return the result + var result = new List(); + result.AddRange(QuickSort(smallerElements.ToList())); + result.Add(firstElement); + result.AddRange(QuickSort(largerElements.ToList())); + return result; + } +} +``` + +Comparing the two sets of code, again we can see that the F# code is much more compact, with less noise and no need for type declarations. + +Furthermore, the F# code reads almost exactly like the actual algorithm, unlike the C# code. This is another key advantage of F# -- the code is generally more declarative ("what to do") and less imperative ("how to do it") than C#, and is therefore much more self-documenting. + + +## A functional implementation in C# ## + +Here's a more modern "functional-style" implementation using LINQ and an extension method: + +```csharp +public static class QuickSortExtension +{ + /// + /// Implement as an extension method for IEnumerable + /// + public static IEnumerable QuickSort( + this IEnumerable values) where T : IComparable + { + if (values == null || !values.Any()) + { + return new List(); + } + + //split the list into the first element and the rest + var firstElement = values.First(); + var rest = values.Skip(1); + + //get the smaller and larger elements + var smallerElements = rest + .Where(i => i.CompareTo(firstElement) < 0) + .QuickSort(); + + var largerElements = rest + .Where(i => i.CompareTo(firstElement) >= 0) + .QuickSort(); + + //return the result + return smallerElements + .Concat(new List{firstElement}) + .Concat(largerElements); + } +} +``` + +This is much cleaner, and reads almost the same as the F# version. But unfortunately there is no way of avoiding the extra noise in the function signature. + +## Correctness + +Finally, a beneficial side-effect of this compactness is that F# code often works the first time, while the C# code may require more debugging. + +Indeed, when coding these samples, the old-style C# code was incorrect initially, and required some debugging to get it right. Particularly tricky areas were the `for` loop (starting at 1 not zero) and the `CompareTo` comparison (which I got the wrong way round), and it would also be very easy to accidentally modify the inbound list. The functional style in the second C# example is not only cleaner but was easier to code correctly. + +But even the functional C# version has drawbacks compared to the F# version. For example, because F# uses pattern matching, it is not possible to branch to the "non-empty list" case with an empty list. On the other hand, in the C# code, if we forgot the test: + +```csharp +if (values == null || !values.Any()) ... +``` + +then the extraction of the first element: + +```csharp +var firstElement = values.First(); +``` + +would fail with an exception. The compiler cannot enforce this for you. In your own code, how often have you used `FirstOrDefault` rather than `First` because you are writing "defensive" code. Here is an example of a code pattern that is very common in C# but is rare in F#: + +```csharp +var item = values.FirstOrDefault(); // instead of .First() +if (item != null) +{ + // do something if item is valid +} +``` + +The one-step "pattern match and branch" in F# allows you to avoid this in many cases. + +## Postscript + +The example implementation in F# above is actually pretty verbose by F# standards! + +For fun, here is what a more typically concise version would look like: + +```fsharp +let rec quicksort2 = function + | [] -> [] + | first::rest -> + let smaller,larger = List.partition ((>=) first) rest + List.concat [quicksort2 smaller; [first]; quicksort2 larger] + +// test code +printfn "%A" (quicksort2 [1;5;23;18;9;1;3]) +``` + +Not bad for 4 lines of code, and when you get used to the syntax, still quite readable. diff --git a/Why use F#/origin/posts/fvsc-sum-of-squares.md b/Why use F#/origin/posts/fvsc-sum-of-squares.md new file mode 100644 index 0000000..8a22220 --- /dev/null +++ b/Why use F#/origin/posts/fvsc-sum-of-squares.md @@ -0,0 +1,161 @@ +--- +layout: post +title: "Comparing F# with C#: A simple sum" +description: "In which we attempt to sum the squares from 1 to N without using a loop" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 3 +categories: [F# vs C#] +--- + + +To see what some real F# code looks like, let's start with a simple problem: "sum the squares from 1 to N". + +We'll compare an F# implementation with a C# implementation. First, the F# code: + +```fsharp +// define the square function +let square x = x * x + +// define the sumOfSquares function +let sumOfSquares n = + [1..n] |> List.map square |> List.sum + +// try it +sumOfSquares 100 +``` + +The mysterious looking `|>` is called the pipe operator. It just pipes the output of one expression into the input of the next. So the code for `sumOfSquares` reads as: + +1. Create a list of 1 to n (square brackets construct a list). +1. Pipe the list into the library function called `List.map`, transforming the input list into an output list using the "square" function we just defined. +1. Pipe the resulting list of squares into the library function called `List.sum`. Can you guess what it does? +1. There is no explicit "return" statement. The output of `List.sum` is the overall result of the function. + +Next, here's a C# implementation using the classic (non-functional) style of a C-based language. (A more functional version using LINQ is discussed later.) + +```csharp +public static class SumOfSquaresHelper +{ + public static int Square(int i) + { + return i * i; + } + + public static int SumOfSquares(int n) + { + int sum = 0; + for (int i = 1; i <= n; i++) + { + sum += Square(i); + } + return sum; + } +} +``` + +What are the differences? + +* The F# code is more compact +* The F# code didn't have any type declarations +* F# can be developed interactively + +Let's take each of these in turn. + +### Less code + +The most obvious difference is that there is a lot more C# code. 13 C# lines compared with 3 F# lines (ignoring comments). The C# code has lots of "noise", things like curly braces, semicolons, etc. And in C# the functions cannot stand alone, but need to be added to some class ("SumOfSquaresHelper"). F# uses whitespace instead of parentheses, needs no line terminator, and the functions can stand alone. + +In F# it is common for entire functions to be written on one line, as the "square" function is. The `sumOfSquares` function could also have been written on one line. In C# this is normally frowned upon as bad practice. + +When a function does have multiple lines, F# uses indentation to indicate a block of code, which eliminates the need for braces. (If you have ever used Python, this is the same idea). So the `sumOfSquares` function could also have been written this way: + +```fsharp +let sumOfSquares n = + [1..n] + |> List.map square + |> List.sum +``` + +The only drawback is that you have to indent your code carefully. Personally, I think it is worth the trade-off. + +### No type declarations + +The next difference is that the C# code has to explicitly declare all the types used. For example, the `int i` parameter and `int SumOfSquares` return type. +Yes, C# does allow you to use the "var" keyword in many places, but not for parameters and return types of functions. + +In the F# code we didn't declare any types at all. This is an important point: F# looks like an untyped language, +but it is actually just as type-safe as C#, in fact, even more so! +F# uses a technique called "type inference" to infer the types you are using from their context. It works amazingly very well most of the time, and reduces the code complexity immensely. + +In this case, the type inference algorithm notes that we started with a list of integers. That in turn implies that the square function and the sum function must be taking ints as well, and that the final value must be an int. You can see what the inferred types are by looking at the result of the compilation in the interactive window. You'll see something like: + +```fsharp +val square : int -> int +``` + +which means that the "square" function takes an int and returns an int. + +If the original list had used floats instead, the type inference system would have deduced that the square function used floats instead. Try it and see: + +```fsharp +// define the square function +let squareF x = x * x + +// define the sumOfSquares function +let sumOfSquaresF n = + [1.0 .. n] |> List.map squareF |> List.sum // "1.0" is a float + +sumOfSquaresF 100.0 +``` + +The type checking is very strict! If you try using a list of floats (`[1.0..n]`) in the original `sumOfSquares` example, or a list of ints (`[1 ..n]`) in the `sumOfSquaresF` example, you will get a type error from the compiler. + +### Interactive development + +Finally, F# has an interactive window where you can test the code immediately and play around with it. In C# there is no easy way to do this. + +For example, I can write my square function and immediately test it: + +```fsharp +// define the square function +let square x = x * x + +// test +let s2 = square 2 +let s3 = square 3 +let s4 = square 4 +``` + +When I am satisfied that it works, I can move on to the next bit of code. + +This kind of interactivity encourages an incremental approach to coding that can become addictive! + +Furthermore, many people claim that designing code interactively enforces good design practices such as decoupling and explicit dependencies, +and therefore, code that is suitable for interactive evaluation will also be code that is easy to test. Conversely, code that cannot be +tested interactively will probably be hard to test as well. + +### The C# code revisited + +My original example was written using "old-style" C#. C# has incorporated a lot of functional features, and it is possible to rewrite the example in a more compact way using the LINQ extensions. + +So here is another C# version -- a line-for-line translation of the F# code. + +```csharp +public static class FunctionalSumOfSquaresHelper +{ + public static int SumOfSquares(int n) + { + return Enumerable.Range(1, n) + .Select(i => i * i) + .Sum(); + } +} +``` + +However, in addition to the noise of the curly braces and periods and semicolons, the C# version needs to declare the parameter and return types, unlike the F# version. + +Many C# developers may find this a trivial example, but still resort back to loops when the logic becomes more complicated. In F# though, you will almost never see explicit loops like this. +See for example, [this post on eliminating boilerplate from more complicated loops](http://fsharpforfunandprofit.com/posts/conciseness-extracting-boilerplate/). + + diff --git a/Why use F#/origin/posts/key-concepts.md b/Why use F#/origin/posts/key-concepts.md new file mode 100644 index 0000000..ff141a0 --- /dev/null +++ b/Why use F#/origin/posts/key-concepts.md @@ -0,0 +1,237 @@ +--- +layout: post +title: "Four Key Concepts" +description: "The concepts that differentiate F# from a standard imperative language" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 6 +categories: +image: "/assets/img/four-concepts2.png" +--- + +In the next few posts we'll move on to demonstrating the themes of this series: conciseness, convenience, correctness, concurrency and completeness. + +But before that, let's look at some of the key concepts in F# that we will meet over and over again. F# is different in many ways from a standard imperative language like C#, but there are a few major differences that are particularly important to understand: + +* **Function-oriented** rather than object-oriented +* **Expressions** rather than statements +* **Algebraic types** for creating domain models +* **Pattern matching** for flow of control + +In later posts, these will be dealt with in much greater depth -- this is just a taster to help you understand the rest of this series. + +![four key concepts](../assets/img/four-concepts2.png) + +### Function-oriented rather than object-oriented + +As you might expect from the term "functional programming", functions are everywhere in F#. + +Of course, functions are first class entities, and can be passed around like any other value: + +```fsharp +let square x = x * x + +// functions as values +let squareclone = square +let result = [1..10] |> List.map squareclone + +// functions taking other functions as parameters +let execFunction aFunc aParam = aFunc aParam +let result2 = execFunction square 12 +``` + +But C# has first-class functions too, so what's so special about functional programming? + +The short answer is that the function-oriented nature of F# infiltrates every part of the language and type system in a way that it does not in C#, so that things +that are awkward or clumsy in C# are very elegant in F#. + +It's hard to explain this in a few paragraphs, but here are some of the benefits that we will see demonstrated over this series of posts: + +* **Building with composition**. Composition is the 'glue' that allows us build larger systems from smaller ones. This is not an optional technique, but is at the very heart of the functional style. Almost every line of code is a composable expression (see below). Composition is used to build basic functions, and then functions that use those functions, and so on. And the composition principle doesn't just apply to functions, but also to types (the product and sum types discussed below). +* **Factoring and refactoring**. The ability to factor a problem into parts depends how easily the parts can be glued back together. Methods and classes that might seem to be indivisible in an imperative language can often be broken down into surprisingly small pieces in a functional design. These fine-grained components typically consist of (a) a few very general functions that take other functions as parameters, and (b) other helper functions that specialize the general case for a particular data structure or application. + Once factored out, the generalized functions allow many additional operations to be programmed very easily without having to write new code. You can see a good example of a general function like this (the fold function) in the [post on extracting duplicate code from loops](../posts/conciseness-extracting-boilerplate.md). +* **Good design**. Many of the principles of good design, such as "separation of concerns", "single responsibility principle", ["program to an interface, not an implementation"](../posts/convenience-functions-as-interfaces.md), arise naturally as a result of a functional approach. And functional code tends to be high level and declarative in general. + +The following posts in this series will have examples of how functions can make code more +concise and convenient, and then for a deeper understanding, there is a whole series on [thinking functionally](../series/thinking-functionally.md). + +### Expressions rather than statements + +In functional languages, there are no statements, only expressions. That is, every chunk of code always returns a value, +and larger chunks are created by combining smaller chunks using composition rather than a serialized list of statements. + +If you have used LINQ or SQL you will already be familiar with expression-based languages. For example, in pure SQL, +you cannot have assignments. Instead, you must have subqueries within larger queries. + +```sql +SELECT EmployeeName +FROM Employees +WHERE EmployeeID IN + (SELECT DISTINCT ManagerID FROM Employees) -- subquery +``` + +F# works in the same way -- every function definition is a single expression, not a set of statements. + +And it might not be obvious, but code built from expressions is both safer and more compact than using statements. +To see this, let's compare some statement-based code in C# with the equivalent expression-based code. + +First, the statement-based code. Statements don't return values, so you have to use temporary variables that are assigned to from within statement bodies. + +```csharp +// statement-based code in C# +int result; +if (aBool) +{ + result = 42; +} +Console.WriteLine("result={0}", result); +``` + +Because the `if-then` block is a statement, the `result` variable must be defined *outside* the statement but assigned to from *inside* the statement, which leads to issues such as: + +* What initial value should `result` be set to? +* What if I forget to assign to the `result` variable? +* What is the value of the `result` variable in the "else" case? + +For comparison, here is the same code, rewritten in an expression-oriented style: + +```csharp +// expression-based code in C# +int result = (aBool) ? 42 : 0; +Console.WriteLine("result={0}", result); +``` + +In the expression-oriented version, none of these issues apply: + +* The `result` variable is declared at the same time that it is assigned. No variables have to be set up "outside" the expression and there is no worry about what initial value they should be set to. +* The "else" is explicitly handled. There is no chance of forgetting to do an assignment in one of the branches. +* It is not possible to forget to assign `result`, because then the variable would not even exist! + +Expression-oriented style is not a choice in F#, and it is one of the things that requires a change of approach when coming from an imperative background. + +### Algebraic Types + +The type system in F# is based on the concept of **algebraic types**. That is, new compound types are built by combining existing types in two different ways: + +* First, a combination of values, each picked from a set of types. These are called "product" types. +* Of, alternately, as a disjoint union representing a choice between a set of types. These are called "sum" types. + +For example, given existing types `int` and `bool`, we can create a new product type that must have one of each: + +```fsharp +//declare it +type IntAndBool = {intPart: int; boolPart: bool} + +//use it +let x = {intPart=1; boolPart=false} +``` + +Alternatively, we can create a new union/sum type that has a choice between each type: + +```fsharp +//declare it +type IntOrBool = + | IntChoice of int + | BoolChoice of bool + +//use it +let y = IntChoice 42 +let z = BoolChoice true +``` + +These "choice" types are not available in C#, but are incredibly useful for modeling many real-world cases, such as states in a state machine (which is a surprisingly common theme in many domains). + +And by combining "product" and "sum" types in this way, it is easy to create a rich set of types that accurately models any business domain. +For examples of this in action, see the posts on [low overhead type definitions](../posts/conciseness-type-definitions.md) and [using the type system to ensure correct code](../posts/correctness-type-checking). + + +### Pattern matching for flow of control + +Most imperative languages offer a variety of control flow statements for branching and looping: + +* `if-then-else` (and the ternary version `bool ? if-true : if-false`) +* `case` or `switch` statements +* `for` and `foreach` loops, with `break` and `continue` +* `while` and `until` loops +* and even the dreaded `goto` + +F# does support some of these, but F# also supports the most general form of conditional expression, which is **pattern-matching**. + +A typical matching expression that replaces `if-then-else` looks like this: + +```fsharp +match booleanExpression with +| true -> // true branch +| false -> // false branch +``` + +And the replacement of `switch` might look like this: + +```fsharp +match aDigit with +| 1 -> // Case when digit=1 +| 2 -> // Case when digit=2 +| _ -> // Case otherwise +``` + +Finally, loops are generally done using recursion, and typically look something like this: + +```fsharp +match aList with +| [] -> + // Empty case +| first::rest -> + // Case with at least one element. + // Process first element, and then call + // recursively with the rest of the list +``` + +Although the match expression seems unnecessarily complicated at first, you'll see that in practice it is both elegant and powerful. + +For the benefits of pattern matching, see the post on [exhaustive pattern matching](../posts/correctness-exhaustive-pattern-matching), and for a worked example that uses pattern matching heavily, see the [roman numerals example](../posts/roman-numerals.md). + +### Pattern matching with union types ### + +We mentioned above that F# supports a "union" or "choice" type. This is used instead of inheritance to work with different variants of an underlying type. Pattern matching works seamlessly with these types to create a flow of control for each choice. + +In the following example, we create a `Shape` type representing four different shapes and then define a `draw` function with different behavior for each kind of shape. +This is similar to polymorphism in an object oriented language, but based on functions. + +```fsharp +type Shape = // define a "union" of alternative structures +| Circle of int +| Rectangle of int * int +| Polygon of (int * int) list +| Point of (int * int) + +let draw shape = // define a function "draw" with a shape param + match shape with + | Circle radius -> + printfn "The circle has a radius of %d" radius + | Rectangle (height,width) -> + printfn "The rectangle is %d high by %d wide" height width + | Polygon points -> + printfn "The polygon is made of these points %A" points + | _ -> printfn "I don't recognize this shape" + +let circle = Circle(10) +let rect = Rectangle(4,5) +let polygon = Polygon( [(1,1); (2,2); (3,3)]) +let point = Point(2,3) + +[circle; rect; polygon; point] |> List.iter draw +``` + +A few things to note: + +* As usual, we didn't have to specify any types. The compiler correctly determined that the shape parameter for the "draw" function was of type `Shape`. +* You can see that the `match..with` logic not only matches against the internal structure of the shape, but also assigns values based on what is appropriate for the shape. +* The underscore is similar to the "default" branch in a switch statement, except that in F# it is required -- every possible case must always be handled. If you comment out the line +```fsharp + | _ -> printfn "I don't recognize this shape" +``` + +see what happens when you compile! + +These kinds of choice types can be simulated somewhat in C# by using subclasses or interfaces, but there is no built in support in the C# type system for this kind of exhaustive matching with error checking. + diff --git a/Why use F#/origin/posts/why-use-fsharp-conclusion.md b/Why use F#/origin/posts/why-use-fsharp-conclusion.md new file mode 100644 index 0000000..d0fc4b6 --- /dev/null +++ b/Why use F#/origin/posts/why-use-fsharp-conclusion.md @@ -0,0 +1,16 @@ +--- +layout: post +title: "Why use F#: Conclusion" +description: "" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 30 +categories: [] +--- + +This completes the tour of F# features. I hope that the examples have given you some appreciation of the power of F# and functional programming. If you have any comments on the series as a whole, please leave them at the bottom of this page. + +In later series I hope to go deeper into data structures, pattern matching, list processing, asynchronous and parallel programming, and much more. + +But before those, I recommend you read the ["thinking functionally"](../series/thinking-functionally.md) series, which will help you understand functional programming at a deeper level. + diff --git a/Why use F#/origin/posts/why-use-fsharp-intro.md b/Why use F#/origin/posts/why-use-fsharp-intro.md new file mode 100644 index 0000000..b5beaa2 --- /dev/null +++ b/Why use F#/origin/posts/why-use-fsharp-intro.md @@ -0,0 +1,34 @@ +--- +layout: post +title: "Introduction to the 'Why use F#' series" +description: "An overview of the benefits of F#" +nav: why-use-fsharp +seriesId: "Why use F#?" +seriesOrder: 1 +--- + +This series of posts will give you a guided tour through the main features of F# and then show you ways that F# can help you in your day-to-day development. + +### Key benefits of F# compared with C# ### + +If you are already familiar with C# or Java, you might be wondering why it would be worth learning yet another language. F# has some major benefits which I have grouped under the following themes: + +* **Conciseness**. F# is not cluttered up with coding "noise" such as curly brackets, semicolons and so on. You almost never have to specify the type of an object, thanks to a powerful type inference system. And it generally takes less lines of code to solve the same problem. +* **Convenience**. Many common programming tasks are much simpler in F#. This includes things like creating and using complex type definitions, doing list processing, comparison and equality, state machines, and much more. And because functions are first class objects, it is very easy to create powerful and reusable code by creating functions that have other functions as parameters, or that combine existing functions to create new functionality. +* **Correctness**. F# has a very powerful type system which prevents many common errors such as null reference exceptions. And in addition, you can often encode business logic using the type system itself, so that it is actually impossible to write incorrect code, because it is caught at compile time as a type error. +* **Concurrency**. F# has a number of built-in tools and libraries to help with programming systems when more than one thing at a time is happening. Asynchronous programming is directly supported, as is parallelism. F# also has a message queuing system, and excellent support for event handling and reactive programming. And because data structures are immutable by default, sharing state and avoiding locks is much easier. +* **Completeness**. Although F# is a functional language at heart, it does support other styles which are not 100% pure, which makes it much easier to interact with the non-pure world of web sites, databases, other applications, and so on. In particular, F# is designed as a hybrid functional/OO language, so it can do almost everything that C# can do as well. Of course, F# integrates seamlessly with the .NET ecosystem, which gives you access to all the third party .NET libraries and tools. Finally, it is part of Visual Studio, which means you get a good editor with IntelliSense support, a debugger, and many plug-ins for unit tests, source control, and other development tasks. + +In the rest of this series of posts, I will try to demonstrate each of these F# benefits, using standalone snippets of F# code (and often with C# code for comparison). I'll briefly cover all the major features of F#, including pattern matching, function composition, and concurrent programming. By the time you have finished this series, I hope that you will have been impressed with the power and elegance of F#, and you will be encouraged to use it for your next project! + +### How to read and use the example code ### + +All the code snippets in these posts have been designed to be run interactively. I strongly recommend that you evaluate the snippets as you read each post. The source for any large code files will be linked to from the post. + +This series is not a tutorial, so I will not go too much into *why* the code works. Don't worry if you cannot understand some of the details; the goal of the series is just to introduce you to F# and whet your appetitite for learning it more deeply. + +If you have experience in languages such as C# and Java, you have probably found that you can get a pretty good understanding of source code written in other similar languages, even if you aren't familiar with the keywords or the libraries. You might ask "how do I assign a variable?" or "how do I do a loop?", and with these answers be able to do some basic programming quite quickly. + +This approach will not work for F#, because in its pure form there are no variables, no loops, and no objects. Don't be frustrated - it will eventually make sense! If you want to learn F# in more depth, there are some helpful tips on the ["learning F#"](../learning-fsharp/index.md) page. + + diff --git a/Why use F#/origin/why-use-fsharp/index.md b/Why use F#/origin/why-use-fsharp/index.md new file mode 100644 index 0000000..d3777e3 --- /dev/null +++ b/Why use F#/origin/why-use-fsharp/index.md @@ -0,0 +1,202 @@ +--- +layout: page +title: "Why use F#?" +description: "Why you should consider using F# for your next project" +nav: why-use-fsharp +hasIcons: 1 +image: "/assets/img/four-concepts2.png" +--- + +Although F# is great for specialist areas such as scientific or data analysis, it is also an excellent choice for enterprise development. +Here are five good reasons why you should consider using F# for your next project. + +## ![](../assets/img/glyphicons/glyphicons_030_pencil.png) Conciseness + +F# is not cluttered up with [coding "noise"](../posts/fvsc-sum-of-squares.md) such as curly brackets, semicolons and so on. + +You almost never have to specify the type of an object, thanks to a powerful [type inference system](../posts/conciseness-type-inference.md). + +And, compared with C#, it generally takes [fewer lines of code](../posts/fvsc-download.md) to solve the same problem. + + +``` +// one-liners +[1..100] |> List.sum |> printfn "sum=%d" + +// no curly braces, semicolons or parentheses +let square x = x * x +let sq = square 42 + +// simple types in one line +type Person = {First:string; Last:string} + +// complex types in a few lines +type Employee = + | Worker of Person + | Manager of Employee list + +// type inference +let jdoe = {First="John";Last="Doe"} +let worker = Worker jdoe +``` + +## ![](../assets/img/glyphicons/glyphicons_343_thumbs_up.png) Convenience + + +Many common programming tasks are much simpler in F#. This includes things like creating and using + [complex type definitions](../posts/conciseness-type-definitions.md), doing [list processing](../posts/conciseness-extracting-boilerplate.md), + [comparison and equality](../posts/convenience-types.md), [state machines](../posts/designing-with-types-representing-states.md), and much more. + +And because functions are first class objects, it is very easy to create powerful and reusable code by creating functions +that have [other functions as parameters](../posts/conciseness-extracting-boilerplate.md), +or that [combine existing functions](../posts/conciseness-functions-as-building-blocks.md) to create new functionality. + +``` +// automatic equality and comparison +type Person = {First:string; Last:string} +let person1 = {First="john"; Last="Doe"} +let person2 = {First="john"; Last="Doe"} +printfn "Equal? %A" (person1 = person2) + +// easy IDisposable logic with "use" keyword +use reader = new StreamReader(..) + +// easy composition of functions +let add2times3 = (+) 2 >> (*) 3 +let result = add2times3 5 +``` + +## ![](../assets/img/glyphicons/glyphicons_150_check.png) Correctness + + +F# has a [powerful type system](../posts/correctness-type-checking.md) which prevents many common errors such +as [null reference exceptions](../posts/the-option-type.md#option-is-not-null). + +Values are [immutable by default](../posts/correctness-immutability.md), which prevents a large class of errors. + +In addition, you can often encode business logic using the [type system](../posts/correctness-exhaustive-pattern-matching.md) itself in such a way +that it is actually [impossible to write incorrect code](../posts/designing-for-correctness.md) +or mix up [units of measure](../posts/units-of-measure.md), greatly reducing the need for unit tests. + + +``` +// strict type checking +printfn "print string %s" 123 //compile error + +// all values immutable by default +person1.First <- "new name" //assignment error + +// never have to check for nulls +let makeNewString str = + //str can always be appended to safely + let newString = str + " new!" + newString + +// embed business logic into types +emptyShoppingCart.remove // compile error! + +// units of measure +let distance = 10 + 10 // error! +``` + +## ![](../assets/img/glyphicons/glyphicons_054_clock.png) Concurrency + + +F# has a number of built-in libraries to help when more than one thing at a time is happening. +Asynchronous programming is [very easy](../posts/concurrency-async-and-parallel.md), as is parallelism. + +F# also has a built-in [actor model](../posts/concurrency-actor-model.md), and excellent support for event handling +and [functional reactive programming](../posts/concurrency-reactive.md). + +And of course, because data structures are immutable by default, sharing state and avoiding locks is much easier. + +``` +// easy async logic with "async" keyword +let! result = async {something} + +// easy parallelism +Async.Parallel [ for i in 0..40 -> + async { return fib(i) } ] + +// message queues +MailboxProcessor.Start(fun inbox-> async{ + let! msg = inbox.Receive() + printfn "message is: %s" msg + }) +``` + +## ![](../assets/img/glyphicons/glyphicons_280_settings.png) Completeness + + +Although it is a functional language at heart, F# does support other styles which are not 100% pure, +which makes it much easier to interact with the non-pure world of web sites, databases, other applications, and so on. + +In particular, F# is designed as a hybrid functional/OO language, so it can do [virtually everything that C# can do](../posts/completeness-anything-csharp-can-do.md). + + +Of course, F# is [part of the .NET ecosystem](../posts/completeness-seamless-dotnet-interop.md), which gives you seamless access to all the third party .NET libraries and tools. +It runs on most platforms, including Linux and smart phones (via Mono and the new .NET Core). + + +Finally, it is well integrated with Visual Studio (Windows) and Xamarin (Mac), which means you get a great IDE with IntelliSense support, a debugger, +and many plug-ins for unit tests, source control, and other development tasks. Or on Linux, you can use the MonoDevelop IDE instead. + +``` +// impure code when needed +let mutable counter = 0 + +// create C# compatible classes and interfaces +type IEnumerator<'a> = + abstract member Current : 'a + abstract MoveNext : unit -> bool + +// extension methods +type System.Int32 with + member this.IsEven = this % 2 = 0 + +let i=20 +if i.IsEven then printfn "'%i' is even" i + +// UI code +open System.Windows.Forms +let form = new Form(Width= 400, Height = 300, + Visible = true, Text = "Hello World") +form.TopMost <- true +form.Click.Add (fun args-> printfn "clicked!") +form.Show() +``` + +## The "Why Use F#?" series + +The following series of posts demonstrates each of these F# benefits, using standalone snippets of F# code (and often with C# code for comparison). + +* [Introduction to the 'Why use F#' series](../posts/why-use-fsharp-intro.md). An overview of the benefits of F# +* [F# syntax in 60 seconds](../posts/fsharp-in-60-seconds.md). A very quick overview on how to read F# code +* [Comparing F# with C#: A simple sum](../posts/fvsc-sum-of-squares.md). In which we attempt to sum the squares from 1 to N without using a loop +* [Comparing F# with C#: Sorting](../posts/fvsc-quicksort.md). In which we see that F# is more declarative than C#, and we are introduced to pattern matching. +* [Comparing F# with C#: Downloading a web page](../posts/fvsc-download.md). In which we see that F# excels at callbacks, and we are introduced to the 'use' keyword +* [Four Key Concepts](../posts/key-concepts.md). The concepts that differentiate F# from a standard imperative language +* [Conciseness](../posts/conciseness-intro.md). Why is conciseness important? +* [Type inference](../posts/conciseness-type-inference.md). How to avoid getting distracted by complex type syntax +* [Low overhead type definitions](../posts/conciseness-type-definitions.md). No penalty for making new types +* [Using functions to extract boilerplate code](../posts/conciseness-extracting-boilerplate.md). The functional approach to the DRY principle +* [Using functions as building blocks](../posts/conciseness-functions-as-building-blocks.md). Function composition and mini-languages make code more readable +* [Pattern matching for conciseness](../posts/conciseness-pattern-matching.md). Pattern matching can match and bind in a single step +* [Convenience](../posts/convenience-intro.md). Features that reduce programming drudgery and boilerplate code +* [Out-of-the-box behavior for types](../posts/convenience-types.md). Immutability and built-in equality with no coding +* [Functions as interfaces](../posts/convenience-functions-as-interfaces.md). OO design patterns can be trivial when functions are used +* [Partial Application](../posts/convenience-partial-application.md). How to fix some of a function's parameters +* [Active patterns](../posts/convenience-active-patterns.md). Dynamic patterns for powerful matching +* [Correctness](../posts/correctness-intro.md). How to write 'compile time unit tests' +* [Immutability](../posts/correctness-immutability.md). Making your code predictable +* [Exhaustive pattern matching](../posts/correctness-exhaustive-pattern-matching.md). A powerful technique to ensure correctness +* [Using the type system to ensure correct code](../posts/correctness-type-checking.md). In F# the type system is your friend, not your enemy +* [Worked example: Designing for correctness](../posts/designing-for-correctness.md). How to make illegal states unrepresentable +* [Concurrency](../posts/concurrency-intro.md). The next major revolution in how we write software? +* [Asynchronous programming](../posts/concurrency-async-and-parallel.md). Encapsulating a background task with the Async class +* [Messages and Agents](../posts/concurrency-actor-model.md). Making it easier to think about concurrency +* [Functional Reactive Programming](../posts/concurrency-reactive.md). Turning events into streams +* [Completeness](../posts/completeness-intro.md). F# is part of the whole .NET ecosystem +* [Seamless interoperation with .NET libraries](../posts/completeness-seamless-dotnet-interop.md). Some convenient features for working with .NET libraries +* [Anything C# can do...](../posts/completeness-anything-csharp-can-do.md). A whirlwind tour of object-oriented code in F# +* [Why use F#: Conclusion](../posts/why-use-fsharp-conclusion.md). diff --git a/Why use F#/ru/assets/img/ShoppingCart.png b/Why use F#/ru/assets/img/ShoppingCart.png new file mode 100644 index 0000000..365ef8a Binary files /dev/null and b/Why use F#/ru/assets/img/ShoppingCart.png differ diff --git a/Why use F#/ru/assets/img/four-concepts2.png b/Why use F#/ru/assets/img/four-concepts2.png new file mode 100644 index 0000000..9f383c3 Binary files /dev/null and b/Why use F#/ru/assets/img/four-concepts2.png differ diff --git a/Why use F#/ru/assets/img/glyphicons/glyphicons_030_pencil.png b/Why use F#/ru/assets/img/glyphicons/glyphicons_030_pencil.png new file mode 100644 index 0000000..24d4fa3 Binary files /dev/null and b/Why use F#/ru/assets/img/glyphicons/glyphicons_030_pencil.png differ diff --git a/Why use F#/ru/assets/img/glyphicons/glyphicons_054_clock.png b/Why use F#/ru/assets/img/glyphicons/glyphicons_054_clock.png new file mode 100644 index 0000000..2f89c16 Binary files /dev/null and b/Why use F#/ru/assets/img/glyphicons/glyphicons_054_clock.png differ diff --git a/Why use F#/ru/assets/img/glyphicons/glyphicons_150_check.png b/Why use F#/ru/assets/img/glyphicons/glyphicons_150_check.png new file mode 100644 index 0000000..8d33317 Binary files /dev/null and b/Why use F#/ru/assets/img/glyphicons/glyphicons_150_check.png differ diff --git a/Why use F#/ru/assets/img/glyphicons/glyphicons_280_settings.png b/Why use F#/ru/assets/img/glyphicons/glyphicons_280_settings.png new file mode 100644 index 0000000..d24b947 Binary files /dev/null and b/Why use F#/ru/assets/img/glyphicons/glyphicons_280_settings.png differ diff --git a/Why use F#/ru/assets/img/glyphicons/glyphicons_343_thumbs_up.png b/Why use F#/ru/assets/img/glyphicons/glyphicons_343_thumbs_up.png new file mode 100644 index 0000000..eaf67fc Binary files /dev/null and b/Why use F#/ru/assets/img/glyphicons/glyphicons_343_thumbs_up.png differ diff --git a/Why use F#/ru/posts/fsharp-in-60-seconds.md b/Why use F#/ru/posts/fsharp-in-60-seconds.md new file mode 100644 index 0000000..7ecbd4a --- /dev/null +++ b/Why use F#/ru/posts/fsharp-in-60-seconds.md @@ -0,0 +1,170 @@ +--- +layout: post +title: "Синтаксис F# за 60 секунд" +description: "Очень краткий обзор того, как читать F#-код" +nav: why-use-fsharp +seriesId: "Зачем использовать F#?" +seriesOrder: 2 +--- +Вот очень быстрый обзор того, как читать F#-код, для незнакомых с синтаксисом данного языка. + +Этот обзор, конечно же, не очень подробный, но его должно быть достаточно для того, чтобы вы могли, читая фрагменты кода в данном цикле статей, понимать их смысл. Не волнуйтесь, если что-то из них вам останется неясным, т.к. я поясню всё более детально, когда мы подберёмся к реальным примерам кода. + +Два основных различия между синтаксисами F# и стандартного C-подобного языка следующие: + +* Для отделения блоков кода не используются фигурные скобки. Вместо этого применяются отступы (похожим образом делается в Python). +* Для разделения параметров [функций] используются пробелы, а не запятые. + +Некоторые находят синтаксис F# отталкивающим. Если вы один из них, то специально для вас привожу цитату: + +>"Улучшать свою нотацию таким образом, чтобы не (смущать/приводить в замешательство) людей в первые 10 минут её чтения, но при этом способствовать ухудшению читаемости (после/потом/позже), есть очень плохая ошибка." +> (Дэвид МакАйвер, из [(поста/статьи) о синтаксисе Scala](http://rickyclarkson.blogspot.co.uk/2008/01/in-defence-of-0l-in-scala.html)). + +Лично я думаю, что синтаксис F# очень (ясен/понятен/чёток) и простой, когда к нему привыкаешь. Во многих аспектах он проще, чем синтаксис C#, с меньшим количеством ключевых слов и исключений. + +Фрагмент кода ниже -- простой F#-скрипт, демонстрирующий большинство понятий, необходимых вам на регулярной основе. + +Я призываю вас опробовать данный код в интерактивном режиме и немного "поиграться" с ним! Или же: + +* (Введите/перепечатайте) его в F#-скрипт-файл (с расширением .fsx) и отправьте его на исполнение в интерактивное окно (F# Interactive). Более подробно о том, как это делается, смотрите на странице ["установка и использование F#"]. +* Также можно запустить этот код в интерактивном окне непосредственно. Не забывайте всегда ставить `;;` в конце строки для указания интерпретатору, что вы закончили ввод и готовы его (вычислить/запустить). + +```fsharp +//однострочные комментарии начинаются с двойного слэша +(* + многострочные комментарии помещаются между двумя скобками такого вида: (* . . . *) +*) + +// ======== "Переменные" (но на самом деле не переменные)" ====== +// С помощью ключевого слова "let" (задаётся/определяется) новое значение (неизменяемое) +let myInt = 5 +let myFloat = 3.14 +let myString = "hello" //заметьте, в объявлении типов нет необходимости + +// ======== Списки ============ +let twoToFive = [2; 3; 4; 5] // С помощью квадратных скобок + // задаётся список, разделителем элементов + // в котором является точка с запятой. + +let oneToFive = 1 :: twoToFive // :: создаёт список с новым 1-м элементом +// Результат: [1; 2; 3; 4;5 ] +let zeroToFive = [0; 1] @ twoToFive // @ используется для + // конкатенации двух списков + +// ВАЖНО: в качестве разделителей никогда не используются запятые, +// только точки с запятой! + +// ======== Функции ======== +// С помощью ключевого слова "let" также задаётся и именнованная функция. +let square x = x * x // Заметьте, что круглые скобки не используются. +square 3 // А теперь выполним функцию. + // Опять же, никаких круглых скобок. + +let add x y = x + y // не пишите add (x,y)! Это означает + // нечто совершенно другое. +add 2 3 // А теперь выполняем функцию. + +// для задания многострочной функции просто используйте отступы. +// Точки с запятой не требуются. +let evens list = + let isEven x = x % 2 = 0 // Задаём функцию "isEven" как внутреннюю ("вложенную") + List.filter isEven list // List.filter -- это библиотечная функция + // с двумя параметрами: + // булевой функцией и списком на обработку + // (a boolean function and a list to work on) + +evens oneToFive // А теперь выполним функцию. + +// Вы можете использовать круглые скобки для уточнения +// (приоритета/последовательности)применения параметров. +// В данном примере сначала примените "map" к двум аргументам, +// а затем к результату этого примените "sum". +// Без расстановки круглых скобок "List.map" будет считаться +// аргументов функции "List.sum" +let sumOfSquaresTo100 = + List.sum ( List.map square [1 .. 100] ) + +// Вы можете передавать выходное значение одной операции на вход к следующей +// (как по конвейеру), используя оператор "|>". +// Вот, например, та же функция sumOfSquares, написанная с использованием +// этих, так называемых, конвейеров +let sumOfSquaresTo100piped = + [1 .. 100] |> List.map square |> List.sum // "square" была задана ранее + +// вы можете задавать лямбда-функции (анонимные функции) +// с помощью ключевого слова "fun" +let sumOfSquaresTo100withFun = + [1 .. 100] |> List.map (fun x -> x * x) |> List.sum + +// В F# возвращаемое значение задаётся неявно -- "return" не требуется. +// Функция всегда возвращает значение последнего +// определённого в ней выражения. + +// ======== Сопоставление с образцом (Pattern Matching) ======== +// match..with.. -- это (улучшенная/"прокачанная") инструкция case/switch. +let simplePatternMatch = + let x = "a" + match x with + | "a" -> printfn "x is a" + | "b" -> printfn "x is b" + | _ -> printfn "x is something else" // нижнее подчёркивание + // сопоставляется с чем угодно + +// Some(..) и None -- приблизительные аналоги (?) обёрток для Nullable (?) +let validValue = Some(99) +let invalidValue = None + +// В данном примере match..with сопоставляется с "Some" и "None" +// и в то же время "распаковывает" значение, "хранящееся" в "Some". +let optionPatternMatch input = + match input with + | Some i -> printfn "input is an int=%d" i + | None -> printfn "input is missing" + +optionPatternMatch validValue +optionPatternMatch invalidValue + +// ========= (Сложные/составные) типы данных ========= + +// К типу-кортежу относятся пары, тройки и т.п. +// В кортежах используются запятые (в качестве разделителей). +let twoTuple = 1, 2 +let threeTuple = "a", 2, true + +// У типов-записей имеются именованные поля. +// Разделителями являются точки с запятой. +type Person = { First : string; Last : string } +let person1 = { First = "john"; Last = "Doe" } + +// С помощью типов-объединений описываются вариативные значения. +// Разделителем является вертикальная черта. +type Temp = + | DegreesC of float + | DegreesF of float +let temp = DegreesF 98.6 + +// Типы могут комбинироваться рекурсивно различными сложными способами. +// Например, вот тип-объединение, содержащий в себе список этого же типа: +type Employee = + | Worker of Person + | Manager of Employee list +let jdoe = { First = "John"; Last = "Doe" } +let worker = Worker jdoe + +// ========= Текстовый вывод ========= +// Функции printf/printfn очень похои на функции +// Console.Write/WriteLine из C#. +printfn "Printing an int %i, a float %f, a bool %b" 1 2.0 true +printfn "A string %s, and something generic %A" "hello" [1; 2; 3; 4] + +// все сложные типы имеют встроенное наглядное текстовое представление (вывод) +printfn "twoTuple=%A,\nPerson=%A,\nTemp=%A,\nEmployee=%A" + twoTuple person1 temp worker + +// Также имеются функции sprintf/sprintfn +// для форматированного вывода в строку, подобно String.Format. + + +``` + +Ну а теперь давайте приступим к сравнению простых фрагментов F#-кода с эквивалентными им фрагментами кода на C#. diff --git a/Why use F#/ru/posts/fvsc-download.md b/Why use F#/ru/posts/fvsc-download.md new file mode 100644 index 0000000..cc6b252 --- /dev/null +++ b/Why use F#/ru/posts/fvsc-download.md @@ -0,0 +1,178 @@ +--- +layout: post +title: "Сравнение F# и C#: Загрузка веб-страницы" +description: "Где мы увидим, что F# превосходит в обратных вызовах, а также мы познакомимся с ключевым словом 'use'" +nav: why-use-fsharp +seriesId: "Зачем использовать F#?" +seriesOrder: 5 +categories: [F# vs C#] +--- + +В этом примере мы сравним код для загрузки веб-страницы, использующий обратный вызов для обработки текстового потока, на F# с аналогичным кодом на C#. + +Начнём с простой реализации на F#: + +> In this example, we will compare the F# and C# code for downloading a web page, with a callback to process the text stream. + +> We'll start with a straightforward F# implementation. + +```fsharp +// "open" делает видимыми типы в указанном пространстве имён .NET (аналогично директиве using в C#) +open System.Net +open System +open System.IO + +// Извлечение содержимого веб-страницы +let fetchUrl callback url = + let req = WebRequest.Create(Uri(url)) + use resp = req.GetResponse() + use stream = resp.GetResponseStream() + use reader = new IO.StreamReader(stream) + callback reader url +``` + +Давайте рассмотрим этот код: + +* Использование "open" в начале позволяет использовать "WebRequest" вместо "System.Net.WebRequest". Подобно "`using System.Net`" заголовку в C#. +* Далее, мы задаём функцию `fetchUrl`, которая принимает два аргумента: обратный вызов для обработки потока и URL извлекаемой страницы. +* Затем мы "оборачиваем" строку с URL в объект `Uri`. F# имеет строгую проверку типов, поэтому если бы вместо этого мы написали: `let req = WebRequest.Create(url)`, компилятор "пожаловался" бы, что не знает, какую из версий `WebRequest.Create` использовать. +* При объявлении значений `response`, `stream` и `reader` используется ключевое слово "`use`" вместо ключевого слова "`let`". Это может использоваться только в сочетании с классами, реализующими `IDisposable`. Оно диктует компилятору автоматически освободить ресурсы, когда значение выходит из области видимости. Это эквивалентно оператору "`using`" в C#. +* В последней строке вызывается функция обратного вызова со StreamReader и URL в качестве параметров. Заметьте, что нет необходимости где-либо явно указывать тип функции обратного вызова. + +А теперь вот аналогичная C#-реализация. + +> Let's go through this code: + +> * The use of "open" at the top allows us to write "WebRequest" rather than "System.Net.WebRequest". It is similar to a "`using System.Net`" header in C#. +> * Next, we define the `fetchUrl` function, which takes two arguments, a callback to process the stream, and the url to fetch. +> * We next wrap the url string in a Uri. F# has strict type-checking, so if instead we had written: +`let req = WebRequest.Create(url)` +the compiler would have complained that it didn't know which version of `WebRequest.Create` to use. +> * When declaring the `response`, `stream` and `reader` values, the "`use`" keyword is used instead of "`let`". This can only be used in conjunction with classes that implement `IDisposable`. + It tells the compiler to automatically dispose of the resource when it goes out of scope. This is equivalent to the C# "`using`" keyword. +> * The last line calls the callback function with the StreamReader and url as parameters. Note that the type of the callback does not have to be specified anywhere. + +> Now here is the equivalent C# implementation. + +```csharp +class WebPageDownloader +{ + public TResult FetchUrl( + string url, + Func callback) + { + var req = WebRequest.Create(url); + using (var resp = req.GetResponse()) + { + using (var stream = resp.GetResponseStream()) + { + using (var reader = new StreamReader(stream)) + { + return callback(url, reader); + } + } + } + } +} +``` + +Как обычно, C#-версия более "зашумлена". + +* Здесь десять строк занимают лишь фигурные скобки, а визуальную сложность добавляют 5 уровней вложенности. +* Типы всех параметров необходимо указывать явно, и обобщённый тип `TResult` приходится повторно указывать три раза. + +* Это верно, что в данном конкретном примере, когда все конструкции `using` являются смежными, [лишние фигурные скобки и отступы могут быть убраны](https://stackoverflow.com/questions/1329739/nested-using-statements-in-c-sharp), +но в более общем случае они необходимы. + +> As usual, the C# version has more 'noise'. + +> * There are ten lines just for curly braces, and there is the visual complexity of 5 levels of nesting* +> * All the parameter types have to be explicitly declared, and the generic `TResult` type has to be repeated three times. + +> * It's true that in this particular example, when all the `using` statements are adjacent, the [extra braces and indenting can be removed](https://stackoverflow.com/questions/1329739/nested-using-statements-in-c-sharp), +> but in the more general case they are needed. + +## Тестирование кода (Testing the code) + +Возвращаясь обратно в мир F#, мы теперь можем протестировать этот код в интерактивном режиме: + +> Back in F# land, we can now test the code interactively: + +```fsharp +let myCallback (reader : IO.StreamReader) url = + let html = reader.ReadToEnd() + let html1000 = html.Substring(0,1000) + printfn "Downloaded %s. First 1000 is %s" url html1000 + html // return all the html + +// проверка +let google = fetchUrl myCallback "http://google.com" +``` + +Наконец, мы вынуждены прибегнуть к явному указанию типа для параметра "reader" (`reader : IO.StreamReader`). Это необходимо ввиду того, что компилятор F# не может определить тип параметра "reader" автоматически. + +Очень полезной/удобной особенностью F# является возможность "встроить" параметры в функцию, чтобы их не приходилось передавать в неё каждый раз. Вот почему параметр `url` был помещён последним, а не первым, как в C#-версии. +Функция обратного вызова может быть настроена один раз, тогда как URL меняется от вызова к вызову. + +> Finally, we have to resort to a type declaration for the reader parameter (`reader:IO.StreamReader`). This is required because the F# compiler cannot determine the type of the "reader" parameter automatically. + +> A very useful feature of F# is that you can "bake in" parameters in a function so that they don't have to be passed in every time. This is why the `url` parameter was placed *last* rather than first, as in the C# version. +> The callback can be setup once, while the url varies from call to call. + +```fsharp +// создание функции со "встроенной в неё" функцией обратного вызова +let fetchUrl2 = fetchUrl myCallback + +// проверка +let google = fetchUrl2 "http://www.google.com" +let bbc = fetchUrl2 "http://news.bbc.co.uk" + +// проверка на списке сайтов +let sites = ["http://www.bing.com"; + "http://www.google.com"; + "http://www.yahoo.com"] + +// обработка каждого из сайтов в списке +sites |> List.map fetchUrl2 +``` + +Последняя строка (в которой используется `List.map`) демонстрирует простоту использования новой функции в сочетании с функциями обработки списков для загрузки всего списка сразу. + +Ниже приведён аналогичный тестовый код на C#: + +> The last line (using `List.map`) shows how the new function can be easily used in conjunction with list processing functions to download a whole list at once. + +> Here is the equivalent C# test code: + +```csharp +[Test] +public void TestFetchUrlWithCallback() +{ + Func myCallback = (url, reader) => + { + var html = reader.ReadToEnd(); + var html1000 = html.Substring(0, 1000); + Console.WriteLine( + "Downloaded {0}. First 1000 is {1}", url, + html1000); + return html; + }; + + var downloader = new WebPageDownloader(); + var google = downloader.FetchUrl("http://www.google.com", + myCallback); + + // проверка на списке сайтов + var sites = new List { + "http://www.bing.com", + "http://www.google.com", + "http://www.yahoo.com"}; + + // обработка каждого из сайтов в списке + sites.ForEach(site => downloader.FetchUrl(site, myCallback)); +} +``` + +Опять же, этот код несколько более "зашумлён", чем вышеприведённый F#-код, в нём много явных указаний типов. Что более важно, в C#-коде нельзя просто встроить некоторые из параметров в функцию, так что функция обратного вызова должна каждый раз вызываться явно. + +> Again, the code is a bit noisier than the F# code, with many explicit type references. More importantly, the C# code doesn't easily allow you to bake in some of the parameters in a function, so the callback must be explicitly referenced every time. diff --git a/Why use F#/ru/posts/fvsc-quicksort.md b/Why use F#/ru/posts/fvsc-quicksort.md new file mode 100644 index 0000000..2ae4900 --- /dev/null +++ b/Why use F#/ru/posts/fvsc-quicksort.md @@ -0,0 +1,277 @@ +--- +layout: post +title: "Сравнение F# и C#: Сортировка" +description: "Где показано, что F# более декларативен, чем C#, и где происходит знакомство с механизмом сопоставления с образцом." +nav: why-use-fsharp +seriesId: "Зачем использовать F#?" +seriesOrder: 4 +categories: [F# vs C#] +--- + +В этом примере мы реализуем алгоритм сортировки, подобный быстрой сортировке (Quick-Sort), для списков и сравним F#-реализацию с реализацией на С#. + +Логика этого упрощённого алгоритма быстрой сортировки следующая: + +
+Если список пуст, то делать ничего не нужно.
+Иначе же:
+  1. Взять первый элемент списка.
+  2. Найти все элементы в оставшейся части списка, которые меньше первого элемента, и отсортировать их.
+  3. Найти все элементы в оставшейся части списка, которые больше первого элемента или равны ему, и отсортировать их.
+  4. Скомбинировать все три части вместе для получения итогового результата:
+      (отсортированные меньшие элементы + первый элемент +
+       отсортированные большие элементы).
+
+ +Стоит отметить, что это упрощённый алгоритм и он не оптимизирован (и он не сортирует "на месте", как в настоящей быстрой сортировке); сейчас важнее сделать акцент на ясности/простоте понимания. + +Вот код на F#: + +> In this next example, we will implement a quicksort-like algorithm for sorting lists and compare an F# implementation to a C# implementation. + +> Here is the logic for a simplified quicksort-like algorithm: + +
+If the list is empty, there is nothing to do.
+Otherwise:
+  1. Take the first element of the list
+  2. Find all elements in the rest of the list that
+      are less than the first element, and sort them.
+  3. Find all elements in the rest of the list that
+      are >= than the first element, and sort them
+  4. Combine the three parts together to get the final result:
+      (sorted smaller elements + firstElement +
+       sorted larger elements)
+
+ +> Note that this is a simplified algorithm and is not optimized (and it does not sort in place, like a true quicksort); we want to focus on clarity for now. + +> Here is the code in F#: + +```fsharp +let rec quicksort list = + match list with + | [] -> // Если список пуст + [] // вернуть пустой список + | firstElem::otherElements -> // Если список не пустой + let smallerElements = // извлечь меньшие элементы + otherElements + |> List.filter (fun e -> e < firstElem) + |> quicksort // и отсортировать их + let largerElements = // извлечь бо́льшие элементы + otherElements + |> List.filter (fun e -> e >= firstElem) + |> quicksort // и отсортировать их + // Вернуть список, скомбинированных из этих трёх частей + List.concat [smallerElements; [firstElem]; largerElements] + +//тест +printfn "%A" (quicksort [1;5;23;18;9;1;3]) +``` + +Следует ещё раз отметить, что это не оптимизированная реализация алгоритма, но спроектированная для точного отражения сути алгоритма. + +Давайте рассмотрим этот код: + +* Здесь нигде нет объявлений типов. Эта функция будет работать на любом списке, содержащем сравнимые элементы (то есть почти любой из F#-типов, т.к. все они автоматически имеют/поддерживают стандартную функцию сравнения). +* Вся функция рекурсивна -- об этом компилятору сигнализирует ключевое слово `rec` в "`let rec quicksort list = `". +* Конструкция `match..with` в некотором роде похожа на `switch/case`-инструкцию. Каждая ветвь отделяется с помощью вертикальной черты, например: + +> Again note that this is not an optimized implementation, but is designed to mirror the algorithm closely. + +> Let's go through this code: + +> * There are no type declarations anywhere. This function will work on any list that has comparable items (which is almost all F# types, because they automatically have a default comparison function). +> * The whole function is recursive -- this is signaled to the compiler using the `rec` keyword in "`let rec quicksort list =`". +> * The `match..with` is sort of like a switch/case statement. Each branch to test is signaled with a vertical bar, like so: + +```fsharp +match x with +| caseA -> something +| caseB -> somethingElse +``` +* Ветвь "`match`" с `[]` сопоставляет c пустым списком и возвращает пустой список. +* Ветвь "`match`" с `firstElem::otherElements` делает две вещи. + * Во-первых, она сопоставляет лишь с непустым списком. + * Во-вторых, в ней автоматически создаётся два новых значения. Первое -- для первого элемента и именованное как "`firstElem`", а второе -- для списка с остальными элементами и именованное как "`otherElements`". С точки зрения C#, это как иметь "`switch`"-инструкцию которая поддерживает не только ветвление, но также позволяет объявлять переменные и означивать их *одновременно*. +* Конструкция с `->` -- это своего рода лямбда-функция (`=>`) в C#. Эквивалентная лямбда функция будет выглядеть как-то так: `(firstElem, otherElements) => do something`. +* "`smallerElements`" принимает оставшуюся часть списка , фильтрует его по первому элементу (*прим.: `firstElem`*), используя (?)встраиваемое(?) лямбда-выражение с оператором "`<`" и затем рекурсивно передаёт результат в функцию быстрой сортировки (`quicksort`). +* "`largerElements`" делает всё то же самое, за ислючением использования оператора "`>=`" . +* Наконец, конструируется результирующий список, используя функция конкатенации списков "`List.concat`". Для этого первый элемент (*прим.: `firstElem`*) должен быть "упакован" в список, для чего и используются квадратные скобки. +* Опять же стоит отметить, что ключевое слово "return" не используется -- возвращаемым является последнее значение. В ветви с "`[]`" возвращаемое значение -- пустой список, а в главной ветви -- новый сконструированный/новообразованный список. + +Вот, для сравнения, реализация на C# в старом стиле (без использования LINQ). + +> * The "`match`" with `[]` matches an empty list, and returns an empty list. +> * The "`match`" with `firstElem::otherElements` does two things. +> * First, it only matches a non-empty list. +> * Second, it creates two new values automatically. One for the first element called "`firstElem`", and one for the rest of the list, called "`otherElements`". + In C# terms, this is like having a "switch" statement that not only branches, but does variable declaration and assignment *at the same time*. +> * The `->` is sort of like a lambda (`=>`) in C#. The equivalent C# lambda would look something like `(firstElem, otherElements) => do something`. +> * The "`smallerElements`" section takes the rest of the list, filters it against the first element using an inline lambda expression with the "`<`" operator and then pipes the result into the quicksort function recursively. +> * The "`largerElements`" line does the same thing, except using the "`>=`" operator +> * Finally the resulting list is constructed using the list concatenation function "`List.concat`". For this to work, the first element needs to be put into a list, which is what the square brackets are for. +> * Again note there is no "return" keyword; the last value will be returned. In the "`[]`" branch the return value is the empty list, and in the main branch, it is the newly constructed list. + +> For comparison here is an old-style C# implementation (without using LINQ). + +```csharp +public class QuickSortHelper +{ + public static List QuickSort(List values) + where T : IComparable + { + if (values.Count == 0) + { + return new List(); + } + + //извлечение первого элемента + T firstElement = values[0]; + + // извлечение бо́льшего и меньшего элементов + var smallerElements = new List(); + var largerElements = new List(); + for (int i = 1; i < values.Count; i++) // исходным значением для i является 1, + { // а не 0! + var elem = values[i]; + if (elem.CompareTo(firstElement) < 0) + { + smallerElements.Add(elem); + } + else + { + largerElements.Add(elem); + } + } + + //возвращаем результат + var result = new List(); + result.AddRange(QuickSort(smallerElements.ToList())); + result.Add(firstElement); + result.AddRange(QuickSort(largerElements.ToList())); + return result; + } +} +``` + +Сравнивая эти варианты кода, мы опять же можем видеть, что F#-код гораздо более компактный, менее "зашумлён" и не требует указания типов. + +Более того, F#-код читается потчи точно также, как и непосредственно алгоритм, в отличии от C#-версии. Это ещё одно ключевое преимущество F# -- код обычно более декларативный ("что сделать") и менее императивный ("как это сделать"), чем код на C#, и поэтому он гораздо более "самодокументирующий". + +> Comparing the two sets of code, again we can see that the F# code is much more compact, with less noise and no need for type declarations. + +> Furthermore, the F# code reads almost exactly like the actual algorithm, unlike the C# code. This is another key advantage of F# -- the code is generally more declarative ("what to do") and less imperative ("how to do it") than C#, and is therefore much more self-documenting. + + +## Реализация в функциональном стиле на C# (A functional implementation in C#) ## + +Вот более современная реализация, в "функциональном стиле", использующая LINQ и методы расширений: + +> Here's a more modern "functional-style" implementation using LINQ and an extension method: + +```csharp +public static class QuickSortExtension +{ + /// + /// Реализация в качестве метода расширения для IEnumerable + /// + public static IEnumerable QuickSort( + this IEnumerable values) where T : IComparable + { + if (values == null || !values.Any()) + { + return new List(); + } + + // разбиение списка на первый элемент и оставшиеся + var firstElement = values.First(); + var rest = values.Skip(1); + + // получение меньшего и бо́льшего элементов + var smallerElements = rest + .Where(i => i.CompareTo(firstElement) < 0) + .QuickSort(); + + var largerElements = rest + .Where(i => i.CompareTo(firstElement) >= 0) + .QuickSort(); + + // возврат результата + return smallerElements + .Concat(new List{firstElement}) + .Concat(largerElements); + } +} +``` + +Так гораздо аккуратнее/яснее, и читается практически так же, как и F#-версия. Но, к сожалению, никак не избежать излишней "зашумлённости" сигнатуры функции. + +> This is much cleaner, and reads almost the same as the F# version. But unfortunately there is no way of avoiding the extra noise in the function signature. + +## Корректность (Correctness) + +Наконец, полезным побочным эффектом от этой компактности заключается в том, что F#-код обычно работает с первого запуска, тогда как C#-код может потребовать дополнительной отладки. + +Действительно, в процессе разработки этих примеров C#-версия "в старом стиле" получилась изначально неправильной, из-за чего потребовалось немного отладить её, прежде чем получить корректный вариант. Особенно каверзными частями были цикл `for` (начинающийся с 1, а не с 0) и сравнение `CompareTo` (которое я понял неправильно), вдобавок к этому было очень просто ненамеренно изменить/модифицировать входной список. Версия в функциональном стиле же, во втором C#-примере, помимо того, что выглядит понятнее, её оказалось проще правильно спроектировать. + +Но даже функциональная C#-версия имеет недостатки, по сравнению с F#-версией. Например, ввиду того, что в F# используется сопоставление с образцом (pattern matching), пустой список не может попасть в ветвь "с непустым списком". С другой стороны, если мы в C#-версии забудем сделать такую проверку: + +> Finally, a beneficial side-effect of this compactness is that F# code often works the first time, while the C# code may require more debugging. + +> Indeed, when coding these samples, the old-style C# code was incorrect initially, and required some debugging to get it right. Particularly tricky areas were the `for` loop (starting at 1 not zero) and the `CompareTo` comparison (which I got the wrong way round), and it would also be very easy to accidentally modify the inbound list. The functional style in the second C# example is not only cleaner but was easier to code correctly. + +> But even the functional C# version has drawbacks compared to the F# version. For example, because F# uses pattern matching, it is not possible to branch to the "non-empty list" case with an empty list. On the other hand, in the C# code, if we forgot the test: + +```csharp +if (values == null || !values.Any()) ... +``` + +то тогда при извлечении первого элемента: + +> then the extraction of the first element: + +```csharp +var firstElement = values.First(); +``` + +возникнет исключение ("упадёт с выдачей исключения"). Компилятор не может сделать этого. Как часто в своём коде вы использовали `FirstOrDefault` вместо `First` для обеспечения "защищённости" кода? Вот пример шаблона кода, который очень часто встречается в C#, но редко в F#: + +> would fail with an exception. The compiler cannot enforce this for you. In your own code, how often have you used `FirstOrDefault` rather than `First` because you are writing "defensive" code. Here is an example of a code pattern that is very common in C# but is rare in F#: + +```csharp +var item = values.FirstOrDefault(); // вместо .First() +if (item != null) +{ + // сделать что-то, если значение item валидно +} +``` + +Сопоставление с образцом и ответвление как один этап в F# позволяет вам избежать подобного во многих случаях. + +The one-step "pattern match and branch" in F# allows you to avoid this in many cases. + +## Послесловие (Postscript) + +Вышеприведённый пример реализации на F# на самом деле довольно громоздкий по меркам F#! + +Ради интереса, вот, как выглядит более привычная краткая версия: + +> The example implementation in F# above is actually pretty verbose by F# standards! + +> For fun, here is what a more typically concise version would look like: + +```fsharp +let rec quicksort2 = function + | [] -> [] + | first::rest -> + let smaller,larger = List.partition ((>=) first) rest + List.concat [quicksort2 smaller; [first]; quicksort2 larger] + +// код для проверки +printfn "%A" (quicksort2 [1;5;23;18;9;1;3]) +``` + +Неплохо для четырёх строк кода, а когда вы привыкаете к синтаксису, ещё и довольно читаемо. + +> Not bad for 4 lines of code, and when you get used to the syntax, still quite readable. diff --git a/Why use F#/ru/posts/fvsc-sum-of-squares.md b/Why use F#/ru/posts/fvsc-sum-of-squares.md new file mode 100644 index 0000000..4ba52e6 --- /dev/null +++ b/Why use F#/ru/posts/fvsc-sum-of-squares.md @@ -0,0 +1,200 @@ +--- +layout: post +title: "Сравние F# с C#: Простая сумма" +description: "Где пробуем сложить квадраты чисел от 1 до N без использования цикла" +nav: why-use-fsharp +seriesId: "Зачем использовать F#?" +seriesOrder: 3 +categories: [F# vs C#] +--- + + +Для того, чтобы увидеть, как выглядит реальный F#-код, давайте начнём с простой задачи: суммирование квадратов чисел от 1 до N. + +Далее мы сравним реализацию на F# с реализацией на C#. Для начала, вот код на F#: + +```fsharp +// объявление функции возведения в квадрат +let square x = x * x + +// объявление функции суммирования квадратов +let sumOfSquares n = + [1 .. n] |> List.map square |> List.sum + +// пробуем; вызов функции +sumOfSquares 100 +``` + +Таинственное сочетание символов `|>` называется *оператором-конвейером* (the *pipe operator*). Он просто передаёт выходное значение одного выражение на вход другого. Так что код для функции `sumOfSquares` читается следующим образом: + +1. Создать список от 1 до n (список конструируется с помощью квадратных скобок). +2. Передать созданный список в библиотечную функцию под названием `List.map`, таким образом преобразуя входной список в выходной с использованием функции "square", объявленной ранее. +3. Передать результирующий список квадратов в библиотечную функцию `List.sum`. Доагадаетесь, что она делает? +4. Явная инструкция "return" не требуется. Выходное значение `List.sum` и есть окончательный результат функции. + +Далее, вот реализация на C# в классическом (не функциональном) стиле C-подобного языка. (Более функциональная версия, использующая LINQ, будет приведена позднее.) + +```csharp +public static class SumOfSquaresHelper +{ + public static int Square(int i) + { + return i * i; + } + + public static int SumOfSquares(int n) + { + int sum = 0; + for (int i = 1; i <= n; i++) + { + sum += Square(i); + } + return sum; + } +} +``` + +В чём отличия? + +* Код на F# более компактный. +* Код на F# не содержит ни одного объявления типа. +* Вести разработку на F# можно в интерактивном режиме) + +Давайте рассмотрим каждый из этих пунктов по очереди. + + +### Меньше кода + +Наиболее очевидное различие -- в C# варианте гораздо больше кода. 13 строкам кода на C# противопоставляется 3 строки F#-кода (без учёта комментариев). C#-код гораздо более "зашумлён" такими вещами, как фигурные скобки, точки с запятой и т.д. И в C# функции не могут быть объявлены отдельно -- необходимо объявлять их в каком-нибудь классе (например, "SumOfSquaresHelper"). В F# же функции могут быть самостоятельными, а вместо скобок в них используются пробелы; также в F# не нужны символы-ограничители строки. + +В F# является общепринятым описывать функцию в одну строку, например как функцию "square". Функция "sumOfSquares" также может быть описана в пределах одной строки. В C# же подобное обычно считается плохой практикой. + +Когда функция всё-таки знаимает несколько строк, в F# используют отступы для выделения блоков кода, что убирает необходимость в фигурных скобках (если вы когда-либо имели дело c Python, то должны быть знакомы с таким подходом). Так что функция `sumOfSquares` может быть также описана следующим образом: + +> The most obvious difference is that there is a lot more C# code. 13 C# lines compared with 3 F# lines (ignoring comments). The C# code has lots of "noise", things like curly braces, semicolons, etc. And in C# the functions cannot stand alone, but need to be added to some class ("SumOfSquaresHelper"). F# uses whitespace instead of parentheses, needs no line terminator, and the functions can stand alone. + +> In F# it is common for entire functions to be written on one line, as the "square" function is. The `sumOfSquares` function could also have been written on one line. In C# this is normally frowned upon as bad practice. + +> When a function does have multiple lines, F# uses indentation to indicate a block of code, which eliminates the need for braces. (If you have ever used Python, this is the same idea). So the `sumOfSquares` function could also have been written this way: + +```fsharp +let sumOfSquares n = + [1 .. n] + |> List.map square + |> List.sum +``` + +Единственный недостаток здесь - необходимость быть внимательным с отступами в коде. Лично я считаю, что оно того стоит / это не проблема. +> The only drawback is that you have to indent your code carefully. Personally, I think it is worth the trade-off. + + +### Отсутствие необходимости в указании типов + +Следующее отличие заключается в необходимости явно указывать все используемые типы в C#-коде. Например, параметр `int i` и тип возвращаемого значения в `int SumOfSquares`. +Да, C# позволяет использовать ключевое слово "`var`" во многих местах, но не в случае параметров и типов возвращаемых значений функций. + +В F#-коде типы не объявляются вообще. Это важный важный момент: F# выглядит, как нетипизированный язык, но на самом деле он не менее типобезоапасный, чем C#, а, фактически, даже более! +В F# используется механизм под названием "вывод типов" для определения используемых типов из контекста. Этот механизм работает удивительно отлично в большинстве случаев и колоссально уменьшает степень сложности кода. + +В этом случае, алгоритм вывода типов определяет, что объявлен список целых чисел. Затем, в свою очередь, предполгаает, что функция возведения в квадрат и функция суммы также должны оперировать целыми числами, а также и то, что конечное значение должно быть целочисленным. Вы можете видеть, какие типы выводятся, посмотря на запись о результате компиляции в интерактивном окне. Вы увидите нечто подобное: + +> The next difference is that the C# code has to explicitly declare all the types used. For example, the `int i` parameter and `int SumOfSquares` return type. +Yes, C# does allow you to use the "var" keyword in many places, but not for parameters and return types of functions. + +> In the F# code we didn't declare any types at all. This is an important point: F# looks like an untyped language, but it is actually just as type-safe as C#, in fact, even more so! +F# uses a technique called "type inference" to infer the types you are using from their context. It works amazingly very well most of the time, and reduces the code complexity immensely. + +> In this case, the type inference algorithm notes that we started with a list of integers. That in turn implies that the square function and the sum function must be taking ints as well, and that the final value must be an int. You can see what the inferred types are by looking at the result of the compilation in the interactive window. You'll see something like: + +```fsharp +val square : int -> int +``` + +что означает, что фунция "square" принимает целое число (`int`) и возвращает также целое число (`int`). + +Если исходный список использовал бы числа с плавающей точкой, система вывода типов вывела бы, что функция возведения в квадрат также использует числа с плавающей точкой. Попробуйте сами и убедитесь: + +> which means that the "square" function takes an int and returns an int. + +> If the original list had used floats instead, the type inference system would have deduced that the square function used floats instead. Try it and see: + +```fsharp +// объявления функции возведения в квадрат +let squareF x = x * x + +// объявление функции суммы квадратов +let sumOfSquaresF n = + [1.0 .. n] |> List.map squareF |> List.sum // "1.0" -- число с плавающей точкой (float) + +sumOfSquaresF 100.0 +``` + +Проверка типов очень строга! Если вы попытаетесь использовать список чисел с плавающей точкой (`float`) - `[1.0..n]` в исходном примере `sumOfSquares` или же список целых чисел (`int`) в примере `sumOfSquaresF`, компилятор сообщит об ошибке несоответствия типов. + +> The type checking is very strict! If you try using a list of floats (`[1.0..n]`) in the original `sumOfSquares` example, or a list of ints (`[1 ..n]`) in the `sumOfSquaresF` example, you will get a type error from the compiler. + +### Интерактивная разработка (Interactive development) + +В конечном счёте, для F# существует интерактивное окно, где вы можете сразу же проверить работу кода и поэкспериментировать с ним. В C# же нету простого способа решения этих задач. + +Например, можно описать свою функцию возведения в квадрат и сразу же протестировать её: + +> Finally, F# has an interactive window where you can test the code immediately and play around with it. In C# there is no easy way to do this. + +> For example, I can write my square function and immediately test it: + +```fsharp +// определение/задание функции возведения в квадрат +let square x = x * x + +// тестирование +let s2 = square 2 +let s3 = square 3 +let s4 = square 4 +``` + +Когда я убедился, что она работает, я могу продвинуться к следующей части кода. + +Такой вид интерактивности подталкивает к инкрементальному кодированию, которое впоследствии может стать привычкой! + +Более того, многие утверждают, что работа с кодом в интерактивном режиме прививает хорошие подходы к проектированию, такие как уменьшение связности и явное указание зависимостей, и, следовательно, код, пригодный для интерактивного исполнения, будет также легко тестируемым. И наоборот, код, работу которого нельзя проверить в интерактивном режиме, вероятно, также будет сложно и протестировать. + +> When I am satisfied that it works, I can move on to the next bit of code. + +> This kind of interactivity encourages an incremental approach to coding that can become addictive! + +> Furthermore, many people claim that designing code interactively enforces good design practices such as decoupling and explicit dependencies, +and therefore, code that is suitable for interactive evaluation will also be code that is easy to test. Conversely, code that cannot be +tested interactively will probably be hard to test as well. + +### Возвращение к C#-коду (The C# code revisited) + +Мой первоначальный пример, который был написан, используя C# "старого образца". В C# было добавлено много функциональных возможностей (возможностей функционального программирования), и сейчас возможно переписать этот пример в более компактном виде, используя LINQ. + +Поэтому вот очередная C#-версия, повторяющая F#-код "строка-в-строку". + +> My original example was written using "old-style" C#. C# has incorporated a lot of functional features, and it is possible to rewrite the example in a more compact way using the LINQ extensions. + +> So here is another C# version -- a line-for-line translation of the F# code. + +```csharp +public static class FunctionalSumOfSquaresHelper +{ + public static int SumOfSquares(int n) + { + return Enumerable.Range(1, n) + .Select(i => i * i) + .Sum(); + } +} +``` + +Как бы то ни было, вдобавок к назойливым фигурнам скобкам, точкам и точкам с запятой в C#-версии необходимо указывать типы параметров и возвращаемые типы, в отличии от F#-версии. + +Многие C#-разработчики могут посчитать этот пример тривиальным, но затем снова вернуться к циклам, когда логика станет более сложной. В F# же вы почти никогда не увидите циклы явно. +Для примера посмотрите [статью про избавление от шаблонного кода сложных циклов](http://fsharpforfunandprofit.com/posts/conciseness-extracting-boilerplate/). + +> However, in addition to the noise of the curly braces and periods and semicolons, the C# version needs to declare the parameter and return types, unlike the F# version. + +> Many C# developers may find this a trivial example, but still resort back to loops when the logic becomes more complicated. In F# though, you will almost never see explicit loops like this. +> See for example, [this post on eliminating boilerplate from more complicated loops](http://fsharpforfunandprofit.com/posts/conciseness-extracting-boilerplate/). diff --git a/Why use F#/ru/posts/key-concepts.md b/Why use F#/ru/posts/key-concepts.md new file mode 100644 index 0000000..8e2165c --- /dev/null +++ b/Why use F#/ru/posts/key-concepts.md @@ -0,0 +1,338 @@ +--- +layout: post +title: "Четыре ключевых понятия" +description: "Понятия, отличающие F# от стандартного императивного языка" +nav: why-use-fsharp +seriesId: "Зачем использовать F#?" +seriesOrder: 6 +categories: +image: "/assets/img/four-concepts2.png" +--- + +В следующих нескольких статьях мы перейдём к рассмотрению таких тем, как: краткость (conciseness), удобство (convenience), корректность (correctness), параллелизм/конкуррентность (concurrency) и полнота (completeness). + +Но прежде давайте рассмотрим некоторые из ключевых понятий, с которыми нам придётся встречаться постоянно. F# во многих аспектах отличается от стандартного императивного языка, такого как C#, но существует несколько значительных различий, которые особенно важно понять: + +* **Функционально-ориентированный**, нежели объектно-ориентированный; +* **Выражения**, нежели утверждения/инструкции; +* **Алгебраические типы данных** для описания моделей предметной области; +* **Сопоставление с образцом** для потока управления. + +В более поздних статьях все эти понятия будут рассмотрены гораздо глубже. Сейчас же была дана лишь вводная информация, необходимая для понимания остальной части серии статей. + +> In the next few posts we'll move on to demonstrating the themes of this series: conciseness, convenience, correctness, concurrency and completeness. + +> But before that, let's look at some of the key concepts in F# that we will meet over and over again. F# is different in many ways from a standard imperative language like C#, but there are a few major differences that are particularly important to understand: + +> * **Function-oriented** rather than object-oriented +> * **Expressions** rather than statements +> * **Algebraic types** for creating domain models +> * **Pattern matching** for flow of control + +> In later posts, these will be dealt with in much greater depth -- this is just a taster to help you understand the rest of this series. + +![four key concepts](../assets/img/four-concepts2.png) + +### Функционально-ориентированный, нежели объектно-ориентированный + +Как вы могли ожидать от термина "функциональное программирование", в F# функции повсюду. + +И конечно же, функции являются сущностями первого класса и могут передаваться как любые другие значения: + +> As you might expect from the term "functional programming", functions are everywhere in F#. + +> Of course, functions are first class entities, and can be passed around like any other value: + +```fsharp +let square x = x * x + +// функции как значения +let squareclone = square +let result = [1..10] |> List.map squareclone + +// функции, принимающие другие функции в качестве параметров +let execFunction aFunc aParam = aFunc aParam +let result2 = execFunction square 12 +``` + +Однако в C# также есть первоклассные функции, так что же тогда особенного в функциональном программировании? + +Короткий ответ следующий: функционально-ориентированная природа F# проникает во все части языка и системы типов, чего не происходит в случае с C#; так что вещи, выглядящие неуклюжими или нелепыми в C#, в F# получаются очень элегантными. + +Очень сложно объяснить всё это в пределах нескольких абзацев, но вот несколько достоинств, которые будут продемонстрированы в рамках этой серии статей: + +> But C# has first-class functions too, so what's so special about functional programming? + +> The short answer is that the function-oriented nature of F# infiltrates every part of the language and type system in a way that it does not in C#, so that things that are awkward or clumsy in C# are very elegant in F#. + +> It's hard to explain this in a few paragraphs, but here are some of the benefits that we will see demonstrated over this series of posts: + +* **Проектирование с помощью композиции**. Композиция является "клеем", который позволяет строить более крупные системы из малых. Это не какая-то вспомогательная методика, это самое сердце функционального стиля. Почти каждая строка кода представляет собой выражение, пригодное для последующей его композиции с другими выражениями (ниже будет продемонстрировано). Композиция используется для построения базовых функций, затем функций, использующие эти базовые функции, и так далее. И принцип композиции применим не только к функциям, но и к типам (типы-произведения и типы-суммы, которые описаны ниже). +* **Факторинг (factoring) и рефакторинг (refactoring)**. Возможность разбиения задачи на части зависит от того, как просто эти части могут быть снова собраны вместе. Методы и классы, которые могут показаться неделимыми в императивном языке могут быть разбиты на удивительно маленькие части при проектировании в функциональном стиле. Эти мелкие компоненты, как правило, состоят из (а) нескольких очень общих функций, которые принимают другие функции в качестве параметров и (б) прочих вспомогательных функций, специализирующие общий случай под конкретную структура данных или приложение. Как только будет реализовано такое разбиение/декомпозиция, обобщённые функции позволят очень легко запрограммировать большое количество дополнительных операций без необходимости писать новый код. Вы можете ознакомиться хороший пример такой общей функции (функции свёртки - fold) в [статье про извлечение повторяющегося кода из циклов](../posts/conciseness-extracting-boilerplate.md). +* **Правильный подход к проектированию**. Многие принципы хорошего/правильного проектирования, такие как "разделение ответственности" ("separation of concerns"), "принцип единственной ответственности" ("single responsibility principle"), ["программирование относительно интерфейса, а не реализации (program to an interface, not an implementation)"](../posts/convenience-functions-as-interfaces.md), возникают естественным образом в результате следования функциональному подходу. Да и в целом, функциональный код склонен быть высокоуровневым и декларативным. + +В последующих статьях этой серии будут приведены примеры того, как функции могут сделать код более кратким и удобным. Ну и также, для более глубокого понимания есть целая серия статей о том, [как мыслить функционально](../series/thinking-functionally.md). + +* **Building with composition**. Composition is the 'glue' that allows us build larger systems from smaller ones. This is not an optional technique, but is at the very heart of the functional style. Almost every line of code is a composable expression (see below). Composition is used to build basic functions, and then functions that use those functions, and so on. And the composition principle doesn't just apply to functions, but also to types (the product and sum types discussed below). +* **Factoring and refactoring**. The ability to factor a problem into parts depends how easily the parts can be glued back together. Methods and classes that might seem to be indivisible in an imperative language can often be broken down into surprisingly small pieces in a functional design. These fine-grained components typically consist of (a) a few very general functions that take other functions as parameters, and (b) other helper functions that specialize the general case for a particular data structure or application. + Once factored out, the generalized functions allow many additional operations to be programmed very easily without having to write new code. You can see a good example of a general function like this (the fold function) in the [post on extracting duplicate code from loops](../posts/conciseness-extracting-boilerplate.md). +* **Good design**. Many of the principles of good design, such as "separation of concerns", "single responsibility principle", ["program to an interface, not an implementation"](../posts/convenience-functions-as-interfaces.md), arise naturally as a result of a functional approach. And functional code tends to be high level and declarative in general. + +The following posts in this series will have examples of how functions can make code more +concise and convenient, and then for a deeper understanding, there is a whole series on [thinking functionally](../series/thinking-functionally.md). + +### Выражения, нежели утверждения/инструкции (Expressions rather than statements) + +В функциональных языках нет утверждений/инструкций, есть только выражения. Это значит, что любой блок кода всегда возвращает значение, и бо́льшие блоки формируются путём комбинирования меньших с использованием композиции, а не просто упорядочиванием инструкций. + +Если вы когда либо использовали LINQ или SQL, вы уже должны быть знакомы с языками, базирующимися на выражениях. Например, в чистом SQL вы не может осуществлять присваивания. Вместо этого вы должны создавать вложенные запросы внутри бо́льших запросов. + +> In functional languages, there are no statements, only expressions. That is, every chunk of code always returns a value, +and larger chunks are created by combining smaller chunks using composition rather than a serialized list of statements. + +> If you have used LINQ or SQL you will already be familiar with expression-based languages. For example, in pure SQL, +you cannot have assignments. Instead, you must have subqueries within larger queries. + +```sql +SELECT EmployeeName +FROM Employees +WHERE EmployeeID IN + (SELECT DISTINCT ManagerID FROM Employees) -- вложенный запрос +``` + +В F# делается точно так же -- каждое определение функции является целым выражением, а не набором инструкций. + +И это может быть неочевидным, однако код, построенный из выражений одновременно и более безопасен, и более компактен, чем код из инструкций. +Для того, чтобы в этом убедиться, давайте сравним фрагмент C#-кода, основанного на инструкциях, с эквивалентным, но состоящем из выражений. + +Для начала код на инструкциях. Инструкции/утверждения не имеют возвращаемого значения, поэтому вам придется использовать временные переменные, которым будут присваиваться какие-то значения внутри тел инструкций. + +> F# works in the same way -- every function definition is a single expression, not a set of statements. + +> And it might not be obvious, but code built from expressions is both safer and more compact than using statements. +> To see this, let's compare some statement-based code in C# with the equivalent expression-based code. + +> First, the statement-based code. Statements don't return values, so you have to use temporary variables that are assigned to from within statement bodies. + +```csharp +// C#-код, основанный на инструкциях +int result; +if (aBool) +{ + result = 42; +} +Console.WriteLine("result={0}", result); +``` + +Так как `if-then`-блок является инструкцией, переменная `result` должна быть объявлена *вне* этой инструкции, но значение должно быть присвоено ей *внутри* инструкции, что приводит к проблемам, наподобие следующих: + +* Какое исходное значение должно быть присвоено переменной `result`? +* Что, если я забуду присвоить значение переменной `result`? +* Какое значение имеет переменная `result` в случае выполнения блока "else"? + +Вот, для сравнения, такой же код, переписанный в стиле выражений: + +> Because the `if-then` block is a statement, the `result` variable must be defined *outside* the statement but assigned to from *inside* the statement, which leads to issues such as: + +> * What initial value should `result` be set to? +> * What if I forget to assign to the `result` variable? +> * What is the value of the `result` variable in the "else" case? + +> For comparison, here is the same code, rewritten in an expression-oriented style: + +```csharp +// C#-код, базирующийся на выражениях +int result = (aBool) ? 42 : 0; +Console.WriteLine("result={0}", result); +``` + +В версии с выражениями не встречается ни одна из вышеописанных проблем: + +* Переменная `result` объявляется одновременно с присваиванием ей значения. Не нужно задавать никаких переменных вне выражения и незачем беспокоиться о том, какое исходное значение нужно назначить переменной. +* Ветвь "else" обрабатывается явно. Нет возможности упустить присваивание переменной значения ни в одной из ветвей. +* Невозможно забыть означить переменную `result`, так как в этом случае переменной попросту не будет существовать! + +Стиль, ориентированный на выражения, не является в F# предметом выбора. Это одна из тех вещей, для которых требуется изменить подход к программированию при начале работы с этим языком, имея опыт лишь императивного программирования. + +> In the expression-oriented version, none of these issues apply: + +> * The `result` variable is declared at the same time that it is assigned. No variables have to be set up "outside" the expression and there is no worry about what initial value they should be set to. +> * The "else" is explicitly handled. There is no chance of forgetting to do an assignment in one of the branches. +> * It is not possible to forget to assign `result`, because then the variable would not even exist! + +> Expression-oriented style is not a choice in F#, and it is one of the things that requires a change of approach when coming from an imperative background. + +### Алгебраические типы данных (Algebraic Types) + +Система типов в F# основана на понятии **алгебраических типов данных**. В этом случае новые составные типы строятся путём комбинирования существующих типов двумя различными способами: + +* Комбинированием значений, где каждое значение берётся из множества типов. Такие типы называются "типами-произведениями". +* Как дизъюнктивное объединение, представляющее выбор из множества типов. Такие типы называются "типами-суммами". + +например, используя существующие типы `int` и `bool`, мы можем создать новый тип-произведение, который должен содержать в себе их оба: + +> The type system in F# is based on the concept of **algebraic types**. That is, new compound types are built by combining existing types in two different ways: + +> * First, a combination of values, each picked from a set of types. These are called "product" types. +> * Of, alternately, as a disjoint union representing a choice between a set of types. These are called "sum" types. + +> For example, given existing types `int` and `bool`, we can create a new product type that must have one of each: + +```fsharp +// объявление +type IntAndBool = {intPart: int; boolPart: bool} + +// использование +let x = {intPart=1; boolPart=false} +``` + +Или же мы можем создать новый тип-сумму/тип-объединение, предоставляющий выбор между типами: + +> Alternatively, we can create a new union/sum type that has a choice between each type: + +```fsharp +// объявление +type IntOrBool = + | IntChoice of int + | BoolChoice of bool + +// использование +let y = IntChoice 42 +let z = BoolChoice true +``` + +Эти "типы выбора" недоступны в C#, однако невероятно полезны для моделирования многих реальных задач, такие как состояния в конечном автомате (которые, на удивление, часто встречаются во многих предметных областях). + +А путём подобного комбинирования типов-произведений и типов-сумм, очень просто спроектировать обширное множество типов, которые в точности описывают любую бизнес-модель. В качестве примерах, демонстрирующих это, посмотрите статьи на темы: ["Определения типов с малыми накладными расходами"](../posts/conciseness-type-definitions.md) и ["Использование системы типов для обеспечения корректности кода"](../posts/correctness-type-checking). + +> These "choice" types are not available in C#, but are incredibly useful for modeling many real-world cases, such as states in a state machine (which is a surprisingly common theme in many domains). + +> And by combining "product" and "sum" types in this way, it is easy to create a rich set of types that accurately models any business domain. +> For examples of this in action, see the posts on [low overhead type definitions](../posts/conciseness-type-definitions.md) and [using the type system to ensure correct code](../posts/correctness-type-checking). + + +### Сопоставление с образцом для потока управления (Pattern matching for flow of control) + +Многие императивные языки предоставляют множество инструкций потока управления для ветвления и циклов: + +* `if-then-else` (и тернарная версия `bool ? if-true : if-false`); +* `case` или `switch` инструкции; +* `for` и `foreach` циклы, с `break` и `continue`; +* `while` и `until` циклы; +* и даже страшную `goto`. + +F# также поддерживает некоторые из них, однако F# также поддерживает и наиболее общую форму условного выражения, которой является **сопоставление с образцом (pattern matching)**. + +Типичное выражения сопоставления с образцом, заменяющее `if-then-else`, выглядит следующим образом: + +> Most imperative languages offer a variety of control flow statements for branching and looping: + +> * `if-then-else` (and the ternary version `bool ? if-true : if-false`) +> * `case` or `switch` statements +> * `for` and `foreach` loops, with `break` and `continue` +> * `while` and `until` loops +> * and even the dreaded `goto` + +> F# does support some of these, but F# also supports the most general form of conditional expression, which is **pattern-matching**. + +> A typical matching expression that replaces `if-then-else` looks like this: + +```fsharp +match booleanExpression with +| true -> // ветвь для true +| false -> // ветвь для false +``` + +А замена для `switch` может выглядеть так: + +> And the replacement of `switch` might look like this: + +```fsharp +match aDigit with +| 1 -> // случай, когда digit=1 +| 2 -> // случай, когда digit=2 +| _ -> // во всех остальных случаях +``` + +Наконец, циклы как правило реализуются с использованием рекурсией и обычно выглядит следующим образом: + +> Finally, loops are generally done using recursion, and typically look something like this: + +```fsharp +match aList with +| [] -> + // случай пустого списка +| first::rest -> + // Случай, когда в списке есть хотя бы один элемент. + // Обрабатываем первый элемент, а затем + // рекурсивно обрабатываем оставшуюся часть списка +``` + +Хотя выражение сопоставления сначала может показаться излишне сложным, позже вы увидите на практике, что оно одновременно и элегантное, и мощное. + +Чтобы узнать преимущества механизма сопоставления с образцом, посмотрите статью ["(?)Исчерпывающее(?) сопоставление с образцом"](../posts/correctness-exhaustive-pattern-matching), а чтобы посмотреть рабочий пример, обильно использующий сопоставление с образцом, посмотрите статью ["Пример про римские цифры"](../posts/roman-numerals.md). + +> Although the match expression seems unnecessarily complicated at first, you'll see that in practice it is both elegant and powerful. + +> For the benefits of pattern matching, see the post on [exhaustive pattern matching](../posts/correctness-exhaustive-pattern-matching), and for a worked example that uses pattern matching heavily, see the [roman numerals example](../posts/roman-numerals.md). + +### Сопоставление с образцом на типах-объединениях (Pattern matching with union types) ### + +Ранее упоминалось о том, что F# поддерживает тип-объединение или "тип-выбор". Он используется вместо механизма наследования для работы с различными вариантами подчинённых типов. Сопоставление с образцом легко/бесшовно работает с такими типами в целях создания потока управления для каждого из вариантов. + +В следующем примере мы создадим типа `Shape`, представляющий собой различные фигуры, а затем определим функцию `draw` с различным поведением для каждого вида фигуры. Это похоже на полиморфизм в объектно-ориентированном языке, однако основано на функциях. + +> We mentioned above that F# supports a "union" or "choice" type. This is used instead of inheritance to work with different variants of an underlying type. Pattern matching works seamlessly with these types to create a flow of control for each choice. + +> In the following example, we create a `Shape` type representing four different shapes and then define a `draw` function with different behavior for each kind of shape. +> This is similar to polymorphism in an object oriented language, but based on functions. + +```fsharp +type Shape = // задание объединения альтернатив +| Circle of int +| Rectangle of int * int +| Polygon of (int * int) list +| Point of (int * int) + +let draw shape = // задание функции "draw" с фигурой shape в качестве параметра + match shape with + | Circle radius -> + printfn "The circle has a radius of %d" radius + | Rectangle (height,width) -> + printfn "The rectangle is %d high by %d wide" height width + | Polygon points -> + printfn "The polygon is made of these points %A" points + | _ -> printfn "I don't recognize this shape" + +let circle = Circle(10) +let rect = Rectangle(4,5) +let polygon = Polygon( [(1,1); (2,2); (3,3)]) +let point = Point(2,3) + +[circle; rect; polygon; point] |> List.iter draw +``` + +Отметим несколько нюансов: + +* Как обычно, мы не обязаны указывать типы явно. Компилятор правильно определяет, что параметр `"shape"` для функции "`draw`" имеет тип `Shape`. +* Вы можете видеть, что `match..with` не только сопоставляет внутреннюю структуру shape, но также устанавливает значения, в зависимости от того, чему соответствует shape. +* Символ нижнего подчёркивания (`_`) подобен ветви "`default`" в инструкции "`switch`", за исключением того что в F# он необходим -- любой возможный случай должен всегда обрабатываться.Посмотрите, что произойдёт при компиляции, если вы закомментируете строку: + ```fsharp + | _ -> printfn "I don't recognize this shape" + ``` + +Подобные "типы с выбором" можно некоторым образом сымитировать в C#, используя вложенные классы или интерфейсы, однако в системе типов C# нету встроенной поддержки для такого рода (?)исчерпывающего(?) сопоставления с проверкой на предмет ошибок. + +> A few things to note: + +> * As usual, we didn't have to specify any types. The compiler correctly determined that the shape parameter for the "draw" function was of type `Shape`. +> * You can see that the `match..with` logic not only matches against the internal structure of the shape, but also assigns values based on what is appropriate for the shape. +> * The underscore is similar to the "default" branch in a switch statement, except that in F# it is required -- every possible case must always be handled. If you comment out the line + ```fsharp + | _ -> printfn "I don't recognize this shape" + ``` + +> see what happens when you compile! + +> These kinds of choice types can be simulated somewhat in C# by using subclasses or interfaces, but there is no built in support in the C# type system for this kind of exhaustive matching with error checking. + diff --git a/Why use F#/ru/posts/why-use-fsharp-intro.md b/Why use F#/ru/posts/why-use-fsharp-intro.md new file mode 100644 index 0000000..d4c51fc --- /dev/null +++ b/Why use F#/ru/posts/why-use-fsharp-intro.md @@ -0,0 +1,32 @@ +--- +layout: post +title: "Введение в цикл статей 'Зачем использовать F#?'" +description: "Обзор преимуществ F#" +nav: why-use-fsharp +seriesId: "Зачем использовать F#?" +seriesOrder: 1 +--- +Данный цикл статей расскажет вам об осовных особенностях F# и затем покажет, как F# может помочь в вашем повседневном процессе разработки. + +### Ключевые преимущества F# перед C# ### + +Если вы уже знакомы с C# или Java, у вас может возникнуть вопрос: "Зачем учить ещё один язык программирования?". F# имеет ряд значительных преимуществ, которые можно объединить под следующими темами: + +* **Краткость**. F# не загружен лишними конструкциями в коде, такими как фигурные скобки, точки с запятыми и т.п. Вам очень редко придётся явно указывать тип объекта, благодаря мощной системе вывода типов. И в большинстве случаев требуется меньше кода для решения той же задачи. +* **Удобство**. Многие общие задачи программирования решаются гораздо проще с использованием F#. Среди них такие, как создание и использование сложных объявлений типов, обработка списков, (?) сравнение и эквивалентность (?), конечные автоматы и многое другое. А т.к. функции в F# -- объекты первого класса, становится очень просто проектировать мощный и переиспользуемый код, создавая функции, параметрами которых являются другие функции, или же комбинируя существующие функции для получения новой функциональности. +* **Корректность**. F# имеет мощную систему типов, которая препятствует возникновению многих распространённых шибок, таких как null-исключения. В дополнение к этому, вы зачастую можете описать бизнес-логику, используя систему типов таким образом, чтобы было буквально невозможно написать ошибочный/некорректный код, т.к. при его проверке во время компиляции в нём будут обнаруживаться ошибки типизации. +* **Параллелизм**. Для F# существует ряд встроенных инструментов и библиотек для упрощения создания программных систем, в которых несколько действий могут выполняться одновременно. Асинхронное программирование, как и параллелизм, поддерживаются непосредственно. F# также имеет систему очереди сообщений и отличные механизмы обработки событий и реактивного программирования. А неизменяемость структур данных по умолчанию позволяет гораздо проще управлять общим состоянием и избегать блокировок. + +* **Полнота (Completeness)**. Хотя F#, по своей натуре, функциональный язык, он поддерживает и другие _стили/парадигмы_, которые не "чисты" на 100%, что обеспечивает гораздо более простое взаимодействие с "нечистым" миром веб-сайтов, баз данных, сторонних приложений и т.п. В частности, F# разрабатывался как гибридный функциональный/объектно-ориентированный язык, в результате чего с его помощью можно делать практически всё то же самое, что и с помощью языка C#. И конечно же, F# легко интегрируется с экосистемой .NET, что предоставляет вам доступ ко всем сторонним .NET-библиотекам и инструментам. В конечном счёте, он поставляется как компонент Visual Studio, в результате чего вы получаете хороший редактор с поддержкой IntelliSense, отладчик и множество плагинов для модульного-тестирования (юнит-тестов), контроля версий и прочих задач, встречающихся в процессе разработки. + +В остальных статьях данной серии я попытаюсь продемонстрировать каждое из вышеперечисленных достоинств языка F#, используя отдельные фрагменты F#-кода (а, зачастую, также фрагменты C#-кода, для сравнения). Я кратко затрону каждую из основных особенностей F#, таких как сопоставление с образцом, композиция функций и конкуррентное программирование. К концу чтения данной серии статей, надеюсь, вы будете впечатлены мощью и элегантностью F#, и у вас будет стимул использовать его в вашем следующем проекте! + +### Как читать и использовать примеры кода ### + +Все фрагменты кода в данных статьях были разработаны для выполнения в интерактивном режиме. Я настоятельно рекомендую запускать каждый из фрагментов кода, встречающихся в процессе чтения каждой статьи. (Какие-либо большие фрагменты кода будут помещены в отдельные файлы, а ссылки на них будут приведены в самой статье. / Ссылки на файлы с большими фрагментами кода будут приведены в статье) + +Данная серия статей не является учебником, поэтому я не буду слишком сильно углубляться в детали, наподобие "почему именно конкретный фрагмент кода работает". Не беспокойтесь если вы не можете понять некоторые аспекты; цель данной серии статей -- лишь познакомить вас с F# и раззадорить вас на его более глубокое изучение. + +Если у вас есть опыт в программировании на таких языках, как C# и Java, вы, вероятно, уже замечали, что можете довольно неплохо понимать исходный код, написанный на других подобных языках, даже если вы не знакомы с ключевыми словами или библиотеками. Вы можете задать себе вопрос: "Как присвоить значение переменно?" или же "Как объявить цикл?", и с такими вопросами вы сможете довольно быстро суметь запрограммировать простые вещи. + +Такой подход не сработает в случае F#, т.к. [в нём в чистом виде / в его чистом виде] не существует перменных, циклов и нет объектов. Не расстраивайтесь -- со временем это обретёт смысл! Если вы хотите изучить F# более глубоко, вы сможете найти несколько полезных советов на странице ["Изучение F#"](../learning-fsharp/index.md). diff --git a/Why use F#/ru/why-use-fsharp/index.md b/Why use F#/ru/why-use-fsharp/index.md new file mode 100644 index 0000000..7dda613 --- /dev/null +++ b/Why use F#/ru/why-use-fsharp/index.md @@ -0,0 +1,250 @@ +--- +layout: page +title: "Зачем использовать F#?" +description: "Почему стоит попробовать F# в вашем следующем проекте" +nav: why-use-fsharp +hasIcons: 1 +image: "/assets/img/four-concepts2.png" +--- +# "Зачем использовать F#?" в двух словах + +Хотя F# хорош для использования в специализированных областях, таких как научные исследования или анализ даных, он также отлично подходит для разработки корпоративных приложений. +> Although F# is great for specialist areas such as scientific or data analysis, it is also an excellent choice for enterprise development. + +Ниже перечислены пять веских причин подумать об использовании F# в вашем следующем проекте. +> Here are five good reasons why you should consider using F# for your next project. + +## ![](../assets/img/glyphicons/glyphicons_030_pencil.png) Краткость (Conciseness) + +F# не загружен [лишними конструкциями в коде](../posts/fvsc-sum-of-squares.md), такими как фигурные скобки, точки с запятыми и т.п. +> F# is not cluttered up with [coding "noise"](../posts/fvsc-sum-of-squares.md) such as curly brackets, semicolons and so on. + +Вам очень редко придётся явно указывать тип объекта, благодаря мощной [системе вывода типов](../posts/conciseness-type-inference.md). +> You almost never have to specify the type of an object, thanks to a powerful [type inference system](../posts/conciseness-type-inference.md). + +И, по сравнению с C#, обычно требуется требуется [меньше кода](../posts/fvsc-download.md), чтобы решить одну и ту же задачу. +> And, compared with C#, it generally takes [fewer lines of code](../posts/fvsc-download.md) to solve the same problem. + +```fsharp +// "однострочники" (one-liners) +[1..100] |> List.sum |> printfn "sum=%d" + +// никаких точек с запятой, фигурных или круглых скобок (no curly braces, semicolons or parentheses) +let square x = x * x +let sq = square 42 + +// простые типы -- в одну строку (simple types in one line) +type Person = { First:string; Last:string } + +// сложные типы -- лишь в несколько строк (complex types in a few lines) +type Employee = + | Worker of Person + | Manager of Employee list + +// вывод типов (type inference) +let jdoe = { First="John"; Last="Doe" } +let worker = Worker jdoe +``` + +## ![](../assets/img/glyphicons/glyphicons_343_thumbs_up.png) Удобство (Convenience) + +Многие общие задачи программирования решаются гораздо проще с использованием F#. Среди них такие, как создание и использование [сложных объявлений типов](../posts/conciseness-type-definitions.md), [обработка списков](../posts/conciseness-extracting-boilerplate.md), [(?) сравнение и эквивалентность (?)](../posts/convenience-types.md), [конечные автоматы/машины состояний](../posts/designing-with-types-representing-states.md) и многое другое. +> Many common programming tasks are much simpler in F#. This includes things like creating and using [complex type definitions](../posts/conciseness-type-definitions.md), doing [list processing](../posts/conciseness-extracting-boilerplate.md), [comparison and equality](../posts/convenience-types.md), [state machines](../posts/designing-with-types-representing-states.md), and much more. + +А т.к. функции в F# -- объекты первого класса, становится очень просто проектировать мощный и переиспользуемый код, создавая [функции, параметрами которых являются другие функции](../posts/conciseness-extracting-boilerplate.md), или же [комбинируя существующие функции](../posts/conciseness-functions-as-building-blocks.md) для получения новой функциональности. +> And because functions are first class objects, it is very easy to create powerful and reusable code by creating functions +that have [other functions as parameters](../posts/conciseness-extracting-boilerplate.md), +or that [combine existing functions](../posts/conciseness-functions-as-building-blocks.md) to create new functionality. + +```fsharp +// автоматически реализуемые эквивалентность и сравнение (automatic equality and comparison) +type Person = { First:string; Last:string } +let person1 = { First="john"; Last="Doe" } +let person2 = { First="john"; Last="Doe" } +printfn "Equal? %A" (person1 = person2) + +// простая работа с IDisposable с использованием ключевого слова "use" (easy IDisposable logic with "use" keyword) +use reader = new StreamReader(..) + +// простая композиция функций (easy composition of functions) +let add2times3 = (+) 2 >> (*) 3 +let result = add2times3 5 +``` + +## ![](../assets/img/glyphicons/glyphicons_150_check.png) Правильность/корректность/безошибочность (Correctness) + +F# имеет [мощную систему типов](../posts/correctness-type-checking.md), которая препятствует возникновению многих распространённых шибок, таких как [null-исключения](../posts/the-option-type.md#option-is-not-null). +> F# has a [powerful type system](../posts/correctness-type-checking.md) which prevents many common errors such +as [null reference exceptions](../posts/the-option-type.md#option-is-not-null). + +Значения [по умолчанию неизменяемы](../posts/correctness-immutability.md), что предупреждает большое множество ошибок. +> Values are [immutable by default](../posts/correctness-immutability.md), which prevents a large class of errors. + +В дополнение к вышеописанному, вы часто можете описать бизнес-логику, используя [систему типов](../posts/correctness-exhaustive-pattern-matching.md) таким образом, чтобы было буквально [невозможно написать ошибочный/некорректный код](../posts/designing-for-correctness.md), а то и по возможности использовать механизм [единиц измерений](../posts/units-of-measure.md), таким образом заметно уменьшив необходимость в модульных тестах (юнит-тестах). +> In addition, you can often encode business logic using the [type system](../posts/correctness-exhaustive-pattern-matching.md) itself in such a way +that it is actually [impossible to write incorrect code](../posts/designing-for-correctness.md) +or mix up [units of measure](../posts/units-of-measure.md), greatly reducing the need for unit tests. + +```fsharp +// строгая провека типов (strict type checking) +printfn "print string %s" 123 //compile error + +// все значения неизменяемы по умолчнанию (all values immutable by default) +person1.First <- "new name" //ошибка присвоения (assignment error) + +// нет необходимости делать проверку на null (never have to check for nulls) +let makeNewString str = + // всегда можно без опасений дополнить str (str can always be appended to safely) + let newString = str + " new!" + newString + +// описывайте бизнес-логику в типах/представляйте бизнес-логику в типах (embed business logic into types) +emptyShoppingCart.remove // compile error! + +// механизм единиц измерений (units of measure) +let distance = 10 + 10 // error! +``` + +## ![](../assets/img/glyphicons/glyphicons_054_clock.png) Параллелизм (Concurrency) + +Для F# существует ряд встроенных библиотек для решения задач, в которых необходимо реализовать одновременное выполнение нескольких действий. +> F# has a number of built-in libraries to help when more than one thing at a time is happening. + +[И асинхронность, и параллелизм](../posts/concurrency-async-and-parallel.md) реализовать очень просто/очень просты в реализации. +> Asynchronous programming is [very easy](../posts/concurrency-async-and-parallel.md), as is parallelism. + +F# также имеет встроенную [модель актёров/акторов](../posts/concurrency-actor-model.md), отличную поддержку обработки событий и и [функционального реактивного программирования](../posts/concurrency-reactive.md). +> F# also has a built-in [actor model](../posts/concurrency-actor-model.md), and excellent support for event handling +and [functional reactive programming](../posts/concurrency-reactive.md). + +И, конечно же, благодаря неизменямым по умолчанию структурам данных, разделять состояне и избегать блокировок гораздо проще. +> And of course, because data structures are immutable by default, sharing state and avoiding locks is much easier. + +```fsharp +// простая асинхронная логика с использованием ключевого слова/конструкции "async" (easy async logic with "async" keyword) +let! result = async { something } + +// простая параллельная обработка (easy parallelism) +Async.Parallel [ for i in 0..40 -> async { return fib(i) } ] + +// очереди сообщений (message queues) +MailboxProcessor.Start(fun inbox-> + async{ + let! msg = inbox.Receive() + printfn "message is: %s" msg + }) +``` + +## ![](../assets/img/glyphicons/glyphicons_280_settings.png) Полнота (Completeness) + +Хотя F#, по сути, и функциональный язык, он поддерживает и другие _стили/парадигмы_, которые не чисты на 100%, что обеспечивает гораздо более простое взаимодействие с "нечистым" миром веб-сайтов, баз данных, сторонних/других приложений и т.п. +> Although it is a functional language at heart, F# does support other styles which are not 100% pure, +which makes it much easier to interact with the non-pure world of web sites, databases, other applications, and so on. + +В частности, F# разрабатывался как гибридный функциональный/объектно-ориентированный язык, в результате чего он позволяет делать [практически всё то же самое, что и C#](../posts/completeness-anything-csharp-can-do.md). +> In particular, F# is designed as a hybrid functional/OO language, so it can do [virtually everything that C# can do](../posts/completeness-anything-csharp-can-do.md). + +И конечно же, F# -- [_часть/представитель_ экосистемы .NET](../posts/completeness-seamless-dotnet-interop.md), что предоставляет Вам беспрепятственный доступ ко всем сторонним .NET-библиотекам и инструментам. +> Of course, F# is [part of the .NET ecosystem](../posts/completeness-seamless-dotnet-interop.md), which gives you seamless access to all the third party .NET libraries and tools. +Он работает/запускается на большинстве платформах, включая Linux и мобильные платформы (_с помощью/через_ Mono и новый .NET Core). +> It runs on most platforms, including Linux and smart phones (via Mono and the new .NET Core). + +В конечном счёте, он хорошо интегрирован с Visual Studio (Windows) и Xamarin (Mac), а значит, вы можете использовать _отличную/качественную/полноценную_ IDE с поддержкой IntelliSense, отладчиком, а также множеством расширений для модульного тестирования (юнит-тестов), контроля версий и прочих задач процесса разработки. +> Finally, it is well integrated with Visual Studio (Windows) and Xamarin (Mac), which means you get a great IDE with IntelliSense support, a debugger, +and many plug-ins for unit tests, source control, and other development tasks. + +А в случае Linux вместо вышеперечисленного вы можете использовать IDE MonoDevelop. +> Or on Linux, you can use the MonoDevelop IDE instead. + +```fsharp +// "нечистый" код, если он нужен (impure code when needed) +let mutable counter = 0 + +// создавайте C#-совместимые классы и интерфейсы (create C# compatible classes and interfaces) +type IEnumerator<'a> = + abstract member Current : 'a + abstract MoveNext : unit -> bool + +// методы-расширения (extension methods) +type System.Int32 with + member this.IsEven = this % 2 = 0 + +let i = 20 +if i.IsEven then printfn "'%i' is even" i + +// код создания и работы с графическим пользовательским интерфейсом (UI code) +open System.Windows.Forms + +let form = new Form(Width= 400, Height = 300, +Visible = true, Text = "Hello World") +form.TopMost <- true +form.Click.Add (fun args-> printfn "clicked!") +form.Show() +``` + +## Цикл статей "Зачем использовать F#?" (The "Why Use F#?" series) + +Следующая серия статей демонстрирует каждое из вышеперечисленных преимуществ F# с приведением отдельных фрагментов F#-кода (а часто и с фрагментами C#-кода для сравнения). + +* [Введение в цикл статей "Зачем использовать F#"](../posts/why-use-fsharp-intro.md). Обзор преимуществ F#. +* [Синтаксис F# за 60 секунд](../posts/fsharp-in-60-seconds.md). Очень краткий обзор о том, как читать F#-код. +* [Сравнение F# и C#: Просто суммирование](../posts/fvsc-sum-of-squares.md). Здесь мы пробуем просуммировать квадраты чисел от 1 до N без использования цикла. +* [Сравнение F# и C#: Сортировка](../posts/fvsc-quicksort.md). Здесь мы увидим, что F# более декларативный, чем C#, а также познакомимся с механизом сопоставления с образцом (pattern matching). +* [Сравнение F# и C#: Загрузка веб-страницы](../posts/fvsc-download.md). Здесь мы увидим, что F# превосходит в работе с обратными вызовами, а также познакомимся с ключевым словом "use". +* [Четрые ключевых понятия](../posts/key-concepts.md). Понятия, отличающие F# от _стандартного/типичного_ императивного языка. +* [Краткость](../posts/conciseness-intro.md). Почему важна краткость? +* [Вывод типов](../posts/conciseness-type-inference.md). Как не дать себя запутать сложным синтаксисом для работы с типами. +* [Малые накладные расходы при объявлении типов](../posts/conciseness-type-definitions.md). Никаких неудобств при создании новых типов. +* [Использование функций во избежание шаблонного (boilerplate) кода](../posts/conciseness-extracting-boilerplate.md). Функциональный подход к принципу DRY _(Don't Repeat Yourself -- не повторяйcя)_. +* [Использование функций как строительных блоков](../posts/conciseness-functions-as-building-blocks.md). Композиция функций и мини-языки делают код более читаемым. +* [Сопоставление с образцом _для/ради_ краткости](../posts/conciseness-pattern-matching.md). Сопоставление с образцом позволяет находить и связывать в одно действие. +* [Удобство](../posts/convenience-intro.md). Возможности, сокращающие рутинное программирование и шаблонный код. +* [Поведение типов "из коробки"](../posts/convenience-types.md). Неизменяемость и встроенная эквивалентность без кодирования. +* [Функции как интерфейсы](../posts/convenience-functions-as-interfaces.md). Шаблоны проектирования ООП могут оказатсья тривиальными, _если использовать функции_/_когда в дело идут функции_. +* [Частичное применение](../posts/convenience-partial-application.md). Как _закрепить_/_зафиксировать_/_"запомнить"_ некоторые параметры функции. +* [Активные _образцы_/_шаблоны_ (Active patterns)](../posts/convenience-active-patterns.md). (???) Динамические образцы для эффективного сопоставления (???). (Dynamic patterns for powerful matching) +* [Правильность/Корректность](../posts/correctness-intro.md). Как писать "модульные тесты времени компиляции". +* [Неизменяемость](../posts/correctness-immutability.md). Делаем код предсказуемым. +* [_Полное_/_исчерпывающее_ сопоставление с образцом](../posts/correctness-exhaustive-pattern-matching.md). _Мощная_/_эффективная_ техника обеспечения _правильности_/_корректности_. +* [Использование системы типов для обеспечения корректности кода](../posts/correctness-type-checking.md). Система типов F# -- ваш друг, а не враг. +* [Рабочий пример: Проектирование ради _корректности_/_правильности_](../posts/designing-for-correctness.md). _Как сделать неправильное непредставимым._ / _Как сделать недопустимое состояние непредставимым._ +* [Параллелизм/Параллельная обработка](../posts/concurrency-intro.md). Следующая крупная революция в подходе к проектированию ПО? +* [Асинхронное программирование](../posts/concurrency-async-and-parallel.md). Инкапсулирование фоновой задачи c помощью класса Async. +* [Сообщения и агенты](../posts/concurrency-actor-model.md). Упрощаем _представление_/_размышление о_/_понимание_ параллельности. +* [Функциональное реактивное программирование](../posts/concurrency-reactive.md). Представляем события в виде потоков. +* [Полнота](../posts/completeness-intro.md). F# -- часть целой экосистемы .NET. +* [_Бесшовное_/_беспрепятственное_ взаимодействие с библиотеками .NET](../posts/completeness-seamless-dotnet-interop.md). Несколько удобных _возможностей_/_компонентов_ для работы с .NET-библиотеками. +* [Всё, что может C#...](../posts/completeness-anything-csharp-can-do.md). Ураганный тур по объектно-ориентированному F#-коду. +* [Зачем использоват F#: Заключение](../posts/why-use-fsharp-conclusion.md). + +> The following series of posts demonstrates each of these F# benefits, using standalone snippets of F# code (and often with C# code for comparison). +> * [Introduction to the 'Why use F#' series](../posts/why-use-fsharp-intro.md). An overview of the benefits of F# +> * [F# syntax in 60 seconds](../posts/fsharp-in-60-seconds.md). A very quick overview on how to read F# code +> * [Comparing F# with C#: A simple sum](../posts/fvsc-sum-of-squares.md). In which we attempt to sum the squares from 1 to N without using a loop +> * [Comparing F# with C#: Sorting](../posts/fvsc-quicksort.md). In which we see that F# is more declarative than C#, and we are introduced to pattern matching. +> * [Comparing F# with C#: Downloading a web page](../posts/fvsc-download.md). In which we see that F# excels at callbacks, and we are introduced to the 'use' keyword +> * [Four Key Concepts](../posts/key-concepts.md). The concepts that differentiate F# from a standard imperative language +> * [Conciseness](../posts/conciseness-intro.md). Why is conciseness important? +> * [Type inference](../posts/conciseness-type-inference.md). How to avoid getting distracted by complex type syntax +> * [Low overhead type definitions](../posts/conciseness-type-definitions.md). No penalty for making new types +> * [Using functions to extract boilerplate code](../posts/conciseness-extracting-boilerplate.md). The functional approach to the DRY principle +> * [Using functions as building blocks](../posts/conciseness-functions-as-building-blocks.md). Function composition and mini-languages make code more readable +> * [Pattern matching for conciseness](../posts/conciseness-pattern-matching.md). Pattern matching can match and bind in a single step +> * [Convenience](../posts/convenience-intro.md). Features that reduce programming drudgery and boilerplate code +> * [Out-of-the-box behavior for types](../posts/convenience-types.md). Immutability and built-in equality with no coding +> * [Functions as interfaces](../posts/convenience-functions-as-interfaces.md). OO design patterns can be trivial when functions are used +> * [Partial Application](../posts/convenience-partial-application.md). How to fix some of a function's parameters +> * [Active patterns](../posts/convenience-active-patterns.md). Dynamic patterns for powerful matching +> * [Correctness](../posts/correctness-intro.md). How to write 'compile time unit tests' +> * [Immutability](../posts/correctness-immutability.md). Making your code predictable +> * [Exhaustive pattern matching](../posts/correctness-exhaustive-pattern-matching.md). A powerful technique to ensure correctness +> * [Using the type system to ensure correct code](../posts/correctness-type-checking.md). In F# the type system is your friend, not your enemy +> * [Worked example: Designing for correctness](../posts/designing-for-correctness.md). How to make illegal states unrepresentable +> * [Concurrency](../posts/concurrency-intro.md). The next major revolution in how we write software? +> * [Asynchronous programming](../posts/concurrency-async-and-parallel.md). Encapsulating a background task with the Async class +> * [Messages and Agents](../posts/concurrency-actor-model.md). Making it easier to think about concurrency +> * [Functional Reactive Programming](../posts/concurrency-reactive.md). Turning events into streams +> * [Completeness](../posts/completeness-intro.md). F# is part of the whole .NET ecosystem +> * [Seamless interoperation with .NET libraries](../posts/completeness-seamless-dotnet-interop.md). Some convenient features for working with .NET libraries +> * [Anything C# can do...](../posts/completeness-anything-csharp-can-do.md). A whirlwind tour of object-oriented code in F# +> * [Why use F#: Conclusion](../posts/why-use-fsharp-conclusion.md).