In some ways, Erlang dict Dictionaries are immutable by design. Each time you insert a value into a dictionary, it creates a brand new dictionary containing the data from the old dictionary, plus the newly inserted entry.
If you wish to have a read-only dictionary, you can structure your program so that the dict term is passed into functions for use, but that you never receive a new dictionary value from another function. In this way, the single-assignment nature of Erlang guarantees that the dictionary is unchanged.
ets tables are more problematic, because the permit updates to the table by name, and Erlang's single-assigment behavior does not provide any protection here.
This seems to be an area where Erlang can only provide this functionality if the programmer uses the single-assignment nature of the language to proper use.