No matter how heterogeneous the map seems to be, there's always some underlying structure. If you have written code to work with it, then you've encoded that structure...by definition. Haskell gives you fantastic tools for managing this structure at whatever level of specificity you want. At the end of the day everything in a computer always boils down to bytes, so in the absolute worst case, you can always drop out to the escape hatch of:
Map ByteString ByteString
This can represent any heterogeneous map that any other language can represent. Sum types can allow us to add another layer of structure to this if we want. For example, take the classic example of JSON.
Map ByteString JValue
...where
data JValue
= JObject (Map Text JValue)
| JArray Array
| JString Text
| JNumber Scientific
| JBool Bool
| JNull
This is just one example of a way you could structure things. Sum types are super powerful for this kind of thing. And no matter what structure you come up with, you can always escape hatch that structure by adding a catch-all constructor like this:
data MyValue
= MyThingA A
| MyThingB B
| ... more things
| Unexpected ByteString
That's actual valid code btw, and it even reads really nicely!
Map ByteString ByteString
This can represent any heterogeneous map that any other language can represent. Sum types can allow us to add another layer of structure to this if we want. For example, take the classic example of JSON.
Map ByteString JValue
...where
This is just one example of a way you could structure things. Sum types are super powerful for this kind of thing. And no matter what structure you come up with, you can always escape hatch that structure by adding a catch-all constructor like this: That's actual valid code btw, and it even reads really nicely!