CurryOn.FSharp.Control 0.1.9

dotnet add package CurryOn.FSharp.Control --version 0.1.9                
NuGet\Install-Package CurryOn.FSharp.Control -Version 0.1.9                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CurryOn.FSharp.Control" Version="0.1.9" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add CurryOn.FSharp.Control --version 0.1.9                
#r "nuget: CurryOn.FSharp.Control, 0.1.9"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install CurryOn.FSharp.Control as a Cake Addin
#addin nuget:?package=CurryOn.FSharp.Control&version=0.1.9

// Install CurryOn.FSharp.Control as a Cake Tool
#tool nuget:?package=CurryOn.FSharp.Control&version=0.1.9                

CurryOn.FSharp.Control

The CurryOn.FSharp.Control library extends the FSharp.Control namespace with a framework for enabling the use of Railway-Oriented Programming patterns with the Task Parallel Library (TPL), Async Workflows, and Lazy Computations.

This is accomplished by providing a set of types for working with Operations and their results. An Operation is any function or expression that is intended to participate in the Railway-Oriented patterns, and is created by use of the operation Computation Expression.

open System.IO

let readFile (fileName: string) =
    operation {
        use fileStream = new StreamReader(fileName)
        return! fileStream.ReadToEndAsync()
    }    

The example above creates a function val readFile : fileName:string -> Operation<string,exn> that takes a file name and returns Operation<string,exn> representing the result of reading all text from the file. The Operation type is a discriminated union with four cases:

type Operation<'result,'event> =
| Completed of Result: OperationResult<'result,'event>
| InProcess of IncompleteOperation: InProcessOperation<'result,'event>
| Deferred of Lazy: EventingLazy<Operation<'result,'event>>
| Cancelled of EventsSoFar: 'event list

The cases of the Operation discriminated union represent the possible states of the Operation after invocation. Since the framework supports working with Tasks and Async Workflows, the Operation may not complete immediately, and may be cancelled, so the InProcess and Cancelled cases represent these states. Since the framework supports working with Lazy computations, the Deferred case represents Operations in the state of waiting for a Lazy to be evaluated.

Operations that are not completed can be waited on synchronously using Operation.wait. They can also be waited on with an F# Async using Operation.waitAsync or as a Task using Operation.waitTask. These functions return the same type as the Completed case of the Operation discriminated union, OperationResult<'result,'event>.

type OperationResult<'result,'event> =
| Success of Result: SuccessfulResult<'result,'event>
| Failure of ErrorList: 'event list

The OperationResult type represents the result of a Completed Operation. In the readFile example above, the result type would be OperationResult<string,exn>, since the resulting value is a string, and since the operation may throw exceptions, such as FileNotFoundException. If no exceptions are thrown and the Operation completed successfully, the OperationResult will be the Success case, and the result will be contained within a SuccessfulResult<'result,'event>. If any exception is thrown during the operation, the OperationResult will be the Failure case, and any exceptions thrown will be present in the list.

The SuccessfulResult<'result,'event> type is used to contain the resulting value and any domain events associated with a successful Operation. The SuccessfulResult type also has members .Result and .Events to provide direct access to the result value and the domain events without pattern-matching.

type SuccessfulResult<'result,'event> =
| Value of ResultValue: 'result
| WithEvents of ResultWithEvents: SuccessfulResultWithEvents<'result,'event>

When no domain events are associated with the SuccessfulResult, the Value case will be used, and the 'result will be directly accessible. When a successful Operation also returns domain events, the results will be contained in a SuccessfulResultWithEvents<'result,'event> record type.

type SuccessfulResultWithEvents<'result,'event> =
    {
        Value: 'result
        Events: 'event list
    }

This allows the framework to support a usage pattern where a successful Operation can also return domain events, or carry Warnings or Informational messages along with the resulting value. To use the framework in this way, it is common practice to create a discriminated union representing the possible errors, warnings, or domain events. Then, the events can be propogated from one operation to another, such as in the following examples:

type FileAccessEvents =
| FileOpenedSuccessfully
| FileReadSuccessfully
| FileNotFound of string
| FileIsInSystemRootWarning
| UnhandledException of exn // This is returned automatically if an unhandled exception is thrown by an Operation

let getFile (fileName: string) =
    operation {
        let file = FileInfo fileName
        return! if not file.Exists
                then Result.failure [FileNotFound file.FullName]
                else Result.success file
    }

let openFile fileName =
    operation {
        let! file = getFile fileName
        return! file.OpenText() |> Result.successWithEvents <| [FileOpenedSuccessfully]
    }

let readFile fileName = 
    operation {
        use! fileStream = openFile fileName
        let! fileText = fileStream.ReadToEndAsync()
        return! Result.successWithEvents fileText [FileReadSuccessfully]
    }

let writeFile fileName contents =
    operation {
        let! file = getFile fileName
        let stream = file.OpenWrite()
        do! stream.AsyncWrite contents
        return! if file.DirectoryName = Environment.SystemDirectory
                then Result.success ()
                else Result.successWithEvents () [FileIsInSystemRootWarning]
    }
Product Compatible and additional computed target framework versions.
.NET Framework net452 is compatible.  net46 was computed.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on CurryOn.FSharp.Control:

Package Downloads
CurryOn.Elastic.FSharp

An F#-idiomatic Library for Elasticsearch, including an F# DSL representation of the Elasticsearch Query Model, and F# modules for searching and indexing documents as well as creating and maintaining indexes.

CurryOn.Akka.Persistence.Streaming

Framework for creating Akka.net Persistence Plug-Ins based on Stream data-stores.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.9 1,149 7/13/2018
0.1.8 931 6/25/2018
0.1.7 968 6/8/2018
0.1.6 989 2/13/2018
0.1.5 1,424 2/9/2018
0.1.4 1,097 2/2/2018
0.1.3 1,369 1/23/2018
0.1.2 2,993 1/4/2018
0.1.1 1,061 1/3/2018
0.1.0 982 1/2/2018

Added Awaitable type and await computation expression, allowing Async and Task to be mixed.