Hiding the process of decoding JSON from a port

I have a type alias like this:

type alias Comment =
    { content : String
    , timestamp : Time.Posix
    }

The problem when creating a port like this…

port receiveComments : (List Comment -> msg) -> Sub msg

is that I get the error The 'receiveComments' port is trying to transmit a 'Posix' value:

I can successfully decode a value from the port using port receiveComments : (Json.Encode.Value -> msg) -> Sub msg and a little help with some decoder utils like these…

timeDecoder : Json.Decode.Decoder Time.Posix
timeDecoder =
    Json.Decode.int |> Json.Decode.andThen timeHelper


timeHelper : Int -> Json.Decode.Decoder Time.Posix
timeHelper time =
    if time < 0 then
        Json.Decode.fail "Cannot decode time less than zero."

    else
        Json.Decode.succeed <| Time.millisToPosix time


commentDecoder : Json.Decode.Decoder Comment
commentDecoder =
    Json.Decode.map2 Comment
        (Json.Decode.field "content" Json.Decode.string)
        (Json.Decode.field "timestamp" timeDecoder)

However, I’d like to hide all the gritty details of decoding a Comment from JSON and allow other users of this port to subscribe to something with the equivalent of receiveComments : (List Comment -> msg) -> Sub msg. Is it somehow possible to wrap my port in a function or otherwise achieve this result?

I think you could define a separate function with the signature you want that would wrap the decoding process. Something along the lines of:

port rawReceiveComments : (Decode.Value -> a) -> Sub a

realReceiveComments : (List Comment -> msg) -> Sub msg
realReceiveComments tagger =
  rawReceiveComments (tagger << Result.withDefault [] << Decode.decodeValue decoder)
4 Likes

That’s the ticket! Thank you! Still wrapping my head around all of the function composition stuff.

This could also be written without using the composition operators with either a lambda:

realReceiveComments : (List Comment -> msg) -> Sub msg
realReceiveComments tagger =
  rawReceiveComments (\value ->
    value
      |> Decoder.decodeValue decoder
      |> Result.withDefault []
      |> tagger
  )

or extracting a named function:

realReceiveComments : (List Comment -> msg) -> Sub msg
realReceiveComments tagger =
  rawReceiveComments (convertToMsg tagger)

convertToMsg : (List Comment -> msg) -> Decode.Value -> msg
convertToMsg tagger value =
    value
    |> Decoder.decodeValue decoder
    |> Result.withDefault []
    |> tagger
2 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.