kowainik / tomland

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

Profunctor of `Codec` can use some generalization. #375

Open kindaro opened 3 years ago

kindaro commented 3 years ago

I want to parse values from a TOML configuration file into a map. Going in another direction, it would mean looking for values in a map. But this operation is partial. I cannot find a standard combinator for this case.

(I cannot use functions from Toml.Codec.Combinator.Map because my map's entries live on the top level of the configuration file — so they are not nested under a key, and all functions from that module require a key!)

I already have a codec of type Toml.Codec part part. That is to say, I know how to read and write my values in the context of themselves. Now I need to read and write them in the context of the whole, so that I may have Toml.Codec whole part, from which I could easily assemble the desired Toml.Codec whole whole with some applicative magic.

So, I need to do this:

Toml.Codec part a → Toml.Codec whole a

The problem is, I only have a partial getter of type whole → Maybe part. If I had a total getter of type whole → part, I would have used .=. But here I need something a little more general.

I need this:

lmapMaybe ∷ (whole → Maybe part) → Toml.Codec part a → Toml.Codec whole a
lmapMaybe getter codec@Toml.Codec {..} = codec {Toml.codecWrite = fixture}
  where
    fixture = Toml.TomlState . fromMaybe (Nowhole, ) . fmap (Toml.unTomlState . codecWrite) . getter

I could also cheaply have this along the way:

dimapMaybe ∷ (whole → Maybe part) → (a → b) → Toml.Codec part a → Toml.Codec whole b
dimapMaybe getter setter = lmapMaybe getter . fmap setter

Are these functions already in the library? If not, can they be added? I can make a pull request.

kindaro commented 3 years ago

While at it, I think some if not all of the code in Toml.Codec.Di can work on more general codecs than TomlCodec a.

For example, I am putting this code into my projects:

dimap ∷ (left → left') → (right → right') → Toml.Codec left' right → Toml.Codec left right'
dimap f g Toml.Codec {..} = Toml.Codec
    { codecRead  = fmap g . codecRead
    , codecWrite = fmap g . codecWrite . f
    }

It is essentially the same as your dimap code except for a more general type signature. It would be better for me if such functions should be exported from the library in their full generality. Is it a good idea overall — for you to decide.