You know, part of a possible solution for this would be a (possibly weird) enhancement to Json.Decode and Json.Encode, to deal with functions.
To back up a little, Json.Decode and Json.Encode are interesting for more than just what we traditionally think of as JSON. Instead, they are a kind of general-purpose way of moving values between arbitrary Javascript objects (Json.Encode.Value or Json.Decode.Value -- they are synonyms) and Elm types. And, that's exactly how elm-html uses them -- it supplies the event object, and we can construct Elm stuff from the event object by using Json.Decode.
However, Json.Decode and Json.Encode don't deal with functions ... that is, they aren't conscious of Javascript functions that might be attached to the Json.Encode.Value.
But, my intuition is that you could construct a perfectly reasonable API within Json.Decode for calling such functions. Here's a quick stab at it:
{-| Opaque type representing a Javascript function -}
type Function = Function
{-| Type representing a Javascript exception }
type Error = Error
... insert some API for extracting interesting stuff from an Error
{-| Extract a function -}
function : Decoder Function
{-| Call a function, using the supplied parameters. -}
call : List Json.Encode.Value -> Function -> Task Error Json.Decode.Value
Now, I'm sure there would be some complications, but I'm pretty confident that I can see how to implement all of that (using native code, of course). And, it may be a bit weird, but it might even be Elm-ish -- really, the only un-Elmish thing about it is that the Function can't be guaranteed to be pure, but that's handled by making calling the Function a Task.
Now, to get weirder yet, you could even imagine a similar API on the Json.Encode side, couldn't you? Something like:
{-| Encode a function -}
function : Function -> Value
{-| Produce a Javascript function given the function body. -}
makeFunction : String -> Result Error Function
{-| Produce a function given an Elm implementation. -}
elmFunction : (List Json.Encode.Value -> Json.Decode.Value) -> Function
Again, I'm sure there would be some complications, but I'm pretty sure I can see how to implement this.
Now, if we had stuff like this, I think it would actually eliminate the need for some kinds of "native" code. If there is stuff in Javascript-land we want to get, we can use `makeFunction` and then `call` to use it. And, if we need to supply an Elm function to Javascript in a way that Javascript can call, we can use `elmFunction`. And the use of Json.Encode/Decode for parameters and return types covers the type issues. Now, of course calling these functions could produce errors. But the `call` Task can wrap exceptions and do the right thing. So, we wouldn't get unhandled runtime exceptions.
Actually, I suppose `makeFunction` could lead to unhandled runtime exceptions if the resulting function is handed off to javascript as a callback. However, I suppose that `makeFunction` could also wrap the function with some exception handling, so that exceptions are logged and don't crash. Or, one could leave out `makeFunction` -- it's probably the weirdest part, and the other stuff would be useful even without it.
Now, this is actually pretty far from the original post -- my apologies for that! But, it set off a train of thought I might try to explore further.