-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Recovered Diego's posts from https://web.archive.org/web/201205080312…
…13/http://www.intellifactory.com/blogs/diego.echeverri/ Interestingly enough, these were not migrated to the blog DB back in the days.
- Loading branch information
Showing
3 changed files
with
323 additions
and
0 deletions.
There are no files selected for viewing
123 changes: 123 additions & 0 deletions
123
user/diego.echeverri/20100125-data-visualization-with-websharper.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
--- | ||
title: "Introducing WIG patterns" | ||
categories: "f#,websharper,web,googlevisualization" | ||
abstract: "In this post I'll show you how to make a nice visualization similar to the ones used by Hans Rosling. For this, we'll use the WebSharper Google Visualization bindings available as an extension package to the core platform." | ||
identity: "-1,-1" | ||
--- | ||
Some time ago, I was looking through some of TED videos and I stumbled upon a really nice presentation by Hans Rosling about AIDS. After that initial video I went through [all his videos](https://www.ted.com/search?q=Hans+Rosling). It's surprising how much difference it makes to have the right visualization. | ||
|
||
In this post I'll show you how to make a nice visualization similar to the ones used by Hans Rosling. For this, we'll use the WebSharper Google Visualization bindings available as an extension package to the core platform. | ||
|
||
## Getting the data | ||
|
||
For this example, I'll plot some population information taken from the population division of the UN[^1]. And to make things easy, I edited the information as an array of tuples. In principle, you could just use any source for the data. For example you could call the database asynchronously, use an Xml file or a web-service. It doesn't really matter. Just to illustrate how my hard-coded data looks like: | ||
|
||
```fsharp | ||
let arrayData = [| | ||
("Colombia",1950,12000) | ||
("Colombia",1955,13828) | ||
("Colombia",1960,16006) | ||
... | ||
("Ukraine",1995,51063) | ||
("Ukraine",2000,48870) | ||
("Ukraine",2005,46936) |] | ||
``` | ||
|
||
We now have to wrap the data inside a `DataTable` object. This one is defined inside the `IntelliFactory.WebSharper.Google.Visualization.Base` module. Keeping in mind that these are just bindings for the Google Visualization API, we should check [the documentation](https://developers.google.com/chart/interactive/docs/gallery/motionchart?csw=1)[^2] to see what's the expected format. | ||
|
||
* The first column must be of type `string` and contain the entity names (e.g., `"Apples"`, `"Oranges"`, `"Bananas"` in the example above). | ||
|
||
* The second column must contain time values. Time can be expressed in any of the following formats: | ||
|
||
``` | ||
* Year - Column type: 'number'. Example: 2008. | ||
... | ||
``` | ||
* Subsequent columns can be of type `number` or `string`. Number columns will show up in the dropdown menus for X, Y, Color and Size axes. String columns will only appear in the dropdown menu for Color. | ||
## Wrapping the data inside the DataTable | ||
With the previous information about the format expected by the `MotionChart` we can proceed to add the columns: | ||
```fsharp | ||
let data = new Base.DataTable() | ||
data.addColumn("string", "Country") |> ignore | ||
data.addColumn("number", "Year") |> ignore | ||
data.addColumn("number", "Population") |> ignore | ||
``` | ||
|
||
To add the data we just iterate through the collection with the `addRow` method. The order of every cell should correspond to the order on which we declared the columns. | ||
|
||
```fsharp | ||
let V: obj -> Cell = Cell.Value | ||
for (c, y, p) in arrayData do | ||
data.addRow [|V c; V y; V p|] |> ignore | ||
``` | ||
|
||
## Creating the Visualization | ||
|
||
The following function creates the visualization: | ||
|
||
```fsharp | ||
let MotionChart () = | ||
Div [] | ||
|> On Events.Attach (fun container _ -> | ||
let visualization = new Visualizations.MotionChart(container) | ||
let options = {Visualizations.MotionChartOptions.Default | ||
with width = 600. | ||
height = 300.} | ||
visualization.draw(MotionChartData, options)) | ||
``` | ||
|
||
There are a couple of things to remember: | ||
|
||
* First, it is necessary to create the visualization using the `Attach` event. The reason is that some of libraries that we rely on, need that the element is already attached to the DOM. | ||
|
||
* Every visualization receives a configuration object. If you check the API's documentation you'll notice that they use simple object literals. In our case we created record types to simulate those options. This gives us the object literal syntax and strong typing. All configuration options are called `(VisualizationName)Options`. | ||
|
||
You can isolate the element and create an ASP.NET control to display the visualization. For example: | ||
|
||
```fsharp | ||
[<JavaScriptType>] | ||
type BlogMotionChart() = | ||
inherit Web.Control() | ||
[<JavaScript>] | ||
override this.Body = Samples.BlogMotionChart() | ||
``` | ||
|
||
## Playing with the Visualization | ||
|
||
The cool thing about the visualization that I chose to show is that it contains different "Modes". The first mode is the one that Hans Rosling uses: | ||
|
||
> **Review note** <br /> | ||
> This resource needs to be re-added. | ||
>  | ||
This one allows you to see the behavior of three variables through time (x, y axis + size of the sphere). In our case it isn't really helpful because we just have the population variable. | ||
|
||
The second visualization uses a histogram. This one is a bit more helpful in our case: | ||
|
||
> **Review note** <br /> | ||
> This resource needs to be re-added. | ||
>  | ||
And finally a simple line diagram from the data: | ||
|
||
> **Review note** <br /> | ||
> This resource needs to be re-added. | ||
>  | ||
## Conclusions | ||
|
||
We can start to throw hypotheses about the behaviour of our data. For example: What happened to Ukraine in the nineties? There's a big drop in the population. Was it related to the [hyperinflationary levels](https://en.wikipedia.org/wiki/Ukraine#Economy)? According to [this page](https://jamestown.org/program/census-ukraine-more-ukrainian/): | ||
|
||
"The acute socioeconomic crisis of the 1990s discouraged families from having children, especially in urban areas, something that affected Russians and Russian speakers more than others." | ||
|
||
I really don't have any idea if there was really a relation between the two variables but it sounds plausible. The important thing here is how easy it was to make this visualization. There are a lot of possibilities in business intelligence, scientific visualizations, creating reports... you name it. Go on and give it a try! | ||
|
||
|
||
[^1]: Original link is dead and was removed. | ||
|
||
[^2]: Link is pointing to the latest version. |
112 changes: 112 additions & 0 deletions
112
user/diego.echeverri/20101007-starting-with-websharper-interface-generator.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
--- | ||
title: "Starting with WebSharper Interface Generator" | ||
categories: "f#,websharper,wig" | ||
abstract: "WIG is an F# DSL and source-level translator for creating bindings for 3rd party libraries." | ||
identity: "-1,-1" | ||
--- | ||
The fact is that there is already plenty of awesome JavaScript libraries. In the spirit of reusability, WebSharper allows you to create wraps around third party libraries just as is shown in a [previous blog entry](/user/granicz/20100930-tutorial-implementing-a-shopping-cart-with-websharper.md). Sometimes though, the library you want to create wraps for contains hundreds of classes, configuration objects and enumerations. Doing this manually is not only error-prone but extremely boring. For this reason we created the WebSharper Interface Generator (WIG)), soon to be available a standalone tool to complement the WebSharper 2.0 toolset. WIG is an F# DSL and source-level translator for creating bindings for 3rd party libraries. WIG provides you with: | ||
|
||
* Common patterns in wrap creation (Enumerations, Configuration objects) | ||
* Concise syntax (There's 1/5-1/8 ratio between the generated code and the WIG code) | ||
* Programmatic code generation (Generate code directly from the documentation) | ||
* No back-reference problems that you may encounter doing the bindings manually. | ||
|
||
## A Simple Example: HTML5 WebStorage Bindings | ||
|
||
One of the proposed features for HTML5 is an [API for persistent data of key-value pair data in Web clients](https://html.spec.whatwg.org/multipage/webstorage.html)[^1]. The main interface in the current draft is the following: | ||
|
||
``` | ||
interface Storage { | ||
readonly attribute unsigned long length; | ||
getter DOMString key(in unsigned long index); | ||
getter any getItem(in DOMString key); | ||
setter creator void setItem(in DOMString key, in any value); | ||
deleter void removeItem(in DOMString key); | ||
void clear(); | ||
}; | ||
``` | ||
|
||
The instance implementing this interface is available through `window.localStorage`. | ||
|
||
## Defining the class | ||
|
||
Let's check how the code for it would look like in WIG: | ||
|
||
```fsharp | ||
module LocalStorage = | ||
let Storage = | ||
let Storage = Class "Storage" | ||
Storage | ||
|+> ["localStorage" =? Storage |> WithGetterInline "localStorage"] | ||
|+> Protocol [ | ||
"length" =? T<int> | ||
"key" => T<int> ^-> T<string> | ||
"getItem" => T<string> ^-> T<string> | ||
"setItem" => T<string> * T<obj> ^-> T<unit> | ||
"removeItem" => T<string> ^-> T<unit> | ||
"clear" => T<unit> ^-> T<unit> | ||
] | ||
``` | ||
|
||
The first thing you do is creating an empty class called `Storage`. The name you give to that class is the name that will be used in the generated code. In this case this name is not really important since this class doesn't have any public constructor. The first thing you'll notice is the operator `|+>`. What it does is that it changes the current class and adds the given members. Its first usage provides the class with a static getter for the `localStorage` object. The `=?` operator creates a getter with the given name and the given type. You can override its translation by using `WithGetterInline`. The second part uses `Protocol` for creating a list of instance members that will be added to the object. Method members are created by using the `=>` operator. The types of the method members can be written by using the already existing types. | ||
|
||
* Product types (i.e tuples) can be written using the `*` operator | ||
* Function types can be written by using the `^->` operator | ||
* Already existing types can be used with the `T` function. | ||
* WIG also supports operators to create overloads, optional arguments and multi-arg functions. | ||
|
||
|
||
## Adding the class to a Namespace | ||
|
||
WIG provides you with the tools to select how you will organize the generated code. | ||
|
||
```fsharp | ||
module Main = | ||
let Assembly = | ||
Assembly [ | ||
Namespace "IntelliFactory.WebSharper.HTML5" [ | ||
LocalStorage.Storage | ||
] | ||
] | ||
do Compiler.Compile stdout Assembly | ||
``` | ||
|
||
First you create a single `Assembly` with a single `Namespace`. In this `Namespace` you'll have the `Storage` class. Having that defined is a matter of "compiling" the `Assembly` to generate the code. | ||
|
||
|
||
## Using the generated code | ||
|
||
Having generated and compiled the code. It is possible to reference the dll in any of your WebSharper projects. Let's see how a simple Storage example looks like: | ||
|
||
```fsharp | ||
[<JavaScriptAttribute>] | ||
let LocalStorage () = | ||
Div [ | ||
H1 [Text "LocalStorage Test"] | ||
] | ||
|>! OnAfterRender (fun elem -> | ||
let storage = Storage.LocalStorage | ||
let key = "intReference" | ||
let intReference = storage.GetItem(key) | ||
if intReference = null || intReference = Undefined then | ||
storage.SetItem(key, "0") | ||
else | ||
let oldValue = int (intReference) | ||
storage.SetItem(key, (oldValue + 1).ToString()) | ||
elem.Html <- elem.Html | ||
+ "<h2>localStorage is now: (" | ||
+ storage.GetItem(key) + ")</h2>") | ||
``` | ||
|
||
The interesting part starts in the line #7. You access the members of the class just like any other WebSharper class. One of the cool things that WIG does for you is changing the member names in order to comply with .NET naming conventions. | ||
|
||
|
||
## Conclusions | ||
|
||
This is a small real-world example of how WIG helps you to simplify the generation of stubs for 3th party libraries. Keep in mind that this was just a simple example. Given enough time it would be possible to do things like: | ||
|
||
* Create an IDL parser to do part of the dirty work for you. | ||
* Scrape the WebStorage draft website to check whether the current bindings are up-to-date. | ||
|
||
|
||
[^1]: Link updated to the latest version. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
--- | ||
title: "Introducing WIG patterns" | ||
categories: "f#,websharper,dsl,wig" | ||
abstract: "" | ||
identity: "-1,-1" | ||
--- | ||
In my last [post](/user/diego.echeverri/20101007-starting-with-websharper-interface-generator.md) you learned how to use WIG to create bindings for 3rd party libraries. We introduced type combinators to express functions and member operators to define properties and methods. Although this is already an improvement over defining the bindings manually, it is not the only way of using WIG. | ||
|
||
## Configuration Objects | ||
|
||
A common pattern in JavaScript libraries is using configuration objects. In JavaScript it is very common use the object literal syntax. One example may be the [following](https://developers.google.com/maps/documentation/javascript/reference?csw=1#MapOptions)[^1]: | ||
|
||
```javascript | ||
var myOptions = { | ||
zoom: 8, | ||
center: myLatlng, | ||
mapTypeId: google.maps.MapTypeId.ROADMAP | ||
}; | ||
``` | ||
|
||
Most of the time, some properties are marked as "Required" (like the `center` property), and some are just optional properties. WIG provides you with a special function to make bindings for such cases. | ||
|
||
```fsharp | ||
let MapOptions = | ||
Pattern.Config "MapOptions" | ||
{ | ||
Optional = | ||
[ | ||
"disableDefaultUI", T<bool> | ||
// (...) | ||
] | ||
Required = | ||
[ | ||
// The initial Map center. Required. | ||
"center", LatLng.Type | ||
// (...) | ||
] | ||
} | ||
``` | ||
|
||
This will generate a new class, where each of the optional members will get translated as getters and setters, and each of the required ones will become parameters in the constructor. This will save you from forgetting to set any of the required attributes, a tangible benefit over using plain JavaScript. | ||
|
||
## Enumerations | ||
|
||
Another common pattern is using enumeration-like types. Here is an example from the Google Maps API that demonstrates this: | ||
|
||
``` | ||
visibility | string | Visibility: Valid values: 'on', 'off' or 'simplifed'. | ||
``` | ||
|
||
In WIG it is possible to generate types for those strings by using the following handy pattern: | ||
|
||
```fsharp | ||
let Visibility = | ||
Pattern.EnumStrings "Visibility" ["on"; "off"; "simplified"] | ||
``` | ||
|
||
This will avoid tedious debugging sessions to discover any mistyped strings, and instead will provide you with IntelliSense to discover the possible values. | ||
|
||
## Building Your Own Patterns | ||
|
||
All these patterns have been built using the class construct covered in my last post. This means that is possible to create your own patterns. For example, say you want an Enumeration pattern to cover integers instead of strings. It's possible to write something like: | ||
|
||
```fsharp | ||
let EnumInts n l = | ||
List.map (fun (x, i) -> (x, sprintf "%d" i)) l | ||
|> List.toSeq | ||
|> Pattern.EnumInlines n | ||
``` | ||
|
||
Where `EnumInlines` works in a similar way to `EnumStrings` but receives an arbitrary string that will be inlined. It can be used in the following way: | ||
|
||
```fsharp | ||
let MediaError = | ||
Utils.EnumInts "MediaError" [ | ||
"MEDIA_ERR_ABORTED", 1 | ||
"MEDIA_ERR_NETWORK", 2 | ||
"MEDIA_ERR_DECODE", 3 | ||
"MEDIA_ERR_SRC_NOT_SUPPORTED", 4 | ||
] | ||
``` | ||
|
||
## Conclusions | ||
|
||
WIG not only provides you with common patterns but it is also extensible. It allows you to define new patterns so you don't have to repeat yourself. This reduces the effort necessary to bind libraries significantly, and also allows you to reuse metabindings across libraries. | ||
|
||
|
||
[^1]: Link is updated to the latest documentation. |