Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add type annotations #640

Closed
paultag opened this issue Aug 22, 2014 · 15 comments
Closed

add type annotations #640

paultag opened this issue Aug 22, 2014 · 15 comments
Labels

Comments

@paultag
Copy link
Member

paultag commented Aug 22, 2014

they're really neat. let's do more of that.

@paultag
Copy link
Member Author

paultag commented Aug 22, 2014

so, syntax proposals?

@paultag
Copy link
Member Author

paultag commented Aug 22, 2014

def foo(a: int, b: str) -> int:
    return 0

# Then, given:
foo.__annotations__
# We get:
{'b': <class 'str'>, 'return': <class 'int'>, 'a': <class 'int'>}

Currently only given meaning by third party stuff.

Let's allow setting them. Who knows some similar syntax for Lisplikes?

@agentultra
Copy link
Member

There are similar libraries for many lisp likes. As long as it’s progressive it should be fine. I hate type annotations unless I really need them. Sometimes duck-typing is actually useful.

http://docs.racket-lang.org/ts-guide/
http://typedclojure.org/

A more progressive-enhancement approach:

http://www.lispworks.com/documentation/HyperSpec/Body/04_bc.htm

But not as popular.

Cheers

On Aug 22, 2014, at 12:53 PM, Paul Tagliamonte notifications@github.com wrote:

def foo(a: int, b: str) -> int:
return 0
Currently only given meaning by third party stuff.

Let's allow setting them. Who knows some similar syntax for Lisplikes?


Reply to this email directly or view it on GitHub.

@refi64
Copy link
Contributor

refi64 commented Aug 22, 2014

For functions, I was thinking of something like this:

(defn my-func [[a int] [b int]]
    (print 123)
)

BTW, Hy has no way of representing function annotations, does it? If it did, then it could be used on top of mypy.

@paultag
Copy link
Member Author

paultag commented Aug 22, 2014

Not a bad syntax.

Yeah not yet, that's what this bug is for! :D
On Aug 22, 2014 3:28 PM, "kirbyfan64" notifications@github.com wrote:

For functions, I was thinking of something like this:

(defn my-func [[a int] [b int]](print 123))

BTW, Hy has no way of representing function annotations, does it? If it
did, then it could be used on top of MyPy.


Reply to this email directly or view it on GitHub
#640 (comment).

@refi64
Copy link
Contributor

refi64 commented Aug 22, 2014

Maybe that same syntax could be used for annotations? That way, people can use libraries like plac from Hy, too.

@paultag
Copy link
Member Author

paultag commented Aug 22, 2014

Erm yeah. I was thinking 3.4 annotations. Bug subject is a typo :)
On Aug 22, 2014 3:29 PM, "kirbyfan64" notifications@github.com wrote:

Maybe that same syntax could be used for annotations? That way, people can
use libraries like plac https://code.google.com/p/plac/ from Hy, too.


Reply to this email directly or view it on GitHub
#640 (comment).

@algernon
Copy link
Member

Clojure uses ^str, so: (defn pow2 [^int n] (^int (* n n))) or something like that. But those are type hints, not annotations. Yet, it is fairly succint.

@vodik
Copy link
Contributor

vodik commented Jan 1, 2018

Any further thoughts on this? It be cool to be able to support typing.NamedTuple and the upcoming data classes

@refi64
Copy link
Contributor

refi64 commented Jan 1, 2018

It would be cool but a bit pointless. You wouldn't be able to type-check any code yet.

We'd probably have to make a lightweight mypy frontend that compiles Hy files to typed_ast (which is identical to the ast module, but with type comment support)...

@Kodiologist
Copy link
Member

With syntax like (defn f [[a int] [b int]] (print a b)), I don't see how you'd distinguish an optional argument from a type hint.

@vodik
Copy link
Contributor

vodik commented Jan 1, 2018

Okay, thought a little bit about this, read more details where Python is going, and played with some code. Apologize upfront if my terminology is off.

First is we should be able to get away with always emitting strings for annotations. With PEP 563 coming, this is effectively going to become python's default behaviour anyways. Tools relying on annotations should already support deferred evaluation.

And I think we have two options in how to implement it, one which might be easier than the other:

Emit annotations in the AST:

This means syntax support, so that something like this:

(def foo ^int)
(defn bar ^int [x ^int] ...)

Would turn into:

foo: int
def bar(x: int) -> int:
    ...

We'd have to teach the compiler to emit ast.AnnAsign and set the annotation attribute on other entities like ast.arg. And we probably have to add another HyObject: HyAnnotation.

Manually build __annotations__:

The above example could also be implemented as the following Python:

def bar(x):
    ...

__annotations__ = {"foo": "int"}
bar.__annotations__ = {"return": "int", "x": "int"}

And now there's no need to change the compiler as we should be able to solve the problem by generating more python instead. However, this could be prove to be slower...

Possibly do the whole thing with just macros

I think I'd much prefer inline annotations, but its also possible we might be able to implement with macros. Basing this off cojure's core.typed, for example:

(ann bar [ int -> int ])
(defn bar [x] ...)

But the problem (and I don't know if this can be done cleanly), needs to attach the annotation after the definition. It also doesn't solve annotated assignment (foo: int). We could use (ann foo int) for that, but could that mean a function foo with no arguments?

Another possibility would be to just have this live in a standalone project too: hy-typed maybe?.

What should HyAnnotation be (if there is one)?

A possible representation is as a string, much like HyKeyword (see justification above) but without the prefix. But how do we interact with the typing library? For example, to annotate a function takes a list, we'd end up with this monstrosity:

(import [typing [List]])
(def foo [data ^(get List int)] ...)

Where it might be more desirable to write:

(def foo [data ^ints] ...)

This could be implemented as:

from hy.types import ints  # where ints = List[int]
def foo(data: ints): ...

But I would rather see the full List[int] there, especially if introspecting, so probably want to expand it first.

But then we have an issue: if there's a layer of rewriting/interpretation, how to we simultaneously support arbitrary annotations from type hints?

Maybe a possible solution is to support both the ann technique for arbitrary annotations and reserve ^annotation for a set of for known types.

@vodik
Copy link
Contributor

vodik commented Jan 1, 2018

@kirbyfan64 another option, but bare minimum, would be to just generate stub files of the function calls.

@vodik
Copy link
Contributor

vodik commented Jan 2, 2018

Oh, an for what its worth, emitting __annotations__ manually works for the case of stuff like named tuples or dataclasses, so that could be macroed away:

(defmacro deftuple [name &rest fields]
  `(do
     (import [typing [NamedTuple]])
     (defclass ~name [NamedTuple]
       (def __annotations__ (dict ~fields)))))

Not sure if you guys would take something like this, polished up, into contrib though.

@Kodiologist
Copy link
Member

Provided by #1810.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants