kowainik / tomland

🏝 Bidirectional TOML serialization
https://kowainik.github.io/posts/2019-01-14-tomland
Mozilla Public License 2.0
120 stars 39 forks source link

tableMap codec with list value is irreversible #335

Closed samhh closed 4 years ago

samhh commented 4 years ago

Me again! :wave:

It's possible I'm misusing the codecs. I've tried and failed to simplify the repro beyond this point, hopefully this helps.

import qualified Data.Map as M
import qualified Toml as Toml
import Toml ((.=))

data Inner = Inner { y :: Text } deriving (Show)
innerCodec = Inner <$> Toml.text "y" .= y

data Thing = Thing { xs :: Map Text [Inner] } deriving (Show)
thingCodec = Thing <$> Toml.tableMap Toml._KeyText (Toml.list innerCodec) "xs" .= xs

thing = Thing $ M.fromList [("k", [Inner "a", Inner "b"])]

reversed = Toml.decode thingCodec $ Toml.encode thingCodec thing
-- Right (Thing {xs = fromList []})

The encoded value looks like this written to a file with Toml.encodeToFile:


[xs]
  [[xs.k]]
    y = "a"

  [[xs.k]]
    y = "b"
chshersh commented 4 years ago

Hi @samhh, can you also tell what output you expect from the codec? It's not clear what's the problem. Also, generally, specifying top-level signatures of all codecs can help with resolving issues, because due to type inference the inferred types of codecs might not be the same you expect.

samhh commented 4 years ago

Hey @chshersh,

In my real codebase I do have explicit type signatures, just tried to write a pared down repro. :slightly_smiling_face:

What's confusing me is that the encoded value cannot be decoded - is this expected behaviour? I'd generally expect decode (encode x) == x to hold.

chshersh commented 4 years ago

@samhh Thanks for more details! From the implementation of the tableMap codec I can see that it doesn't handle array of tables as an argument codec to tableMap at the moment. I guess, it should be possible to patch tableMap and take this into consideration. Just wonder, what is your real use case and why you want to have such nested configurations? I'm curious about different use-cases for TOML and how it can be used in various scenarios 🙂

samhh commented 4 years ago

My use case is something I've just started working on.

I'm using TOML for user configuration and, until I'm given a compelling reason not to, also for data caching. The structure of said data is currently like this:

data Cache = Cache
  { timestamp :: LocalTime,
    feeds :: Map PodcastId (Map EpisodeId Episode)
  }

I'm contemplating changing that to this, which prompted this ticket:

data Cache = Cache
  { timestamp :: LocalTime,
    feeds :: Map PodcastId [Episode]
  }
vrom911 commented 4 years ago

@samhh we prepared a fix in #337 . Let us know if it doesn't work for your use-case 🙂

Current known limitation of the solution – it doesn't preserve empty lists in values of the map.

samhh commented 4 years ago

That's working perfectly, many thanks!