Phillip Trelford's Array

POKE 36879,255

MSDNify Types

Following on from yesterday’s post on Disinherited Types where I implement an F# type provider that hides inherited members of a type to let you focus on the useful members. The example was the WPF Button control which contains around 300 members via 9 levels of inheritance and appears to flout the object-oriented principle of composition over inheritance.

Microsoft’s MSDN documentation takes another approach to the problem of making relevant members discoverable by grouping members by type, i.e. all properties, all methods, all fields and all events:

Button class docs

MSDNify Type Provider

To give the same discoverable experience in the editor I’ve created the MSDNify Type Provider that groups members by type:

Button by member type

From the filtered member type groups you can select the member you are interested in:

Button events

This in effect mirrors the MSDN docs making it easier to find the useful members.

Source Code

The implementation is very similar to the Disinherited Type Provider discussed in yesterday’s post.

The code is available on GitHub: https://github.com/ptrelford/MSDNify

Disinherited Types

One of the things that’s always bugged me when using Windows UI libraries like WinForms and WPF is the sheer number of members that pop up in intellisense for a control. The Button type in WPF has around 300 members and a total of 9 levels of inheritance making it hard to find the useful members. Or to put it another way the inherited members occlude the useful members:

Button members

The picture above shows what you actually see in the code completion box in the editor, however for a button you’re probably more interested in the Click event, but that’s several pages away.

Disinherit Type Provider

As a thought experiment I’ve implemented an F# Type Provider that hides type members after a specified level of inheritance:

Disinherited button

In the picture above the members now almost fit in a single page. The last property, not visible here, is an extra property added by the type provider that provides all the hidden members for the instance:

Button instance

 

Type Provider Implementation

Underneath the covers the type provider takes an assembly name as a static parameter and optionally the level of inheritance to expose.

[<Literal>]
let name = @"PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
type WPF = Disinherited< name , level=1 >
let button = WPF.Button()

The provider then reflects over the types in the assembly and creates a proxy type as a provided type and exposes members that are declared within the specified inheritance level.

To provide the member we simply create a corresponding provided member with an expression that evaluates to the underlying type’s member:

    let addMember (def:ProvidedTypeDefinition) ty (mem:MemberInfo) =
        match mem.MemberType with
        | MemberTypes.Constructor ->
            let ci = mem :?> ConstructorInfo
            let c = ProvidedConstructor(toParams ci)
            c.InvokeCode <- fun args -> Expr.Coerce(Expr.NewObject(ci,args), typeof<obj>)
            def.AddMember(c)
        | MemberTypes.Field ->
            let fi = mem :?> FieldInfo
            let field = ProvidedField(mem.Name, fi.FieldType)
            def.AddMember(field)
        | MemberTypes.Property ->
            let pi = mem :?> PropertyInfo
            let prop = ProvidedProperty(mem.Name, pi.PropertyType) 
            prop.GetterCode <- fun args -> Expr.PropertyGet(Expr.Coerce(args.[0],ty), pi)
            def.AddMember(prop)
        | MemberTypes.Event ->
            let ei = mem :?> EventInfo
            let ev = ProvidedEvent(mem.Name, ei.EventHandlerType) 
            ev.AdderCode <- fun args -> Expr.Call(Expr.Coerce(args.Head,ty),ei.GetAddMethod(), args.Tail)
            ev.RemoverCode <- fun args -> Expr.Call(Expr.Coerce(args.Head,ty), ei.GetRemoveMethod(), args.Tail)
            def.AddMember(ev)
        | MemberTypes.Method ->
            let mi = mem :?> MethodInfo
            if not mi.IsSpecialName then
                let m = ProvidedMethod(mi.Name, toParams mi, mi.ReturnType)
                m.InvokeCode <- fun args -> Expr.Call(Expr.Coerce(args.Head,ty), mi, args.Tail)
                def.AddMember(m)
        | _ -> ()

The entire provider is implemented in around 100 lines of code, I’d be interested to hear if anybody else finds it useful :)

Source code

The source is available on GitHub: https://github.com/ptrelford/Disinherit

Calendar Types

Welcome to the 2015 F# Advent Calendar and one of 2 posts for December 3rd. For last year’s advent calendar I tried to follow the seasonal theme and produced an article on generating snowflakes. This year I thought I’d be more literal and look at producing calendar types using F#’s Type Provider mechanism resurrecting a project from 2014, FSharp.Date.

FSharp.Date

FSharp.Date is a simple F# Type Provider, inspired by VB.Net’s date literal feature,that lets you define dates and time values in F# by pressing dot and selecting only valid values:

2015 December 3rd

Further inspired by the advent calendar theme I’ve added a new feature that lets you visualize the calendar month as a tooltip in your editor:

2015 December Calendar

The source is available on BitBucket or you can download the package from Nuget.

But wait there’s more…

On This Day

That got me thinking, what if you could provide topical information on a particular day direct to the editor, and then I found the BBC news site On This Day.

The web site contains a set of both historically significant and quirky stories from the same day in the past.

First I needed a way of finding an article and scraping the news items from the page.

Thankfully the BBC pages use a uniform URL taking the month and day making it easy to get a specific page:

let getPage (month,day) =
    let date = System.DateTime(2005,month,day)    
    let monthName = date.ToString("MMMM").ToLower()
    let url = sprintf "http://news.bbc.co.uk/onthisday/low/dates/stories/%s/%d/default.stm"
                 monthName date.Day
    use client = new System.Net.WebClient()
    client.DownloadString(url)  

The page’s HTML is not well formed so I resorted to a regular expression to parse the news items:

let getNewsItems html =
    let pattern = """<a href="([^"]*)"><span class="h1">(.*?)</span></a><br>(.*?)<br clear="ALL">"""
    let matches = Regex.Matches(html, pattern, RegexOptions.Singleline)
    let newsItems = [for m in matches -> [for i in 1..m.Groups.Count-1 -> m.Groups.[i].Value]]
    [for newsItem in newsItems do
        match newsItem with
        | [link;title;description] ->
            yield "http://news.bbc.co.uk" + link, title.Trim(), description.Trim().Replace("\n","").Replace("\r","")
        | _ -> ()
    ]

Which returns a list of news items for the specified month and day:

> (12,3)|> getPage |> getNewsItems;;
val it : (string * string * string) list =
  [("http://news.bbc.co.uk/onthisday/low/dates/stories/december/3/newsid_2698000/2698709.stm",
    "1984: Hundreds die in Bhopal chemical accident",
    "A dense cloud of lethal gas escapes from a chemical factory in the central Indian city of Bhopal, killing hundreds of people.");
   ("http://news.bbc.co.uk/onthisday/low/dates/stories/december/3/newsid_2519000/2519715.stm",
    "1992: Bomb explosions in Manchester",
    "Emergency services are dealing with casualties at the scene of two bomb blasts in the centre of Manchester.");
   ("http://news.bbc.co.uk/onthisday/low/dates/stories/december/3/newsid_4119000/4119950.stm",
    "1989: Malta summit ends Cold War",
    "The leaders of the two world superpowers declare an end to the Cold War after two days of storm-lashed talks at the Malta summit.");
   ("http://news.bbc.co.uk/onthisday/low/dates/stories/december/3/newsid_4119000/4119070.stm",
    "1965: White jury convicts Ku Klux Klansmen",
    "For the first time an all-white jury convicts members of the KKK over the murder of a white civil rights activist Viola Liuzzo.");
   ("http://news.bbc.co.uk/onthisday/low/dates/stories/december/3/newsid_2519000/2519133.stm",
    "1971: Pakistan intensifies air raids on India",
    "India declares a state of emergency as  airports are hit during a Pakistani attack on the country.");
   ("http://news.bbc.co.uk/onthisday/low/dates/stories/december/3/newsid_2519000/2519451.stm",
    "1988: Egg industry fury over salmonella claim",
    "Claims by a health minister that eggs contain salmonella are branded alarmist and incorrect.")]
> 

Now to create a type provider.

Creating your own Type Provider

If you’re new to writing your own type provider I’d recommend starting with Michael Newton’s Type Providers From the Ground Up post.

First off we need to reference two F# files from the FSharp.TypeProviders.StarterPack.

Paket

Rather than make a static copy I used Paket, a handy .Net dependency management tool, to reference the files directly from GitHub.

This required a simple paket.dependencies file:

source https://nuget.org/api/v2

github fsprojects/FSharp.TypeProviders.StarterPack src/ProvidedTypes.fsi 
github fsprojects/FSharp.TypeProviders.StarterPack src/ProvidedTypes.fs 

and a paket.references file:

File:ProvidedTypes.fsi
File:ProvidedTypes.fs

With that in place all that’s left to do is run > paket install.

Defining the Type Provider

To create a type provider you need a type with the TypeProvider attribute that inherits from TypeProviderFromNamespaces. For this example I only need a single type OnThisDay which provides the news items via a set of static properties:

[<TypeProvider>]
type OnThisDayProvider (config:TypeProviderConfig) as this = 
   inherit TypeProviderForNamespaces ()

   let getProperties newsItems =
       [for (url, title, description) in newsItems ->
           let property = 
               ProvidedProperty(title, typeof<string>, IsStatic=true,
                  GetterCode=fun _ -> <@@ url @@>)
           property.AddXmlDoc(description)
           property]

   let ns = "FsAdvent"
   let asm = System.Reflection.Assembly.GetExecutingAssembly()
   let today = System.DateTime.Now
   let providedType = ProvidedTypeDefinition(asm, ns, "OnThisDay", Some typeof<obj>)
   do  providedType.AddXmlDoc(today.ToLongDateString())
   do  providedType.AddMembersDelayed(fun () ->             
            (today.Month,today.Day) |> getPage |> getNewsItems |> getProperties
       )
   do  this.AddNamespace(ns, [providedType])

Once it’s built you can reference the type provider and get a list of topical news items for the day directly in your editor:

Tunnel

The selected property returns the URL as the value, which you can easily launch with your default browser using Process.Start:

OnThisDay.``1995: Rogue trader jailed for six years``
|> System.Diagnostics.Process.Start

If you fancy a play the source code is available on GitHub: https://github.com/ptrelford/OnThisDay

Happy holidays!