RethinkDb.Driver.FSharp/README.md
2022-05-25 14:34:37 -04:00

4.0 KiB

RethinkDb.Driver.FSharp

Idiomatic F# extensions for the C# RethinkDB driver

Nuget (with prereleases)

Using

Install the NuGet package RethinkDb.Driver.FSharp. You will need to specify pre-release, as the package currently has a beta designation.

What It Provides

The full documentation is on the project site; TL;DR below.

A composable pipeline for creating ReQL statements

open RethinkDb.Driver.FSharp.Functions

/// string -> (IConnection -> Task<Post option>)
let fetchPost (postId : string) =
    db "Blog"
    |> table "Post"
    |> get postId
    |> runResult<Post>
    |> asOption
    |> withRetryDefault

An F# domain-specific language (DSL) using a rethink computation expression (CE)

open RethinkDb.Driver.FSharp

/// string -> (IConnection -> Task<Post>)
let fetchPost (postId : string) =
    rethink<Post> {
        withTable "Blog.Post"
        get postId
        result
        withRetryDefault
    }

A standard way to translate JSON into a strongly-typed configuration

/// type: DataConfig
let config = DataConfig.fromJsonFile "data-config.json"
// OR
let config = DataConfig.fromConfiguration (config.GetSection "RethinkDB")

/// type: IConnection
let conn = config.Connect ()

/// type: Post (utilizing either example above)
// (within a task CE)
let! post = fetchPost "the-post-id" conn

Robust queries

The RethinkDB connection is generally stored as a singleton. Over time, this connection can lose its connection to the server. Both the CE and functions have withRetryDefault, which will retry a failed command up to 3 times (4 counting the initial try), waiting 200ms, 500ms, and 1 second between the respective attempts. There are other options as well; withRetryOnce will retry one time immediately. withRetry takes a list of floats, which will be interpreted as seconds to delay between each retry; it will retry until it has exhausted the delays.

The examples above both use the default retry logic.

Only rename functions/methods where required

Within the CE, there are a few differing names, mostly notably at the start (selecting databases and tables); this is to allow for a more natural language flow. Also, while CEs now support overloading (thank you F# 6 developers!), they do not detect if the first value in the tupled arguments is different. This is most noticeable once result* or write* commands have been issued; these support Task<'T>, Async<'T>, and synchronous 'T operations, but the follow-on commands will be different (e.x. withRetryDefault (tasks) vs. withAsyncRetryDefault vs. withSyncRetryDefault). There are also versions of these that support optional arguments (for all) and cancellation tokens (for task/async).

The functions show this pattern throughout, as functions in a module do not support overloading; an example for filter is below.

// Function names cannot be polymorphic the way object-oriented methods can, so filter's three overloads become
filter (r.HashMap ("age", 30))
// and
filterFunc (fun row -> row.G("age").Eq 30)
// and
filterJS "function (row) { return 30 == row['age'] }"

Functions that support optional arguments end with WithOptArgs; those that support cancellation tokens end with WithCancel; and, those that support both end with WithOptArgsAndCancel.

Licensing

While no specific additional license restrictions exist for this project, there are modifications to the Apache v2 license on this project's dependencies. Please see the heading on the C# driver page for details.


If you are using the project, feel free to file issues about your pain points; there is no substitute for real-world feedback!