How do I solve encoder problem with the Elmish Debugger?

231 Views Asked by At

I've got an application that I've built in SAFE-Stack using websockets, more or less following the approach here: https://github.com/CompositionalIT/safe-sockets

It works fine but the Elmish debugger doesn't like the type of WsSender in this example:


type ConnectionState =
        | DisconnectedFromServer
        | ConnectedToServer of WsSender
        | Connecting

        member this.IsConnected =
            match this with
            | ConnectedToServer _ -> true
            | DisconnectedFromServer | Connecting -> false

    and WsSender = Msg -> Unit

giving the following error message in the Browser Console:

ElmishDebuggerMessage

Can anyone tell me how to go about fixing this issue? (Assuming it's fixable and that I've diagnosed the problem correctly.) Thanks.

2

There are 2 best solutions below

0
On BEST ANSWER

you see this error because of Elmish.Debugger using Thoth.Json to serialize your Msg/Model to a JSON format.

The type WsSender can't be represented in a JSON format because it is a function. So Thoth.Json is asking you to explain how it should encode this type.

You can do that by creating what is called an extraCoder like that:

In your case, you will have to create a fake encoder/decoder "just" to make the Debugger happy.

module CustomEncoders =

    let wsSenderEncoder (_ : WsSender) = Encode.string "WsSender function"

    let wsSenderDecoder = Decode.fail "Decoding is not supported for WsSender type"

    let myExtraCoders =
        Extra.empty
        |> Extra.withCustom wsSenderEncoder wsSenderDecoder 


    let modelEncoder = Encode.Auto.generateEncoder(extra = myExtraCoders)
    let modelDecoder = Decode.Auto.generateDecoder(extra = myExtraCoders)

In your Program creation, you should replace Program.withDebugger by Program.withDebuggerCoders and give it the encoder and decoder you created.

Program.withDebuggerCoders CustomEncoders.modelEncoder CustomEncoders.modelDecoder
0
On

I had a bit of a play around to try and come up with something that would make it easier to have multiple extra coders if required. This seems to work - thought it might be helpful to others.


module CustomEncoders =

    let inline addDummyCoder<'b> extrasIn =
        let typeName = string typeof<'b>
        let simpleEncoder(_ : 'b) = Encode.string (sprintf "%s function" typeName)
        let simpleDecoder = Decode.fail (sprintf "Decoding is not supported for %s type" typeName)
        extrasIn |> Extra.withCustom simpleEncoder simpleDecoder

    let inline buildExtras<'a> extraCoders =
        let myEncoder:Encoder<'a> = Encode.Auto.generateEncoder(extra = extraCoders)
        let myDecoder:Decoder<'a> = Decode.Auto.generateDecoder(extra = extraCoders)
        (myEncoder, myDecoder)

type TestType = Msg -> Unit
type TestType2 = string -> Unit

let extras = Extra.empty
                |> CustomEncoders.addDummyCoder<TestType>
                |> CustomEncoders.addDummyCoder<TestType2>
                |> CustomEncoders.buildExtras<Model.Model>


#if DEBUG
open Elmish.Debug
open Elmish.HMR
#endif


Program.mkProgram Model.init Model.update View.render
|> Program.withSubscription subs 
#if DEBUG
|> Program.withConsoleTrace
#endif
|> Program.withReactBatched "elmish-app"
#if DEBUG
|> Program.withDebuggerCoders (fst extras) (snd extras)
#endif
|> Program.run


If anyone has a better idea of how to do it, I'd be happy to update this answer with their suggestions. Also, the apostrophe in the generic type seems to upset the code prettifier above - do I need to do something to fix that?