artiq icon indicating copy to clipboard operation
artiq copied to clipboard

Type annotations in kernel don't support Callable[[...], ...]

Open charlesbaynham opened this issue 5 years ago • 6 comments

Passing a kernel function as an argument works fine. However, annotating the receiving function breaks with a TypeError:

from artiq.experiment import *
from typing import Callable


class TestTyping(EnvExperiment):

    def build(self):
        self.setattr_device('core')

    @kernel
    def run(self):
        
        # This goes fine
        self.test_func(1)

        # This is fine too
        self.call_passed_func(self.test_func)

        # This causes a compiler error
        self.call_passed_func_annotated(self.test_func)

    @kernel
    def test_func(self, a: TInt32):
        print(a)

    @kernel
    def call_passed_func(self, f):
        f(2)

    @kernel
    def call_passed_func_annotated(self, f: Callable[[TInt32], TNone]):
        f(3)

This raises a root:Terminating with exception (TypeError: Callable[args, result]: result must be a type. Got artiq.compiler.types.TMono('NoneType', OrderedDict()).) error.

charlesbaynham avatar Aug 22 '19 08:08 charlesbaynham

Unfortunately, the ARTIQ compiler isn't compatible with standard Python type annotations; the equivalent would be artiq.compiler.types.TFunction.

dnadlinger avatar Aug 22 '19 09:08 dnadlinger

Thanks for that pointer. It seems like TFunction is present in artiq.compiler.types but not in artiq.compiler.builtins or artiq.language.types, which is presumably why

@kernel
    def call_passed_func_annotated(self, f: TFunction):
        ...

produces error: type annotation for argument 'f', '<class 'artiq.compiler.types.TFunction'>', is not an ARTIQ type.

Do you know the rational behind this, or is it an oversight?

charlesbaynham avatar Aug 22 '19 10:08 charlesbaynham

Try something like TFunction({"a": TInt32}, {}, TNone).

whitequark avatar Aug 22 '19 18:08 whitequark

Thanks @whitequark but that gives

File "C:\Users\cb6\AppData\Local\Continuum\anaconda3\envs\artiq\lib\site-packages\artiq\compiler\types.py", line 224, in __init__
    assert isinstance(args, OrderedDict)
AssertionError

Defining it instead as

@kernel
    def call_passed_func_annotated(self, f: TFunction(OrderedDict({"a": TInt32}), OrderedDict(), TNone)):
        f(3)

gives

[...] error: cannot unify method(fn=(self:<instance artiq_worker_<repository>\testing\typing_MWE.TestTyping>, a:numpy.int32)->'a delay('b), self=<instance artiq_worker_<repository>\testing\typing_MWE.TestTyping>)
with
(a:numpy.int32)->NoneType delay('c)
[...]

And I'm not sure how to pass the class type in, since this method is a part of the class that's being defined, so the class does not yet exist at the point of its definition.... (f: TFunction(OrderedDict({"self": TestTyping,"a": TInt32}) doesn't work)

charlesbaynham avatar Aug 23 '19 13:08 charlesbaynham

Trying

@kernel
    def call_passed_func_annotated(self, f: TFunction(OrderedDict({"self": EnvExperiment,"a": TInt32}), OrderedDict(), TNone)):
        f(3)

but still a no-go:

AttributeError: type object 'EnvExperiment' has no attribute 'find'

charlesbaynham avatar Aug 23 '19 13:08 charlesbaynham

Ah yes. I don't think you can currently write that annotation. In fact I don't think it's realistic to expect to be able to write annotations for arguments that involve curried functions (that is, methods) passed to them, with the way the compiler is currently structured.

For background, the compiler was written to support global type inference exclusively, and annotations were added for a few edge cases (syscalls and unconstrained arguments).

whitequark avatar Aug 23 '19 21:08 whitequark