TP

Library patterns Multiple levels of abstraction

Over the last few years, I created or contributed to a number of libraries including F# Data for data access, Deedle for exploratory data science with C# and F#, Markdown parser and code-formatter F# Formatting and other fun libraries such as one for composing 3D objects used in my Christmas blog post.

Building libraries is a fun and challenging task - even if we ignore all the additional things that you need to get right such as testing, documentation and building (see my earlier blog post) and focus just on the API design. In this blog post (or perhaps a series), I'll share some of the things I learned when trying to answer the question: What should a good library look like?

Library design patterns

I was recently watching Mark Seemann's course A Functional architecture with F#, which is a great material on designing functional applications. But I also realised that not much has been written on designing functional libraries. For some aspect, you can use functional patterns like monads (see Scott Wlaschin's presentation), but this only answers a part of the question - it tells you how to design individual types, but not an entire library.

The key library design principles that I find useful (and follow in libraries that I work on) are:

The concepts in the above list are related, but I think they are all key to good library design. They would each deserve a separate article (which may happen if there is enough interest :-)), but I'm going to focus on levels of abstractions first...

How are libraries used?

Each library is designed with some typical scenarios in mind. For example, F# Formatting lets you generate documentation from all files in a directory. This is all you need in, perhaps, 80% of scenarios. Sometimes, you need to handle individual files differently (say, use a different template). A good library needs to make this possible too. But sometimes, you actually want to process a file differently - for example, add an automatically generated TOC (table of contents).

An approach that I find extremely useful is to build a library that exposes the functionality as multiple layers of abstraction. At the highest level, you can handle the 80% of scenarios with just a single function call. If you need, you can go one level deeper and implement the next 15% of more interesting scenarios. And if you really need, you can go even deeper and implement the next 4% of scenarios (and for the last 1%, you have to send a pull request!)

As you'll see, this design pattern is heavily used in core functional libraries such as the F# library for working with lists. Following this pattern well also turns your libraries into domain specific languages and makes the code more readable.

Demo #1: Working with functional lists

The idea of having multiple levels of abstraction can be easily demonstrated using lists.

High-level: Higher-order functions

At the high level, you can process lists using higher-order functions (or using LINQ in C#). As a (somewhat) silly example, if we want to get a list of positive sin values of integers from 0 to 100, we can write:

1: 
2: 
3: 
[0 .. 100]
|> List.map (float >> sin)
|> List.filter (fun n -> n > 0.0)

Higher-order functions like List.map and List.filter cover the 80% of scenarios when working with lists (or perhaps even more). But sometimes you may want to do something that is not covered by the higher-order functions.

Low-level: Recursion and patter matching

For example, say we want to split a list into two, around the point where the sign of the values in the list changes. That is splitAtSignChange [1; 4; -3; 2] should return [1; 4] and [-3; 2]. This is not easy to do using higher-order functions, but we can use the lower-level API and write a recursive function that pattern matches on the list:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
/// Split a list at the point where the 
/// sign of adjacent elements changes
let inline splitAtSignChange list =
  // Keeps the last element and elements visited so
  // far (before the sign change) in a reversed list
  let rec loop last acc = function
    | [] -> List.rev acc, []
    | first::tail as list when sign first <> sign last -> 
        List.rev acc, list
    | first::tail -> loop first (first::acc) tail

  // Use the first element as 'last' argument of 'loop'
  match list with
  | [] -> [], []
  | head::tail -> loop head [head] tail

The function is more difficult to understand than the code we can write with higher-order functions, but it is still quite readable. Inside loop, there are 3 cases that handle the situations when 1.) all elements have the same sign 2.) we find a sign change and 3.) we skip over element before a sign change.

If you were writing this in C# using IEnumerable<T>, you don't get nice pattern matching on lists, but you still get some lower-level API (using GetEnumerator, temporary collections and mutation).

Going from low-level to high-level

The beautiful thing about the collection API design is that we can now move back from the lower level to the higher level. The function splitAtSignChange can be seen as an instance of a more general operation where we split a list based on a predicate applied to two adjacent elements.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
module List =
  /// Splits a list into two when the predicate 'f'
  /// returns 'true' on two adjacent elements of the list
  let splitAt f list =
    // Same as before with 'f first last' in the second case
    let rec loop last acc = function
      | [] -> List.rev acc, []
      | first::tail as list when f last first -> 
          List.rev acc, list
      | first::tail -> loop first (first::acc) tail

    // Use the first element as 'last' argument of 'loop'
    match list with
    | [] -> [], []
    | head::tail -> loop head [head] tail

The function is pretty much the same as before - it takes an additional parameter f and uses it to decide when to break the list. Although the function is itself implemented using the lower-level API, it gives us a way to get back to the higher-level!

Getting back to the higher level means that we can recover the much simpler programming style. Say, we wanted to take sin values of integers from 1 to 10 and split them into two lists, based on when the value crosses the X axis:

1: 
2: 
3: 
[1 .. 10]
|> List.map (float >> sin)
|> List.splitAt (fun a b -> sign a <> sign b)

Now we are back at the high-level and we can solve the problem using two simple and easy to understand lines of code.

Functional lists with their low-level pattern matching and high-level functions are a nice example of a library with multiple levels of abstractions. But how does this work in other scenarios?

Non-Example: Reactive Extensions

One of the things I always found quite unfortunate about the Rx library is that it fails this principle. It provides a nice high-level abstraction (through the standard LINQ operators), but it does not give you any convenient way to implement your own operators using a lower level API. You can see this when you look at the standard Select operator, which is 125 lines of code (compare this with List.map in the F# library, which is 16 lines and could be 3 if it was not optimized using mutation).

Of course, Rx is solving significantly more complicated task than the List module, but that is not the point - the point is that Select should be easy to implement using some lower-level abstraction. What should this look like? In F#, this would probably be done using agents or library such as Hopac.

Demo #2: Domain-specific languages for 3D

In a blog post that I wrote for the F# Advent Calendar, I used a library for composing 3D objects. This is another good example of a library that provides multiple levels of abstraction - especially when you use it to model custom objects.

Very-high-level: Building castles

For example, have a look at what you can achieve with the following snippet:

1: 
2: 
3: 
4: 
tower -2.0 -2.0 $ tower 2.0 -2.0 $ 
tower -2.0 2.0 $ tower 2.0 2.0 $
wall -2.0 true $ wall 2.0 true $
wall -2.0 false $ wall 2.0 false

At the highest level of abstraction, we can create a castle with just 4 lines of code! Of course, this level of abstraction lets you create only very limited things (castles with walls and towers), but if you are a castle-builder, this might be what you need...

Domain-specific languages

The term "domain-specific languages" (DSLs) means different things to different people. For me, a library like this is an (embedded) DSL, because it lets you express your intentions in a very readable way using just a vocabulary of the specific domain. In a sense, the higher levels of abstraction of any composable functional library should always be domain-specific languages.

High-level: Composing 3D objects

Now, what if we wanted to build a castle with different towers? To do that, we still don't need to look at any low-level 3D rendering code. The tower function itself is written in terms of another "language" or "level of abstraction". A tower is just a coloured cylinder, composed with a coloured cone that are appropriately rotated:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
let tower x z = 
  (Fun.cylinder
     |> Fun.scale (1.0, 1.0, 3.0) 
     |> Fun.translate (0.0, 0.0, 1.0)
     |> Fun.color Color.DarkGoldenrod ) $ 
  (Fun.cone 
     |> Fun.scale (1.3, 1.3, 1.3) 
     |> Fun.translate (0.0, 0.0, -1.0)
     |> Fun.color Color.Red )
  |> Fun.rotate (90.0, 0.0, 0.0)
  |> Fun.translate (x, 0.5, z)

When using the library, you might start at the very high level of abstraction (solving specific narrow problems), but as you become more familiar with it, you can move one level down. At this level, you can build your own languages (abstractions) in the same way as when we implemented List.splitAt in terms of recursion and pattern matching.

Low-level: Rendering faces with OpenGL

Now, there is still a lower level at which the actual 3D rendering happens. In this library, the lower level is simply calling OpenGL primitives (using the OpenTK wrapper), and so this is not particularly interesting, but it is not very complicated either:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
let cube = DF (fun ctx ->
  GL.Material
    ( MaterialFace.FrontAndBack, 
      MaterialParameter.Diffuse, ctx.Color)
  GL.Begin(BeginMode.Quads)
  GLEx.Face 
    (-1.f, 0.f, 0.f) 
    [ (-0.5f, -0.5f, -0.5f); (-0.5f, -0.5f,  0.5f); 
      (-0.5f,  0.5f,  0.5f);  (-0.5f,  0.5f, -0.5f) ] 
  (Remaining 5 faces omitted)
  GL.End() )

If we wanted to make it easier to create custom basic shapes, we could certainly add another layer between the 3D primitives and rendering, but this was not the aim here. Even without that, you can see that having multiple levels of abstraction has important benefits. The most obvious one is that you can easily move from a higher level to a lower level to explore how things are created (say, how tower is constructed from cone and cylinder) and use the lower-level primitives in a different way.

In other words, if you implemented tower as a function that directly calls OpenGL, you could hardly provide enough overloads (and optional parameters) to capture all possible kinds of towers that someone might want to create. But if your library has multiple levels of abstraction, this is not a problem.

Demo #3: Documentation generation

The examples I discussed so far may look like special libraries - one was a core functional library with just two levels and another was specifically designed as a domain-specific language. To convince you that this approach is valuable in general, I'll look at one more example, this time from the F# Formatting library.

The library consists of a Markdown parser and F# code formatter (and is used by most F# projects around). The most common scenario for using the library is to turn a folder with Markdown files and F# Script files into an output folder with generated HTML documentation. This is what the ProjectScaffold template does.

High-level: Processing directories

So assuming webHome is a folder with Markdown files (like the F# Foundation guides), we can generate HTML documentation just by calling Literate.ProcessDirectory:

1: 
2: 
3: 
4: 
Literate.ProcessDirectory
  ( inputDirectory=webHome, 
    outputDirectory=webOutput, 
    processRecursive=true )

This is the high-level version of the API provided by the library and also the code that you'll need to write in 80% of cases. The ProcessDirectory has a number of arguments that let you specify how the processing is done (such as a template file, F# Compiler instance for colorization of snippets etc.)

However, it does not let you customize which files in the directory are processed and it does not let you perform any transformations on the individual files. For that, we need to look at the lower-level API.

Medium-level: Processing individual files

For an example, let's say that we don't want to look at Markdown files in a local folder, but instead we want to crawl the guides directory of the F# Foundation page directly from GitHub. To do this, we'll use the GitHub API (to get a list of folders and files in them). Using the JSON type provider, we start with a function that returns the result of GitHub contents query:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
open FSharp.Data

/// Type representing returned JSON, inferred from a sample
type GitHubDir = JsonProvider<"../data/repos.json">

/// Query the contents of a specified 'dir' in a 'repo'
let queryDir repo dir =
  Http.RequestString
    ( sprintf "%s/repos/%s/contents/%s?anon=1" api repo dir,
      headers = [HttpRequestHeaders.UserAgent "My App"] )
  |> GitHubDir.Parse

Now, we can use queryDir to get all sub-directories in the "guides" folder and then get all the files under each guide. We can then process each file using the medium-level Literate.ProcessMarkdown function:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
// Iterate over all Markdown guides on fsharp.org
let wc = new System.Net.WebClient()
let guides = queryDir "fsharp/fsfoundation" "guides"
for subdir in guides do
  let files = queryDir "fsharp/fsfoundation" ("guides/" + subdir.Name)
  for file in files do
    // Downloda the guide to a local Temp file
    let tempFile = Path.GetTempFileName()
    wc.DownloadFile(root + subdir.Name + "/" + file.Name, tempFile)
    let dir = Path.Combine(webOutput, subdir.Name)
    Directory.CreateDirectory(dir) |> ignore
    // Process the guide using medium-level API
    let out = Path.ChangeExtension(Path.Combine(dir, file.Name), ".html")
    Literate.ProcessMarkdown(tempFile, output=out)

So far, we replaced the default behaviour for processing directories with our own custom behaviour for crawling documents on GitHub. This looks quite simple - which is the point of the library! But imagine some alternative approaches that you could use here.

Non-example: Abstracting directories with interfaces

If you were solving this problem in an object-oriented style, you might try to design an interface that models "directory structure browser" or something like that. The Literate.ProcessDirectory method would then take IDirectoryBrowser.

Your IDirectoryBrowser would have methods GetSubDirectories and GetFiles and default implementation walking over file structure. You could then implement it differently using the GitHub API.

The problem with this is that it inverts the control - rather than being a library that you can call to do things you want, it becomes a framework that dictates what you need to provide. And if you need something else, then you're out of luck! For example, if you suddenly wanted to process documents that do not naturally fit into a tree structure, the IDirectoryBrowser abstraction would break. With a library that provides multiple levels of abstraction, this is not a problem, because you are in control.

So far, we looked at processing whole directories and individual files, but there is one more level we can look at...

Low-level: Transforming documents

Now, let's say that we wanted to perform some transformation on the documents as part of the processing. If you're using something like Jekyll (on GitHub), then the way to do this is to use Jekyll plugins. There are four kinds of plugins (generators, converters, commands and tags) - so, no matter what you're doing, it will have to fit into one of the patterns (interfaces) that Jekyll developers expect you to use.

In F# Formatting, we again avoid using inversion of control caused by interfaces (or plugins). Instead, you can look one level deeper and access the data structure that represents the parsed Markdown and perform some transformation on the structure.

Let's say that we are currently processing a file guide. Rather than using Literate.ProcessMarkdown, we can call Literate.ParseMarkdownFile, which gives us the result of parsing (first of the two steps that processing does):

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
// Parse the Literate Markdown file
let doc = Literate.ParseMarkdownFile(guide)

/// Helper function that collects headers
let rec collectHeaders = function
  | Heading(level, content) -> [level, content]
  | Matching.ParagraphNested(_, pars) ->
      pars |> List.concat |> List.collect collectHeaders
  | _ -> []

// Returns a list of headings with their level
doc.Paragraphs |> List.collect collectHeaders

The low level of the API is now starting to look similar to the list processing function in the first example. We get the paragraphs of the document as a value of the MarkdownParagraphs type and we can pattern match on it. The pattern matching uses active patterns, so we do not need to handle all possible kinds of paragraphs, but just the one we are interested in (Heading) and one that represents nesting (you can find more about this pattern in Chapter 3 of F# Deep Dives).

Now that we have the headings, we can generate new Markdown paragraphs representing the <ol> element with the table of contents and create a new document that includes the TOC as an additional part of the body:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
/// Generates Markdown nodes with the TOC
let generateToc items = 
  /// Split the given list of 'items' into the first node,
  /// its children (following items with larger level) and 
  /// process the remaining items recursively.
  let rec getChildren acc items = 
    (Implementation hidden)
  getChildren [] items

// Generate new Markdown content
// (collect all headers & generate TOC)
let tocPars = 
  doc.Paragraphs 
  |> List.collect collectHeaders
  |> generateToc

// Create document with additional paragraphs
// (for the TOC) and format it as HTML
doc.With(List.append tocPars doc.Paragraphs)
|> Literate.WriteHtml

Just like in the example with processing lists, we could now identify a more general pattern and provide a higher-level function that encapsulates the behaviour. But having access to the lower-level API means that we are not limited to a couple of functions that the library developers thought of! Instead, we can always switch to the lower-level API when there is something we want to do that is not directly exposed at the higher-level.

Summary

In this article, I talked about one of the design patterns that I find useful when designing libraries in functional style. The idea of the pattern is quite simple.

Levels of abstraction pattern

In your library, you should provide high-level functions that handle 80% of the tasks that your users need. For the next 15% of tasks, the library should provide a lower-level API. The key point is that the high-level API should be easy to express in terms of the lower-level API. This means that one should be able to "unroll" the single higher-level call into code that composes a couple of lower-level calls and customize the code to do whatever is needed. Then, you should be able to "fold" the lower-level code back into a new reusable higher-level operation.

I demonstrated this using three examples - when working with functional lists, you can "unroll" higher-order functions to recursive processing and pattern matching; when composing 3D graphics, you can "unroll" concepts such as tower or wall into primitive objects like cubes. Finally, the documentation generation library F# Formatting lets you do the same thing. You can "unroll" code that processes entire folder into calls to process individual files - and even further, you can "unroll" processing and insert custom code between the parsing and rendering phases.

Benefits of the pattern

Why would you design libraries in this way? I discussed some of the advantages along the way - but the key point is that libraries with multiple levels of abstraction make the most common 80% of tasks really easy. But they also do not restrict the remaining tasks to what the author of the library could imagine when designing it. Instead, you are giving the library users the power to use it in interesting ways and customize it to their needs.

In case of F# Formatting, this already paid off - the high-level API is used by many F# open-source projects through ProjectScaffold, but the low-level API provides enough flexibility for amazing tools like FsReveal.

namespace System
namespace System.Drawing
module Functional3D
namespace OpenTK
namespace OpenTK.Graphics
namespace OpenTK.Graphics.OpenGL
val tower : x:float -> z:float -> Drawing3D

Full name: Library-layers.tower
val x : float
val z : float
module Fun

from Functional3D
val cylinder : Drawing3D

Full name: Functional3D.Fun.cylinder


 Generates a 3D cylinder object of a unit size
val scale : x:float * y:float * z:float -> Drawing3D -> Drawing3D

Full name: Functional3D.Fun.scale


 Scale the specified 3D object by the specified scales along the 3 axes
val translate : x:float * y:float * z:float -> Drawing3D -> Drawing3D

Full name: Functional3D.Fun.translate


 Move the specified object by the provided offsets
val color : clr:Color -> Drawing3D -> Drawing3D

Full name: Functional3D.Fun.color


 Set color to be used when drawing the specified 3D objects
type Color =
  struct
    member A : byte
    member B : byte
    member Equals : obj:obj -> bool
    member G : byte
    member GetBrightness : unit -> float32
    member GetHashCode : unit -> int
    member GetHue : unit -> float32
    member GetSaturation : unit -> float32
    member IsEmpty : bool
    member IsKnownColor : bool
    ...
  end

Full name: System.Drawing.Color
property Color.DarkGoldenrod: Color
val cone : Drawing3D

Full name: Functional3D.Fun.cone


 Generate the sphere
 Generates a 3D cylinder object of a unit size
property Color.Red: Color
val rotate : x:float * y:float * z:float -> Drawing3D -> Drawing3D

Full name: Functional3D.Fun.rotate


 Scale the specified 3D object by the specified scales along the 3 axes
val sizedCube : height:float -> Drawing3D

Full name: Library-layers.sizedCube
val height : float
val cube : Drawing3D

Full name: Functional3D.Fun.cube


 Creates a 3D cube of unit size using the current color
val twoCubes : Drawing3D

Full name: Library-layers.twoCubes
val block : Drawing3D

Full name: Library-layers.block
val offset : float
module Seq

from Microsoft.FSharp.Collections
val reduce : reduction:('T -> 'T -> 'T) -> source:seq<'T> -> 'T

Full name: Microsoft.FSharp.Collections.Seq.reduce
property Color.DarkGray: Color
val wall : offs:float -> rotate:bool -> Drawing3D

Full name: Library-layers.wall
val offs : float
val rotate : bool
val rotationArg : float * float * float
val translationArg : float * float * float
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Literate
namespace FSharp.Markdown
namespace System.IO
val guide : string

Full name: Library-layers.guide
val webHome : string

Full name: Library-layers.webHome
val webOutput : string

Full name: Library-layers.webOutput
val root : string

Full name: Library-layers.root
val api : string

Full name: Library-layers.api
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val map : mapping:('T -> 'U) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.map
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
val sin : value:'T -> 'T (requires member Sin)

Full name: Microsoft.FSharp.Core.Operators.sin
val filter : predicate:('T -> bool) -> list:'T list -> 'T list

Full name: Microsoft.FSharp.Collections.List.filter
val n : float
val splitAtSignChange : list:'a list -> 'a list * 'a list (requires member get_Sign)

Full name: Library-layers.splitAtSignChange


 Split a list at the point where the
 sign of adjacent elements changes
Multiple items
val list : 'a list (requires member get_Sign)

--------------------
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
val loop : ('a -> 'a list -> 'a list -> 'a list * 'a list) (requires member get_Sign)
val last : 'a (requires member get_Sign)
val acc : 'a list (requires member get_Sign)
val rev : list:'T list -> 'T list

Full name: Microsoft.FSharp.Collections.List.rev
val first : 'a (requires member get_Sign)
val tail : 'a list (requires member get_Sign)
val sign : value:'T -> int (requires member get_Sign)

Full name: Microsoft.FSharp.Core.Operators.sign
val head : 'a (requires member get_Sign)
val splitAt : f:('a -> 'a -> bool) -> list:'a list -> 'a list * 'a list

Full name: Library-layers.List.splitAt


 Splits a list into two when the predicate 'f'
 returns 'true' on two adjacent elements of the list
val f : ('a -> 'a -> bool)
Multiple items
val list : 'a list

--------------------
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
val loop : ('a -> 'a list -> 'a list -> 'a list * 'a list)
val last : 'a
val acc : 'a list
val first : 'a
val tail : 'a list
val head : 'a
Multiple items
module List

from Library-layers

--------------------
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
Multiple items
val splitAt : f:('a -> 'a -> bool) -> list:'a list -> 'a list * 'a list

Full name: Library-layers.List.splitAt


 Splits a list into two when the predicate 'f'
 returns 'true' on two adjacent elements of the list


--------------------
val splitAt : index:int -> list:'T list -> 'T list * 'T list

Full name: Microsoft.FSharp.Collections.List.splitAt
val a : float
val b : float
val cube : Drawing3D

Full name: Library-layers.cube
union case Drawing3D.DF: (Drawing3DContext -> unit) -> Drawing3D
val ctx : Drawing3DContext
Multiple items
type GL =
  inherit GraphicsBindingsBase
  new : unit -> GL
  static member Accum : op:AccumOp * value:float32 -> unit
  static member ActiveShaderProgram : pipeline:int * program:int -> unit + 1 overload
  static member ActiveTexture : texture:TextureUnit -> unit
  static member AlphaFunc : func:AlphaFunction * ref:float32 -> unit
  static member AreTexturesResident : n:int * textures:int[] * residences:bool[] -> bool + 5 overloads
  static member ArrayElement : i:int -> unit
  static member AttachShader : program:int * shader:int -> unit + 1 overload
  static member Begin : mode:BeginMode -> unit + 1 overload
  static member BeginConditionalRender : id:int * mode:ConditionalRenderType -> unit + 1 overload
  ...
  nested type Amd
  nested type Apple
  nested type Arb
  nested type Ati
  nested type Ext
  nested type GL_3dfx
  nested type Gremedy
  nested type HP
  nested type Ibm
  nested type Ingr
  nested type Intel
  nested type Khr
  nested type Mesa
  nested type NV
  nested type Nvx
  nested type Oes
  nested type Pgi
  nested type Sgi
  nested type Sgis
  nested type Sgix
  nested type Sun
  nested type Sunx

Full name: OpenTK.Graphics.OpenGL.GL

--------------------
GL() : unit
GL.Material(face: MaterialFace, pname: MaterialParameter, params: Color4) : unit
GL.Material(face: MaterialFace, pname: MaterialParameter, params: Vector4) : unit
GL.Material(face: MaterialFace, pname: MaterialParameter, params: nativeptr<int>) : unit
GL.Material(face: MaterialFace, pname: MaterialParameter, params: int []) : unit
GL.Material(face: MaterialFace, pname: MaterialParameter, param: int) : unit
GL.Material(face: MaterialFace, pname: MaterialParameter, params: nativeptr<float32>) : unit
GL.Material(face: MaterialFace, pname: MaterialParameter, params: float32 []) : unit
GL.Material(face: MaterialFace, pname: MaterialParameter, param: float32) : unit
type MaterialFace =
  | Front = 1028
  | Back = 1029
  | FrontAndBack = 1032

Full name: OpenTK.Graphics.OpenGL.MaterialFace
field MaterialFace.FrontAndBack = 1032
type MaterialParameter =
  | Ambient = 4608
  | Diffuse = 4609
  | Specular = 4610
  | Emission = 5632
  | Shininess = 5633
  | AmbientAndDiffuse = 5634
  | ColorIndexes = 5635

Full name: OpenTK.Graphics.OpenGL.MaterialParameter
field MaterialParameter.Diffuse = 4609
Drawing3DContext.Color: Color4
GL.Begin(mode: PrimitiveType) : unit
type BeginMode =
  | Points = 0
  | Lines = 1
  | LineLoop = 2
  | LineStrip = 3
  | Triangles = 4
  | TriangleStrip = 5
  | TriangleFan = 6
  | Quads = 7
  | QuadStrip = 8
  | Polygon = 9
  ...

Full name: OpenTK.Graphics.OpenGL.BeginMode
field BeginMode.Quads = 7
type GLEx =
  static member Face : x:float32 * y:float32 * z:float32 -> vertices:seq<float32 * float32 * float32> -> unit
  static member Vertices : vertices:seq<float32 * float32 * float32> -> unit

Full name: Functional3D.GLEx
static member GLEx.Face : x:float32 * y:float32 * z:float32 -> vertices:seq<float32 * float32 * float32> -> unit


 Add mesh to the GL and set the specified normal vector first
GLEx.Face
    ( 1.f, 0.f, 0.f)
    [ ( 0.5f, -0.5f, -0.5f); ( 0.5f, -0.5f, 0.5f);
      ( 0.5f, 0.5f, 0.5f); ( 0.5f, 0.5f, -0.5f) ]
  GLEx.Face
    (0.f, -1.f, 0.f)
    [ (-0.5f, -0.5f, -0.5f); (-0.5f, -0.5f, 0.5f);
      ( 0.5f, -0.5f, 0.5f); ( 0.5f, -0.5f, -0.5f) ]
  GLEx.Face
    (0.f, 1.f, 0.f)
    [ (-0.5f, 0.5f, -0.5f); (-0.5f, 0.5f, 0.5f);
      ( 0.5f, 0.5f, 0.5f); ( 0.5f, 0.5f, -0.5f) ]
  GLEx.Face
    (0.f, 0.f, -1.f)
    [ (-0.5f, -0.5f, -0.5f); (-0.5f, 0.5f, -0.5f);
      ( 0.5f, 0.5f, -0.5f); ( 0.5f, -0.5f, -0.5f) ]
  GLEx.Face
    (0.f, 0.f, 1.f)
    [ (-0.5f, -0.5f, 0.5f); (-0.5f, 0.5f, 0.5f);
      ( 0.5f, 0.5f, 0.5f); ( 0.5f, -0.5f, 0.5f) ]
GL.End() : unit
type Literate =
  private new : unit -> Literate
  static member FormatLiterateNodes : doc:LiterateDocument * ?format:OutputKind * ?prefix:string * ?lineNumbers:bool * ?generateAnchors:bool -> LiterateDocument
  static member ParseMarkdownFile : path:string * ?formatAgent:CodeFormatAgent * ?compilerOptions:string * ?definedSymbols:#seq<string> * ?references:bool * ?fsiEvaluator:IFsiEvaluator -> LiterateDocument
  static member ParseMarkdownString : content:string * ?path:string * ?formatAgent:CodeFormatAgent * ?compilerOptions:string * ?definedSymbols:#seq<string> * ?references:bool * ?fsiEvaluator:IFsiEvaluator -> LiterateDocument
  static member ParseScriptFile : path:string * ?formatAgent:CodeFormatAgent * ?compilerOptions:string * ?definedSymbols:#seq<string> * ?references:bool * ?fsiEvaluator:IFsiEvaluator -> LiterateDocument
  static member ParseScriptString : content:string * ?path:string * ?formatAgent:CodeFormatAgent * ?compilerOptions:string * ?definedSymbols:#seq<string> * ?references:bool * ?fsiEvaluator:IFsiEvaluator -> LiterateDocument
  static member ProcessDirectory : inputDirectory:string * ?templateFile:string * ?outputDirectory:string * ?format:OutputKind * ?formatAgent:CodeFormatAgent * ?prefix:string * ?compilerOptions:string * ?lineNumbers:bool * ?references:bool * ?fsiEvaluator:IFsiEvaluator * ?replacements:(string * string) list * ?includeSource:bool * ?layoutRoots:string list * ?generateAnchors:bool * ?assemblyReferences:string list * ?processRecursive:bool -> unit
  static member ProcessMarkdown : input:string * ?templateFile:string * ?output:string * ?format:OutputKind * ?formatAgent:CodeFormatAgent * ?prefix:string * ?compilerOptions:string * ?lineNumbers:bool * ?references:bool * ?replacements:(string * string) list * ?includeSource:bool * ?layoutRoots:string list * ?generateAnchors:bool * ?assemblyReferences:string list -> unit
  static member ProcessScriptFile : input:string * ?templateFile:string * ?output:string * ?format:OutputKind * ?formatAgent:CodeFormatAgent * ?prefix:string * ?compilerOptions:string * ?lineNumbers:bool * ?references:bool * ?fsiEvaluator:IFsiEvaluator * ?replacements:(string * string) list * ?includeSource:bool * ?layoutRoots:string list * ?generateAnchors:bool * ?assemblyReferences:string list -> unit
  static member WriteHtml : doc:LiterateDocument * ?prefix:string * ?lineNumbers:bool * ?generateAnchors:bool -> string
  ...

Full name: FSharp.Literate.Literate
static member Literate.ProcessDirectory : inputDirectory:string * ?templateFile:string * ?outputDirectory:string * ?format:OutputKind * ?formatAgent:FSharp.CodeFormat.CodeFormatAgent * ?prefix:string * ?compilerOptions:string * ?lineNumbers:bool * ?references:bool * ?fsiEvaluator:IFsiEvaluator * ?replacements:(string * string) list * ?includeSource:bool * ?layoutRoots:string list * ?generateAnchors:bool * ?assemblyReferences:string list * ?processRecursive:bool -> unit
namespace Microsoft.FSharp.Data
type GitHubDir = obj

Full name: Library-layers.GitHubDir


 Type representing returned JSON, inferred from a sample
val queryDir : repo:'a -> dir:'b -> 'c

Full name: Library-layers.queryDir


 Query the contents of a specified 'dir' in a 'repo'
val repo : 'a
val dir : 'b
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val wc : Net.WebClient

Full name: Library-layers.wc
namespace System.Net
Multiple items
type WebClient =
  inherit Component
  new : unit -> WebClient
  member BaseAddress : string with get, set
  member CachePolicy : RequestCachePolicy with get, set
  member CancelAsync : unit -> unit
  member Credentials : ICredentials with get, set
  member DownloadData : address:string -> byte[] + 1 overload
  member DownloadDataAsync : address:Uri -> unit + 1 overload
  member DownloadFile : address:string * fileName:string -> unit + 1 overload
  member DownloadFileAsync : address:Uri * fileName:string -> unit + 1 overload
  member DownloadString : address:string -> string + 1 overload
  ...

Full name: System.Net.WebClient

--------------------
Net.WebClient() : unit
val guides : seq<obj>

Full name: Library-layers.guides
val subdir : obj
val files : seq<obj>
val file : obj
val tempFile : string
type Path =
  static val DirectorySeparatorChar : char
  static val AltDirectorySeparatorChar : char
  static val VolumeSeparatorChar : char
  static val InvalidPathChars : char[]
  static val PathSeparator : char
  static member ChangeExtension : path:string * extension:string -> string
  static member Combine : [<ParamArray>] paths:string[] -> string + 3 overloads
  static member GetDirectoryName : path:string -> string
  static member GetExtension : path:string -> string
  static member GetFileName : path:string -> string
  ...

Full name: System.IO.Path
Path.GetTempFileName() : string
Net.WebClient.DownloadFile(address: Uri, fileName: string) : unit
Net.WebClient.DownloadFile(address: string, fileName: string) : unit
val dir : string
Path.Combine([<ParamArray>] paths: string []) : string
Path.Combine(path1: string, path2: string) : string
Path.Combine(path1: string, path2: string, path3: string) : string
Path.Combine(path1: string, path2: string, path3: string, path4: string) : string
type Directory =
  static member CreateDirectory : path:string -> DirectoryInfo + 1 overload
  static member Delete : path:string -> unit + 1 overload
  static member EnumerateDirectories : path:string -> IEnumerable<string> + 2 overloads
  static member EnumerateFileSystemEntries : path:string -> IEnumerable<string> + 2 overloads
  static member EnumerateFiles : path:string -> IEnumerable<string> + 2 overloads
  static member Exists : path:string -> bool
  static member GetAccessControl : path:string -> DirectorySecurity + 1 overload
  static member GetCreationTime : path:string -> DateTime
  static member GetCreationTimeUtc : path:string -> DateTime
  static member GetCurrentDirectory : unit -> string
  ...

Full name: System.IO.Directory
Directory.CreateDirectory(path: string) : DirectoryInfo
Directory.CreateDirectory(path: string, directorySecurity: Security.AccessControl.DirectorySecurity) : DirectoryInfo
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
val out : string
Path.ChangeExtension(path: string, extension: string) : string
static member Literate.ProcessMarkdown : input:string * ?templateFile:string * ?output:string * ?format:OutputKind * ?formatAgent:FSharp.CodeFormat.CodeFormatAgent * ?prefix:string * ?compilerOptions:string * ?lineNumbers:bool * ?references:bool * ?replacements:(string * string) list * ?includeSource:bool * ?layoutRoots:string list * ?generateAnchors:bool * ?assemblyReferences:string list -> unit
val doc : LiterateDocument

Full name: Library-layers.doc
static member Literate.ParseMarkdownFile : path:string * ?formatAgent:FSharp.CodeFormat.CodeFormatAgent * ?compilerOptions:string * ?definedSymbols:#seq<string> * ?references:bool * ?fsiEvaluator:IFsiEvaluator -> LiterateDocument
val collectHeaders : _arg1:MarkdownParagraph -> (int * MarkdownSpans) list

Full name: Library-layers.collectHeaders


 Helper function that collects headers
union case MarkdownParagraph.Heading: int * MarkdownSpans -> MarkdownParagraph
val level : int
val content : MarkdownSpans
Multiple items
module Matching

from FSharp.Markdown

--------------------
module Matching

from FSharp.Literate
Multiple items
val ParagraphNested : Matching.ParagraphNestedInfo * pars:MarkdownParagraphs list -> MarkdownParagraph

Full name: FSharp.Markdown.Matching.ParagraphNested

--------------------
active recognizer ParagraphNested: MarkdownParagraph -> Choice<Matching.ParagraphLeafInfo,(Matching.ParagraphNestedInfo * MarkdownParagraphs list),(Matching.ParagraphSpansInfo * MarkdownSpans)>

Full name: FSharp.Markdown.Matching.( |ParagraphLeaf|ParagraphNested|ParagraphSpans| )
val pars : MarkdownParagraphs list
val concat : lists:seq<'T list> -> 'T list

Full name: Microsoft.FSharp.Collections.List.concat
val collect : mapping:('T -> 'U list) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.collect
property LiterateDocument.Paragraphs: MarkdownParagraphs
val generateToc : items:('a * MarkdownSpans) list -> MarkdownParagraph list (requires comparison)

Full name: Library-layers.generateToc


 Generates Markdown nodes with the TOC
val items : ('a * MarkdownSpans) list (requires comparison)
val getChildren : (MarkdownParagraph list -> ('b * MarkdownSpans) list -> MarkdownParagraph list) (requires comparison)


 Split the given list of 'items' into the first node,
 its children (following items with larger level) and
 process the remaining items recursively.
val acc : MarkdownParagraph list
val items : ('b * MarkdownSpans) list (requires comparison)
match items with
    | [] -> List.rev acc
    | [_, leaf] -> [Span leaf]
    | ((level, this)::rest) as items ->
        let nested, other =
          rest |> List.splitAt (fun _ (next, _) -> next <= level)
        let sub = getChildren [] nested
        let par = [ListBlock(Ordered, sub |> List.map (fun s -> [s])); Span this]
        getChildren (List.append par acc) other
val tocPars : MarkdownParagraph list

Full name: Library-layers.tocPars
member LiterateDocument.With : ?paragraphs:MarkdownParagraphs * ?formattedTips:string * ?definedLinks:Collections.Generic.IDictionary<string,(string * string option)> * ?source:LiterateSource * ?sourceFile:string * ?errors:seq<FSharp.CodeFormat.SourceError> -> LiterateDocument
val append : list1:'T list -> list2:'T list -> 'T list

Full name: Microsoft.FSharp.Collections.List.append
static member Literate.WriteHtml : doc:LiterateDocument * ?prefix:string * ?lineNumbers:bool * ?generateAnchors:bool -> string
static member Literate.WriteHtml : doc:LiterateDocument * writer:TextWriter * ?prefix:string * ?lineNumbers:bool * ?generateAnchors:bool -> unit

Published: Tuesday, 3 February 2015, 4:54 PM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: f#, open source, functional programming