Phillip Trelford's Array

POKE 36879,255

Running TAP

The Test Anything Protocol (TAP) is a text-based protocol for test results:

 1..4
 ok 1 - Input file opened
 not ok 2 - First line of the input valid
 ok 3 - Read the rest of the file
 not ok 4 - Summarized correctly # TODO Not written yet

 

I think the idea is an good one, a simple cross-platform human readable standard for formatting test results. There are TAP producers and consumers for Perl, Java, JavaScript etc. allowing you to join up tests for cross-platform projects.

NUnit runner

Over the last week or so I’ve created a TAP runner F# script for NUnit test methods. It supports the majority of NUnit’s attributes including ExpectedException, TimeOut and test generation with TestCase, TestCaseSource, Values, etc., .

The runner can be used in a console app to produce TAP output to the console or directly in F# interactive for running tests embedded in a script.

TAP run

Tests can be organized in classes:

type TAPExample () =
    [<Test>] member __.``input file opened`` () = Assert.Pass()
    [<Test>] member __.``First line of the input valid`` () = Assert.Fail()
    [<Test>] member __.``Read the rest of the file`` () = Assert.Pass()
    [<Test>] member __.``Summarized correctly`` () = Assert.Fail()

Tap.Run typeof<TAPExample>

or in modules:

let [<Test>] ``input file opened`` () = Assert.Pass()
let [<Test>] ``First line of the input valid`` () = Assert.Fail()
let [<Test>] ``Read the rest of the file`` () = Assert.Pass()
let [<Test>] ``Summarized correctly`` () = Assert.Fail()
type Marker = interface end
Tap.Run typeof<Marker>.DeclaringType

Note: the marker interface is used to reflect the module’s type.

Console output

In the console test cases are marked in red or green:

image


Debugging

If you create an F# Tutorial project you get both, an F# script file that runs as a console application allowing you to set breakpoints in your script with the Visual Studio debugger.

image


Prototyping

When I’m prototyping a new feature I typically use the F# interactive environment for quick feedback and to do exploratory testing. The TAP runner lets you create and run NUnit  formatted tests directly in the script file before later promoting to a full fat project for use in a continuous build environment.

F# Scripting

Interested in learning more about F# scripting, pop along to the Phil Nash’s talk at the F#unctional Londoners tonight.

Loan calculator

Yesterday I came across a handy loan payment calculator in C# by Jonathon Wood via Alvin Ashcraft’s Morning Dew links. The implementation appears to be idiomatic C# using a class, mutable properties and wrapped in a host console application to display the results.

I thought it’d be fun to spend a few moments re-implementing it in F# so it can be executed in F# interactive as a script or a console application.

Rather than use a class, I’ve plumped for a record type that captures all the required fields:

/// Loan record
type Loan = {
   /// The total purchase price of the item being paid for.
   PurchasePrice : decimal
   /// The total down payment towards the item being purchased.
   DownPayment : decimal
   /// The annual interest rate to be charged on the loan
   InterestRate : double
   /// The term of the loan in months. This is the number of months
   /// that payments will be made.
   TermMonths : int
   }

 

And for the calculation simply a function:

/// Calculates montly payment amount
let calculateMonthlyPayment (loan:Loan) =
   let monthsPerYear = 12
   let rate = (loan.InterestRate / double monthsPerYear) / 100.0
   let factor = rate + (rate / (Math.Pow(rate+1.,double loan.TermMonths) 1.))
   let amount = loan.PurchasePrice - loan.DownPayment
   let payment = amount * decimal factor
   Math.Round(payment,2)

 

We can test the function immediately in F# interactive

let loan = {
   PurchasePrice = 50000M
   DownPayment = 0M
   InterestRate = 6.0
   TermMonths = 5 * 12
   }

calculateMonthlyPayment loan

 

Then a test run (which produces the same results as the original code):

let displayLoanInformation (loan:Loan) =
   printfn "Purchase Price: %M" loan.PurchasePrice
   printfn "Down Payment: %M" loan.DownPayment
   printfn "Loan Amount: %M" (loan.PurchasePrice - loan.DownPayment)
   printfn "Annual Interest Rate: %f%%" loan.InterestRate
   printfn "Term: %d months" loan.TermMonths
   printfn "Monthly Payment: %f" (calculateMonthlyPayment loan)
   printfn ""

for i in 0M .. 1000M .. 10000M do
   let loan = { loan with DownPayment = i }
   displayLoanInformation loan

 

Another option is to simply skip the record and use arguments:

/// Calculates montly payment amount
let calculateMonthlyPayment(purchasePrice,downPayment,interestRate,months) =
   let monthsPerYear = 12
   let rate = (interestRate / double monthsPerYear) / 100.0
   let factor = rate + (rate / (Math.Pow(rate + 1.0, double months) - 1.0))
   let amount = purchasePrice - downPayment
   let payment = amount * decimal factor
   Math.Round(payment,2

Case for VB.Net vNext

Following up on the last Roslyn preview way back in 2012, this week saw the availability of a new preview with a more complete compiler along with a few new language features for C# and VB. A lot of inspiration for these features seems to have come from the F# language.

The C# interactive shell from 2012 appears to be missing, perhaps ScriptCS is expected to fill this space, or you could just use F# interactive which already exists in Visual Studio.

On the language features side, C# 6 gets primary constructors for classes, heavily inspired by F#, and using static which brings parity with Java and VB.Net.

For me VB.Net gets the most interesting new feature in the form of Select Case TypeOf. which provides the first steps towards pattern matching.

Shapes

Taking a hierarchy of shapes as an example:

Public MustInherit Class Shape
End Class

Public Class Rectangle
    Inherits Shape
    Public Property Width As Integer
    Public Property Height As Integer
End Class

Public Class Circle
    Inherits Shape
    Public Property Radius As Integer
End Class

Sub Main()
    Dim shape As Shape = New Rectangle With {.Width = 10, .Height = 10}
    Select Case shape
        Case r As Rectangle When r.Width = r.Height
            Console.WriteLine("Square of {0}", r.Width)
        Case r As Rectangle
            Console.WriteLine("Rectangle of {0},{1}", r.Width, r.Height)
        Case c As Circle
            Console.WriteLine("Circle of {0}", c.Radius)
    End Select
End Sub

The functionality is still quite limited and quite verbose in comparison to say F# or Scala, but I feel it’s definitely an interesting development for VB.Net.

For comparison here’s an equivalent F# version using discriminated unions:

type Shape =
    | Rectangle of width:int * height:int
    | Circle of radius:int

let shape = Rectangle(10,10)
match shape with
| Rectangle(w,h) when w=h -> printfn "Square %d" w
| Rectangle(w,h) -> printfn "Rectangle %d, %d" w h
| Circle(r) -> printfn "Circle %d" r

Eval

Pattern matching can be really useful when writing compilers, here’s a simple expression tree evaluator in F#:

type Expression =
   | Factor of value:int
   | Add of lhs:Expression * rhs:Expression

let rec eval e =
   match e with
   | Factor(x) -> x
   | Add(l,r) -> eval l + eval r

let onePlusOne = Add(Factor(1),Factor(1))

VB.Net vNext can approximate this, albeit in a rather more verbose way:

Public MustInherit Class Expression
End Class

Public Class Factor
    Inherits Expression
    Public Property Value As Integer
    Sub New(x As Integer)
        Value = x
    End Sub
End Class

Public Class Op
    Inherits Expression
    Public Property Lhs As Expression
    Public Property Rhs As Expression
End Class

Public Class Add
    Inherits Op
End Class

Function Eval(e As Expression) As Integer
    Select Case e
        Case x As Factor
            Return x.Value
        Case op As Add
            Return Eval(op.Lhs) + Eval(op.Rhs)
        Case Else
            Throw New InvalidOperationException
    End Select
End Function

Sub Main()
    Dim onePlusOne As Expression =
        New Add With {.Lhs = New Factor(1), .Rhs = New Factor(1)}
    Console.WriteLine(Eval(onePlusOne))
End Sub

Summary

It will be interesting to see how VB.Net vNext develops. I think first-class support for tuples could be an interesting next step for the language.