Has to implement both functions as arguments and type place holders (variables) for type constructors. Probably have to introduce a type for functions
189 lines
5.7 KiB
Python
189 lines
5.7 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any, Hashable, Iterable, List
|
|
|
|
if TYPE_CHECKING:
|
|
from .typeclasses import Type3Class
|
|
from .types import Type3
|
|
|
|
|
|
class TypeVariableApplication_Base[T: Hashable, S: Hashable]:
|
|
"""
|
|
Records the constructor and arguments used to create this type.
|
|
|
|
Nullary types, or types of kind *, have both arguments set to None.
|
|
"""
|
|
constructor: T
|
|
arguments: S
|
|
|
|
def __init__(self, constructor: T, arguments: S) -> None:
|
|
self.constructor = constructor
|
|
self.arguments = arguments
|
|
|
|
def __hash__(self) -> int:
|
|
return hash((self.constructor, self.arguments, ))
|
|
|
|
def __eq__(self, other: Any) -> bool:
|
|
if not isinstance(other, TypeVariableApplication_Base):
|
|
raise NotImplementedError
|
|
|
|
return (self.constructor == other.constructor # type: ignore[no-any-return]
|
|
and self.arguments == other.arguments)
|
|
|
|
def __repr__(self) -> str:
|
|
return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
|
|
|
|
class TypeVariable:
|
|
"""
|
|
Types variable are used in function definition.
|
|
|
|
They are used in places where you don't know the exact type.
|
|
They are different from PlaceholderForType, as those are instanced
|
|
during type checking. These type variables are used solely in the
|
|
function's definition
|
|
"""
|
|
__slots__ = ('name', 'application', )
|
|
|
|
name: str
|
|
application: TypeVariableApplication_Base[Any, Any]
|
|
|
|
def __init__(self, name: str, application: TypeVariableApplication_Base[Any, Any]) -> None:
|
|
self.name = name
|
|
self.application = application
|
|
|
|
def __hash__(self) -> int:
|
|
return hash((self.name, self.application, ))
|
|
|
|
def __eq__(self, other: Any) -> bool:
|
|
if not isinstance(other, TypeVariable):
|
|
raise NotImplementedError
|
|
|
|
return (self.name == other.name
|
|
and self.application == other.application)
|
|
|
|
def __repr__(self) -> str:
|
|
return f'TypeVariable({repr(self.name)})'
|
|
|
|
class TypeVariableApplication_Nullary(TypeVariableApplication_Base[None, None]):
|
|
"""
|
|
For the type for this function argument it's not relevant if it was constructed.
|
|
"""
|
|
|
|
class TypeConstructorVariable:
|
|
"""
|
|
Types constructor variable are used in function definition.
|
|
|
|
They are a lot like TypeVariable, except that they represent a
|
|
type constructor rather than a type directly.
|
|
|
|
For now, we only have type constructor variables for kind
|
|
* -> *.
|
|
"""
|
|
__slots__ = ('name', )
|
|
|
|
name: str
|
|
|
|
def __init__(self, name: str) -> None:
|
|
self.name = name
|
|
|
|
def __hash__(self) -> int:
|
|
return hash((self.name, ))
|
|
|
|
def __eq__(self, other: Any) -> bool:
|
|
if other is None:
|
|
return False
|
|
|
|
if not isinstance(other, TypeConstructorVariable):
|
|
raise NotImplementedError
|
|
|
|
return (self.name == other.name)
|
|
|
|
def __call__(self, tvar: TypeVariable) -> 'TypeVariable':
|
|
return TypeVariable(
|
|
self.name + ' ' + tvar.name,
|
|
TypeVariableApplication_Unary(self, tvar)
|
|
)
|
|
|
|
def __repr__(self) -> str:
|
|
return f'TypeConstructorVariable({self.name!r})'
|
|
|
|
class TypeVariableApplication_Unary(TypeVariableApplication_Base[TypeConstructorVariable, TypeVariable]):
|
|
"""
|
|
The type for this function argument should be constructed from a type constructor.
|
|
|
|
And we need to know what construtor that was, since that's the one we support.
|
|
"""
|
|
|
|
class ConstraintBase:
|
|
__slots__ = ()
|
|
|
|
class Constraint_TypeClassInstanceExists(ConstraintBase):
|
|
__slots__ = ('type_class3', 'types', )
|
|
|
|
type_class3: 'Type3Class'
|
|
types: list[TypeVariable]
|
|
|
|
def __init__(self, type_class3: 'Type3Class', types: Iterable[TypeVariable]) -> None:
|
|
self.type_class3 = type_class3
|
|
self.types = list(types)
|
|
|
|
# Sanity check. AFAIK, if you have a multi-parameter type class,
|
|
# you can only add a constraint by supplying types for all variables
|
|
assert len(self.type_class3.args) == len(self.types)
|
|
|
|
def __str__(self) -> str:
|
|
return self.type_class3.name + ' ' + ' '.join(x.name for x in self.types)
|
|
|
|
def __repr__(self) -> str:
|
|
return f'Constraint_TypeClassInstanceExists({self.type_class3.name}, {self.types!r})'
|
|
|
|
class TypeVariableContext:
|
|
__slots__ = ('constraints', )
|
|
|
|
constraints: list[ConstraintBase]
|
|
|
|
def __init__(self, constraints: Iterable[ConstraintBase] = ()) -> None:
|
|
self.constraints = list(constraints)
|
|
|
|
def __copy__(self) -> 'TypeVariableContext':
|
|
return TypeVariableContext(self.constraints)
|
|
|
|
def __str__(self) -> str:
|
|
if not self.constraints:
|
|
return ''
|
|
|
|
return '(' + ', '.join(str(x) for x in self.constraints) + ') => '
|
|
|
|
def __repr__(self) -> str:
|
|
return f'TypeVariableContext({self.constraints!r})'
|
|
|
|
class FunctionArgument:
|
|
__slots__ = ('args', 'name', )
|
|
|
|
args: list[Type3 | TypeVariable]
|
|
name: str
|
|
|
|
def __init__(self, args: list[Type3 | TypeVariable]) -> None:
|
|
self.args = args
|
|
|
|
self.name = '(' + ' -> '.join(x.name for x in args) + ')'
|
|
|
|
class FunctionSignature:
|
|
__slots__ = ('context', 'args', )
|
|
|
|
context: TypeVariableContext
|
|
args: List[Type3 | TypeVariable | FunctionArgument]
|
|
|
|
def __init__(self, context: TypeVariableContext, args: Iterable[Type3 | TypeVariable | list[Type3 | TypeVariable]]) -> None:
|
|
self.context = context.__copy__()
|
|
self.args = list(
|
|
FunctionArgument(x) if isinstance(x, list) else x
|
|
for x in args
|
|
)
|
|
|
|
def __str__(self) -> str:
|
|
return str(self.context) + ' -> '.join(x.name for x in self.args)
|
|
|
|
def __repr__(self) -> str:
|
|
return f'FunctionSignature({self.context!r}, {self.args!r})'
|