ENH: Please add immutable/hashable FrozenDataFrame
#### Is your feature request related to a problem?

I would like to use (algorithm, DataFrame) tuples as dict keys to record results of various experiments on various datasets, but while we can make immutable algorithms, we cannot make immutable DataFrames.

There are a bajillion benefits we miss by not enforcing immutability.
For example in parallelism, we need to pickle data to share, which requires hashing.
Caching often requires hash comparison to be performant (functools.lru_cache)
Dict keys must be hashable.  Immutable data programs are easier to reason about.
The code can be simpler which allows faster easier development.
Many bugs can be prevented, etc etc.

#### Describe the solution you'd like

It's a big ask, but a FrozenDataFrame (or whatever name the maintainers like for it) class would be handy because it could be hashable, and updates could return new FrozenDataFrame with pointers to updated data.  Zero Copy operations would be preferred since this would be faster and save memory. 

#### API breaking implications

FrozenDataFrame can be a subclass of DataFrame and thus this would be an addition, so there wouldn't be any requirement to change previous stuff. Perhaps the subclass can override methods which mutate data, with new methods which raise helpful error messages, or return new pointers to new FrozenDataFrame instances if possible.

Perhaps down the road if the API were stabilized and well tested, this new subclass could replace the pd.DataFrame in a major version change, if the maintainers wanted.

#### Describe alternatives you've considered

There are hacks around this but it seems like something Pandas community would use. 
It seems the Pandas community already favors immutability in a lot of places.
immutables.Map can hold google/jax arrays but this loses a LOT of useful features. 

#### Additional context

```python
algo: str = 'string representation of an algorithm'
candles: pd.DataFrame = get_candles("SPY", 5, "min")

key: tuple = (algo, candles)
value: float =  backtest(algo, candles)
results: immutables.Map = immutables.Map()

results_with_backtest = R.assoc(key, value, results)
```

> ~/miniconda3/envs/py38/lib/python3.8/site-packages/pandas/core/generic.py in __hash__(self)
>    1666 
>    1667     def __hash__(self):
> -> 1668         raise TypeError(
>    1669             f"{repr(type(self).__name__)} objects are mutable, "
>    1670             f"thus they cannot be hashed"
> 
> TypeError: 'DataFrame' objects are mutable, thus they cannot be hashed