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
<PackageReference Include="CurryOn.FSharp.Control" Version="0.1.9" />
paket add CurryOn.FSharp.Control --version 0.1.9
#r "nuget: CurryOn.FSharp.Control, 0.1.9"
// 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 | Versions 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. |
-
- System.ValueTuple (>= 4.3.0)
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.
Added Awaitable type and await computation expression, allowing Async and Task to be mixed.