I have the following code that I want to review
printfn "calling openai embeddings API"
let sw = System.Diagnostics.Stopwatch ()
sw.Start ()
let embs : MyRedis.RedisEmbedding array =
docs
|> MyOpenAI.rebuild2EmbeddingsSlow
// here the part of the code to be defined and reviewed
sw.Stop ()
printfn "Artificially slowed down to %f" (sw.Elapsed.TotalSeconds)
The goal of the code is to show the timing of the sequential execution of an array of tasks in the log and possibly also to guarantee that the execution is sequential and not parallel, as it would be, I think, with a simpler Task.WhenAll
.
Though I 've had to introduce a mutable
accumulator because Task.WhenAll
returns a Task
that is not yet executed and I was not able to insert the timing part within it.
Also, something like
|> Array.fold (fun s t ->
printfn "slow down - intermediate elapsed start %f " (sw.Elapsed.TotalSeconds)
let! e1 = t
printfn "intermediate elapsed end %f " (sw.Elapsed.TotalSeconds)
Array.concat [| e1; s |]
)
would not compile because of the let!
But I want to be sure that the following code is F# idiomatic and that the usage of a mutable
keyword is unavoidable.
printfn "calling openai embeddings API"
let sw = System.Diagnostics.Stopwatch ()
sw.Start ()
let embs : MyRedis.RedisEmbedding array =
docs
|> MyOpenAI.rebuild2EmbeddingsSlow
|> Array.fold (fun s t ->
let mutable acc = [||]
printfn "slow down - intermediate elapsed start %f " (sw.Elapsed.TotalSeconds)
task {
let! e1 = t
acc <- Array.append e1 s
} |> Task.WaitAll
printfn "intermediate elapsed end %f " (sw.Elapsed.TotalSeconds)
acc
)
[| |]
sw.Stop ()
printfn "Artificially slowed down to %f" (sw.Elapsed.TotalSeconds)
I think that the let!
binding cannot be used directly inside the Array.fold
function as it is outside the computation expression, hence the code does indeed use a mutable
variable acc correctly within the Array.fold
function and the mutable
variable is necessary to accumulate the embeddings in the array.
Is that agreeable? Is the code F# idiomatic?
Here's an example of creating an array of F# asyncs from underlying .NET tasks, and then executing them sequentially.
To start with, I wrote a dummy function that mimics a call to OpenAI's API:
let openAiTask i =
task {
do! Task.Delay(1000)
return! Task.FromResult i
}
Then I created a batch of F# async calls to the API:
let asyncs =
let sw = System.Diagnostics.Stopwatch()
sw.Start()
[| 1..10 |]
|> Array.map (fun i ->
async {
printfn "%A" sw.Elapsed.TotalSeconds
return! openAiTask i |> Async.AwaitTask
})
Now I can execute the async calls sequentially whenever I want:
let results =
Async.Sequential asyncs
|> Async.RunSynchronously
printfn "%A" results
Note that none of the API calls occur until I explicitly run them. Output is something like:
0.0238377
1.0503682
2.0602829
3.0719534
4.0832107
5.0838953
6.0951127
7.1090538
8.1239493
9.1347231
[|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]