Skip to content

Commit

Permalink
Enable callback evaluation for boxed IConvertible sequences aswell
Browse files Browse the repository at this point in the history
  • Loading branch information
kMutagene committed Feb 4, 2021
1 parent 66ba10d commit 49fcae9
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 4 deletions.
4 changes: 2 additions & 2 deletions dev/Program.fs
Expand Up @@ -74,8 +74,8 @@ let callbackArrayInput =
],
(fun (inputs:float []) (nclicks:float) ->
[
"output-1" @. Children => (Array.last inputs) * nclicks * 1.
"output-2" @. Children => (Array.last inputs) * nclicks * 2.
(Array.last inputs) * nclicks * 1.
(Array.last inputs) * nclicks * 2.
]
),
State = [
Expand Down
31 changes: 29 additions & 2 deletions src/Dash.NET/Callback.fs
Expand Up @@ -367,9 +367,12 @@ type Callback<'Function>

match r with
| :? seq<CallbackResultBinding> as bindings ->
// The result is a sequence of boxed CallbackResultBindings

CallbackResponse.multiOut(bindings)

| :? seq<obj> as boxedResults ->
// The result of the multioutput is a sequence of boxed types

let outputs =
handler?outputDependencies
Expand All @@ -381,13 +384,37 @@ type Callback<'Function>
outputs
|> Seq.zip boxedResults
|> Seq.map (fun (boxedRes, output) -> output.Id, output.Property, boxedRes)

)

else
failwithf "amount of multi callback outputs did not match to the actual callback binding (expected %i but got %i)" (Seq.length outputs) (Seq.length boxedResults)

| _ -> failwithf "multi callback result %O was not a collection." r
| r when (isIConvertibleSeq (r.GetType())) ->
// The result of the multioutput is a boxed sequence of IConvertibles, e.g.
// [2;3;4], [|"1";"2"|]; seq{2.;3.}

let primitiveSeq =
r
|> unbox<System.Collections.IEnumerable>
|> Seq.cast<IConvertible>

let outputs =
handler?outputDependencies
|> unbox<seq<CallbackOutput>>

if (Seq.length outputs) = (Seq.length primitiveSeq) then

CallbackResponse.multiOut(
outputs
|> Seq.zip primitiveSeq
|> Seq.map (fun (result, output) -> output.Id, output.Property, (box result))

)

else
failwithf "The amount of multi callback outputs returned by the callback function did not match to the amount of outputs defined by the callback dependency (expected %i vs %i). Make sure that the callback function returns a collection of results of length %i" (Seq.length primitiveSeq) (Seq.length outputs) (Seq.length outputs)

| _ -> failwithf "multi callback result %O of type %O was not a supported collection (supported: seq<IConvertible>, seq<obj>, seq<CallbackResultBinding>). You might be able to circumvent this problem by boxing the return values of your results to generate a seq<obj>." r (r.GetType())

else

Expand Down
17 changes: 17 additions & 0 deletions src/Dash.NET/DynamicInvoke.fs
Expand Up @@ -31,6 +31,23 @@ module DynamicInvoke =
f
loop t

let isIConvertible (t:Type) =
t.GetInterfaces()
|> Array.exists (fun t -> t = typeof<IConvertible>)

let isSeq (t:Type) =
t.GetInterfaces()
|> Array.exists (fun t -> t = typeof<System.Collections.IEnumerable>)

let isIConvertibleSeq (t:Type) =
if isSeq t then
let genericArgs = t.GetGenericArguments()
if genericArgs.Length > 0 then
isIConvertible genericArgs.[0]
else
false
else
false

//This function is (as far as i see it) a 'necessary evil' for solving the problem of callbacks having arbitrary amounts of parameters.
//However, just like DynamicObj in Plotly.NET, it is definately usable when correctly encapsulated to prevent direct usage.
Expand Down

0 comments on commit 49fcae9

Please sign in to comment.