Improve the metrics API

### Describe the workflow you want to enable

Currently metrics in scikit-learn have several shortcomings:
- Although it is possible to pass custom metrics as callables, they are called for each pair of inputs. This makes impossible to create custom metrics with the same performance as the existing ones (#22219).
- Every metric has different additional parameters. This requires that methods using them expose the particular parameters or provide a catch all `metric_params` parameter (and not all of them do that).
- Currently, metrics for types other than numeric vectors are not well supported (#15932).
- Some metrics, such as Mahalanobis, depend on the data. There is currently no way to use them in a cross-validation setting without incurring in data leakage (#6915).

### Describe your proposed solution

IMHO, metrics should be:
- Instances of classes, not functions. The additional parameters of the metrics (e.g. the `p` for Minkowski) could then be passed to the metric at creation time. Advantages: no need to homogeneize args, no need for `metric_params`. If the metrics inherit from `BaseEstimator`, the parameters could also be optimized in cross validation.
- They should accept two matrices with the same first dimension and compute all the paired distances. Advantage: no distinction between provided metrics and custom metrics: all of them could be implemented efficiently without multiple calls for each pair of vectors.
- There should be a way to implement an optimized version of pairwise distances. Although a default ok-ish version can be implemented by calling the function several times with the necessary matrices, for some metrics it is possible to have a better implementation. Either there should be a `Metric` ABC class with a default `pairwise` method that can be overriden, or a `singledispatch` function could be made for pairwise distances, so that one can register the implementation for their class. Advantages: again, allowing custom metrics to be implemented efficiently.
- Metrics know their input types. One should be able to provide metrics to compare strings or other custom types, and algorithms that only need to apply a metric, such as k-NN classification, should delegate the validation to the metric.
- (Optional) Some metrics, such as Mahalanobis, depend on the data. These metrics could have a `fit` method, allowing them to be used in cross-validation without incurring in data leakage.

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
As far as I can see, the recent work in exposing the `DistanceMetric` objects and make them directly usable as the `metric` parameter is going in the line proposed in this issue already. However, in my opinion using `get_metric` with a string and kwargs is worse than simply using the constructor of the subclass, as I proposed here. Doing it with the constructor is more elegant, allows for type hints in the future, and makes no distinction between custom and predefined metrics.

With `DistanceMetric` I am not even sure if it is possible to subclass it to create custom metrics, and how to create the custom instances in that case.

What do you think about that?

Labels: New Feature, API, Needs Decision, module:metrics