Repository: mne-tools/mne-python

Issue:
Use a `float` default value for `fmin`-param in `mne.time_frequency.psd_array_multitaper`
Hello,
 
Static type checkers infers the expected type of the `fmin`-param for the `mne.time_frequency.psd_array_multitaper`-function to be of type `int` since the default value used is an `int`.

The docstring clearly states that `psd_array_multitaper` supports `fmin`-values of type `float`.

Can we change the default-value of `fmin` to `0.0` to avoid errors in static type checkers?

![image](https://user-images.githubusercontent.com/25052106/160796443-395b4dfa-a8f2-49e0-97e9-f4983f59ee81.png)


https://github.com/mne-tools/mne-python/blob/091da8f01aeeecd7d583ba596cf5a85cd649f192/mne/time_frequency/multitaper.py#L365


Discussion:
Hello! 👋 Thanks for opening your first issue here! ❤️ We will try to get back to you soon. 🚴🏽‍♂️

I'm not a big fan of complicating code just because static type checkers are not happy. But granted, this is a minimal change.
We don't supply type annotations or type stubs for MNE-Python yet. Static type checkers therefore make **a lot** of mistakes with this package. I'm against "fixing" things in just a single place. I'd like to start a discussion on how and when we add type annotations or complementary type stubs to MNE. It's IMHO overdue. 
Actually, we're already providing type annotations in our docstrings.
So a transition to proper type annotations should not bother you, then. 😛
Apparently you already know my opinion on type annotations 😄... But yes, looking forward to a heated discussion.
> I'm not a big fan of complicating code just because static type checkers are not happy. But granted, this is a minimal change.

I'm not sure I agree that changing from `0` -> `0.0` is complicating the code.

> We don't supply type annotations or type stubs for MNE-Python yet. Static type checkers therefore make **a lot** of mistakes with this package. I'm against "fixing" things in just a single place. I'd like to start a discussion on how and when we add type annotations or complementary type stubs to MNE. It's IMHO overdue.

If there is a lot of mistakes when using static type checkers with this package I'm all for doing a more complete overhaul rather than fixing things in a single place. This were just an inconsistency I found when using the package in my project. I do my best in trying to avoid comments which tells the type-checker to ignore the line but I were unable to fix this without a ignore-comment.
> I'm not sure I agree that changing from 0 -> 0.0 is complicating the code.

I would - you need 3 characters instead of 1. I did say that this is a minimal change of course, but you can't argue that it doesn't get more complicated/complex/verbose. However, that's not the point anyway, I think we agree that discussing this on a larger scale makes sense.
> > I'm not sure I agree that changing from 0 -> 0.0 is complicating the code.
> 
> I would - you need 3 characters instead of 1. I did say that this is a minimal change of course, but you can't argue that it doesn't get more complicated/complex/verbose. However, that's not the point anyway, I think we agree that discussing this on a larger scale makes sense.

Yes, we agree on your final point! Would love a larger scale discussion :)
I also tend to write 0. or 0.0 to make my code explicit that float is expected.

+0.5 on fixing this one with a small PR.

@sondrfos would you have time to do this,

regarding type annotations everywhere in MNE let's discuss this over a drink to cool down the conversation ;)
I understand that annotations can be helpful in some cases, mostly when developing a library. For a scientific Python package I think type annotations are distractive at best and extremely annoying at worst. Consider this function header with and without type hints:

```
(function) tight_layout: (pad: float = 1.2, h_pad: Any | None = None, w_pad: Any | None = None, fig: Any | None = None) -> None
(function) tight_layout(pad=1.2, h_pad=None, w_pad=None, fig=None)
```

Which of the two do you think makes it easier to see the parameters with their defaults? At least for me it's the second one without annotations. I know that the first one is auto-generated by Pylance from our code that doesn't have annotations, so in the real-world it would look a bit better, something like this:

```
(function) tight_layout: (pad: float = 1.2, h_pad: float = None, w_pad: float = None, fig: matplotlib.figure.Figure | None = None) -> None
```

Not much better. We have type hints in our docstrings, which are much more readable for humans.

I'd be happy to discuss this over a drink, but I guess this would have to be a virtual drink then?
And yes, let's fix this tiny number here.
@cbrnr 
Your examples are not well chosen. Well, they're chosen to support your point, but they're not good examples per se. 

When using type annotations, one would obviously format the function signatures differently. Typically, one parameter per line. 

Your example would be like tryingtoprovethatleavingoutspacesinwrittentextisabadidea. We all already know that it is. It doesn't support your point though. 
Are we starting the discussion now? @hoechenberger even if formatted as you say, it is harder to quickly see the default values. So unless the IDEs do not have an option to hide type annotations, I don't want to see them.

In any case, my main issue with annotations is that this greatly waters down Python's main strength: readability. Let's not make Python look like Java. If I want a good type system, I use Julia - here, I don't even *have* to use types, Julia's speed does not depend on annotating types. In Python, code never gets faster when typed. It's only automatic static type checkers that benefit, but not people. (Again, I would be open to type annotations if we were talking about some Python library that will be used as a basis for other Python packages.)

Argh, I really hate those annotations, sorry for the rant. I really don't want us spending time thinking about how to come up with nice and efficient annotations when we could do much more valuable stuff. Lately, I have the feeling that some things in MNE drift off in the wrong direction, and this unfortunately is another example...

But maybe there's some middle ground: if you show me a concrete example where type annotations would make sense, I'm not against adding annotations where really necessary. Just remember that this will probably not save us from `ifinstance` checks, unless we include Mypy in our CIs.
And BTW, I'm already using type annotations in https://github.com/cbrnr/sleepecg, so it's not like I have never used them.
Sorry @cbrnr, I realize my comments may come across as a bit hostile; this was not my intention. Let's postpone this discussion and do it over a beer or so, as @agramfort suggested. Sorry for stirring this up so much! And thanks for sharing your thoughts; this is greatly appreciated. 
I didn't think they were hostile, no problem @hoechenberger! Yes, I think it's best to discuss this IRL!
> I also tend to write 0. or 0.0 to make my code explicit that float is expected.
> 
> +0.5 on fixing this one with a small PR.
> 
> @sondrfos would you have time to do this,
> 
> regarding type annotations everywhere in MNE let's discuss this over a drink to cool down the conversation ;)

I can definitely try :) 

Resolving patch:
diff --git a/mne/time_frequency/multitaper.py b/mne/time_frequency/multitaper.py
--- a/mne/time_frequency/multitaper.py
+++ b/mne/time_frequency/multitaper.py
@@ -362,7 +362,7 @@ def _compute_mt_params(n_times, sfreq, bandwidth, low_bias, adaptive,
 
 
 @verbose
-def psd_array_multitaper(x, sfreq, fmin=0, fmax=np.inf, bandwidth=None,
+def psd_array_multitaper(x, sfreq, fmin=0.0, fmax=np.inf, bandwidth=None,
                          adaptive=False, low_bias=True, normalization='length',
                          output='power', n_jobs=1, verbose=None):
     """Compute power spectral density (PSD) using a multi-taper method.


Test patch: