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
Related
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
}
Assuming I have the following pseudo-C# code:
TResult MyMethod()
{
var firstTry = SomeExpensiveComputation1();
if (firstTry.IsSuccessful) return firstTry;
var secondTry = SomeExpensiveComputation2();
if (secondTry.IsPartiallySuccessful)
{
var subTry1 = SomeExpensiveComputationOn2_1(secondTry);
if (subTry1.IsSuccessful) return subTry1;
var subTry1 = SomeExpensiveComputationOn2_2(secondTry);
if (subTry1.IsSuccessful) return subTry1;
}
return LastExpensiveComputationThatNeverFails();
}
If I were to do this in F#, it'd look like this:
let MyMethod () =
let firstTry = SomeExpensiveComputation1 ()
if firstTry.IsSuccessful then firstTry else
let secondTry = SomeExpensiveComputation2 ()
if secondTry.IsSuccessful then
let subTry1 = SomeExpensiveComputationOn2_1 ()
if subTry1.IsSuccessful then subTry1 else
let subTry2 = SomeExpensiveComputationOn2_2 ()
if subTry2.IsSuccessful then subTry2 else LastExpensiveComputationThatNeverFails ()
else
LastExpensiveComputationThatNeverFails()
As you can see above, I had to repeat LastExpensiveComputationThatNeverFails twice. This doesn't have to be a method call, it can be many lines of inline computations (e.g. try to get some value from cache, if it doesn't exist calculate it.) One could refactor the code into another function, but I still don't like how the same code, even if it's just one line, has to be written twice (or more), as it leads to duplication and messy maintenance. What is the correct way to write such code in F#?
I think it's fine to make LastExpensiveComputationThatNeverFails a local function that is called whenever the result is needed.
However, one could also change the operations to return Option<_> and use the built-in combinator functions.
let MyMethod () =
SomeExpensiveComputation1 ()
|> Option.orElseWith
( fun () ->
SomeExpensiveComputation2 ()
|> Option.bind (fun _ -> SomeExpensiveComputationOn2_1 () |> Option.orElseWith SomeExpensiveComputationOn2_2)
)
|> Option.orElseWith LastExpensiveComputationThatNeverFails
Option.orElseWith LastExpensiveComputationThatNeverFails is only executed if the previous result is None which it will be upon failure.
I'm having a bit of trouble with the following FSharp/F# code:
module File1
let api a =
printf ("VALUE = %A") a
let router ops =
[|
api (ops (fun (list, _) -> list()))
api (ops (fun (_, get) -> get 1))
|]
let withContext ops handler =
let context = "CONTEXT"
handler (ops context)
let operations context =
printf ("CONTEXT = %s") context
let list () = [|1;2;3|]
let get id = "Test"
(list, get)
let setup() =
let ops = withContext operations
router ops
Results in the following error
Results in the following compation error
Error 1 Type mismatch. Expecting a
((unit -> int []) * (int -> int []) -> int []) -> 'a
but given a
((unit -> int []) * (int -> string) -> 'b) -> 'b
The type 'int []' does not match the type 'string'
I know the problem is that ops function has been bound to return a int[] but I want to be able to also return a string.
I think I'm missing a trick with some generic declarations but after hours of moving code around I can’t seem to work it out.
(I've simplified the code to highlight my problem)
The error is because ops needs to have a return type of its handler resolved at compilation, and you want to return different types base on some run-time logic.
It is basically an equivalent of:
let fun1 switch arg2 arg3 =
if switch then
arg2
else
arg3
and you want to run it this way:
fun1 true 1 "string"
Of course, arg2 and arg3 need to have the same type, so it won't work
What you can do is to run "api" function on a handler result, before returning it (so it will always the same type - unit).
let router ops =
[|
ops (fun (list, _) -> api <| list()))
ops (fun (_, get) -> api <| get 1))
|]
Alternatively, you could return objects of discriminated union type (then you will need some more logic in api function).
(Technically, you could also return obj).
Bonus
You don't need the array of units to be returned in a router function, returning one unit is just fine:
let router ops =
ops (fun (list, _) -> api <| list()))
ops (fun (_, get) -> api <| get 1))
In this way, setup function will also return unit and you will be able to run it without a need to run ignore on the result to get rid of This expression should have type 'unit', but has type 'unit[]' warning.
Your code is hard for me to understand, but I think the basic issue is that you want withContext to have a "rank 2" type (so that the universal quantification of the type variable 'b can happen after the application of the first argument). In F#, this can be accomplished by creating a new type with a generic method and using that:
let api a =
printf ("VALUE = %A") a
type Handler<'a> = abstract Handle<'b> : f:('a->'b) -> 'b
let router (ops:Handler<_>) =
[|
api (ops.Handle (fun (list, _) -> list()))
api (ops.Handle (fun (_, get) -> get 1))
|]
let withContext ops =
let context = "CONTEXT"
{ new Handler<_> with member __.Handle f = f (ops context) }
let operations context =
printf ("CONTEXT = %s") context
let list () = [|1;2;3|]
let get id = "Test"
(list, get)
let setup() =
let ops = withContext operations
router ops
I have the following code snippet using the reactive extensions:
let value : 't = ...
Observable.Create<'t>(fun observer ->
let subject = new BehaviorSubject<'t>(value)
let d0 = subject.Subscribe(observer)
let d1 = observable.Subscribe(subject)
new CompositeDisposable(d0, d1) :> IDisposable
)
This works. However if I drop the upcast to IDisposable then the code fails
to compile, citing ambiguous overloads. However CompositeDisposable is an
IDisposable. Why is the type inference engine failing to resolve this? Note I use this pattern almost all the time in C# returning CompositeDisposable from Observable.Create without having to upcast.
As #kvb said, functions don't support variance so upcast is required for interfaces and subclasses.
Here is a small example demonstrating the behavior with subclasses:
type A() =
member x.A = "A"
type B() =
inherit A()
member x.B = "B"
let f (g: _ -> A) = g()
let a = f (fun () -> A()) // works
let b = f (fun () -> B()) // fails
If function f is written by you, adding type constraints could help:
// This works for interface as well
let f (g: _ -> #A) = g()
let a = f (fun () -> A()) // works
let b = f (fun () -> B()) // works
Otherwise, you have to do a litle upcast as your example described.
EDIT: Since F# 6.0, auto-upcasting of interfaces and subclasses is now supported by default.
So I just wanted to ask why this works :
let internal X th =
foo()
th()
bar()
let Start() =
X <| fun () -> ( foo(); bar(); etc... )
And this doesn't work :
let internal XD A =
let X th =
foo()
th()
bar()
(X <| fun () -> A)
let Start() =
XD ( foo(); bar(); etc... )
it's looking like the same for me but first variant works as wrapper and I completely can't understand how second variant works.
I suppose that the confusing thing is that in your second version, the variable A is just a unit. The F# compiler infers this from the fact that you return A from a function that's used as th and the type of th is unit -> unit. This means that foo is called in Start before stepping in XD.
However, it is a bit difficult to tell what results were you expecting. Did you want to pass foo to XD as a function, instead of calling it immediately? If yes, then you'd need:
let internal XD A =
let X th =
foo()
th()
bar()
(X <| fun () -> A()) // Change: Call A with unit argument: 'A ()'
XD foo // Change: Pass a function instead of calling it
The below is the correct code for 2nd version for what you want to achieve (without lambda using lazy values).
let internal XD (A:Lazy<unit>) =
let X th =
foo()
th()
bar()
X <| (fun () -> A.Force())
let Start() =
XD ( lazy(foo(); bar();) )