Skip to content

<nothing> is inferred when mimicking GADT #8252

Closed as not planned
Closed as not planned
@msakai

Description

@msakai

As mypy understands isinstance checks, I wondered if I can use this to mimic GADT (Generalized algebraic data type) in functional languages such as Haskell and OCaml.

I tried to mimic following Haskell code

data Expr a where
    Const :: a  -> Expr a
    Add :: Expr Int -> Expr Int -> Expr Int

eval :: Expr a -> a
eval (Const x) = x
eval (Add a b) = eval a + eval b

with the following Python code:

import abc
from typing import Generic, TypeVar


A = TypeVar('A')


class Expr(Generic[A], metaclass=abc.ABCMeta):
    pass


class Const(Expr[A]):
    value: A
    
    def __init__(self, value: A):
        self.value = value


class Add(Expr[int]):
    left: Expr[int]
    right: Expr[int]
    
    def __init__(self, left: Expr[int], right: Expr[int]):
        self.left = left
        self.right = right


def eval(e: Expr[A]) -> A:
    if isinstance(e, Const):
        # reveal_type(e)  # => Const[Any]
        return e.value
    elif isinstance(e, Add):
        # reveal_type(e)  # => <nothing>
        return eval(e.left) + eval(e.right)
        # error: <nothing> has no attribute "left"
        # error: <nothing> has no attribute "right"
    else:
        assert False


example = Add(Const[int](2), Const[int](1))
print(eval(example))

But in the elif isinstance(e, Add) branch the type of e is inferred as '<nothing>' causing type errors.

As '<nothing>' is not documented (c.f. #3030), I'm not sure if this is intended behavior. So I report it here just in case, though the attempt is just out of curiosity.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions