Having trouble with this code:
let subscribe (nm : NamespaceManager) (subName : string) (desc : TopicDescription) : Async<SubscriptionDescription> =
let rec first_create () =
async {
let! exists = desc |> exists nm
if exists then return! (then_create_subscription () : Async<SubscriptionDescription>)
try
let beginCreate = nm.BeginCreateTopic : string * AsyncCallback * obj -> IAsyncResult
logger.DebugFormat("creating topic '{0}'", desc)
let! tdesc = Async.FromBeginEnd(desc.Path, beginCreate, nm.EndCreateTopic)
return! first_create ()
with | :? MessagingEntityAlreadyExistsException -> return! then_create_subscription () }
and then_create_subscription () : Async<SubscriptionDescription> =
async {
let beginCreate = nm.BeginCreateSubscription : string * string * AsyncCallback * obj -> IAsyncResult
return! Async.FromBeginEnd(desc.Path, subName, beginCreate, nm.EndCreateSubscription) }
first_create ()
On line 5, it underlines then_create_subscription () : Async<SubscriptionDescription> stating:
Type mismatch. Expecting a Async<unit> but given a Async<SubscriptionDescription> The type 'unit' does not match the type 'SubscriptionDescription'
Exists looks like this:
let exists (nm : NamespaceManager ) (desc : PathBasedEntity) =
async { return! Async.FromBeginEnd(desc.Path, nm.BeginTopicExists, nm.EndTopicExists) }
I want it to create the topic and then go on to create the subscription for it.
Any ideas?
There needs to be an else branch after if, otherwise the if is a statement, not an expression. The else is not implicit.
Related
How can I achieve something like this in a clean way?
let's imagine this simple code:
let a () = checkSomeStuff (); None
let b () = do Something (); Some "thing"
let c () = checkSomethingElse (); None
"getOne" {
do! a()
do! b()
do! c()
}
and it would return the first "Some".
I could achieve this exact behavior by using Result where I'd return the value through an Error and continue through with Ok, but that is not readable / nice:
let a () = checkSomeStuff (); Ok ()
let b () = do Something (); Error "thing"
let c () = checkSomethingElse (); Ok ()
result {
do! a()
do! b()
do! c()
}
this would work, but I'm looking to achieve that without mis-using the Result type. Can it be done with the existing expressions?
You don't need a computation expression for this. F# has a built-in function called Seq.tryPick that applies a given function to successive elements of a sequence, returning the first Some result, if any. You can use tryPick to define getOne like this:
let getOne fs =
fs |> Seq.tryPick (fun f -> f ())
Trying it with your example:
let a () = checkSomeStuff ();
let b () = Something ();
let c () = checkSomethingElse ();
let x = getOne [ a; b; c ]
printfn "%A" x // Some "thing"
Some time ago, I wrote a post about imperative computation expression builder that does something along those lines. You can represent computations as option-returning functions:
type Imperative<'T> = unit -> option<'T>
In the computation builder, the main thing is the Combine operation that represents sequencing of operations, but you need a few others to make it work:
type ImperativeBuilder() =
member x.ReturnFrom(v) = v
member x.Return(v) = (fun () -> Some(v))
member x.Zero() = (fun () -> None)
member x.Delay(f:unit -> Imperative<_>) =
(fun () -> f()())
member x.Combine(a, b) = (fun () ->
match a() with
| Some(v) -> Some(v)
| _ -> b() )
let imperative = new ImperativeBuilder()
You can then reimplement your example - to return a value, you just use return, but you need to combine individual operations using return!, because the builder does not support do!:
let a () = imperative { printfn "one" }
let b () : Imperative<string> = imperative { return "result" }
let c () = imperative { printfn "two" }
let f = imperative {
return! a()
return! b()
return! c()
}
f()
You could create a function that does what you want. But you have to think throughout what you want to do.
So, your logic is.
You execute a function that returns an option
Then you check that option. if it is None you execute another function, if it is Some you return the value.
A function like these could look like this:
let getSome f opt =
match opt with
| None -> f ()
| Some x -> Some x
With such a function, you then could write. ***
let x =
checkSomeStuff ()
|> getSome (fun _ -> Something () )
|> getSome checkSomethingElse
But then i think, hmmm.... isn't there a better name for getSome? In some way i want to say:
Execute some code and check if it is Some, or else pick the next thing.
With this in mind, i think. hmm.... isn't there already a Option.orElse? And yes! There is! There is also a Option.orElseWith function, that fits your need even better. So now, you can write.
let y =
checkSomeStuff ()
|> Option.orElseWith (fun _ -> Something () )
|> Option.orElseWith checkSomethingElse
If you have functions with side-effects, then you should use Option.orElseWith, otherwise, you can just sue Option.orElse
***: I assume you have the following function defined
let checkSomeStuff () =
None
let Something () =
Some "thing"
let checkSomethingElse () =
None
This is not for a practical need, but rather to try to learn something.
I am using FSToolKit's asyncResult expression which is very handy and I would like to know if there is a way to 'combine' expressions, such as async and result here, or does a custom expression have to be written?
Here is an example of my function to set the ip to a subdomain, with CloudFlare:
let setSubdomainToIpAsync zoneName url ip =
let decodeResult (r: CloudFlareResult<'a>) =
match r.Success with
| true -> Ok r.Result
| false -> Error r.Errors.[0].Message
let getZoneAsync (client: CloudFlareClient) =
asyncResult {
let! r = client.Zones.GetAsync()
let! d = decodeResult r
return!
match d |> Seq.filter (fun x -> x.Name = zoneName) |> Seq.toList with
| z::_ -> Ok z // take the first one
| _ -> Error $"zone '{zoneName}' not found"
}
let getRecordsAsync (client: CloudFlareClient) zoneId =
asyncResult {
let! r = client.Zones.DnsRecords.GetAsync(zoneId)
return! decodeResult r
}
let updateRecordAsync (client: CloudFlareClient) zoneId (records: DnsRecord seq) =
asyncResult {
return!
match records |> Seq.filter (fun x -> x.Name = url) |> Seq.toList with
| r::_ -> client.Zones.DnsRecords.UpdateAsync(zoneId, r.Id, ModifiedDnsRecord(Name = url, Content = ip, Type = DnsRecordType.A, Proxied = true))
| [] -> client.Zones.DnsRecords.AddAsync(zoneId, NewDnsRecord(Name = url, Content = ip, Proxied = true))
}
asyncResult {
use client = new CloudFlareClient(Credentials.CloudFlare.Email, Credentials.CloudFlare.Key)
let! zone = getZoneAsync client
let! records = getRecordsAsync client zone.Id
let! update = updateRecordAsync client zone.Id records
return! decodeResult update
}
It is interfacing with a C# lib that handles all the calls to the CloudFlare API and returns a CloudFlareResult object which has a success flag, a result and an error.
I remapped that type to a Result<'a, string> type:
let decodeResult (r: CloudFlareResult<'a>) =
match r.Success with
| true -> Ok r.Result
| false -> Error r.Errors.[0].Message
And I could write an expression for it (hypothetically since I've been using them but haven't written my own yet), but then I would be happy to have an asyncCloudFlareResult expression, or even an asyncCloudFlareResultOrResult expression, if that makes sense.
I am wondering if there is a mechanism to combine expressions together, the same way FSToolKit does (although I suspect it's just custom code there).
Again, this is a question to learn something, not about the practicality since it would probably add more code than it's worth.
Following Gus' comment, I realized it would be good to illustrate the point with some simpler code:
function DoA : int -> Async<AWSCallResult<int, string>>
function DoB : int -> Async<Result<int, string>>
AWSCallResultAndResult {
let! a = DoA 3
let! b = DoB a
return b
}
in this example I would end up with two types that can take an int and return an error string, but they are different. Both have their expressions so I can chain them as needed.
And the original question is about how these can be combined together.
It's possible to extend CEs with overloads.
The example below makes it possible to use the CustomResult type with a usual result builder.
open FsToolkit.ErrorHandling
type CustomResult<'T, 'TError> =
{ IsError: bool
Error: 'TError
Value: 'T }
type ResultBuilder with
member inline _.Source(result : CustomResult<'T, 'TError>) =
if result.IsError then
Error result.Error
else
Ok result.Value
let computeA () = Ok 42
let computeB () = Ok 23
let computeC () =
{ CustomResult.Error = "oops. This went wrong"
CustomResult.IsError = true
CustomResult.Value = 64 }
let computedResult =
result {
let! a = computeA ()
let! b = computeB ()
let! c = computeC ()
return a + b + c
}
Have I correctly implemented map for Task?
let map continuation (t: Task<'A>) =
t.ContinueWith(fun (antecedent: Task<'A>) ->
if antecedent.Status <> TaskStatus.Canceled &&
antecedent.Status <> TaskStatus.Faulted then
continuation antecedent.Result
else
raise antecedent.Exception // must I?
)
I got the TaskStatus checks from the docs. I feel most uncertain about raise antecedent.Exception, but I can't think of another way to handle it.
As background, yes I'm aware of Async, but my current stack uses Entity Framework and Blazor, so I have a backend that uses things like .ToListAsync() and a front end in C#, so I'd rather just not deal with converting from Task to Async then back again.
I would suggest implementing your solution in terms of the interfaces behind the concept of awaitable in the TPL, namely INotifyCompletion and ICriticalNotifyCompletion. Also, to implement map correctly, you should really do it in terms of bind. This is something that there are already some existing solutions for in F#, such as the TaskBuilder library. Personally, I have been using the following in a library for years without any issues:
open System.Runtime.CompilerServices
open System.Threading.Tasks
type TaskStep<'result> =
| Value of 'result
| AsyncValue of 'result Task
| Continuation of ICriticalNotifyCompletion * (unit -> 'result TaskStep)
and StateMachine<'a>(firstStep) as this =
let methodBuilder = AsyncTaskMethodBuilder<'a Task>()
let mutable continuation = fun () -> firstStep
let nextAwaitable() =
try
match continuation() with
| Value r ->
methodBuilder.SetResult(Task.FromResult(r))
null
| AsyncValue t ->
methodBuilder.SetResult(t)
null
| Continuation (await, next) ->
continuation <- next
await
with
| exn ->
methodBuilder.SetException(exn)
null
let mutable self = this
member __.Run() =
methodBuilder.Start(&self)
methodBuilder.Task
interface IAsyncStateMachine with
member __.MoveNext() =
let mutable await = nextAwaitable()
if not (isNull await) then
methodBuilder.AwaitUnsafeOnCompleted(&await, &self)
member __.SetStateMachine(_) =
()
type Binder<'out> =
static member inline GenericAwait< ^abl, ^awt, ^inp
when ^abl : (member GetAwaiter : unit -> ^awt)
and ^awt :> ICriticalNotifyCompletion
and ^awt : (member get_IsCompleted : unit -> bool)
and ^awt : (member GetResult : unit -> ^inp) >
(abl : ^abl, continuation : ^inp -> 'out TaskStep) : 'out TaskStep =
let awt = (^abl : (member GetAwaiter : unit -> ^awt)(abl))
if (^awt : (member get_IsCompleted : unit -> bool)(awt))
then continuation (^awt : (member GetResult : unit -> ^inp)(awt))
else Continuation (awt, fun () -> continuation (^awt : (member GetResult : unit -> ^inp)(awt)))
module TaskStep =
let inline bind f step : TaskStep<'a> =
Binder<'a>.GenericAwait(step, f)
let inline toTask (step: TaskStep<'a>) =
try
match step with
| Value x -> Task.FromResult(x)
| AsyncValue t -> t
| Continuation _ as step -> StateMachine<'a>(step).Run().Unwrap()
with
| exn ->
let src = new TaskCompletionSource<_>()
src.SetException(exn)
src.Task
module Task =
let inline bind f task : Task<'a> =
TaskStep.bind f task |> TaskStep.toTask
let inline map f task : Task<'b> =
bind (f >> Value) task
FsToolkit.ErrorHandling implements it here. I'll paste the current version below as it's quite short. It uses the TaskBuilder library Aaron mentioned.
module Task =
let singleton value = value |> Task.FromResult
let bind (f : 'a -> Task<'b>) (x : Task<'a>) = task {
let! x = x
return! f x
}
let map f x = x |> bind (f >> singleton)
Additionally, FSharpPlus has an independent implementation of Task.map here.
Throwing the exception again in the continuation would make for an incorrect stack trace.
It's a mapping from 'A -> 'B, so it's probably best to lay it out explicitly.
let rec map (continuation: 'A -> 'B) (t: Task<'A>) =
let rec map_resolved (task: Task<'A>) =
match task.Status with
| TaskStatus.RanToCompletion -> Task.FromResult(continuation task.Result)
| TaskStatus.Faulted -> Task.FromException<'B>(task.Exception)
| TaskStatus.Canceled -> Task.FromCanceled<'B>(CancellationToken.None)
| _ -> task.ContinueWith(map_resolved).Unwrap()
map_resolved t
So I currently have a sequence of type seq<System.Threading.Tasks.Task<Restaurant>> and I want to turn it into a sequence of type seq<Restaurant>.
I'm currently using TaskBuilder.fs library and from my research, I need to use either let! or do! for this situation but they require task {} which when used with Seq.map bring back the same Task type.
let joinWithReviews (r : Restaurant) =
task {
let! reviewResult = Reviews.Database.getByLocationId cnf.connectionString r.Restaurant_Id
match reviewResult with
| Ok reviewResult ->
let restaurant = { r with Reviews = (List.ofSeq reviewResult)}
return restaurant
| Error ex ->
return raise ex
}
let indexAction (ctx : HttpContext) =
task {
let (cnf:Config) = Controller.getConfig ctx
let! result = Restaurants.Database.getAll cnf.connectionString
match result with
| Ok result ->
let restaurantWithReviews = (Seq.map joinWithReviews result)
return index ctx (List.ofSeq restaurantWithReviews)
| Error ex ->
return raise ex
}
So my result is of type Seq<Restaurant> and I need to add reviews to each restaurant so I use Seq.map to get restaurantWithReviews which is type seq<System.Threading.Tasks.Task<Restaurant>> which I won't be able to use.
The .NET method System.Threading.Tasks.Task.WhenAll will convert seq<Task<'a>> to Task<'a[]>. You can get the result with let! if you're inside a task { } block.
let restaurants: seq<Restaurant>
let! withReviews: Restaurant[] =
restaurants
|> Seq.map joinWithReviews
|> Task.WhenAll
I have an F# 3.0 agent wrapped in a class:
type AgentWrapper() =
let myAgent = Agent.Start(fun inbox ->
let rec loop (state: int) =
async {
let! (replyChannel: AsyncReplyChannel<int>) = inbox.Receive()
let newState = state + 1
replyChannel.Reply newState
return! loop newState
}
loop 0 )
member private this.agent = myAgent
member this.Send () =
this.agent.PostAndReply (fun replyChannel -> replyChannel)
When I send messages to it as follows:
let f = new AgentWrapper ()
f.Send () |> printf "Reply: %d\n"
f.Send () |> printf "Reply: %d\n"
f.Send () |> printf "Reply: %d\n"
I get the expected responses:
Reply: 1
Reply: 2
Reply: 3
However, if I remove the let binding for the agent and directly assign it to the this.agent property:
type AgentWrapper() =
member private this.agent = Agent.Start(fun inbox ->
let rec loop (state: int) =
async {
let! (replyChannel: AsyncReplyChannel<int>) = inbox.Receive()
let newState = state + 1
replyChannel.Reply newState
return! loop newState
}
loop 0 )
member this.Send () =
this.agent.PostAndReply (fun replyChannel -> replyChannel)
I then get the responses:
Reply: 1
Reply: 1
Reply: 1
I've been staring at this for hours and I can't understand why the agent is getting re-initialised every time I call AgentWrapper.Send. It feels like this.agent is getting reassigned every time I call it (i.e. acting like a method, not a property). What am I missing?
It feels like this.agent is getting reassigned every time I call it
(i.e. acting like a method, not a property). What am I missing?
This is exactly what happens, and is documented in the spec (relevant part from 18.13.1 follows)
Static and instance property members are evaluated every time the
member is invoked. For example, in the following, the body of the
member is evaluated each time C.Time is evaluated:
type C () =
static member Time = System.DateTime.Now
This is analgous to your situation