Repository: dask/dask

Issue:
merge() with how="cross" does not work 
<!-- Please include a self-contained copy-pastable example that generates the issue if possible.

Please be concise with code posted. See guidelines below on how to provide a good bug report:

- Craft Minimal Bug Reports http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports
- Minimal Complete Verifiable Examples https://stackoverflow.com/help/mcve

Bug reports that follow these guidelines are easier to diagnose, and so are often handled much more quickly.
-->

**What happened**:
Running `dd.merge(left, right, how="cross")` fails with
```
MergeError: Can not pass on, right_on, left_on or set right_index=True or left_index=True
```

**What you expected to happen**:

**Minimal Complete Verifiable Example**:

```python
left = pd.DataFrame(
    {
        "key": ["K0", "K1", "K2", "K3"],
        "A": ["A0", "A1", "A2", "A3"],
        "B": ["B0", "B1", "B2", "B3"],
    }
)
right = pd.DataFrame(
    {
        "key": ["K0", "K1", "K2", "K3"],
        "A": ["C0", "C1", "C2", "C3"],
        "B": ["D0", "D1", "D2", "D3"],
    }
)
dd.merge(left, right, how="cross")
```

**Anything else we need to know?**:

**Environment**:

- Dask version: '2021.09.0'
- Python version: 3.7.10
- Operating System: MacOS
- Install method (conda, pip, source): pip



Discussion:
Hi @johanbog for what I'm reading in the docs the reason why `dd.merge()` with `how="cross"` does not work, it's because it's not supported. [`dd.merge` documentation](https://docs.dask.org/en/latest/generated/dask.dataframe.multi.DataFrame.merge.html#dask.dataframe.multi.DataFrame.merge)
[merge source code](https://docs.dask.org/en/latest/_modules/dask/dataframe/core.html#DataFrame.merge)

If what you want is a feature request, you might want to open an issue requesting this feature. 
In the meantime we could raise a clearer exception.
@jsignell that sounds like a great idea. I can take a crack at that but I have few questions

Looking at the code where `merge` is defined more explicitly here https://github.com/dask/dask/blob/9f0b00f039a7775dccccffe59d6802468508d9f7/dask/dataframe/core.py#L4614-L4634

Should the raise go in the definition in `.multi`, like somewhere in here? 
https://github.com/dask/dask/blob/9f0b00f039a7775dccccffe59d6802468508d9f7/dask/dataframe/multi.py#L466

probably right before https://github.com/dask/dask/blob/9f0b00f039a7775dccccffe59d6802468508d9f7/dask/dataframe/multi.py#L496

Would that be a good place?
Cool! Yeah I think it should go in multi right above the line that you are pointing to. 

Resolving patch:
diff --git a/dask/dataframe/multi.py b/dask/dataframe/multi.py
--- a/dask/dataframe/multi.py
+++ b/dask/dataframe/multi.py
@@ -511,6 +511,13 @@ def merge(
         left_on = right_on = on
         on = None
 
+    supported_how = ("left", "right", "outer", "inner", "leftanti", "leftsemi")
+    if how not in supported_how:
+        raise ValueError(
+            f"dask.dataframe.merge does not support how='{how}'. Options are: {supported_how}."
+            f" Note that 'leftanti' and 'leftsemi' are only dask_cudf options."
+        )
+
     if isinstance(left, (pd.Series, pd.DataFrame)) and isinstance(
         right, (pd.Series, pd.DataFrame)
     ):


Test patch:
diff --git a/dask/dataframe/tests/test_multi.py b/dask/dataframe/tests/test_multi.py
--- a/dask/dataframe/tests/test_multi.py
+++ b/dask/dataframe/tests/test_multi.py
@@ -849,6 +849,26 @@ def test_merge(how, shuffle_method):
     #         pd.merge(A, B, left_index=True, right_on='y'))
 
 
+def test_merge_how_raises():
+    left = pd.DataFrame(
+        {
+            "A": ["A0", "A1", "A2", "A3"],
+            "B": ["B0", "B1", "B2", "B3"],
+        }
+    )
+    right = pd.DataFrame(
+        {
+            "A": ["C0", "C1", "C2", "C3"],
+            "B": ["D0", "D1", "D2", "D3"],
+        }
+    )
+
+    with pytest.raises(
+        ValueError, match="dask.dataframe.merge does not support how='cross'"
+    ):
+        dd.merge(left, right, how="cross")
+
+
 @pytest.mark.parametrize("parts", [(3, 3), (3, 1), (1, 3)])
 @pytest.mark.parametrize("how", ["leftsemi", "leftanti"])
 @pytest.mark.parametrize(