I take it there is no way to send Bytes through a port:
38| port sendBytes : Bytes.Bytes -> Cmd msg
^^^^^^^^^
I cannot handle that. The types that CAN flow in and out of Elm include:
Ints, Floats, Bools, Strings, Maybes, Lists, Arrays, tuples, records, and
JSON values.
These functions could be added to elm-json:
-- In Json.Decode
bytes : Decoder Bytes
-- In Json.Encode
bytes : Bytes -> Value
They would map onto the underlying DataView or ArrayBuffer on the JS side.
Can anyone see reasons why allowing this would be a bad idea? Would this be a worthwhile addition to the platform?
In general I think this should be added. Converting to anything else to pass it over a port negates any efficiency benefits of using bytes. There have been several cases where people ran into this limitation.
I’m not sure if adding to elm/json makes sense (creates a dependency to elm/bytes for elm/json).
A problem is that the elm-bytes package currently uses a DataView under the hood, which is a view on a byte array (i.e. elements are 8 bits wide). Therefore it is not obvious what should happen to a typed array with larger elements (e.g. Int32Array). Silently converting seems like a bad idea, but really any kind of conversion is bad because the whole goal of these data types is to be efficient. So either we don’t support typed arrays for the moment, or we need to be able to efficiently get them into the elm world.
We could also only support ArrayBuffer/DataView. A little against the elm spirit of finding a good general solution (that covers all low-level array types), but it would be practical.
I ran into this issue recently, I was using a js package to parse a binary file format.
I worked around it by converting the bytes into a base64 string and passing that through the port, but I agree that it would have been nice to pass the bytes straight through the port
It has also been suggested that passing through as a list of ints might be more performant than base64. I will likely use one of these solutions despite being quite horrible. Interested to understand if we can have a better way though.
Where would you put these functions if not in elm/json? If Bytes is now a fundamental data type in Elm, I don’t really see having it as a common dependency is much of an issue. Perhaps Bytes should even migrate into elm/core?
I think here you are talking about the case where on the JS side you have an Int32Array that you are passing into a port. When wrapped by DataView this gets presented as an 8-bit byte array. I also notice that elm/bytes understands what the host endianness is. So if you were to call Bytes.Decode.signedInt32 on these Bytes it should correctly decode to the right integer value.
The same argument could be made about elm/json. In fact, you cannot compile an Elm program without referencing elm/json as a (indirect) dependency, even though elm/core doesn’t depend on it.
The reason elm/json is such a fundamental package is because flags and ports can automatically generate code to decode/encode Javascript objects to Elm values and vice-versa. However, Javascript objects are infinitely more powerful than JSON. This thread for example asks for the ability to pass Bytes to Javascript, which have no native representation in JSON, but could be represented as a Javascript object (e.g. the native DataView or some TypedArray). Before elm/file existed, we (at work) passed raw Json.Encode.Values from the event handler through the port to process the Javascript File object there.
So, maybe we need to seperate communication with JSON things (like Web APIs) and communication with the host environment (basically the browser, so Javascript) more?
Its a good point. If you really did want to put binary data in JSON format for sending over a Web API, you would most likely base64 encode it. This is already perfectly possibly with the existing APIs.
Perhaps what is needed is simply to allow Bytes to be passed over ports, but not to bother with encoders/decoders that turn them into Values representing javascript objects. The Bytes would have to be top-level arguments to the port functions and not embedded deeper as a field of some object.