objects () |> functions


Tres - Step 3

We'll start out, as we did with Dos, with adding the RethinkDB dependency to project.json's dependencies section:

1: 
"RethinkDb.Driver": "2.3.15"

dotnet restore installs it as usual.

Configurable Connection

Since Tres is more-or-less a C#-to-F# conversion from Dos, we'll use the same data-config.json file, in the root of the project:

1: 
2: 
3: 
4: 
{
  "Hostname": "my-rethinkdb-server",
  "Database": "O2F3"
}

We'll also add it to project.json, just below the list of files to compile:

1: 
2: 
3: 
"copyToOutput": {
  "include": "data-config.json"
}

Now, we'll create a file Data.fs to hold what we had in the Data directory in the prior two solutions. It should be added to the build order after Entities.fs and before HomeModule.fs. We'll start this file off with our DataConfig implementation:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
namespace Tres

open Newtonsoft.Json
open RethinkDb.Driver
open RethinkDb.Driver.Ast
open RethinkDb.Driver.Net

type DataConfig =
  { Hostname : string
    Port : int
    AuthKey : string
    Timeout : int
    Database : string
    }
with
  member this.CreateConnection () : IConnection =
    let bldr =
      seq<Connection.Builder -> Connection.Builder> {
        yield fun builder -> match this.Hostname with null -> builder | host -> builder.Hostname host
        yield fun builder -> match this.Port with 0 -> builder | port -> builder.Port port
        yield fun builder -> match this.AuthKey with null -> builder | key -> builder.AuthKey key
        yield fun builder -> match this.Database with null -> builder | db -> builder.Db db
        yield fun builder -> match this.Timeout with 0 -> builder | timeout -> builder.Timeout timeout
        }
      |> Seq.fold (fun builder block -> block builder) (RethinkDB.R.Connection())
    upcast bldr.Connect()
  static member FromJson json = JsonConvert.DeserializeObject<DataConfig> json

This should be familiar at this point; we're using a record type instead of a class, and the CreateConnection function utilizes the sequence style from Dos, just inlined as a computation expression (more on those in a bit). We also see Seq.fold, which takes the parameters in pretty much the opposite order of LINQ's Aggregate; instead of [collection].Aggregate([initial-state], [folder-func]), it's Seq.fold [folder-func] [initial-state] [collection] (which we're piping in with the |> operator).

The upcast is new. Notice that CreateConnection is typed as IConnection; what's returned from the connection builder is a Connection. In most cases, F# requires an implementation to be explicitly cast to the interface it is claiming to implement. In our case, we want IConnection (vs. IDisposable, which it also implements). There are two ways to do this; if the type can be inferred, as it can be here (because we've explicitly said what our return type should be), you can use upcast. Alternately, the last line of that function could read (bldr.Connect()) :> IConnection. (I tend to prefer upcast when possible.)

At this point, we need to take a detour through the land of asynchronous processing. Uno and Dos both used async/await to perform the RethinkDB calls, utilizing the Task-based async introduced in .NET 4.5. F#'s approach to asynchrony is different, but there are a few functions that provide the interoperability we need. F# also uses an async computation expression to construct these. The most important difference, for our purposes here, is that F# Async instances are not automatically started the way a Task is in the C# world.

And, a quick detour from the detour - I promised there would be more on computation expressions. These are expressions that utilize an expression builder to declaratively create workflows within code. They typically operate in a specialized context; we've seen seq, we're about to see async, but there are many other uses as well. Within a computation expression, let and do have their same familiar behavior, and return returns a value; however, let!, do!, and return! call into the builder to manipulate the specialized context. A complete education in computation expressions is outside of our scope; F# for Fun and Profit has an excellent series on them.

Before we see our first async computation expression, though, we need to make it be able to handle Tasks as well as F# async. The code for Extensions.fs is below. I won't delve too far into it at this point, but trust that what it does is let us say let! x = someTask and it works just like it was F# async. (This code is in the AutoOpened module Tres.Extensions.)

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
  open System.Threading.Tasks

  // H/T: Suave
  type AsyncBuilder with
    /// An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on
    /// a standard .NET task
    member x.Bind(t : Task<'T>, f:'T -> Async<'R>) : Async<'R> = async.Bind (Async.AwaitTask t, f)

    /// An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on
    /// a standard .NET task which does not commpute a value
    member x.Bind(t : Task, f : unit -> Async<'R>) : Async<'R> = async.Bind (Async.AwaitTask t, f)
    
    member x.ReturnFrom(t : Task<'T>) : Async<'T> = Async.AwaitTask t

Add Extensions.fs to the build order at the top. With those in place, we are now ready to return to Data.fs and our startup code. Before we move on, review the startup code from Dos; it has a main driver method at the top, with several methods below to perform each step. Our F# code structure will be somewhat inverted from this; as you generally cannot call forward into a source file, the "driver" code will be the last code in the function, and the other code above it.

The Table.cs static class with constants is brought over as a module (below the data configuration code). The RequireQualifiedAccess attribute means that Table cannot be opened; this prevents us from possibly providing an unintended version of the identifier Post (for example).

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
[<RequireQualifiedAccess>]
module Table =
  let Category = "Category"
  let Comment = "Comment"
  let Page = "Page"
  let Post = "Post"
  let User = "User"
  let WebLog = "WebLog"

Extensions are defined differently in F#. Whereas C# uses a static class and methods where the first parameter has this before it, F# uses a module for the definitions (usually AutoOpened, so they're visible when the enclosing namespace is opened), and a type declaration. Here's the top of ours (below the table module):

1: 
2: 
3: 
4: 
5: 
[<AutoOpen>]
module DataExtensions =
  type IConnection with
    member this.EstablishEnvironment database =
      // more to come...

Rather than go through the entire file, let's just look at a representative example. Here is the code to check for table existence in C#:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
private static async Task CheckTables(this IConnection conn)
{
    var existing = await R.TableList().RunResultAsync<List<string>>(conn);
    var tables = new List<string>
    {
        Table.Category, Table.Comment, Table.Page, Table.Post, Table.User, Table.WebLog
    };
    foreach (var table in tables)
    {
        if (!existing.Contains(table)) { await R.TableCreate(table).RunResultAsync(conn); }
    }
}

Now, here's what it looks like in F#:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
      let checkTables () =
        async {
          let! existing = r.TableList().RunResultAsync<string list> this
          [ Table.Category; Table.Comment; Table.Page; Table.Post; Table.User; Table.WebLog ]
          |> List.filter (fun tbl -> not (existing |> List.contains tbl))
          |> List.map (fun tbl -> async { do! r.TableCreate(tbl).RunResultAsync this })
          |> List.iter Async.RunSynchronously
        }

The more interesting differences:

  • In C#, existing is awaited; in F#, we use let! within the async computation expression to accomplish the same thing.
  • In C#, we defined a List<string> that we filled with our table names; in F#, we inlined a string list.
  • In C#, we have a nice imperative loop that iterates over each table, checks to see whether it is in the list of tables from the server, and creates it if it is not. In F#, we declare that the list should be filtered to only names not occurring in the list (List.filter); then that each of those names should be turned into an Async that will create the table when it's run (List.map); then, that the list should be iterated, passing each item into Async.RunSynchronously (List.iter).
  • In C#, the return type of the method is Task; in F#, the type of checkTables is Async<unit>.
  • When the C# CheckTables method call returns, the work has already been done. When the F# checkTables function returns, it returns an async workflow that is ready to be started.

The last 5 lines of the EstablishEnvironment extension method look like this:

1: 
2: 
3: 
4: 
5: 
      async {
        do! checkDatabase database
        do! checkTables ()
        do! checkIndexes ()
      }

There are a few interesting observations here as well:

  • do! is the equivalent of let!, except that we don't care about the result.
  • As we saw above, checkTables returns an async workflow; yet, we're do!ing it in yet another async workflow; this is perfectly acceptable. If you've ever added async/await to a C# application, usually at a lower layer, and noticed how async and await bubble up to the top layer - that's a similar concept to what we have here.
  • EstablishEnvironment's return type is Async<unit>. It still hasn't run anything at this point; it has merely assembled an asynchronous workflow that will do all of our environment checks once it is run.

Dependency Injection

We'll do the same thing we did for Dos - override DefaultNancyBootstrapper and register our connection there. We'll do all of this in App.fs. The first part, above the definition for type Startup():

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
type TresBootstrapper() =
  inherit DefaultNancyBootstrapper()
  
  override this.ConfigureApplicationContainer container =
    base.ConfigureApplicationContainer container
    let cfg = DataConfig.FromJson (File.ReadAllText "data-config.json")
    let conn = cfg.CreateConnection ()
    conn.EstablishEnvironment cfg.Database |> Async.RunSynchronously
    container.Register<IConnection> conn |> ignore

Ah ha! There's where we finally run our async workflow! Now, again, we need to modify Startup (just below where we put this code) to use this new bootstrapper.

1: 
2: 
  member this.Configure (app : IApplicationBuilder) =
    app.UseOwin (fun x -> x.UseNancy (fun opt -> opt.Bootstrapper <- new TresBootstrapper()) |> ignore) |> ignore

At this point, once dotnet run displays the "listening on port 5000" message, we should be able to look at RethinkDB's O2F3 database, tables, and indexes, just as we could for Uno and Dos.

Back to Step 3

namespace Newtonsoft
namespace Newtonsoft.Json
namespace RethinkDb
namespace RethinkDb.Driver
namespace RethinkDb.Driver.Ast
namespace RethinkDb.Driver.Net
type DataConfig =
  {Hostname: string;
   Port: int;
   AuthKey: string;
   Timeout: int;
   Database: string;}
  member CreateConnection : unit -> IConnection
  static member FromJson : json:string -> DataConfig

Full name: Tres.DataConfig
DataConfig.Hostname: string
Multiple items
val string : value:'T -> string

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

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
DataConfig.Port: int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

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

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
DataConfig.AuthKey: string
DataConfig.Timeout: int
DataConfig.Database: string
val this : DataConfig
member DataConfig.CreateConnection : unit -> IConnection

Full name: Tres.DataConfig.CreateConnection
type IConnection =
  member RunAsync<'T> : term:ReqlAst * globalOpts:obj * cancelToken:CancellationToken -> Task<obj>
  member RunAtomAsync<'T> : term:ReqlAst * globalOpts:obj * cancelToken:CancellationToken -> Task<'T>
  member RunCursorAsync<'T> : term:ReqlAst * globalOpts:obj * cancelToken:CancellationToken -> Task<Cursor<'T>>
  member RunNoReply : term:ReqlAst * globalOpts:obj -> unit
  member RunResultAsync<'T> : term:ReqlAst * globalOpts:obj * cancelToken:CancellationToken -> Task<'T>

Full name: RethinkDb.Driver.Net.IConnection
val bldr : Connection.Builder
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

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

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
type Connection =
  member CheckOpen : unit -> unit
  member ClientEndPoint : IPEndPoint
  member Close : ?shouldNoReplyWait:bool -> unit
  member Db : string
  member Dispose : unit -> unit
  member HasError : bool
  member Hostname : string
  member NoReplyWait : unit -> unit
  member NoReplyWaitAsync : ?cancelToken:CancellationToken -> Task
  member Open : bool
  ...
  nested type Builder

Full name: RethinkDb.Driver.Net.Connection
type Builder =
  new : unit -> Builder
  member AuthKey : key:string -> Builder
  member Connect : unit -> Connection
  member ConnectAsync : unit -> Task<Connection>
  member Db : val:string -> Builder
  member Hostname : val:string -> Builder
  member Port : val:int -> Builder
  member Timeout : val:int -> Builder
  member User : user:string * password:string -> Builder

Full name: RethinkDb.Driver.Net.Connection.Builder
val builder : Connection.Builder
val host : string
Connection.Builder.Hostname(val: string) : Connection.Builder
val port : int
Connection.Builder.Port(val: int) : Connection.Builder
val key : string
Connection.Builder.AuthKey(key: string) : Connection.Builder
val db : string
Connection.Builder.Db(val: string) : Connection.Builder
val timeout : int
Connection.Builder.Timeout(val: int) : Connection.Builder
module Seq

from Microsoft.FSharp.Collections
val fold : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> 'State

Full name: Microsoft.FSharp.Collections.Seq.fold
val block : (Connection.Builder -> Connection.Builder)
Multiple items
type RethinkDB =
  inherit TopLevel
  new : unit -> RethinkDB
  member Connection : unit -> Builder
  member ConnectionPool : unit -> Builder
  static val R : RethinkDB

Full name: RethinkDb.Driver.RethinkDB

--------------------
RethinkDB() : unit
field RethinkDB.R
RethinkDB.Connection() : Connection.Builder
Connection.Builder.Connect() : Connection
static member DataConfig.FromJson : json:string -> DataConfig

Full name: Tres.DataConfig.FromJson
val json : string
type JsonConvert =
  static val True : string
  static val False : string
  static val Null : string
  static val Undefined : string
  static val PositiveInfinity : string
  static val NegativeInfinity : string
  static val NaN : string
  static member DefaultSettings : Func<JsonSerializerSettings> with get, set
  static member DeserializeAnonymousType<'T> : value:string * anonymousTypeObject:'T -> 'T + 1 overload
  static member DeserializeObject : value:string -> obj + 7 overloads
  ...

Full name: Newtonsoft.Json.JsonConvert
JsonConvert.DeserializeObject<'T>(value: string) : 'T
JsonConvert.DeserializeObject(value: string) : obj
JsonConvert.DeserializeObject<'T>(value: string, settings: JsonSerializerSettings) : 'T
JsonConvert.DeserializeObject<'T>(value: string, [<System.ParamArray>] converters: JsonConverter []) : 'T
JsonConvert.DeserializeObject(value: string, type: System.Type) : obj
JsonConvert.DeserializeObject(value: string, settings: JsonSerializerSettings) : obj
JsonConvert.DeserializeObject(value: string, type: System.Type, settings: JsonSerializerSettings) : obj
JsonConvert.DeserializeObject(value: string, type: System.Type, [<System.ParamArray>] converters: JsonConverter []) : obj
Multiple items
type AutoOpenAttribute =
  inherit Attribute
  new : unit -> AutoOpenAttribute
  new : path:string -> AutoOpenAttribute
  member Path : string

Full name: Microsoft.FSharp.Core.AutoOpenAttribute

--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
namespace System
namespace System.Threading
namespace System.Threading.Tasks
type AsyncBuilder =
  private new : unit -> AsyncBuilder
  member Bind : computation:Async<'T> * binder:('T -> Async<'U>) -> Async<'U>
  member Combine : computation1:Async<unit> * computation2:Async<'T> -> Async<'T>
  member Delay : generator:(unit -> Async<'T>) -> Async<'T>
  member For : sequence:seq<'T> * body:('T -> Async<unit>) -> Async<unit>
  member Return : value:'T -> Async<'T>
  member ReturnFrom : computation:Async<'T> -> Async<'T>
  member TryFinally : computation:Async<'T> * compensation:(unit -> unit) -> Async<'T>
  member TryWith : computation:Async<'T> * catchHandler:(exn -> Async<'T>) -> Async<'T>
  member Using : resource:'T * binder:('T -> Async<'U>) -> Async<'U> (requires 'T :> IDisposable)
  ...

Full name: Microsoft.FSharp.Control.AsyncBuilder
val x : AsyncBuilder
member AsyncBuilder.Bind : t:Task<'T> * f:('T -> Async<'R>) -> Async<'R>

Full name: Tres.ExampleExtensions.Bind


 An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on
 a standard .NET task
val t : Task<'T>
Multiple items
type Task =
  new : action:Action -> Task + 7 overloads
  member AsyncState : obj
  member ContinueWith : continuationAction:Action<Task> -> Task + 9 overloads
  member CreationOptions : TaskCreationOptions
  member Dispose : unit -> unit
  member Exception : AggregateException
  member Id : int
  member IsCanceled : bool
  member IsCompleted : bool
  member IsFaulted : bool
  ...

Full name: System.Threading.Tasks.Task

--------------------
type Task<'TResult> =
  inherit Task
  new : function:Func<'TResult> -> Task<'TResult> + 7 overloads
  member ContinueWith : continuationAction:Action<Task<'TResult>> -> Task + 9 overloads
  member Result : 'TResult with get, set
  static member Factory : TaskFactory<'TResult>

Full name: System.Threading.Tasks.Task<_>

--------------------
Task(action: System.Action) : unit
Task(action: System.Action, cancellationToken: System.Threading.CancellationToken) : unit
Task(action: System.Action, creationOptions: TaskCreationOptions) : unit
Task(action: System.Action<obj>, state: obj) : unit
Task(action: System.Action, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : unit
Task(action: System.Action<obj>, state: obj, cancellationToken: System.Threading.CancellationToken) : unit
Task(action: System.Action<obj>, state: obj, creationOptions: TaskCreationOptions) : unit
Task(action: System.Action<obj>, state: obj, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : unit

--------------------
Task(function: System.Func<'TResult>) : unit
Task(function: System.Func<'TResult>, cancellationToken: System.Threading.CancellationToken) : unit
Task(function: System.Func<'TResult>, creationOptions: TaskCreationOptions) : unit
Task(function: System.Func<obj,'TResult>, state: obj) : unit
Task(function: System.Func<'TResult>, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : unit
Task(function: System.Func<obj,'TResult>, state: obj, cancellationToken: System.Threading.CancellationToken) : unit
Task(function: System.Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : unit
Task(function: System.Func<obj,'TResult>, state: obj, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : unit
val f : ('T -> Async<'R>)
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task -> Async<unit>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
member AsyncBuilder.Bind : t:Task * f:(unit -> Async<'R>) -> Async<'R>


 An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on
 a standard .NET task which does not commpute a value

member AsyncBuilder.Bind : t:Task<'T> * f:('T -> Async<'R>) -> Async<'R>


 An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on
 a standard .NET task

member AsyncBuilder.Bind : computation:Async<'T> * binder:('T -> Async<'U>) -> Async<'U>
static member Async.AwaitTask : task:Task -> Async<unit>
static member Async.AwaitTask : task:Task<'T> -> Async<'T>
member AsyncBuilder.Bind : t:Task * f:(unit -> Async<'R>) -> Async<'R>

Full name: Tres.ExampleExtensions.Bind


 An extension method that overloads the standard 'Bind' of the 'async' builder. The new overload awaits on
 a standard .NET task which does not commpute a value
val t : Task
val f : (unit -> Async<'R>)
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
member AsyncBuilder.ReturnFrom : t:Task<'T> -> Async<'T>

Full name: Tres.ExampleExtensions.ReturnFrom
Multiple items
type RequireQualifiedAccessAttribute =
  inherit Attribute
  new : unit -> RequireQualifiedAccessAttribute

Full name: Microsoft.FSharp.Core.RequireQualifiedAccessAttribute

--------------------
new : unit -> RequireQualifiedAccessAttribute
Multiple items
type Table =
  inherit ReqlExpr
  new : arg:obj -> Table + 2 overloads
  member Config : unit -> Config
  member Get : expr:obj -> Get
  member GetAll : [<ParamArray>] exprs:obj[] -> GetAll + 2 overloads
  member GetIntersecting : expr:obj -> GetIntersecting
  member GetNearest : expr:obj -> GetNearest
  member Grant : expr:obj * exprA:obj -> Grant
  member IndexCreate : expr:obj -> IndexCreate + 4 overloads
  member IndexDrop : expr:obj -> IndexDrop
  member IndexList : unit -> IndexList
  ...

Full name: RethinkDb.Driver.Ast.Table

--------------------
Table(arg: obj) : unit
Table(args: Model.Arguments) : unit
Table(args: Model.Arguments, optargs: Model.OptArgs) : unit
val Category : string

Full name: Tres.Table.Category
val Comment : string

Full name: Tres.Table.Comment
val Page : string

Full name: Tres.Table.Page
val Post : string

Full name: Tres.Table.Post
val User : string

Full name: Tres.Table.User
val WebLog : string

Full name: Tres.Table.WebLog
val this : IConnection
member IConnection.EstablishEnvironment : database:string -> Async<unit>

Full name: Tres.DataExtensions.EstablishEnvironment
val database : string
val r : RethinkDB
val checkTables : (unit -> Async<unit>)
val existing : string list
TopLevel.TableList() : TableList
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
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 filter : predicate:('T -> bool) -> list:'T list -> 'T list

Full name: Microsoft.FSharp.Collections.List.filter
val tbl : string
val not : value:bool -> bool

Full name: Microsoft.FSharp.Core.Operators.not
val contains : value:'T -> source:'T list -> bool (requires equality)

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

Full name: Microsoft.FSharp.Collections.List.map
TopLevel.TableCreate(expr: obj) : TableCreate
val iter : action:('T -> unit) -> list:'T list -> unit

Full name: Microsoft.FSharp.Collections.List.iter
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:System.Threading.CancellationToken -> 'T
val checkDatabase : (string -> Async<unit>)
val checkIndexes : (unit -> Async<unit>)
namespace Microsoft
namespace Microsoft.AspNetCore
namespace Microsoft.AspNetCore.Builder
namespace Nancy
namespace Nancy.Owin
namespace System.IO
Multiple items
type TresBootstrapper =
  inherit DefaultNancyBootstrapper
  new : unit -> TresBootstrapper
  member Configure : app:IApplicationBuilder -> unit
  override ConfigureApplicationContainer : container:TinyIoCContainer -> unit

Full name: Tres.TresBootstrapper

--------------------
new : unit -> TresBootstrapper
Multiple items
type DefaultNancyBootstrapper =
  inherit NancyBootstrapperWithRequestContainerBase<TinyIoCContainer>
  new : unit -> DefaultNancyBootstrapper
  member GetEnvironment : unit -> INancyEnvironment
  static val DefaultAutoRegisterIgnoredAssemblies : IEnumerable<Func<Assembly, bool>>

Full name: Nancy.DefaultNancyBootstrapper

--------------------
DefaultNancyBootstrapper() : unit
val this : TresBootstrapper
override TresBootstrapper.ConfigureApplicationContainer : container:TinyIoc.TinyIoCContainer -> unit

Full name: Tres.TresBootstrapper.ConfigureApplicationContainer
val container : TinyIoc.TinyIoCContainer
val cfg : DataConfig
static member DataConfig.FromJson : json:string -> DataConfig
type File =
  static member AppendAllLines : path:string * contents:IEnumerable<string> -> unit + 1 overload
  static member AppendAllText : path:string * contents:string -> unit + 1 overload
  static member AppendText : path:string -> StreamWriter
  static member Copy : sourceFileName:string * destFileName:string -> unit + 1 overload
  static member Create : path:string -> FileStream + 3 overloads
  static member CreateText : path:string -> StreamWriter
  static member Decrypt : path:string -> unit
  static member Delete : path:string -> unit
  static member Encrypt : path:string -> unit
  static member Exists : path:string -> bool
  ...

Full name: System.IO.File
File.ReadAllText(path: string) : string
File.ReadAllText(path: string, encoding: System.Text.Encoding) : string
val conn : IConnection
member DataConfig.CreateConnection : unit -> IConnection
member IConnection.EstablishEnvironment : database:string -> Async<unit>
TinyIoc.TinyIoCContainer.Register<'RegisterType,'RegisterImplementation (requires reference type and reference type and 'RegisterImplementation :> 'RegisterType)>() : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType (requires reference type)>() : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType (requires reference type)>(factory: System.Func<TinyIoc.TinyIoCContainer,TinyIoc.NamedParameterOverloads,'RegisterType>) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType,'RegisterImplementation (requires reference type and reference type and 'RegisterImplementation :> 'RegisterType)>(instance: 'RegisterImplementation) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType (requires reference type)>(instance: 'RegisterType) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType,'RegisterImplementation (requires reference type and reference type and 'RegisterImplementation :> 'RegisterType)>(name: string) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType (requires reference type)>(name: string) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register(registerType: System.Type) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType (requires reference type)>(factory: System.Func<TinyIoc.TinyIoCContainer,TinyIoc.NamedParameterOverloads,'RegisterType>, name: string) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
TinyIoc.TinyIoCContainer.Register<'RegisterType,'RegisterImplementation (requires reference type and reference type and 'RegisterImplementation :> 'RegisterType)>(instance: 'RegisterImplementation, name: string) : TinyIoc.TinyIoCContainer.RegisterOptions
   (+0 other overloads)
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
member TresBootstrapper.Configure : app:IApplicationBuilder -> unit

Full name: Tres.TresBootstrapper.Configure
val app : IApplicationBuilder
type IApplicationBuilder =
  member ApplicationServices : IServiceProvider with get, set
  member Build : unit -> RequestDelegate
  member New : unit -> IApplicationBuilder
  member Properties : IDictionary<string, obj>
  member ServerFeatures : IFeatureCollection
  member Use : middleware:Func<RequestDelegate, RequestDelegate> -> IApplicationBuilder

Full name: Microsoft.AspNetCore.Builder.IApplicationBuilder
(extension) IApplicationBuilder.UseOwin() : System.Action<System.Func<System.Func<System.Collections.Generic.IDictionary<string,obj>,System.Threading.Tasks.Task>,System.Func<System.Collections.Generic.IDictionary<string,obj>,System.Threading.Tasks.Task>>>
(extension) IApplicationBuilder.UseOwin(pipeline: System.Action<System.Action<System.Func<System.Func<System.Collections.Generic.IDictionary<string,obj>,System.Threading.Tasks.Task>,System.Func<System.Collections.Generic.IDictionary<string,obj>,System.Threading.Tasks.Task>>>>) : IApplicationBuilder
val x : System.Action<System.Func<System.Func<System.Collections.Generic.IDictionary<string,obj>,System.Threading.Tasks.Task>,System.Func<System.Collections.Generic.IDictionary<string,obj>,System.Threading.Tasks.Task>>>
(extension) System.Action.UseNancy(action: System.Action<NancyOptions>) : System.Action<System.Func<System.Func<System.Collections.Generic.IDictionary<string,obj>,System.Threading.Tasks.Task>,System.Func<System.Collections.Generic.IDictionary<string,obj>,System.Threading.Tasks.Task>>>
(extension) System.Action.UseNancy(?options: NancyOptions) : System.Action<System.Func<System.Func<System.Collections.Generic.IDictionary<string,obj>,System.Threading.Tasks.Task>,System.Func<System.Collections.Generic.IDictionary<string,obj>,System.Threading.Tasks.Task>>>
val opt : NancyOptions
property NancyOptions.Bootstrapper: Bootstrapper.INancyBootstrapper
Fork me on GitHub