Please see my replies inline.
I'm not sure about the abstraction in other places, but imho the ideal way would be to keep the separation of concerns, i.e. the migration is only concerned with how data is stored in the database. It has been the case for {:array, :map}, as :array is a native data type in Postgres. However, as far as I understand, :map already represents a JSON-encoded data field, so it should not have to be further specified at the migration level. You wouldn't need a migration to change a column from :map to {:map, :map}, for instance, as the database doesn't care what's inside the JSON; this is handled by the application (the schema).
Potentially, if we had a column type that ecto uses to store arbitrary JSON-encoded data, the schema logic (including casting and validations) could even be extended further, e.g. to support nested inline embeds and the like, which could be extremely powerful.
That's definitely in line with what I thought. Being new to ecto (and elixir in general), I had a hard time figuring out what the column type (!) :map stands for. The only hint in
the docs is actually a code comment
add :object, :map # Elixir type which is handled by the database
so I inferred that it means "a column to store arbitrary JSON-encoded data". I'm still not sure what exactly it represents. In MySQL it creates a TEXT column, maybe it creates an hstore in Postgres?
To wrap up:
Ideally imho, we'd have a column type (:map? :json??) to store arbitrary JSON-encoded data, that is managed on the schema level (embeds, casting, validation), and merely encoded/decoded at the db adapter level, if the column type is used in the migration.
Is this way off from what you had in mind for ecto, José? I'm willing to spend some time working on a PR, if that's welcome, but I'd first have to figure out how to approach this :)