SAFE-Stack / SAFE-template

dotnet CLI template for SAFE project
MIT License
282 stars 87 forks source link

Boxing / unboxing: unboxed list has length 0 (unboxing as an array works however) #507

Closed reneederer closed 1 year ago

reneederer commented 2 years ago

Hi,

from the server, I return a boxed list

box [ DateTimeOffset.MinValue; DateTimeOffset.MaxValue ]

On the client, unboxing via

let datesAsList = unbox<DateTimeOffset list> datesFromServer
printfn $"datesAsList: length: {datesAsList.Length}, dates: {datesAsList}"

let datesAsArray = unbox<DateTimeOffset array> datesFromServer
printfn $"datesAsArray: length: {datesAsArray.Length}, dates: {datesAsArray}"

prints

datesAsList: length: 0, dates: 0001-01-01T00:00:00+00:00,9999-12-31T23:59:59.9999999+00:00
datesAsArray: length: 2, dates: 0001-01-01T00:00:00+00:00,9999-12-31T23:59:59.9999999+00:00

I would have expected: datesAsList has length 2, datesAsArray throws an exception.

Furthermore, trying to access datesAsArray.[0].DateTime throws

Uncaught TypeError: date.getTime is not a function
    at fromDateTimeOffset (Date.js?a63a:161:1)
    at eval (SearchTable.fs?840b:498:51)
    at map (Array.js?34b9:77:1)

I found an ugly workaround with

unbox<string array> datesFromServer
|> Array.map DateTimeOffset.Parse
|> List.ofArray
Zaid-Ajaj commented 2 years ago

@reneederer Why do you return a boxed list from the server?

reneederer commented 2 years ago

On the client, I need to display a table with different column types. One of the columns has cells that contain list of dates. I fetch the table rows from the server as list<list<obj>>, and in the date-column-case, obj is list<DateTimeOffset> Capture

Is this understandable? Can I solve it without boxing/unboxing?

Zaid-Ajaj commented 2 years ago

@reneederer I see, working with dynamic data can be annoying. Note that on the client side, unbox doesn't do anything at runtime! This is not a bug, that's by design. So depending on the shape of data (list<list<obj>> in this case) the runtime representation might not be unboxable to the type you want.

I do suggest you use a proper type for sending data from server to client:

[<RequireQualififedAccess>]
type TableField = 
  | Text of string
  | Date of DateTimeOffset
  | List of TableField list

type TableRow = TableRow of Map<string, TableField>

type DatabaseApi = {
  events : unit -> Async<TableRow list>
}

Then on the client, you can try to render the data in the table or I am guessing you are using a component? Then you would have to write a utility function that converts the data into array<array<obj>> before giving that to the component

reneederer commented 2 years ago

Ok, thanks for the quick answer!