Rash thoughts about .NET, C#, F# and Dynamics NAV.


"Every solution will only lead to new problems."

Saturday, 24. April 2010


Solving KataYahtzee with F# and NaturalSpec

Filed under: F#,Informatik,Kata — Steffen Forkmann at 19:04 Uhr

Today I’m starting a new blog post series about solving code katas in F# and with the help of my NaturalSpec project. A code kata is a programming exercise which helps to improve your skills through practice and repetition. In this series we want to use the Test Driven Development TDD approach which means in the context of NaturalSpec that we have to write our specs before we implement the algorithm.

Problem Description

“The game of yahtzee is a simple dice game. Each round, each player rolls five six sided dice. The player may choose to reroll some or all of the dice up to three times (including the original roll). The player then places the roll at a category, such as ones, twos, sixes, pair, two pairs etc. If the roll is compatible with the score, the player gets a score for this roll according to the rules. If the roll is not compatible, the player gets a score of zero for this roll.

The kata consists of creating the rules to score a roll in any of a predefined category. Given a roll and a category, the final solution should output the score for this roll placed in this category.”

[codingdojo.org]

Category 1 – Ones, Twos, Threes, Fours, Fives, Sixes

“Ones, Twos, Threes, Fours, Fives, Sixes: The player scores the sum of the dice that reads one, two, three, four, five or six, respectively. For example, 1, 1, 2, 4, 4 placed on "fours" gives 8 points.”

After reading this category description we could come up with the following spec:

module Yahtzee.Specs

 

open NaturalSpec

 

let placed_on category list =

    printMethod category

    calcValue category list

 

[<Scenario>]    

let “Given 1,1,2,4,4 placed on "fours" gives 8 points.“ () =  

    Given (1, 1, 2, 4, 4)

      |> When (placed_on Fours)

      |> It should equal 8

      |> Verify

Since we really want to implement six different categories we should also add some more scenarios like this one:

[<Scenario>]    

let “Given 1,1,6,4,6 placed on "sixes" gives 12 points.“ ()=  

    Given (1, 1, 6, 4, 6)

      |> When (placed_on Sixes)

      |> It should equal 12

      |> Verify

 

// …

Now we have some specs but they will all fail since we don’t have anything implemented yet. So we have to come up with a model of dice rolls and categories. As the specs suggests we model the dice roll as a tuple of ints and the category as a discriminated union:

module Yahtzee.Model

 

type Roll = int * int * int * int * int

 

type Category =

| Ones

| Twos

| Threes

| Fours

| Fives

| Sixes  

The tuple is a natural choice for the dice roll, but for easier calculation we add a helper function which converts it into a list. This allows use to use the standard list functions and therefore summing the values becomes trivial:

let toList (roll:Roll) =

    let a,b,c,d,e = roll

    [a;b;c;d;e]

 

let sumNumber number =

    Seq.filter ((=) number)

      >> Seq.sum

 

let calcValue category roll =

    let list = toList roll

    match category with

    | Ones   -> sumNumber 1 list

    | Twos   -> sumNumber 2 list

    | Threes -> sumNumber 3 list

    | Fours  -> sumNumber 4 list

    | Fives  -> sumNumber 5 list

    | Sixes  -> sumNumber 6 list

Now we can run our scenarios with any NUnit runner. I’m using the default NUnit GUI runner here, which gives me a picture like this:

Both specs in NUnit

Category 2 – Pair

“Pair: The player scores the sum of the two highest matching dice. For example, 3, 3, 3, 4, 4 placed on "pair" gives 8.”

The kata description gives us some new scenarios. As seen above we should specify them before writing the code.

[<Scenario>]    

let “Given 3,3,3,4,4 placed on "pair" gives 8.“ () =  

    Given (3, 3, 3, 4, 4)

      |> When (placed_on Pair)

      |> It should equal 8

      |> Verify

 

[<Scenario>]    

let “Given 5,3,5,4,4 placed on "pair" gives 10.“ () =  

    Given (5, 3, 5, 4, 4)

      |> When (placed_on Pair)

      |> It should equal 10

      |> Verify   

 

[<Scenario>]    

let “Given 1,2,3,4,5 placed on "pair" gives 0.“ () =  

    Given (1, 2, 3, 4, 5)

      |> When (placed_on Pair)

      |> It should equal 0

      |> Verify     

Since we use a new category we now have to extend our model and the calcValue function:

type Category =

| Ones

  // …

| Sixes

| Pair

 

// …

let sumAsPair list number =

    let numberCount =

        list

          |> Seq.filter ((=) number)

          |> Seq.length

 

    if numberCount >= 2 then 2 * number else 0

 

let calcValue category roll =

    let list = toList roll

    match category with

    | Ones   -> sumNumber 1 list

        // …

    | Sixes  -> sumNumber 6 list

    | Pair   ->

        [1..6]

          |> Seq.map (sumAsPair list)

          |> Seq.max  

Category 3 – Two pairs

“Two pairs: If there are two pairs of dice with the same number, the player scores the sum of these dice. If not, the player scores 0. For example, 1, 1, 2, 3, 3 placed on "two pairs" gives 8.”

[<Scenario>]    

let “Given 1,1,2,3,3 placed on "two pair" gives 8.“ () =  

    Given (1, 1, 2, 3, 3)

      |> When (placed_on TwoPair)

      |> It should equal 8

      |> Verify

 

[<Scenario>]    

let “Given 1,6,6,3,3 placed on "two pair" gives 18.“ () =  

    Given (1, 6, 6, 3, 3)

      |> When (placed_on TwoPair)

      |> It should equal 18

      |> Verify

 

[<Scenario>]    

let “Given 1,1,2,4,3 placed on "two pair" gives 0.“ () =  

    Given (1, 1, 2, 4, 3)

      |> When (placed_on TwoPair)

      |> It should equal 0

      |> Verify 

Implementing this category is a little bit tricky but with the help of our Pair function and some more standard sequence combinators we can get our spec green:

type Category =

| Ones

  // …

| TwoPair

 

let allPairs =

    [for i in 1..6 do

       for j in 1..6 -> i,j]

 

let calcValue category roll =

    // …

    | TwoPair   ->

        allPairs

          |> Seq.filter (fun (a,b) -> a <> b)

          |> Seq.map (fun (a,b) ->

                let a’ = sumAsPair list a

                let b’ = sumAsPair list b

                if a’ = 0 || b’ = 0 then 0 else a’ + b’)

          |> Seq.max    

Category 4 – Three of a kind

“Three of a kind: If there are three dice with the same number, the player scores the sum of these dice. Otherwise, the player scores 0. For example, 3, 3, 3, 4, 5 places on "three of a kind" gives 9.”

[<Scenario>]    

let “Given 3,3,3,4,5 placed on "three of a kind" gives 9“()=  

    Given (3, 3, 3, 4, 5)

      |> When (placed_on ThreeOfAKind)

      |> It should equal 9

      |> Verify

 

[<Scenario>]    

let “Given 3,4,3,4,5 placed on "three of a kind" gives 0“()=  

    Given (3, 4, 3, 4, 5)

      |> When (placed_on ThreeOfAKind)

      |> It should equal 0

      |> Verify

Now it is time to refactor our code. The sumAsPair function should be extended to a sumAsTuple function:

type Category =

| Ones

  // …

| ThreeOfAKind

 

let sumAsTuple value list number =

    let numberCount =

        list

          |> Seq.filter ((=) number)

          |> Seq.length

 

let takeBestTuple value list =

    [1..6]

        |> Seq.map (sumAsTuple value list)

        |> Seq.max 

    if numberCount >= value then value * number else 0

 

let calcValue category roll =

      // …

    | Pair   -> takeBestTuple 2 list

    | TwoPair   ->

        allPairs

          |> Seq.filter (fun (a,b) -> a <> b)

          |> Seq.map (fun (a,b) ->

                let a’ = sumAsTuple 2 list a

                let b’ = sumAsTuple 2 list b

                if a’ = 0 || b’ = 0 then 0 else a’ + b’)

          |> Seq.max

    | ThreeOfAKind –> takeBestTuple 3 list

Category 5 – Four of a kind

“Four of a kind: If there are four dice with the same number, the player scores the sum of these dice. Otherwise, the player scores 0. For example, 2, 2, 2, 2, 5 places on "four of a kind" gives 8.”

[<Scenario>]    

let “Given 2,2,2,2,5 placed on "four of a kind" gives 8“ ()=  

    Given (2, 2, 2, 2, 5)

      |> When (placed_on FourOfAKind)

      |> It should equal 8

      |> Verify

 

[<Scenario>]    

let “Given 2,6,2,2,5 placed on "four of a kind" gives 0“ ()=  

    Given (2, 6, 2, 2, 5)

      |> When (placed_on FourOfAKind)

      |> It should equal 0

      |> Verify  

With the help of the takeBestTuple function this becomes trivial:

type Category =

| Ones

  // …

| FourOfAKind

 

let calcValue category roll =

      // …

    | FourOfAKind  -> takeBestTuple 4 list

 

Category 6 – Small straight

“Small straight: If the dice read 1,2,3,4,5, the player scores 15 (the sum of all the dice), otherwise 0.”

[<Scenario>]    

let “Given 1,2,3,4,5 placed on "Small Straight" gives 15“()=  

    Given (1,2,3,4,5)

      |> When (placed_on SmallStraight)

      |> It should equal 15

      |> Verify

 

[<Scenario>]    

let “Given 1,2,5,4,3 placed on "Small Straight" gives 15“()=  

    Given (1,2,5,4,3)

      |> When (placed_on SmallStraight)

      |> It should equal 15

      |> Verify

 

[<Scenario>]    

let “Given 1,2,6,4,3 placed on "Small Straight" gives 0“()=  

    Given (1,2,6,4,3)

      |> When (placed_on SmallStraight)

      |> It should equal 0

      |> Verify 

As in all the above scenarios we don’t assume any specific order in our rolls but for this category it is easier to test if the data is sorted:

type Category =

| Ones

  // …

| SmallStraight

 

let calcValue category roll =

      // …

    | SmallStraight ->

        match list |> List.sort with

        | [1;2;3;4;5] -> 15

        | _ -> 0

Category 7 – Large straight

“Large straight: If the dice read 2,3,4,5,6, the player scores 20 (the sum of all the dice), otherwise 0.”

[<Scenario>]    

let “Given 2,3,4,5,6 placed on "Large Straight" gives 20“()=  

    Given (2,3,4,5,6)

      |> When (placed_on LargeStraight)

      |> It should equal 20

      |> Verify

 

[<Scenario>]    

let “Given 6,2,5,4,3 placed on "Large Straight" gives 20“()=  

    Given (6,2,5,4,3)

      |> When (placed_on LargeStraight)

      |> It should equal 20

      |> Verify

 

[<Scenario>]    

let “Given 1,2,6,4,3 placed on "Large Straight" gives 0“()=  

    Given (1,2,6,4,3)

      |> When (placed_on LargeStraight)

      |> It should equal 0

      |> Verify 

Of course the implementation is exactly the same as for the small straight:

type Category =

| Ones

  // …

| LargeStraight

 

let calcValue category roll =

      // …

    | LargeStraight ->

        match list |> List.sort with

        | [2;3;4;5;6] -> 20

        | _ -> 0

Category 8 – Full house

“Full house: If the dice are two of a kind and three of a kind, the player scores the sum of all the dice. For example, 1,1,2,2,2 placed on "full house" gives 8. 4,4,4,4,4 is not "full house".”

[<Scenario>]    

let “Given 1,1,2,2,2 placed on "full house" gives 8.“ () =  

    Given (1,1,2,2,2)

      |> When (placed_on FullHouse)

      |> It should equal 8

      |> Verify

 

[<Scenario>]    

let “Given 4,4,4,4,4 placed on "full house" gives 0.“ () =  

    Given (4,4,4,4,4)

      |> When (placed_on FullHouse)

      |> It should equal 0

      |> Verify

 

[<Scenario>]    

let “Given 1,1,2,3,2 placed on "full house" gives 0.“ () =  

    Given (1,1,2,3,2)

      |> When (placed_on FullHouse)

      |> It should equal 0

      |> Verify

Implementing the FullHouse category is easy if we reuse our solutions to the Two pairs category:

type Category =

| Ones

  // …

| FullHouse

 

let takeBestCombo value1 value2 list =

    allPairs

        |> Seq.filter (fun (a,b) -> a <> b)

        |> Seq.map (fun (a,b) ->

            let a’ = sumAsTuple value1 list a

            let b’ = sumAsTuple value2 list b

            if a’ = 0 || b’ = 0 then 0 else a’ + b’)

        |> Seq.max

 

let calcValue category roll =

      // …

    | TwoPair   -> takeBestCombo 2 2 list

      // …

    | FullHouse   -> takeBestCombo 2 3 list

Category 9 – Yahtzee

“Yahtzee: If all dice are the have the same number, the player scores 50 points, otherwise 0.”

Here we can use NaturalSpec’s ScenarioTemplates in order to specify all Yahtzees:

[<ScenarioTemplate(1)>]

[<ScenarioTemplate(2)>]

[<ScenarioTemplate(3)>]

[<ScenarioTemplate(4)>]

[<ScenarioTemplate(5)>]

[<ScenarioTemplate(6)>]

let “Given n,n,n,n,n placed on "Yahtzee" gives 50.“ n =  

    Given (n,n,n,n,n)

      |> When (placed_on Yahtzee)

      |> It should equal 50

      |> Verify

 

[<Scenario>]    

let “Given 1,1,1,2,1 placed on "Yahtzee" gives 50.“ () =  

    Given (1,1,1,2,1)

      |> When (placed_on Yahtzee)

      |> It should equal 0

      |> Verify

The implementation is pretty easy:

type Category =

| Ones

  // …

| Yahtzee

 

let calcValue category roll =

      // …

    | Yahtzee ->

        let a,b,c,d,e = roll

        if a = b && a = c && a = d && a = e then 50 else 0

Category 10 – Chance

“Chance: The player gets the sum of all dice, no matter what they read.”

[<Scenario>]    

let “Given 1,1,1,2,1 placed on "Chance" gives 6.“ () =  

    Given (1,1,1,2,1)

      |> When (placed_on Chance)

      |> It should equal 6

      |> Verify

 

[<Scenario>]    

let “Given 1,6,1,2,1 placed on "Chance" gives 11.“ () =  

    Given (1,6,1,2,1)

      |> When (placed_on Chance)

      |> It should equal 11

      |> Verify

This seems to be the easiest category as we only have to sum the values:

type Category =

| Ones

  // …

| Chance

 

let calcValue category roll =

      // …

    | Chance -> List.sum list

Conclusion

We used a lot of F#’s sequence combinators, pattern matching and discriminated unions in this kata. I think this shows that F# is very well suited for such a problem and with NaturalSpec we can easily use a TDD/BDD approach.

The complete source code can be found in the NaturalSpec repository.

If you want to know more about a specific part of the kata or NaturalSpec feel free to contact me.

Tags: , , , , , , ,

Tuesday, 13. April 2010


“FAKE – F# Make” and NaturalSpec released

Filed under: F#,FAKE - F# Make,Visual Studio — Steffen Forkmann at 8:06 Uhr

Yesterday Microsoft released the RTM versions of Visual Studio 2010, .NET Framework 4.0 and F# 2.0.0.0 and so it is time to announce the first official releases of “Fake – F# Make” and NaturalSpec. Both projects are now compatible with Visual Studio 2010 RC and RTM and the corresponding F# versions.

Fake – F# Make version 1.0.0

"FAKE – F# Make" is a build automation system, which is intended to provide a much better tooling support than XML-based build languages like MSBuild or NAnt. Due to its integration in F#, all benefits of the .NET Framework and functional programming can be used, including the extensive class library, powerful debuggers and integrated development environments like Visual Studio 2008, Visual Studio 2010 or SharpDevelop, which provide syntax highlighting and code completion.

Like F# itself the new build language was designed to be succinct, typed, declarative, extensible and easy to use.

NaturalSpec version 1.0.0

NaturalSpec is a UnitTest framework based on NUnit and completely written in F# – but you don’t have to learn F# to use it. The idea is that you can write your spec mostly in a natural language like in the following sample:

[<Scenario>]

let “When removing an element from a list it should not contain the element“() =

  Given [1;2;3;4;5]                 // "Arrange" test context

    |> When removing 3              // "Act"

    |> It shouldn’t contain 3       // "Assert"

    |> It should contain 4          // another assertion

    |> It should have (Length 4)    // Assertion for length

    |> It shouldn’t have Duplicates // it contains duplicates ?

    |> Verify                       // Verify scenario

If you have any questions about the projects feel free to contact me.

Tags: , , ,

Monday, 15. March 2010


F# Vortrag in Hamburg-Altona

Filed under: F#,Veranstaltungen — Steffen Forkmann at 9:25 Uhr

Am Mittwoch, den 17.03.2010 werde ich in Hamburg-Altona einen Vortrag zu “Funktionaler Programmierung mit F#” halten.

“Funktionale Programmiersprachen nehmen seit geraumer Zeit einen hohen Stellenwert in der Wissenschaft ein. Demnächst könnte es eine dieser Sprachen sogar aus dem Forschungsbereich direkt in den Mainstream schaffen. Visual Studio 2010 wird neben C# und VB.NET die funktionale Programmiersprache F# als dritte Hauptsprache anbieten. Der Vortrag soll einen Einblick in funktionale Konzepte und deren Umsetzung in F# geben. Insbesondere soll auf “Funktionen höherer Ordnung”, Typinferenz, Currying, Pattern Matching, “Unveränderlichkeit” und parallele Programmierung eingegangen werden.”

Mittwoch, 17.03.2010 um 19:00 Uhr
Kinderkrankenhaus Altona
Bleickenallee 38
22763 Hamburg
Treffpunkt: Haupteingang Empfang

Tags:

Sunday, 14. February 2010


“FAKE – F# Make” 0.29 released – Ready for F# February 2010 CTP and .NET 4.0 RC

Filed under: English posts,F#,FAKE - F# Make — Steffen Forkmann at 16:56 Uhr

Last week I released version 0.29 of my build automation tool “FAKE – F# Make”. The new version comes along with a couple of changes which I will now describe.

F# February 2010 CTP and .NET 4.0 RC

“FAKE – F# Make” should be completely compatible with both, the F# February 2010 CTP and the F# version which is included in Visual Studio 2010 RC.

FAKE self build and binaries on teamcity.codebetter.com

Since “FAKE – F# Make” is a build automation tool, it was always used for it’s own build process. Now this build process could be moved to an open CI server at teamcity.codebetter.com. If you login as a guest you can download the latest “FAKE – F# Make” binaries from there.

FAKE on build servers without F#

With the new F# CTP there is no longer a need for installing F# on the build agents. In fact FAKE itself was built on build agents which don’t have a F# installation.

If you want to create such build scripts you have to do the following:

  1. Download the standalone zip file of the F# CTP
  2. Put the bin subfolder into your project tree
  3. Modify the FSIPath value in the FAKE.exe.config file to match your FSharp bin folder
    1. If you copy the contents the F# bin folder into ./tools/FSharp/ it should just work.

If you have F# projects you also need to modify your .fsproj files like this:

<PropertyGroup>
   ….
  <FscToolPath>..\..\..\tools\FSharp\</FscToolPath>
</PropertyGroup>
…
<Import Project="..\..\..\tools\FSharp\Microsoft.FSharp.Targets" />

This modifications should take care that MSBuild will use the F# compiler from your tools paths.

Generate your documentations with “docu”

“What’s a docu? A documentation generator for .Net that isn’t complicated, awkward, or difficult to use. Given an assembly and the XML that’s generated by Visual Studio, docu can produce an entire website of documentation with a single command.” [docu Homepage]

“FAKE – F# Make” 0.29 is bundled with this new documentation tool which can easily convert your xml-Documentation into some nice html pages. You can use the tool with the new Docu task like this:

Target? GenerateDocumentation <-

    fun _ ->

        Docu (fun p ->

            {p with

               ToolPath = @".\tools\FAKE\docu.exe"

               TemplatesPath = @".\tools\FAKE\templates"

               OutputPath = docDir })

            (buildDir + "MyAssembly.dll")

You will also need the docu templates, which you can download from the product homepage. I’m planning to bundle some basic templates with the next version of FAKE.

What’s next?

At the moment I’m working on ILMerge task for FAKE. I hope to release this with the next version. There are also some open issues with the Mono support but since teamcity.codebetter.com is getting a mono build agent I hope to make some progress here too.

If you have any questions or ideas for new features please contact me.

Tags: , ,

Monday, 8. February 2010


New syntactic sugar for “FAKE – F# Make” – Getting rid of magic strings

Filed under: English posts,F#,FAKE - F# Make — Steffen Forkmann at 17:58 Uhr

The new version 0.27 of “FAKE – F# Make” comes with new syntactic sugar for build targets and build dependencies. Don’t be afraid the old version is still supported – all scripts should still work with the new version.

The problem

Consider the following target definition:

let buildDir = "./build/"

 

Target "Clean" (fun _ ->

  CleanDir buildDir

)

 

Target "Default" (fun _ ->

  trace "Hello World from FAKE"

)

 

"Default" <== ["Clean"]

 

run "Default"

As you can see we are having a lot of “magic strings” for the target names and the dependency definitions. This was always a small shortcoming in FAKE, since this doesn’t allow refactoring and may result in runtime errors.

One of my goals for “FAKE – F# Make” is to remove these strings in future versions. Unfortunately this is not that easy, because it causes a lot of internal issues. In particular logging to the build server is much harder if you don’t have a target name.

The first step

As posted in a bitbucket comment by cipher we could use the “dynamic lookup operator” to remove some of the magic strings without breaking any internal code.

As a result we can rewrite the above sample as:

let buildDir = "./build/"

 

Target? Clean <-

    fun _ -> CleanDir buildDir

 

Target? Default <-

    fun _ -> trace "Hello World from FAKE"

 

For? Default <- Dependency? Clean

 

Run? Default

All magic strings suddenly disappeared. I think this syntax looks really nice, but unfortunately the strings are not really gone, since the token Default is only checked at runtime.

The idea for future versions

Since the new syntax is really just syntactic sugar I’m always interested in a better solution. Currently I’m working on a syntax using monads. The result could look like this:

let buildDir = "./build/"

 

let Clean = target { CleanDir buildDir }

let Default =

  target {

    trace "Hello World from FAKE"

    trace "Another line"

  }

 

Default <== [Clean]

Run Default

This way the magic string are really gone, but my current problem is retrieving the target name from the let-binding name. Please leave a comment if you have an idea to solve this issue.

Tags: , ,

Monday, 11. January 2010


Terminverschiebung des F#-Vortrages in Bad Homburg

Filed under: F# — Steffen Forkmann at 14:04 Uhr

Der Termin für meinen F#-Talk bei der .NET Usergroup Frankfurt hat sich auf nächste Woche Donnerstag (21.01.2010) 18:30 verschoben.

Veranstaltungsort ist die Geschäftsstelle von Microsoft in Bad Homburg, Siemensstrasse (Anfahrtsbeschreibung).

Bitte beachten Sie, dass für den Besuch dieses User Group Meetings eine Anmeldung auf der Web Seite der .NET User Group zwingend notwendig ist.

Abstract:

Funktionale Programmiersprachen nehmen seit geraumer Zeit einen hohen Stellenwert in der Wissenschaft ein. Demnächst könnte es eine dieser Sprachen sogar aus dem Forschungsbereich direkt in den Mainstream schaffen. Visual Studio 2010 wird neben C# und VB.NET die funktionale Programmiersprache F# als dritte Hauptsprache anbieten. Der Vortrag soll einen Einblick in funktionale Konzepte und deren Umsetzung in F# geben. Insbesondere soll auf “Funktionen höherer Ordnung”, Typinferenz, Currying, Pattern Matching, “Unveränderlichkeit” und parallele Programmierung eingegangen werden.

Tags: , ,

Monday, 21. December 2009


Observing asynchronous downloads with F# and the Reactive Extensions for .NET

Filed under: F# — Steffen Forkmann at 18:01 Uhr

In one of my lasts posts I showed how we can transform some of the operators from the Reactive Framework for an easier use in F#. This time I will demonstrate how we can use this API and asynchronous workflows to download a couple of websites asynchronous and in parallel.

First of all we create a synchronous download function:

let SyncHttp (url:string) =

  let request = WebRequest.Create url

  let response = request.GetResponse()

  use stream = response.GetResponseStream()

  use reader = new StreamReader(stream)

  let result = reader.ReadToEnd()

  url,result

In F# we can easily make this function asynchronously:

let AsyncHttp (url:string) =

  async { // using asynchronous workflows

    let request = WebRequest.Create url

    // async call of GetResponse

    let! response = request.AsyncGetResponse()

    use stream = response.GetResponseStream()

    use reader = new StreamReader(stream)

    // async call of ReadToEnd

    let! result = reader.AsyncReadToEnd()

    return url,result} 

Now we use this AsyncHttp function to create a list of async downloads:

/// Async<string * string> list

let downloads =

  ["http://www.google.com";

   "http://www.twitter.com";

   "http://www.nytimes.com/";

   "http://www.navision-blog.de/";

   "http://www.nba.com/"] 

    |> List.map AsyncHttp

The next step is to convert the list of async calls into a list of observables and to merge this list into a single IObservable. The effect is that whenever one download is completed we will be notified about the result:

/// IObservable<string * string>

let observableDownloads =

  downloads

    |> Seq.map Observable.ofAsync

    |> Observable.merge

 

 

observableDownloads

  |> Observable.subscribe (fun (url,result) ->

       printfn "%A: %d" url result.Length)

 

If you want to learn more about Observable.merge and see the marble diagram you should watch this video.

https://hrvatskaedfarmacija.com
Tags: , ,

Saturday, 12. December 2009


Christmas tree in F#

Filed under: Diverses — Steffen Forkmann at 12:19 Uhr

Today I had way too much time on the train 😉 , so I wrote a little functional christmas tree program:

let line width j =

  List.init

    width

    (fun i -> if abs(width/2 – i) <= j then "*" else " ")

 

let tree n =

  List.init (n/2) (line n) @        // treetop

    List.init 2 (fun _ -> line n 0) // trunk

 

let printTree =

  tree >> Seq.iter (Seq.fold (+) "" >> printfn "%s")

 

printTree 11

 

    *     
   ***    
  *****
 *******  
********* 
    *     
    * 
Tags: ,

Friday, 27. November 2009


Observing the FileSystem

Filed under: Diverses — Steffen Forkmann at 10:38 Uhr

The following class is a wrapper for the System.IO.FileSystemWatcher and converts the FileSystem events into observables. You need to download and reference the Reactive Extensions for .NET (Rx) to use this code:

public class FileSystemObservable

{

  private readonly FileSystemWatcher _fileSystemWatcher;

 

 

  public FileSystemObservable(string directory,

    string filter, bool includeSubdirectories)

  {

    _fileSystemWatcher =

      new FileSystemWatcher(directory, filter)

        {

          EnableRaisingEvents = true,

          IncludeSubdirectories = includeSubdirectories

        };

 

 

    ChangedFiles =

      Observable.FromEvent<FileSystemEventHandler,

                             FileSystemEventArgs>

      (h => h.Invoke,

       h => _fileSystemWatcher.Changed += h,

       h => _fileSystemWatcher.Changed -= h)

      .Select(x => x.EventArgs);

 

    CreatedFiles =

      Observable.FromEvent<FileSystemEventHandler,

                             FileSystemEventArgs>

      (h => h.Invoke,

       h => _fileSystemWatcher.Created += h,

       h => _fileSystemWatcher.Created -= h)

      .Select(x => x.EventArgs);

 

    DeletedFiles =

      Observable.FromEvent<FileSystemEventHandler,

                             FileSystemEventArgs>

      (h => h.Invoke,

       h => _fileSystemWatcher.Deleted += h,

       h => _fileSystemWatcher.Deleted -= h)

      .Select(x => x.EventArgs);

 

    RenamedFiles =

      Observable.FromEvent<RenamedEventHandler,

                             RenamedEventArgs>

      (h => h.Invoke,

       h => _fileSystemWatcher.Renamed += h,

       h => _fileSystemWatcher.Renamed -= h)

      .Select(x => x.EventArgs);

 

    Errors =

      Observable.FromEvent<ErrorEventHandler, ErrorEventArgs>

      (h => h.Invoke,

       h => _fileSystemWatcher.Error += h,

       h => _fileSystemWatcher.Error -= h)

      .Select(x => x.EventArgs);

  }

 

  /// <summary>

  /// Gets or sets the errors.

  /// </summary>

  /// <value>The errors.</value>

  public IObservable<ErrorEventArgs> Errors

           { get; private set; }

 

  /// <summary>

  /// Gets the changed files.

  /// </summary>

  /// <value>The changed files.</value>

  public IObservable<FileSystemEventArgs> ChangedFiles

           { get; private set; }

 

  /// <summary>

  /// Gets the created files.

  /// </summary>

  /// <value>The created files.</value>

  public IObservable<FileSystemEventArgs> CreatedFiles

           { get; private set; }

 

  /// <summary>

  /// Gets the deleted files.

  /// </summary>

  /// <value>The deleted files.</value>

  public IObservable<FileSystemEventArgs> DeletedFiles

           { get; private set; }

 

  /// <summary>

  /// Gets the renamed files.

  /// </summary>

  /// <value>The renamed files.</value>

  public IObservable<RenamedEventArgs> RenamedFiles

           { get; private set; }

}

Now we can use the observable and can easily create meta-events:

IDisposable writer =

    new FileSystemObservable(@"d:\Test\", "*.*", false)

        .CreatedFiles

        .Where(x => (new FileInfo(x.FullPath)).Length > 0)

          // … you can do much more with the combinators

        .Select(x => x.Name)

        .Subscribe(Console.WriteLine);

Tags: ,

Tuesday, 24. November 2009


Generating an IObservable<T> from an IEvent in F#

Filed under: F# — Steffen Forkmann at 12:39 Uhr

Yesterday I showed how we can map some of the Rx operators to an API which looks more like the F# base classes. Today I wanted to use these mapped operators in a WPF-application written in F#.

F# gives us a nice way to use events as first class citizen (via IEvent) but these events implement their own version of IObservable<T> (in FSharp.Core.dll), which is unfortunately incompatible with the Rx version and therefore with the mapped API.

The solution I found is to wrap the F# IEvent with a Rx IObservable<T>:

/// Generates an observable from an IEvent

let fromEvent (event:IEvent<_,_>) =      

  Observable.Create<_>(fun x ->

    event.Subscribe x.OnNext |> ignore

    new System.Action(fun () -> ()))     

Now we are able to use the WPF events as observables:

// Register ListBox Commands

listBox1.KeyDown

  |> Observable.fromEvent

  |> Observable.filter (fun args -> args.Key = Key.Delete)

  |> Observable.subscribe deleteElement

I am interested if someone has a different and maybe better solution to this problem.

Updated: 21.12.2009 – Observable.Context is no longer supported by Rx ==> Removed

Tags: ,