Source code for curry.typecheck.sigtable

'''
The signature table: the type schemes of Curry symbols, read from the
FlatCurry interfaces that the front end writes.

The front end writes the interface ``M.fint`` of every module ``M`` it
compiles.  The interface holds the type of every function and constructor,
after the dictionary translation of the type classes, and the translation to
ICurry drops it.  This module reads the interface on demand.  It never parses
a whole file.  :class:`Interface` indexes the text once, with one regular
expression that skips quoted names, and records the offset of every ``Func``,
``Type``, ``TypeSyn``, ``TypeNew``, ``Cons`` and ``NewCons`` entry.  An entry
is decoded with the FlatCurry reader the first time a symbol asks for it.

A :class:`Scheme` is the type of one symbol.  The class context comes from
the leading dictionary parameters of the FlatCurry type: a constraint ``C a``
is a parameter of type ``() -> _Dict#C a``.  The scheme of a constructor
comes from its ``Type`` entry.  :func:`show_scheme` prints a scheme in Curry
syntax, for example ``Num a => a -> a -> a``.

The instances come from the names ``_inst#Class#Type`` of the loaded
modules; the context of an instance comes from its type.  The naming
convention is the one of curry-frontend 2.0.0, the front end of PAKCS 3.4.1.
The table checks for ``_inst#Prelude.Num#Prelude.Int`` when it reads the
interface of the Prelude.

One :class:`SignatureTable` belongs to one interpreter, as
``interp.sigtable``.  ``Interpreter.reset`` clears it.  A symbol without a
scheme is an error only when the caller asks for one with ``required=True``;
an untyped use of a symbol never reads the table.
'''

from .. import config, exceptions
from ..common import T_CTOR
from ..toolchain.flat2icurry import flatcurry as fc
from ..toolchain.flat2icurry.terms import constructor as _term
from ..utility import readcurry as rc
import logging, os, re, weakref

logger = logging.getLogger(__name__)

__all__ = [
    'Instance', 'Interface', 'InterfaceError', 'Predicate', 'Scheme'
  , 'SignatureTable', 'find_interface', 'interface_candidates', 'module_root'
  , 'show_predicate', 'show_scheme', 'show_type', 'typevar_name'
  ]

PRELUDE = 'Prelude'
DICT_PREFIX = '_Dict#'
INST_PREFIX = '_inst#'
# The instance the table looks for when it reads the interface of the
# Prelude.  The name follows curry-frontend 2.0.0; version 3.0.0 renames the
# instance functions.
NUM_INT_INSTANCE = '_inst#Prelude.Num#Prelude.Int'
UNIT = fc.TCons(fc.prelude('()'), [])

[docs] class InterfaceError(exceptions.CurryTypeError): ''' An interface is missing or unreadable, a needed entry is absent, or a name does not follow the convention of the pinned front end. '''
# Types without an interface entry. Sprite's Prelude declares the boxed # literals Int, Char and Float as constructors with one unboxed argument. # Their schemes are the nullary types. The other symbols of Sprite's own # Prelude, the arrow, IO, the lifted case and let functions, and the builders # of Python strings and iterators, have no scheme. BUILTIN_SCHEMES = { (PRELUDE, 'Int') : fc.TCons(fc.prelude('Int'), []) , (PRELUDE, 'Char') : fc.TCons(fc.prelude('Char'), []) , (PRELUDE, 'Float') : fc.TCons(fc.prelude('Float'), []) } # The schemes # ===========
[docs] class Predicate(_term('Predicate', 'classname typeexpr')): ''' A class constraint ``C t``. ``classname`` is the qualified name of the class, e.g., ``Prelude.Num``; ``typeexpr`` is the constrained type. ''' __slots__ = () def __str__(self): return show_predicate(self)
[docs] class Scheme: ''' The type scheme of one symbol. Attributes: modulename: The module of the symbol. name: The name of the symbol in its module. typevars: The quantified type variables as pairs ``(index, kind)``, the top quantifier first, then the own quantifier of a class method. context: The class constraints, a tuple of :class:`Predicate`, recovered from the leading dictionary parameters in their order. typeexpr: The type without the dictionary parameters and the quantifiers. arity: The FlatCurry arity. The leading dictionary parameters count. ndicts: The number of dictionary parameters, in the order the symbol takes them. A class method with its own constraints, such as ``round``, takes the dictionaries of those constraints after its FlatCurry parameters. flat_typeexpr: The type as the interface writes it, dictionaries and quantifiers included. None for a built-in scheme. is_constructor: Whether the symbol is a data constructor. ''' __slots__ = ( 'modulename', 'name', 'typevars', 'context', 'typeexpr', 'arity' , 'ndicts', 'flat_typeexpr', 'is_constructor' ) def __init__( self, modulename, name, typevars, context, typeexpr, arity, ndicts , flat_typeexpr=None, is_constructor=False ): self.modulename = modulename self.name = name self.typevars = tuple(typevars) self.context = tuple(context) self.typeexpr = typeexpr self.arity = arity self.ndicts = ndicts self.flat_typeexpr = flat_typeexpr self.is_constructor = is_constructor @property def fullname(self): return '%s.%s' % (self.modulename, self.name) @property def source_arity(self): ''' The number of value parameters among the FlatCurry parameters: the arity less the dictionaries, at least zero. ''' return max(self.arity - self.ndicts, 0) def __str__(self): return show_scheme(self) def __repr__(self): return '<Scheme %s :: %s>' % (self.fullname, self)
[docs] class Instance: ''' A class instance, from the function ``_inst#Class#Type`` of the module that declares it. Attributes: classname: The qualified class name, e.g., ``Prelude.Show``. typename: The type constructor as the name spells it: qualified, e.g., ``Prelude.Maybe``, or one of the special constructors ``[]``, ``()``, ``(,)`` and so on, and ``(->)``. modulename: The module that declares the instance. name: The name of the instance function in that module. scheme: The scheme of the instance function. Its context is the instance context; its type is ``() -> _Dict#Class head``. ''' __slots__ = ('classname', 'typename', 'modulename', 'name', 'scheme') def __init__(self, classname, typename, modulename, name, scheme): self.classname = classname self.typename = typename self.modulename = modulename self.name = name self.scheme = scheme @property def fullname(self): return '%s.%s' % (self.modulename, self.name) @property def context(self): return self.scheme.context @property def head(self): '''The instance head, e.g., ``TCons ("Prelude","Maybe") [TVar 0]``.''' result = self.scheme.typeexpr if isinstance(result, fc.FuncType): result = result.range assert isinstance(result, fc.TCons) and len(result.args) == 1 return result.args[0] def __repr__(self): return '<Instance %s %s from %s>' % ( self.classname, self.typename, self.fullname )
def dict_predicate(typeexpr): ''' The predicate of a dictionary parameter: ``() -> _Dict#C t`` gives ``Predicate('M.C', t)``. Returns None for another type. ''' if isinstance(typeexpr, fc.FuncType) and typeexpr.domain == UNIT: rng = typeexpr.range if isinstance(rng, fc.TCons) and len(rng.args) == 1: modulename, name = rng.name if name.startswith(DICT_PREFIX): classname = '%s.%s' % (modulename, name[len(DICT_PREFIX):]) return Predicate(classname, rng.args[0]) return None def scheme_of_function(func): ''' The scheme of a FlatCurry ``Func`` entry. The dictionary parameters are collected past every quantifier. A class method quantifies its own variables after the dictionary of its class, and a method with its own constraints, such as ``round :: (RealFrac a, Integral b) => a -> b``, has more dictionaries after that quantifier. ''' modulename, name = func.name typevars = [] context = [] nleading = None typeexpr = func.typeexpr while True: if isinstance(typeexpr, fc.ForallType): # A quantifier after the dictionaries ends the FlatCurry parameters. if context and nleading is None: nleading = len(context) typevars.extend(typeexpr.typevars) typeexpr = typeexpr.typeexpr continue if isinstance(typeexpr, fc.FuncType): pred = dict_predicate(typeexpr.domain) if pred is not None: context.append(pred) typeexpr = typeexpr.range continue break if nleading is None: nleading = len(context) if nleading > func.arity: logger.warning( '%s.%s has %d dictionary parameters but arity %d' , modulename, name, nleading, func.arity ) return Scheme( modulename, name, typevars, context, typeexpr, func.arity, len(context) , flat_typeexpr=func.typeexpr ) def scheme_of_constructor(typedecl, consdecl): ''' The scheme of a constructor from its ``Type`` or ``TypeNew`` entry: the argument types lead to the declared type applied to its variables. ''' modulename, name = consdecl.name typevars = list(typedecl.typevars) result = fc.TCons(typedecl.name, [fc.TVar(i) for i, _ in typevars]) if isinstance(consdecl, fc.NewCons): argtypes = [consdecl.typeexpr] else: argtypes = list(consdecl.argtypes) typeexpr = result for argtype in reversed(argtypes): typeexpr = fc.FuncType(argtype, typeexpr) flat = fc.ForallType(typevars, typeexpr) if typevars else typeexpr return Scheme( modulename, name, typevars, (), typeexpr, len(argtypes), 0 , flat_typeexpr=flat, is_constructor=True ) def builtin_scheme(modulename, name): '''The scheme of a symbol of Sprite's own Prelude, or None.''' typeexpr = BUILTIN_SCHEMES.get((modulename, name)) if typeexpr is None: return None return Scheme( modulename, name, (), (), typeexpr, 0, 0, is_constructor=True ) # The printer # ===========
[docs] def typevar_name(i): '''The name of the i-th type variable: a to z, then a1 to z1, and so on.''' q, r = divmod(i, 26) return chr(ord('a') + r) + (str(q) if q else '')
class _Names: '''Names the type variables in the order of their first use.''' def __init__(self, names=None): self.names = {} if names is None else dict(names) def __call__(self, index): name = self.names.get(index) if name is None: used = set(self.names.values()) i = 0 while typevar_name(i) in used: i += 1 name = self.names[index] = typevar_name(i) return name def _collect_typevars(typeexpr, names): '''Names the variables of a type in the order of their first occurrence.''' stack = [typeexpr] while stack: te = stack.pop() if isinstance(te, fc.TVar): names(te.index) elif isinstance(te, fc.FuncType): stack.append(te.range) stack.append(te.domain) elif isinstance(te, fc.TCons): stack.extend(reversed(te.args)) elif isinstance(te, fc.ForallType): stack.append(te.typeexpr) def _paren(text, yes): return '(%s)' % text if yes else text def _is_tuple_name(name): return len(name) > 2 and name[0] == '(' and name[-1] == ')' \ and set(name[1:-1]) == {','} def _qualify(qname, module): '''Writes a name unqualified when it belongs to the Prelude or to ``module``.''' modulename, name = qname if modulename == PRELUDE or modulename == module: return name return '%s.%s' % (modulename, name) # Precedence levels of the printer: 0 allows an arrow, 1 is the domain of an # arrow or the head of an application, 2 is the argument of an application. def _show(te, names, prec, module): if isinstance(te, fc.TVar): return names(te.index) if isinstance(te, fc.FuncType): text = '%s -> %s' % ( _show(te.domain, names, 1, module), _show(te.range, names, 0, module) ) return _paren(text, prec > 0) if isinstance(te, fc.ForallType): text = 'forall %s. %s' % ( ' '.join(names(i) for i, _ in te.typevars) , _show(te.typeexpr, names, 0, module) ) return _paren(text, prec > 0) if isinstance(te, fc.TCons): modulename, name = te.name args = te.args if modulename == PRELUDE: if name == '[]' and len(args) == 1: return '[%s]' % _show(args[0], names, 0, module) if name == '()' and not args: return '()' if _is_tuple_name(name) and len(args) == len(name) - 1: return '(%s)' % ', '.join(_show(a, names, 0, module) for a in args) if name == '(->)' and len(args) == 2: return _show(fc.FuncType(*args), names, prec, module) if name == 'Apply' and len(args) == 2: text = '%s %s' % ( _show(args[0], names, 1, module), _show(args[1], names, 2, module) ) return _paren(text, prec > 1) head = _qualify(te.name, module) if not args: return head text = ' '.join([head] + [_show(a, names, 2, module) for a in args]) return _paren(text, prec > 1) raise TypeError('not a type expression: %r' % (te,))
[docs] def show_type(typeexpr, names=None, module=None): ''' Prints a type in Curry syntax. Variables are named a, b, c, ... in the order of their first occurrence; ``names`` may be a mapping from variable index to name. Names of the Prelude and of ``module`` are unqualified. ''' return _show(typeexpr, _Names(names), 0, module)
[docs] def show_predicate(pred, names=None, module=None): '''Prints a class constraint, e.g., ``Show (Maybe a)``.''' names = _Names(names) classname = _qualify(tuple(pred.classname.rsplit('.', 1)), module) return '%s %s' % (classname, _show(pred.typeexpr, names, 2, module))
[docs] def show_scheme(scheme, module=None, qualify=None): ''' Prints a scheme in Curry syntax with the context folded back, e.g., ``Num a => a -> a -> a``. The variables are named in the order of their first occurrence in the type; a variable of the context alone comes last. Names of the Prelude and of the scheme's own module are unqualified; ``module`` names another module whose names stay unqualified, and ``qualify=True`` qualifies every name outside the Prelude. ''' if qualify: module = None elif module is None: module = scheme.modulename names = _Names() _collect_typevars(scheme.typeexpr, names) for pred in scheme.context: _collect_typevars(pred.typeexpr, names) body = _show(scheme.typeexpr, names, 0, module) if not scheme.context: return body preds = [show_predicate(pred, names.names, module) for pred in scheme.context] if len(preds) == 1: return '%s => %s' % (preds[0], body) return '(%s) => %s' % (', '.join(preds), body)
# The index of one interface # ========================== # One pass finds the entries. A quoted name is matched first, so that a # name such as "(,)" or an operator never starts an entry or ends one. _ENTRY = re.compile( rb'"(?:[^"\\]|\\.)*"' rb'|\b(?P<kind>Func|TypeSyn|TypeNew|Type|NewCons|Cons)' rb' \("(?P<module>(?:[^"\\]|\\.)*)","(?P<name>(?:[^"\\]|\\.)*)"\)' ) _HEADER = re.compile(rb'Prog "(?P<name>(?:[^"\\]|\\.)*)" \[(?P<imports>[^\]]*)\]') _QUOTED = re.compile(rb'"(?:[^"\\]|\\.)*"') _DELIM = re.compile(rb'["()\[\],]') _TYPE_KINDS = (b'Type', b'TypeSyn', b'TypeNew') def _unquote(raw): '''The text of a quoted name without its quotes.''' if b'\\' not in raw: return raw.decode('utf-8') return str(rc.parse('"%s"' % raw.decode('utf-8')))
[docs] class Interface: ''' One FlatCurry interface, indexed by the offsets of its entries. Attributes: path: The file. data: Its text, as bytes. name: The module name the interface declares. imports: The imported modules. ''' # The interfaces read in this process, by file. The key holds the size # and the modification time, so a rewritten file is read again. _loaded = {} def __init__(self, path, data=None): if data is None: with open(path, 'rb') as istream: data = istream.read() self.path = path self.data = data header = _HEADER.match(data) if header is None: raise InterfaceError('%s is not a FlatCurry interface' % path) self.name = _unquote(header.group('name')) self.imports = tuple( _unquote(m.group()[1:-1]) for m in _QUOTED.finditer(header.group('imports')) ) self._funcs = {} self._types = {} self._ctors = {} self._decoded = {} self._index()
[docs] @classmethod def load(cls, path): '''Reads an interface. Files are cached by name, size, and time.''' st = os.stat(path) key = (path, st.st_size, st.st_mtime_ns) iface = cls._loaded.get(key) if iface is None: iface = cls._loaded[key] = cls(path) return iface
[docs] @classmethod def purge(cls): '''Forgets the cached interfaces whose files are gone.''' for key in list(cls._loaded): if not os.path.isfile(key[0]): del cls._loaded[key]
def __repr__(self): return '<Interface %s at %s>' % (self.name, self.path) def _index(self): last_type = None for m in _ENTRY.finditer(self.data): kind = m.group('kind') if kind is None: continue name = _unquote(m.group('name')) if kind == b'Func': self._funcs[name] = m.start() elif kind in _TYPE_KINDS: self._types[name] = m.start() last_type = name else: self._ctors[name] = (last_type, m.start()) def _end(self, start): '''The end of the entry at ``start``: the first unmatched bracket or comma.''' depth = 0 pos = start while True: m = _DELIM.search(self.data, pos) if m is None: return len(self.data) c = m.group() if c == b'"': pos = _QUOTED.match(self.data, m.start()).end() continue if c in b'([': depth += 1 elif c == b',': if depth == 0: return m.start() else: if depth == 0: return m.start() depth -= 1 pos = m.end() def _decode(self, start): term = self._decoded.get(start) if term is None: text = self.data[start:self._end(start)].decode('utf-8') try: term = fc.decode(rc.parse(text)) except (Exception, fc.Flat2ICurryError) as err: raise InterfaceError( 'cannot decode the entry at offset %d of %s: %s' % (start, self.path, err) ) from err self._decoded[start] = term return term
[docs] def function_names(self): return self._funcs.keys()
[docs] def type_names(self): return self._types.keys()
[docs] def constructor_names(self): return self._ctors.keys()
[docs] def function(self, name): '''The ``Func`` entry of ``name``, or None.''' start = self._funcs.get(name) return None if start is None else self._decode(start)
[docs] def type_decl(self, name): '''The ``Type``, ``TypeSyn`` or ``TypeNew`` entry of ``name``, or None.''' start = self._types.get(name) return None if start is None else self._decode(start)
[docs] def constructor(self, name): ''' The constructor ``name``: a pair of its type declaration and its ``Cons`` or ``NewCons`` entry, or None. ''' entry = self._ctors.get(name) if entry is None: return None typename, start = entry return self.type_decl(typename), self._decode(start)
[docs] def scheme(self, name, is_constructor=False): '''The scheme of a function or constructor of this module, or None.''' if is_constructor: entry = self.constructor(name) return None if entry is None else scheme_of_constructor(*entry) func = self.function(name) return None if func is None else scheme_of_function(func)
[docs] def instance_names(self): '''The names of the instance functions, ``_inst#Class#Type``.''' return [name for name in self._funcs if name.startswith(INST_PREFIX)]
# Finding the file # ================
[docs] def module_root(dirname, modulename): ''' The root of the module ``modulename`` whose source lies in ``dirname``: the directory without the package path. The root of ``A.B`` at ``<root>/A/B.curry`` is ``<root>``. ''' parts = modulename.split('.')[:-1] if parts: tail = os.sep + os.path.join(*parts) if dirname.endswith(tail): return dirname[:-len(tail)] or os.sep return dirname
[docs] def interface_candidates(filename, modulename): ''' The places where the interface of a module may lie, in the order of the search. ``filename`` is the Curry source. The copy beside the module's ICurry file is read, under the directory of the source and under the root of the module; the step that writes the ICurry file writes it (see ``cache.INTERFACE_SUFFIXES``). The front end's own copy under ``.curry/<frontend_subdir>/`` is never read: after a hit of the ICurry cache it can belong to another version of the source. ''' filename = os.path.abspath(filename) dirname, base = os.path.split(filename) stem = base[:-len('.curry')] if base.endswith('.curry') else base parts = modulename.split('.')[:-1] root = module_root(dirname, modulename) tail = stem + '.fint' candidates = [ os.path.join(dirname, '.curry', config.intermediate_subdir(), tail) , os.path.join(root, '.curry', config.intermediate_subdir(), *parts, tail) ] return list(dict.fromkeys(candidates))
[docs] def find_interface(filename, modulename): '''The interface file of a module, or None.''' for candidate in interface_candidates(filename, modulename): if os.path.isfile(candidate): return candidate return None
# The table # ========= class _ModuleEntry: '''What the table knows about one loaded module.''' __slots__ = ('interface', 'schemes', 'instances') def __init__(self, interface): self.interface = interface self.schemes = {} self.instances = None def parse_instance_name(name, modulename): '''Splits ``_inst#Class#Type`` into the class and the type names.''' parts = name.split('#') if len(parts) != 3 or parts[0] != INST_PREFIX[:-1] or not all(parts[1:]): raise InterfaceError( 'cannot read the instance name %r of module %s; the table expects ' 'the names of curry-frontend 2.0.0, _inst#Class#Type' % (name, modulename) ) return parts[1], parts[2]
[docs] class SignatureTable: ''' The type schemes of the symbols of one interpreter. The table finds the interface of a module from the module's source file (:func:`interface_candidates`), indexes it once, and decodes one entry per symbol on first use. It memoizes per module object, so the modules of ``compile(mode='expr')`` are served while they live. :meth:`clear` forgets everything; ``Interpreter.reset`` calls it. ''' def __init__(self, interp): self._interp = weakref.ref(interp) self._modules = weakref.WeakKeyDictionary() self._instances = {} @property def interp(self): return self._interp()
[docs] def clear(self): '''Forgets every interface, scheme and instance.''' self._modules.clear() self._instances.clear() Interface.purge()
[docs] def modules(self): '''The names of the modules whose interface the table has found.''' return [ module.__name__ for module, entry in self._modules.items() if entry.interface is not None ]
# Interfaces. def _module(self, module): ''' The module object of ``module``: a module object, or the name of a loaded module or of a module of the system library, which is imported. ''' if isinstance(module, str): return self.interp.module(module) return module def _entry(self, module): module = self._module(module) entry = self._modules.get(module) if entry is None: entry = self._modules[module] = _ModuleEntry(self._find(module)) return entry def _find(self, module): '''Finds and indexes the interface of a module object, or None.''' from ..objects.handle import getHandle handle = getHandle(module) if handle.is_package: return None filename = handle.icurry.filename if filename is None: logger.debug('module %s has no source file', module.__name__) return None path = find_interface(filename, handle.fullname) if path is None: logger.debug( 'no FlatCurry interface for module %s; tried %s' , module.__name__, ', '.join(interface_candidates(filename, handle.fullname)) ) return None iface = Interface.load(path) logger.debug('module %s has its interface at %s', module.__name__, path) if handle.fullname == PRELUDE: self.check_frontend(iface) return iface
[docs] @staticmethod def check_frontend(iface): ''' Checks that an interface of the Prelude names its instances as curry-frontend 2.0.0 does. Raises :class:`InterfaceError` otherwise. ''' if NUM_INT_INSTANCE not in iface.function_names(): raise InterfaceError( 'the FlatCurry interface %s has no entry %s; the signature table ' 'expects the naming convention of curry-frontend 2.0.0, the front ' 'end of PAKCS 3.4.1' % (iface.path, NUM_INT_INSTANCE) )
[docs] def interface(self, module): ''' The :class:`Interface` of a module, given as a module object or as the name of a loaded module, or None when no interface file exists. ''' return self._entry(module).interface
[docs] def interface_filename(self, module): '''The interface file of a module, or None.''' iface = self.interface(module) return None if iface is None else iface.path
# Schemes.
[docs] def lookup(self, symbol, required=False): ''' The :class:`Scheme` of a symbol, or None when the symbol has none. Args: symbol: A ``CurryNodeInfo`` or the fully-qualified name of a symbol of a loaded module. required: Whether a missing scheme is an error. The typed path asks for a scheme that way; an untyped use never asks. Raises: InterfaceError: ``required`` is set and the symbol has no scheme. ''' if isinstance(symbol, str): symbol = self.interp.symbol(symbol) icur = getattr(symbol, 'icurry', None) if icur is None: return self._missing(symbol, required, 'the symbol has no ICurry') modulename, name = icur.modulename, icur.name scheme = builtin_scheme(modulename, name) if scheme is not None: return scheme module = getattr(symbol, '_module', None) module = None if module is None else module() if module is None: return self._missing(symbol, required, 'the module is gone') entry = self._entry(module) is_constructor = symbol.info.tag >= T_CTOR key = name, is_constructor if key not in entry.schemes: iface = entry.interface scheme = None if iface is None else iface.scheme(name, is_constructor) entry.schemes[key] = scheme scheme = entry.schemes[key] if scheme is None: if entry.interface is None: return self._missing(symbol, required, None) return self._missing( symbol, required , 'the FlatCurry interface %s has no entry %r' % (entry.interface.path, name) ) return scheme
@staticmethod def _missing(symbol, required, reason): if not required: return None icur = getattr(symbol, 'icurry', None) fullname = symbol if icur is None else icur.fullname modulename = fullname.rpartition('.')[0] if icur is None else icur.modulename if reason is None: raise InterfaceError( 'no type for %s: no FlatCurry interface found; recompile %s or use ' 'raw_expr' % (fullname, modulename) ) raise InterfaceError('no type for %s: %s; use raw_expr' % (fullname, reason))
[docs] def type_decl(self, typename): ''' The ``Type``, ``TypeSyn`` or ``TypeNew`` entry of a type, given by its fully-qualified name, e.g., ``Prelude.IO``, or None. ''' modulename, _, name = typename.rpartition('.') iface = self.interface(modulename) return None if iface is None else iface.type_decl(name)
# Instances.
[docs] def instances(self): ''' The instance map over the loaded modules: a dict from the pair of the qualified class name and the type constructor name to the :class:`Instance`. Every loaded module is scanned for its ``_inst#Class#Type`` functions. Two modules that declare the same instance are an error. ''' for modulename, module in list(self.interp.modules.items()): entry = self._entry(module) if entry.instances is not None: continue found = [] iface = entry.interface if iface is not None: for name in iface.instance_names(): classname, typename = parse_instance_name(name, modulename) scheme = scheme_of_function(iface.function(name)) inst = Instance(classname, typename, modulename, name, scheme) prev = self._instances.get((classname, typename)) if prev is not None and prev.modulename != modulename: raise InterfaceError( 'instance %s %s is declared in module %s and in module %s' % (classname, typename, prev.modulename, modulename) ) self._instances[classname, typename] = inst found.append(inst) entry.instances = found return self._instances
[docs] def instance(self, classname, typename): ''' The :class:`Instance` of a class at a type constructor, or None. The class is qualified, e.g., ``Prelude.Show``; the type constructor is qualified or special, e.g., ``Prelude.Maybe`` or ``[]``. ''' return self.instances().get((classname, typename))
[docs] def instances_of(self, classname): '''The instances of one class, by type constructor name.''' return { typename: inst for (cls, typename), inst in self.instances().items() if cls == classname }