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.JavaScriptAxial.FileSystem turns file access into a declared dependency with a typed failure channel. Where
File.ReadAllText throws one of a dozen exception types, FileSystem.readAllText returns
Flow<'env, FileSystemError, string>, so the ways it can fail are part of the signature.
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.
type Config = { Name: string }
let parseConfig (text: string) = { Name = text.Trim() }
let loadConfig (path: string) : Flow<#IHasFileSystem, FileSystemError, Config> =
flow {
let! text = FileSystem.readAllText path
return parseConfig text
}
FsLiveDocsGeneratedPage0_F4A2DC77945C.ConfigName: stringstringAn abbreviation for the CLI type . Basic Types
parseConfig: string -> Configtext: stringTrim: unit -> stringRemoves all leading and trailing white-space characters from the current object. The string that remains after all white-space characters are removed from the start and end of the current string. If no characters can be trimmed from the current instance, the method returns the current instance unchanged.
loadConfig: string -> Flow<'a,FileSystemError,Config>path: stringAxial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
Axial.FileSystem.IHasFileSystemAxial.FileSystem.FileSystemErrorDescribes a meaningful file-system failure.
flow: FlowBuilderThe universal flow { } computation expression.
Axial.FileSystem.FileSystemModulereadAllText: string -> Flow<'env,FileSystemError,string>Reads all text through an explicit file-system service.
Supplying the service
IFileSystem is supplied the same way as any other explicit service:
type AppEnv =
{ FileSystem: IFileSystem }
interface IHasFileSystem with
member this.FileSystem = this.FileSystem
let live = { FileSystem = FileSystem.live }
FsLiveDocsGeneratedPage0_F4A2DC77945C.AppEnvFileSystem: IFileSystemAxial.FileSystem.IFileSystemProvides access to common file, directory, and path operations.
Axial.FileSystem.IHasFileSystemthis: AppEnvFileSystem: AppEnv -> unit -> IFileSystemlive: AppEnvAxial.FileSystem.FileSystemModulelive: IFileSystemCreates a live file-system service backed by , , and .
The examples on this page work in a fresh temporary directory:
let root = Path.Combine(Path.GetTempPath(), "axial-docs-filesystem", Guid.NewGuid().ToString "N")
Directory.CreateDirectory root |> ignore
root: 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 * string -> stringCombines three strings into a path. The first path to combine. The second path to combine. The third path to combine. The combined paths. , , 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.GuidRepresents a globally unique identifier (GUID).
NewGuid: unit -> GuidInitializes a new instance of the structure. A new GUID object.
ToString: string -> stringReturns a string representation of the value of this instance, according to the provided format specifier. A single format specifier that indicates how to format the value of this . The parameter can be "N", "D", "B", "P", or "X". If is or an empty string (""), "D" is used. The value of this , represented as a series of lowercase hexadecimal digits in the specified format. The value of is not , an empty string (""), "N", "D", "B", "P", or "X".
System.IO.DirectoryExposes static methods for creating, moving, and enumerating through directories and subdirectories. This class cannot be inherited.
CreateDirectory: string -> DirectoryInfoCreates all directories and subdirectories in the specified path unless they already exist. The directory to create. An object that represents the directory at the specified path. This object is returned regardless of whether a directory at the specified path already exists. The directory specified by is a file. -or- The network name is not known. The caller does not have the required permission. is a zero-length string, contains only white space, or contains one or more invalid characters. You can query for invalid characters by using the method. -or- is prefixed with, or contains, only a colon character (:). 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). contains a colon character (:) that is not part of a drive label ("C:\").
(|>): '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
ignore: 'T -> unitIgnore the passed value. This is often used to throw away results of a computation. The value to ignore. ignore 55555 // Evaluates to ()
File.WriteAllText(Path.Combine(root, "app.json"), "orders ")
loadConfig (Path.Combine(root, "app.json")) |> Flow.run live |> shouldEqual (Exit.Success { Name = "orders" })
System.IO.FileProvides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of objects.
WriteAllText: string * string -> unitCreates a new file, writes the specified string to the file, and then closes the file. If the target file already exists, it is overwritten. The file to write to. The string to write to the file. 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). An I/O error occurred while opening the file. 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- specified a directory. -or- The caller does not have the required permission. is in an invalid format. The caller does not have the required permission.
System.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 .
root: stringloadConfig: string -> Flow<'a,FileSystemError,Config>(|>): '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.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
live: AppEnvshouldEqual: '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.
Name: stringFor a runtime assembled with layers, wrap it: Layer.succeed FileSystem.live.
Typed errors
Every operation fails with FileSystemError, a union that classifies what went wrong:
| Case | Raised when |
|---|---|
FileNotFound path |
The file does not exist |
DirectoryNotFound path |
A directory in the path does not exist |
AlreadyExists path |
The target path is already taken |
Unauthorized (path, message) |
The process lacks permission |
InvalidPath (path, message) |
The path is malformed |
PathTooLong (path, message) |
The platform rejected the path length |
Io (path, message) |
A general I/O failure |
Unsupported (path, message) |
The platform or path shape does not support the operation |
Unexpected (path, message) |
Anything else that escaped the operation |
Because failures are typed, recovery is a match rather than an exception filter:
let defaults = { Name = "default" }
let loadOrDefault (path: string) : Flow<AppEnv, FileSystemError, Config> =
loadConfig path
|> Flow.orElseWith (function
| FileSystemError.FileNotFound _ -> Flow.succeed defaults
| error -> Flow.fail error)
defaults: ConfigName: stringloadOrDefault: string -> Flow<AppEnv,FileSystemError,Config>path: stringstringAn abbreviation for the CLI type . Basic Types
Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
FsLiveDocsGeneratedPage0_F4A2DC77945C.AppEnvAxial.FileSystem.FileSystemErrorDescribes a meaningful file-system failure.
FsLiveDocsGeneratedPage0_F4A2DC77945C.ConfigloadConfig: string -> Flow<'a,FileSystemError,Config>(|>): '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.FloworElseWith: ('error -> Flow<'env,'error,'value>) -> Flow<'env,'error,'value> -> Flow<'env,'error,'value>Computes a fallback flow from the typed error when the source flow fails. The fallback runs only for expected typed failures represented by Cause.Fail. It does not catch interruption or defects. Use this for domain-level recovery, not for swallowing cancellation or unexpected exceptions. A function that produces a new flow from the error value. The source flow. A flow that recovers from errors using the fallback function. let flow = Flow.fail "error" |> Flow.orElseWith (fun err -> Flow.succeed "recovered")
FileNotFoundA file was not found.
succeed: 'value -> Flow<'env,'error,'value>Same as ok. The value to wrap in a successful flow. A flow that always succeeds with the provided value. let result = Flow.succeed 42 |> Flow.run () // result = Success 42
error: FileSystemErrorfail: 'error -> Flow<'env,'error,'value>Same as error. The error value to wrap in a failing flow. A flow that always fails with the provided error. let result = Flow.fail "error" |> Flow.run () // result = Failure (Cause.Fail "error")
loadOrDefault (Path.Combine(root, "missing.json")) |> Flow.run live |> shouldEqual (Exit.Success defaults)
loadOrDefault: string -> Flow<AppEnv,FileSystemError,Config>System.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 .
root: 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.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
live: AppEnvshouldEqual: '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.
defaults: ConfigFileSystemError.describe formats a case for logs and messages. FileSystemError.fromException performs the
classification itself, which is useful when adapting a third-party API into the same error type.
Files
Whole-file reads and writes come in text, line, and byte forms, each with an encoding-explicit and an asynchronous variant:
let notes (path: string) : Flow<AppEnv, FileSystemError, string array * int64> =
flow {
do! FileSystem.writeAllLines path [ "one"; "two" ]
do! FileSystem.appendAllText path "three"
let! lines = FileSystem.readAllLines path
let! length = FileSystem.getFileLength path
return lines, length
}
notes: string -> Flow<AppEnv,FileSystemError,(string array * int64)>path: stringstringAn abbreviation for the CLI type . Basic Types
Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
FsLiveDocsGeneratedPage0_F4A2DC77945C.AppEnvAxial.FileSystem.FileSystemErrorDescribes a meaningful file-system failure.
arraySingle dimensional, zero-based arrays, written int array, string array etc. Use the values in the module to manipulate values of this type, or the notation arr.[x] to get/set array values. Basic Types
int64An abbreviation for the CLI type . Basic Types
flow: FlowBuilderThe universal flow { } computation expression.
Axial.FileSystem.FileSystemModulewriteAllLines: string -> string seq -> Flow<'env,FileSystemError,unit>Writes all lines through an explicit file-system service.
appendAllText: string -> string -> Flow<'env,FileSystemError,unit>Appends all text through an explicit file-system service.
lines: string arrayreadAllLines: string -> Flow<'env,FileSystemError,string array>Reads all lines through an explicit file-system service.
length: int64getFileLength: string -> Flow<'env,FileSystemError,int64>Gets the size of a file in bytes through an explicit file-system service. let sizeOf path = FileSystem.getFileLength path
let lines, length = notes (Path.Combine(root, "notes.txt")) |> Flow.run live |> Exit.toResult |> Result.defaultWith (failwithf "%A")
lines |> shouldEqual [| "one"; "two"; "three" |]
length |> shouldEqual (int64 ("one" + Environment.NewLine + "two" + Environment.NewLine + "three").Length)
lines: string arraylength: int64notes: string -> Flow<AppEnv,FileSystemError,(string array * int64)>System.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 .
root: 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.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
live: AppEnvAxial.ExittoResult: Exit<'v,'e> -> Result<'v,'e>Converts an exit outcome to a standard F# Result. The exit outcome to convert. A Result representing the successful value or the domain failure. Re-throws the original exception if the exit was Cause.Die. Throws if the exit was Cause.Interrupt.
Microsoft.FSharp.Core.ResultModuleContains operations for working with values of type . Choices and Results
defaultWith: ('Error -> 'T) -> Result<'T,'Error> -> 'TGets the value of the result if the result is Ok, otherwise evaluates and returns the result. A thunk that provides a default value when evaluated. The input result. The result if the result is Ok, else the result of evaluating . is not evaluated unless is Error. Ok 1 |> Result.defaultWith (fun error -> 99) // evaluates to 1 Error 2 |> Result.defaultWith (fun error -> 99) // evaluates to 99
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.
shouldEqual: 'a -> 'a -> unitint64: ^T -> int64Converts the argument to signed 64-bit integer. This is a direct conversion for all primitive numeric types. For strings, the input is converted using Int64.Parse() with InvariantCulture settings. Otherwise the operation requires an appropriate static conversion method on the input type. The input value. The converted int64 int64 'A' // evaluates to 65L int64 0xff // evaluates to 255L int64 -10 // evaluates to -10L
Length: intGets the number of characters in the current object. The number of characters in the current string.
(+): ^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.EnvironmentProvides information about, and means to manipulate, the current environment and platform. This class cannot be inherited.
NewLine: stringGets the newline string defined for this environment. A string containing "\r\n" for non-Unix platforms, or a string containing "\n" for Unix platforms.
The same family has readAllTextWithEncoding, readAllTextAsync, readAllBytes, writeAllBytes, and the other
encoding-explicit and asynchronous forms.
The Async variants pass the flow's cancellation token to the underlying call, so an interrupted workflow stops a
large read in progress rather than after it. Prefer them for anything that is not small.
fileExists, exists, deleteFile, copyFile, and moveFile cover the other common operations.
getFileLength returns a file's size in bytes, and file metadata has getters and setters for attributes and the creation, last-access, and last-write times in both local and
UTC forms, such as getFileLastWriteTimeUtc, setFileAttributes, and so on.
Symbolic links are first class: createFileSymbolicLink, createDirectorySymbolicLink, getSymbolicLinkTarget
(which returns None when the path is not a link), and resolveSymbolicLinkTarget, whose boolean argument decides
whether to follow the whole chain or stop at the immediate target.
Streams and scopes
openRead, openText, openWrite, createFile, createText, appendText, and the openFile family return open
handles. An open handle is a resource, so acquire it inside a scope rather than trusting a later Dispose:
let readAndTransform (stream: Stream) (destination: string) : Flow<AppEnv, FileSystemError, unit> =
use reader = new StreamReader(stream)
FileSystem.writeAllText destination (reader.ReadToEnd().ToUpperInvariant())
let copyThrough (source: string) (destination: string) : Flow<AppEnv, FileSystemError, unit> =
Flow.scopeAcquireRelease
(FileSystem.openRead source)
(fun stream _ ->
stream.Dispose()
Task.CompletedTask)
|> Flow.bind (fun stream -> readAndTransform stream destination)
|> Flow.scoped
readAndTransform: Stream -> string -> Flow<AppEnv,FileSystemError,unit>stream: StreamSystem.IO.StreamProvides a generic view of a sequence of bytes. This is an abstract class.
destination: stringstringAn abbreviation for the CLI type . Basic Types
Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
FsLiveDocsGeneratedPage0_F4A2DC77945C.AppEnvAxial.FileSystem.FileSystemErrorDescribes a meaningful file-system failure.
unitThe type 'unit', which has only one value "()". This value is special and always uses the representation 'null'. Basic Types
reader: StreamReaderSystem.IO.StreamReaderImplements a that reads characters from a byte stream in a particular encoding.
Axial.FileSystem.FileSystemModulewriteAllText: string -> string -> Flow<'env,FileSystemError,unit>Writes all text through an explicit file-system service.
ReadToEnd: unit -> stringReads all characters from the current position to the end of the stream. The rest of the stream as a string, from the current position to the end. If the current position is at the end of the stream, returns an empty string (""). There is insufficient memory to allocate a buffer for the returned string. An I/O error occurs.
ToUpperInvariant: unit -> stringReturns a copy of this object converted to uppercase using the casing rules of the invariant culture. The uppercase equivalent of the current string.
copyThrough: string -> string -> Flow<AppEnv,FileSystemError,unit>source: stringAxial.FlowscopeAcquireRelease: Flow<'env,'error,'resource> -> ('resource -> CancellationToken -> Task) -> Flow<'env,'error,'resource>Acquires a value and registers its release with the current runtime scope. The flow that acquires the value. The release action run when the current scope closes. A flow that succeeds with the acquired value.
openRead: string -> Flow<'env,FileSystemError,Stream>Opens a file for reading through an explicit file-system service.
Dispose: unit -> unitReleases all resources used by the .
System.Threading.Tasks.TaskRepresents an asynchronous operation.
CompletedTask: TaskGets a task that has already completed successfully. The successfully completed task.
(|>): '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
bind: ('value -> Flow<'env,'error,'next>) -> Flow<'env,'error,'value> -> Flow<'env,'error,'next>Sequences a dependent flow after a successful value. This is the flatmap operation for . The continuation only runs when the source flow succeeds, and it receives the successful value. Use bind when the next effect depends on the previous result; use map when the next step is pure. A function that takes the successful value and returns a new flow. The source flow to sequence. A representing the combined workflow. let flow = Flow.succeed 1 |> Flow.bind (fun x -> Flow.succeed (x + 1))
scoped: Flow<'env,'error,'value> -> Flow<'env,'error,'value>Runs a flow in a child scope and closes that scope before returning. Resources and fibers acquired inside the flow are released after success, typed failure, defect, or interruption without waiting for the surrounding application scope to close.
Cleanup then runs whether the workflow succeeds, fails, defects, or is interrupted. See scopes and resources.
Directories and paths
createDirectory creates missing parents. deleteDirectory path recursive takes the recursion flag explicitly, so a
recursive delete is visible at the call site. Listing comes in eager (getFiles, getDirectories,
getFileSystemEntries) and lazy (enumerateFiles, enumerateDirectories, enumerateFileSystemEntries) forms, each
taking a search pattern and a SearchOption:
let fsharpSources (directory: string) : Flow<AppEnv, FileSystemError, string list> =
flow {
let! files = FileSystem.enumerateFiles directory "*.fs" SearchOption.AllDirectories
return files |> Seq.map Path.GetFileName |> Seq.sort |> List.ofSeq
}
fsharpSources: string -> Flow<AppEnv,FileSystemError,string list>directory: stringstringAn abbreviation for the CLI type . Basic Types
Axial.Flow`3Represents a cold workflow that reads an environment, returns a typed result, and is executed explicitly through one of its execution members such as ToTask, ToAsync, or RunSynchronously. The type of the environment dependency. The type of the failure value. The type of the success value.
FsLiveDocsGeneratedPage0_F4A2DC77945C.AppEnvAxial.FileSystem.FileSystemErrorDescribes a meaningful file-system failure.
listThe type of immutable singly-linked lists. See the module for further operations related to lists. Use the constructors [] and :: (infix) to create values of this type, or the notation [1; 2; 3]. Use the values in the List module to manipulate values of this type, or pattern match against the values directly. See also F# Language Guide - Lists.
flow: FlowBuilderThe universal flow { } computation expression.
files: string seqAxial.FileSystem.FileSystemModuleenumerateFiles: string -> string -> SearchOption -> Flow<'env,FileSystemError,string seq>Enumerates files through an explicit file-system service.
System.IO.SearchOptionSpecifies whether to search the current directory, or the current directory and all subdirectories.
AllDirectories: SearchOptionIncludes the current directory and all its subdirectories in a search operation. This option includes reparse points such as mounted drives and symbolic links in the search.
(|>): '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
Microsoft.FSharp.Collections.SeqModuleContains operations for working with values of type .
map: ('T -> 'U) -> 'T seq -> 'U seqBuilds a new collection whose elements are the results of applying the given function to each of the elements of the collection. The given function will be applied as elements are demanded using the MoveNext method on enumerators retrieved from the object. The returned sequence may be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should not be accessed concurrently. Sequence construction is O(1). Enumeration is O(n), where n is the length of the sequence. A function to transform items from the input sequence. The input sequence. The result sequence. Thrown when the input sequence is null. let inputs = ["a"; "bbb"; "cc"] inputs |> Seq.map (fun x -> x.Length) Evaluates to a sequence yielding the same results as seq { 1; 3; 2 }
System.IO.PathPerforms operations on instances that contain file or directory path information. These operations are performed in a cross-platform manner.
GetFileName: string -> stringReturns the file name and extension of the specified path string. The path string from which to obtain the file name and extension. The characters after the last directory separator character in . If the last character of is a directory or volume separator character, this method returns . If is , this method returns . contains one or more of the invalid characters defined in .
sort: 'T seq -> 'T seqYields a sequence ordered by keys. This function returns a sequence that digests the whole initial sequence as soon as that sequence is iterated. As a result this function should not be used with large or infinite sequences. The function makes no assumption on the ordering of the original sequence and uses a stable sort, that is the original order of equal elements is preserved. This is an O(n log n) operation, where n is the length of the sequence. The input sequence. The result sequence. Thrown when the input sequence is null. let input = seq { 8; 4; 3; 1; 6; 1 } Seq.sort input Evaluates to a sequence yielding the same results as seq { 1; 1 3; 4; 6; 8 }.
Microsoft.FSharp.Collections.ListModuleContains operations for working with values of type . Operations for collections such as lists, arrays, sets, maps and sequences. See also F# Collection Types in the F# Language Guide.
ofSeq: 'T seq -> 'T listBuilds a new list from the given enumerable object. The input sequence. The list of elements from the sequence. let inputs = seq { 1; 2; 5 } inputs |> List.ofSeq Evaluates to [ 1; 2; 5 ]. This is an O(n) operation, where n is the length of the sequence.
Directory.CreateDirectory(Path.Combine(root, "src", "nested")) |> ignore
File.WriteAllText(Path.Combine(root, "src", "Program.fs"), "")
File.WriteAllText(Path.Combine(root, "src", "nested", "Library.fs"), "")
File.WriteAllText(Path.Combine(root, "src", "notes.md"), "")
fsharpSources (Path.Combine(root, "src")) |> Flow.run live |> shouldEqual (Exit.Success [ "Library.fs"; "Program.fs" ])
System.IO.DirectoryExposes static methods for creating, moving, and enumerating through directories and subdirectories. This class cannot be inherited.
CreateDirectory: string -> DirectoryInfoCreates all directories and subdirectories in the specified path unless they already exist. The directory to create. An object that represents the directory at the specified path. This object is returned regardless of whether a directory at the specified path already exists. The directory specified by is a file. -or- The network name is not known. The caller does not have the required permission. is a zero-length string, contains only white space, or contains one or more invalid characters. You can query for invalid characters by using the method. -or- is prefixed with, or contains, only a colon character (:). 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). contains a colon character (:) that is not part of a drive label ("C:\").
System.IO.PathPerforms operations on instances that contain file or directory path information. These operations are performed in a cross-platform manner.
Combine: string * string * string -> stringCombines three strings into a path. The first path to combine. The second path to combine. The third path to combine. The combined paths. , , or contains one or more of the invalid characters defined in . , , or is .
root: 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
ignore: 'T -> unitIgnore the passed value. This is often used to throw away results of a computation. The value to ignore. ignore 55555 // Evaluates to ()
System.IO.FileProvides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of objects.
WriteAllText: string * string -> unitCreates a new file, writes the specified string to the file, and then closes the file. If the target file already exists, it is overwritten. The file to write to. The string to write to the file. 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). An I/O error occurred while opening the file. 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- specified a directory. -or- The caller does not have the required permission. is in an invalid format. The caller does not have the required permission.
Combine: string * string * string * string -> stringCombines four strings into a path. The first path to combine. The second path to combine. The third path to combine. The fourth path to combine. The combined paths. , , , or contains one or more of the invalid characters defined in . , , , or is .
fsharpSources: string -> Flow<AppEnv,FileSystemError,string list>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 .
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
live: AppEnvshouldEqual: '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.
Path manipulation is also on the service: combine, getFullPath, getFileName, getExtension, getRelativePath,
getTempPath, getRandomFileName, and the rest. These are pure string operations on .NET, but routing them through
the service keeps platform-specific separator and rooting behaviour substitutable in tests.
Testing
IFileSystem is a wide interface, and implementing it in full to fake three calls is rarely worth it. Two approaches
work better:
Use FileSystem.live against a temporary directory. This is what Axial's own tests do, and what the examples on
this page do. The workflow exercises real I/O, and the test owns cleanup:
try
File.WriteAllText(Path.Combine(root, "app.json"), "orders")
loadOrDefault (Path.Combine(root, "app.json")) |> Flow.run live |> shouldEqual (Exit.Success { Name = "orders" })
finally
Directory.Delete(root, true)
System.IO.FileProvides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of objects.
WriteAllText: string * string -> unitCreates a new file, writes the specified string to the file, and then closes the file. If the target file already exists, it is overwritten. The file to write to. The string to write to the file. 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). An I/O error occurred while opening the file. 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- specified a directory. -or- The caller does not have the required permission. is in an invalid format. The caller does not have the required permission.
System.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 .
root: stringloadOrDefault: string -> Flow<AppEnv,FileSystemError,Config>(|>): '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.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
live: AppEnvshouldEqual: '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.
Name: stringSystem.IO.DirectoryExposes static methods for creating, moving, and enumerating through directories and subdirectories. This class cannot be inherited.
Delete: string * bool -> unitDeletes the specified directory and, if indicated, any subdirectories and files in the directory. The name of the directory to remove. to remove directories, subdirectories, and files in ; otherwise, . A file with the same name and location specified by exists. -or- The directory specified by is read-only, or is and is not an empty directory. -or- The directory is the application's current working directory. -or- The directory contains a read-only file. -or- The directory is being used by another process. The caller does not have the required permission. is a zero-length string, contains only white space, or contains one or more invalid characters. You can query for invalid characters by using the method. is . The specified path, file name, or both exceed the system-defined maximum length. does not exist or could not be found. -or- The specified path is invalid (for example, it is on an unmapped drive).
Wrap FileSystem.live to inject one failure. When the point of the test is error handling, delegate every member
to the live service and override the one that should fail, so every other member behaves as in production.
Fable
FileSystem.live is not compiled for Fable, and Layer.succeed FileSystem.live fails with PlatformNotSupportedException there.
See packages and platforms.
Related
- Service contracts: how a package declares the service it needs.
- Scopes and resources: deterministic cleanup for open handles.
- Error handling: expected failures against defects.

