phasm/phasm/type3/functions.py
Johan B.W. de Vries b5f0fda133 Implements sum for Foldable types
Foldable take a TypeConstructor. The first argument must be a
NatNum.

The FunctionSignatureRouter wasn't completely on point, instead
this commit adds an TypeClassArgsRouter lookup router. This
makes sense since the only available arguments we have to find
a router is the list of type class arguments.
2025-05-12 18:36:37 +02:00

173 lines
5.3 KiB
Python

from typing import TYPE_CHECKING, Any, Hashable, Iterable, List, Union
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 FunctionSignature:
__slots__ = ('context', 'args', )
context: TypeVariableContext
args: List[Union['Type3', TypeVariable]]
def __init__(self, context: TypeVariableContext, args: Iterable[Union['Type3', TypeVariable]]) -> None:
self.context = context.__copy__()
self.args = list(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})'