Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.PlatformService
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScriptResources and Results
Own a resource for the life of a stream
A stream over a file, a socket, or a database cursor has to close it when consumption ends, however it ends.
FlowStream.using acquires a Resource on the first pull and releases it when the consuming Flow finishes: when the
stream is exhausted, when it fails, when it is interrupted, or when take stops it early.
Shared setup
// Setup for the checked examples on this page.
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.PlatformService
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScript
/// Fails the docs test when an example's result differs from the value shown.
let shouldEqual expected actual =
if actual <> expected then failwithf "Expected %A but got %A" expected actual
SystemIOThreadingTasksAxialLayersConsoleFileSystemHostingBrowserNodePlatformServiceStateTelemetryJavaScriptshouldEqual: 'a -> 'a -> unitexpected: 'aactual: 'a(<>): 'T -> 'T -> boolStructural inequality The first parameter. The second parameter. The result of the comparison. 5 <> 5 // Evaluates to false 5 <> 6 // Evaluates to true [1; 2] <> [1; 2] // Evaluates to false
failwithf: Printf.StringFormat<'T,'Result> -> 'TPrint to a string buffer and raise an exception with the given result. Helper printers must return strings. The formatter. The formatted result. See Printf.failwithf (link: ) for examples.
let readersClosed = ref 0
let openReader (path: string) : Resource<unit, Never, StreamReader> =
Resource.create
(Flow.delay (fun () -> Flow.ok (File.OpenText path)))
(fun reader _ ->
reader.Dispose()
readersClosed.Value <- readersClosed.Value + 1
Task.CompletedTask)
let lines (path: string) : FlowStream<string> =
FlowStream.using (openReader path) (fun reader ->
FlowStream.repeatFlow (Flow.fromBlocking (fun _ -> reader.ReadLine()))
|> FlowStream.takeWhile (isNull >> not))
readersClosed: int refref: 'T -> 'T refCreate a mutable reference cell The value to contain in the cell. The created reference cell. let count = ref 0 // Creates a reference cell object with a mutable Value property count.Value // Evaluates to 0 count.Value <- 1 // Updates the value count.Value // Evaluates to 1
openReader: string -> Resource<unit,Never,StreamReader>path: stringstringAn abbreviation for the CLI type . Basic Types
Axial.Resource`3Describes acquisition of a value together with registration of its release in the current Flow scope. The environment required to acquire the value. The typed acquisition failure. The acquired value.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
Axial.NeverRepresents an error channel that cannot occur.
System.IO.StreamReaderImplements a that reads characters from a byte stream in a particular encoding.
Axial.ResourceModulecreate: Flow<'env,'error,'value> -> ('value -> CancellationToken -> Task) -> Resource<'env,'error,'value>Describes acquisition together with a task-based release registered in the current Flow scope.
Axial.Flowdelay: (unit -> Flow<'env,'error,'value>) -> Flow<'env,'error,'value>Defers flow construction until execution time. A function that returns the flow to execute. A flow that lazily evaluates the factory when executed. let flow = Flow.delay (fun () -> Flow.succeed 42)
ok: 'value -> Flow<'env,'error,'value>Creates a successful synchronous flow. The value to wrap in a successful flow. A flow that always succeeds with the provided value.
System.IO.FileProvides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of objects.
OpenText: string -> StreamReaderOpens an existing UTF-8 encoded text file for reading. The file to be opened for reading. A on the specified path. The caller does not have the required permission. is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . is . The specified path, file name, or both exceed the system-defined maximum length. The specified path is invalid, (for example, it is on an unmapped drive). The file specified in was not found. is in an invalid format.
reader: StreamReaderDispose: unit -> unitReleases all resources used by the object.
Value: intThe current value of the reference cell
(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "World" // Evaluates to "Hello World"
System.Threading.Tasks.TaskRepresents an asynchronous operation.
CompletedTask: TaskGets a task that has already completed successfully. The successfully completed task.
lines: string -> FlowStream<string>FlowStreamA stream with no environment requirement and no typed failure.
Axial.FlowStreamModuleusing: Resource<'env,'error,'resource> -> ('resource -> FlowStream<'env,'error,'value>) -> FlowStream<'env,'error,'value>Creates a stream that owns a resource: acquired when consumption starts, released when it ends. The resource is acquired on the first pull and released when the consuming Flow finishes, whether the stream was exhausted, failed, was interrupted, or was cut short by take. Use it for streams over a file, a socket, or a database cursor. The resource to acquire. Builds the stream from the acquired resource. let lines path = FlowStream.using (Resource.create (Flow.fromBlocking (fun _ -> File.OpenText path)) (fun reader _ -> reader.Dispose(); Task.CompletedTask)) (fun reader -> FlowStream.repeatFlow (Flow.fromBlocking (fun _ -> reader.ReadLine())) |> FlowStream.takeWhile (isNull >> not))
repeatFlow: Flow<'env,'error,'value> -> FlowStream<'env,'error,'value>Creates a stream that emits the result of running again for every pull, forever. Each pull runs the flow once, so the stream never runs ahead of its consumer. Bound it with take or takeWhile, or pace it with throttle. A failure ends the stream. FlowStream.repeatFlow readSensor |> FlowStream.takeWhile (fun reading -> reading.Ok)
fromBlocking: (CancellationToken -> 'value) -> Flow<'env,'error,'value>Creates a flow from synchronous, blocking work, such as a database driver or native library call. On .NET the operation runs on the thread pool, so a blocking call does not stall the thread that runs the workflow. Once started it runs to completion even if the workflow is interrupted, because blocking work cannot be abandoned safely; it receives the runtime's token so it can stop early. Thrown exceptions are defects (Cause.Die); cancellation the token requested is an interruption. On JavaScript the operation runs inline. The blocking operation, observing the supplied cancellation token where it can. Fable compatible let commits = Flow.fromBlocking (fun _ -> repository.Commits |> Seq.truncate 50 |> List.ofSeq)
ReadLine: unit -> stringReads a line of characters from the current stream and returns the data as a string. The next line from the input stream, or if the end of the input stream is reached. There is insufficient memory to allocate a buffer for the returned string. An I/O error occurs.
(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
takeWhile: ('a -> bool) -> FlowStream<'b,'c,'a> -> FlowStream<'b,'c,'a>Emits values while a predicate remains true. stream |> FlowStream.takeWhile (fun value -> value < 100)
isNull: 'T -> boolDetermines whether the given value is null. The value to check. True when value is null, false otherwise. isNull null // Evaluates to true isNull "Not null" // Evaluates to false
(>>): ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3Compose two functions, the function on the left being applied first The first function to apply. The second function to apply. The composition of the input functions. let addOne x = x + 1 let doubleIt x = x * 2 let addThenDouble = addOne >> doubleIt addThenDouble 3 // Evaluates to 8
``not``: bool -> boolNegate a logical value. Not True equals False and not False equals True The value to negate. The result of the negation. not (2 + 2 = 5) // Evaluates to true // not is a function that can be compose with other functions let fileDoesNotExist = System.IO.File.Exists >> not
let path = Path.Combine(Path.GetTempPath(), $"axial-docs-lines-{Guid.NewGuid():N}.txt")
File.WriteAllLines(path, [ "first"; "second"; "third" ])
lines path |> FlowStream.runCollect |> Flow.run () |> shouldEqual (Exit.Success [ "first"; "second"; "third" ])
lines path |> FlowStream.take 1 |> FlowStream.runCollect |> Flow.run () |> shouldEqual (Exit.Success [ "first" ])
readersClosed.Value |> shouldEqual 2
File.Delete path
path: stringSystem.IO.PathPerforms operations on instances that contain file or directory path information. These operations are performed in a cross-platform manner.
Combine: string * string -> stringCombines two strings into a path. The first path to combine. The second path to combine. The combined paths. If one of the specified paths is a zero-length string, this method returns the other path. If contains an absolute path, this method returns . or contains one or more of the invalid characters defined in . or is .
GetTempPath: unit -> stringReturns the path of the current user's temporary folder. The path to the temporary folder, ending with a backslash. The caller does not have the required permissions.
System.IO.FileProvides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of objects.
WriteAllLines: string * string seq -> unitCreates a new file, writes a collection of strings to the file, and then closes the file. The file to write to. The lines to write to the file. is a zero-length string, contains only white space, or contains one or more invalid characters defined by the method. Either or is . is invalid (for example, it is on an unmapped drive). An I/O error occurred while opening the file. exceeds the system-defined maximum length. is in an invalid format. The caller does not have the required permission. specified a file that is read-only. -or- specified a file that is hidden. -or- This operation is not supported on the current platform. -or- is a directory. -or- The caller does not have the required permission.
lines: string -> FlowStream<string>(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Axial.FlowStreamModulerunCollect: FlowStream<'a,'b,'c> -> Flow<'a,'b,'c list>Collects all emitted values into a list. stream |> FlowStream.runCollect
Axial.Flowrun: 'env -> Flow<'env,'error,'value> -> Exit<'value,'error>Runs the workflow and blocks until the final exit is available. The environment used by the workflow. The workflow to run. The final workflow exit. let exit = workflow |> Flow.run environment
shouldEqual: 'a -> 'a -> unitAxial.Exit`2Represents the final outcome of a workflow execution. The type of the success value. The type of the domain-specific failure value.
SuccessThe workflow completed successfully.
take: int -> FlowStream<'a,'b,'c> -> FlowStream<'a,'b,'c>Emits at most values. stream |> FlowStream.take 10
readersClosed: int refValue: intThe current value of the reference cell
Delete: string -> unitDeletes the specified file. The name of the file to be deleted. Wildcard characters are not supported. is a zero-length string, contains only white space, or contains one or more invalid characters as defined by . is . The specified path is invalid (for example, it is on an unmapped drive). The specified file is in use. -or- There is an open handle on the file, and the operating system is Windows XP or earlier. This open handle can result from enumerating directories and files. For more information, see How to: Enumerate Directories and Files. is in an invalid format. The specified path, file name, or both exceed the system-defined maximum length. The caller does not have the required permission. -or- The file is an executable file that is in use. -or- is a directory. -or- specified a read-only file.
The reader was closed after the full read and after take 1 stopped the stream early.
FlowStream.repeatFlow runs its flow once per pull, forever, so it never runs ahead of the consumer. Bound it with
take or takeWhile, or pace it with throttle.
Take one result
Some consumers only need part of a stream:
FlowStream.runTryHeadpulls a single value and returns it, orNonefor an empty stream. It stops the stream as soon as the value arrives, so resources and producer fibers are released right away.FlowStream.runTryLastconsumes the whole stream and returns its last value, orNone.FlowStream.runCountconsumes the whole stream and returns how many values it emitted.
let firstLongLine (path: string) : Flow<string option> =
lines path |> FlowStream.filter (fun line -> line.Length > 5) |> FlowStream.runTryHead
firstLongLine: string -> Flow<string option>path: stringstringAn abbreviation for the CLI type . Basic Types
FlowA flow that requires no environment and cannot fail with a typed error.
optionThe type of optional values. When used from other CLI languages the empty option is the null value. Use the constructors Some and None to create values of this type. Use the values in the Option module to manipulate values of this type, or pattern match against the values directly. 'None' values will appear as the value null to other CLI languages. Instance methods on this type will appear as static methods to other CLI languages due to the use of null as a value representation. Options
lines: string -> FlowStream<string>(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Axial.FlowStreamModulefilter: ('a -> bool) -> FlowStream<'b,'c,'a> -> FlowStream<'b,'c,'a>Keeps values that satisfy a predicate. stream |> FlowStream.filter (fun value -> value > 0)
line: stringLength: intGets the number of characters in the current object. The number of characters in the current string.
(>): 'T -> 'T -> boolStructural greater-than The first parameter. The second parameter. The result of the comparison. 5 > 1 // Evaluates to true 5 > 5 // Evaluates to false (1, "a") > (1, "z") // Evaluates to false
runTryHead: FlowStream<'env,'error,'value> -> Flow<'env,'error,'value option>Returns the first value, or None for an empty stream, then stops the stream. Only one value is pulled; resources and producer fibers are released as soon as it arrives. stream |> FlowStream.runTryHead

