evhub / coconut

Simple, elegant, Pythonic functional programming.
http://coconut-lang.org
Apache License 2.0
4.04k stars 120 forks source link

type-checking and operator partials do not play nice with each other #828

Closed deli73 closed 6 months ago

deli73 commented 6 months ago

trying to add static typing to the vector type in the tutorial, and the following code doesn't seem to have a way to annotate its type in a way Mypy can understand.

def __abs__(self) -> float =
        """Return the magnitude of the vector."""
        self.pts |> map$(.**2) |> sum |> (.**0.5)

Mypy can't check the type correctly, so we just get this.

hello.py: note: In member "__abs__" of class "vector":
hello.py:132:9: error: Returning Any from function declared to return "float"  [no-any-return]
            return _coconut_tail_call((_coconut.operator.pow), (sum)((map)(_coconut_complex_partial(_coconut.opera...
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~...
Found 1 error in 1 file (checked 1 source file)
Coconut exiting with error: MyPy error

(replacing (.**0.5) with math.sqrt fixes this.)

deli73 commented 6 months ago

on second thought, the isinstance check might be redundant anyways with type checking. however, similar issues here:

def __abs__(self) -> float =
        """Return the magnitude of the vector."""
        self.pts |> map$(.**2) |> sum |> (.**0.5)

Mypy can't check the type correctly, so we just get this.

hello.py: note: In member "__abs__" of class "vector":
hello.py:132:9: error: Returning Any from function declared to return "float"  [no-any-return]
            return _coconut_tail_call((_coconut.operator.pow), (sum)((map)(_coconut_complex_partial(_coconut.opera...
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~...
Found 1 error in 1 file (checked 1 source file)
Coconut exiting with error: MyPy error
evhub commented 6 months ago

Hmm... I don't think there's much that I can do here. The core problem as you noted is that the type of builtins.pow is (typing.Any, typing.Any) -> typing.Any, which is really a typeshed issue. An easy fix is just to do

import typing
self.pts |> map$(.**2) |> sum |> (.**0.5) |> typing.cast$(float)

but the correct fix here is to tell typeshed to type builtins.pow with a typing.Protocol instead of just with typing.Any.

deli73 commented 6 months ago

casting still gives us the same error, actually? how does one "tell typeshed" to type something with a protocol anyway?

evhub commented 6 months ago

casting still gives us the same error, actually?

Strange; I can't seem to reproduce that. Maybe try moving to coconut-develop (pip uninstall coconut && pip install -U coconut-develop)? The code I'm running here is:

from typing import cast
data vector(pts):
    def __abs__(self) -> float =
        """Return the magnitude of the vector."""
        self.pts |> map$(.**2) |> sum |> (.**0.5) |> cast$(float)

how does one "tell typeshed" to type something with a protocol anyway?

Just raise an issue telling them to do it or open a pull request making the change yourself.

deli73 commented 6 months ago

still getting the error; would it matter if it's vector(*pts) instead of vector(pts) in the data type?

evhub commented 6 months ago

It works for me with data vector(*pts) too. How are you testing this? What happens if you try pasting it into the terminal with coconut --mypy?

deli73 commented 6 months ago
>coconut --mypy
Coconut Interpreter v3.0.4-post_dev22 (Python 3.11):
(enter 'exit()' or press Ctrl-D to end)
>>> from typing import cast
data vector(pts):
    def __abs__(self) -> float =
        """Return the magnitude of the vector."""
        self.pts |> map$(.**2) |> sum |> (.**0.5) |> cast$(float)
<string>: note: In member "__add__" of class "vector":
<string>:63:5: error: Function is missing a type annotation  [no-untyped-def]
<string>: note: In member "__mul__" of class "vector":
<string>:64:5: error: Function is missing a type annotation  [no-untyped-def]
<string>: note: In member "__rmul__" of class "vector":
<string>:65:5: error: Function is missing a type annotation  [no-untyped-def]
<string>: note: In member "__eq__" of class "vector":
<string>:67:5: error: Function is missing a type annotation  [no-untyped-def]
<string>: note: In member "__hash__" of class "vector":
<string>:69:5: error: Function is missing a return type annotation  [no-untyped-def]
<string>: note: In member "__abs__" of class "vector":
<string>:75:9: error: Returning Any from function declared to return "float"  [no-any-return]

still same error, as far as we can tell... are we using the wrong version of Python for this?