Fable
Effects can be used in F# compiled to JavaScript with Fable: Effect, eff and the Effect
module work in Fable 5, for JavaScript. Support is experimental: what's there behaves as on .NET, and is tested on both,
but it covers the core of Orsak, not all of it.
Setup
Reference the Orsak package, as on .NET:
|
The package carries the sources Fable compiles, and Fable chooses them itself. An effect written once, in a project shared by a .NET server and a Fable client, compiles on both:
type IGreeter =
abstract Greet: string -> string
let greet name = Effect.Create(fun (g: #IGreeter) -> g.Greet name)
let greetAll names = eff {
let! greetings = names |> List.map greet |> Effect.par
return greetings
}
// in the Fable client
task {
match! greetAll [ "Fable"; "Node" ] |> Effect.run (Greeter()) with
| Ok greetings -> printfn "%A" greetings
| Error e -> printfn "failed: %s" e
}
|> ignore
What differs from .NET
-
Effects run on
Task, which Fable compiles to a JavaScript promise, rather thanValueTask, which Fable doesn't have:Effect.runreturns aTask<Result<'a, 'e>>. Code that usesAsyncResult<'a, 'e>, the type of an effect's result, compiles on both. -
*
effis an ordinary computation expression,* rather than a compiled state machine, as Fable can't translate F#'s resumable code. It binds effects, tasks,AsyncandResult, and hasif,while,forover sequences,try,useandand!. -
Concurrent, not parallel. JavaScript runs one thing at a time, so
Effect.par,whenAll,parLimitedandracerun their effects concurrently, interleaved where they wait, but never in parallel. What they return is the same as on .NET. -
No type tests against interfaces. In JavaScript,
:? IClockcompiles tofalse, without a warning, so an effect that checks whether its environment implements an interface never finds it. Require the interface instead, asEffect.Create(fun (clock: #IClock) -> ...)does.
What is .NET only
Effect.fanOut, effect sequences (effSeq,EffSeq) andEffect.Createof aValueTask;forover anIAsyncEnumerable, anduseof anIAsyncDisposable;- the base effects (
Time,Log,CancellationSource, ...),Orsak.Resilience, scopes andCancellationScope; - Orsak.AspNetCore, Orsak.AspNetCore.OpenApi and Orsak.Myriad.
Some may follow; Supported frameworks lists what each package targets.
Multiple items
val string: value: 'T -> string
--------------------
type string = System.String
val string: value: 'T -> string
--------------------
type string = System.String
Multiple items
module List from Microsoft.FSharp.Collections
--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
module List from Microsoft.FSharp.Collections
--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
val map: mapping: ('T -> 'U) -> list: 'T list -> 'U list
val task: TaskBuilder
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>
val ignore: value: 'T -> unit
Orsak