Source code for hask.lang.lazylist

from __future__ import (division as _py3_division,
                        print_function as _py3_print,
                        absolute_import as _py3_abs_import)

import collections
import itertools
import sys

from .hindley_milner import TypeVariable
from .hindley_milner import ListType
from .hindley_milner import unify

from .type_system import typeof
from .type_system import Typeclass
from .type_system import Hask
from .type_system import build_instance

from .typeclasses import Show
from .typeclasses import show
from .typeclasses import Eq
from .typeclasses import Ord

from .syntax import Syntax
from .syntax import instance
from .syntax import sig
from .syntax import H

try:
    from __builtin__ import cmp
except ImportError:
    def cmp(a, b):
        if a == b:
            return 0
        elif a < b:
            return -1
        else:
            return 1


[docs]class Enum(Typeclass): """ Class Enum defines operations on sequentially ordered types. The enumFrom... methods are used in translation of arithmetic sequences. Instances of Enum may be derived for any enumeration type (types whose constructors have no fields). The nullary constructors are assumed to be numbered left-to-right by fromEnum from 0 through n-1. Attributes: - ``toEnum`` - ``fromEnum`` - ``succ`` - ``pred`` - ``enumFrom`` - ``enumFromThen`` - ``enumFrom`` - ``enumFromThenTo`` - ``EnumFromTo`` Minimal complete definition: - ``toEnum`` - ``fromEnum`` """ @classmethod def make_instance(typeclass, cls, toEnum, fromEnum): def succ(a): return toEnum(fromEnum(a) + 1) def pred(a): return toEnum(fromEnum(a) - 1) def enumFromThen(start, second): pointer = fromEnum(start) step = fromEnum(second) - pointer while True: yield toEnum(pointer) pointer += step def enumFrom(start): return enumFromThen(start, succ(start)) def enumFromThenTo(start, second, end): if start == end: yield start return elif (second >= start > end) or (second <= start < end): return pointer, stop = fromEnum(start), fromEnum(end) step = fromEnum(second) - pointer while (start < end and pointer <= stop) or \ (start > end and pointer >= stop): yield toEnum(pointer) pointer += step def enumFromTo(start, end): second = succ(start) if start < end else pred(start) return enumFromThenTo(start, second, end) attrs = {"toEnum": toEnum, "fromEnum": fromEnum, "succ": succ, "pred": pred, "enumFromThen": enumFromThen, "enumFrom": enumFrom, "enumFromThenTo": enumFromThenTo, "enumFromTo": enumFromTo} build_instance(Enum, cls, attrs)
@sig(H/ "a" >> int) def fromEnum(a): """``fromEnum :: a -> int`` Convert to an int. """ return Enum[a].toEnum(a) @sig(H/ "a" >> "a") def succ(a): """``succ :: a -> a`` the successor of a value. For numeric types, succ adds 1. """ return Enum[a].succ(a) @sig(H/ "a" >> "a") def pred(a): """ pred :: a -> a the predecessor of a value. For numeric types, pred subtracts 1. """ return Enum[a].pred(a) @sig(H/ "a" >> "a" >> ["a"]) def enumFromThen(start, second): """``enumFromThen :: a -> a -> [a]`` Used in translation of [n, n_, ...] """ return L[Enum[start].enumFromThen(start, second)] @sig(H/ "a" >> ["a"]) def enumFrom(start): """``enumFrom :: a -> [a]`` Used in translation of L[n, ...] """ return L[Enum[start].enumFrom(start)] @sig(H/ "a" >> "a" >> "a" >> ["a"]) def enumFromThenTo(start, second, end): """``enumFromThenTo :: a -> a -> a -> [a]`` Used in translation of L[n, n_, ..., m] """ return L[Enum[start].enumFromThenTo(start, second, end)] @sig(H/ "a" >> "a" >> ["a"]) def enumFromTo(start, end): """``enumFromTo :: a -> a -> [a]`` Used in translation of L[n, ..., m] """ return L[Enum[start].enumFromTo(start, end)] instance(Enum, int).where(fromEnum=int, toEnum=int) instance(Enum, bool).where(fromEnum=int, toEnum=bool) instance(Enum, str).where(fromEnum=ord, toEnum=chr) if sys.version[0] == '2': long = long # noqa instance(Enum, long).where(fromEnum=int, toEnum=long)
[docs]class List(collections.Sequence, Hask): """Statically typed lazy sequence datatype. See `L`:obj: for more information. """ def __init__(self, head=None, tail=None): self.__head = [] self.__tail = itertools.chain([]) self.__is_evaluated = True if head is not None and len(head) > 0: fst = head[0] for fst, other in zip(itertools.repeat(fst), head): unify(typeof(fst), typeof(other)) self.__head.extend(head) if tail is not None: self.__tail = itertools.chain(self.__tail, tail) self.__is_evaluated = False def __type__(self): if self.__is_evaluated: if len(self.__head) == 0: return ListType(TypeVariable()) return ListType(typeof(self[0])) elif len(self.__head) == 0: self.__next() return self.__type__() return ListType(typeof(self[0])) def __next(self): """ Evaluate the next element of the tail, and add it to the head. """ if self.__is_evaluated: raise StopIteration else: try: next_iter = next(self.__tail) if len(self.__head) > 0: unify(typeof(self[0]), typeof(next_iter)) self.__head.append(next_iter) except StopIteration: self.__is_evaluated = True def __evaluate(self): """ Evaluate the entire List. """ while not self.__is_evaluated: self.__next() def __rxor__(self, item): """ ^ is the cons operator (equivalent to : in Haskell) """ unify(self.__type__(), ListType(typeof(item))) if self.__is_evaluated: return List(head=[item] + self.__head) return List(head=[item] + self.__head, tail=self.__tail) def __add__(self, other): """ (+) :: [a] -> [a] -> [a] + is the list concatenation operator, equivalent to ++ in Haskell and + for Python lists """ unify(self.__type__(), typeof(other)) if self.__is_evaluated and other.__is_evaluated: return List(head=self.__head + other.__head) elif self.__is_evaluated and not other.__is_evaluated: return List(head=self.__head + other.__head, tail=other.__tail) return List(head=self.__head, tail=itertools.chain(self.__tail, iter(other))) def __str__(self): if len(self.__head) == 0 and self.__is_evaluated: return "L[[]]" elif len(self.__head) == 1 and self.__is_evaluated: return "L[[%s]]" % show(self.__head[0]) body = ", ".join((show(s) for s in self.__head)) return "L[%s]" % body if self.__is_evaluated else "L[%s ...]" % body def __cmp__(self, other): if self.__is_evaluated and other.__is_evaluated: return cmp(self.__head, other.__head) elif len(self.__head) >= len(other.__head): # check the evaluated heads heads = zip(self.__head[:len(other.__head)], other.__head) heads_comp = ((cmp(h1, h2) for h1, h2 in heads)) for comp in heads_comp: if comp != 0: return comp # evaluate the shorter-headed list until it is the same size while len(self.__head) > len(other.__head): if other.__is_evaluated: return 1 other.__next() comp = cmp(self.__head[len(other.__head)-1], other.__head[-1]) if comp != 0: return comp # evaluate the tails, checking each time while not self.__is_evaluated or not other.__is_evaluated: if not self.__is_evaluated: self.__next() if not other.__is_evaluated: other.__next() len_comp = cmp(len(self.__head), len(other.__head)) if len_comp != 0: return len_comp if len(self.__head) > 0: value_comp = cmp(self.__head[-1], other.__head[-1]) if value_comp != 0: return value_comp elif len(other.__head) > len(self.__head): return -other.__cmp__(self) return 0 def __eq__(self, other): return self.__cmp__(other) == 0 def __lt__(self, other): return self.__cmp__(other) == -1 def __gt__(self, other): return self.__cmp__(other) == 1 def __le__(self, other): comp = self.__cmp__(other) return comp in (-1, 0) def __ge__(self, other): comp = self.__cmp__(other) return comp in (1, 0) def __len__(self): self.__evaluate() return len(self.__head) def __iter__(self): for item in self.__head: yield item for item in self.__tail: self.__head.append(item) yield item def count(self, x): unify(self.__type__(), ListType(typeof(x))) self.__evaluate() return self.__head.count(x) def index(self, x): unify(self.__type__(), ListType(typeof(x))) self.__evaluate() return self.__head.index(x) def __contains__(self, x): unify(self.__type__(), ListType(typeof(x))) for item in iter(self): if item is x: return True return False def __getitem__(self, ix): is_slice = isinstance(ix, slice) if is_slice: i = ix.start if ix.stop is None else ix.stop else: i = ix # make sure that the list is evaluated enough to do the indexing, but # not any more than necessary # if index is negative, evaluate the entire list if i is None: # In Python 3, `None >= 0` is a TypeError, but in Python 2 returns # False. So let's go negative in any case... i = -1 if i >= 0: while (i+1) > len(self.__head): try: self.__next() except StopIteration: break else: self.__evaluate() if is_slice: if ix.stop is None and not self.__is_evaluated: return List(head=self.__head[ix], tail=self.__tail) return List(head=self.__head[ix]) return self.__head[i]
# Basic typeclass instances for list instance(Show, List).where( show = List.__str__ ) instance(Eq, List).where( eq = List.__eq__ ) instance(Ord, List).where( lt = List.__lt__, gt = List.__gt__, le = List.__le__, ge = List.__ge__ ) class __list_comprehension__(Syntax): """ L is the syntactic construct for Haskell-style list comprehensions and lazy list creation. To create a new List, just wrap an interable in L[ ]. List comprehensions can be used with any instance of Enum, including the built-in types int, long, float, and char. There are four basic list comprehension patterns: >>> L[1, ...] # list from 1 to infinity, counting by ones >>> L[1, 3, ...] # list from 1 to infinity, counting by twos >>> L[1, ..., 20] # list from 1 to 20 (inclusive), counting by ones >>> L[1, 5, ..., 20] # list from 1 to 20 (inclusive), counting by fours """ def __getitem__(self, lst): if isinstance(lst, tuple) and len(lst) < 5 and \ any((Ellipsis is x for x in lst)): # L[x, ...] if len(lst) == 2 and lst[1] is Ellipsis: return enumFrom(lst[0]) # L[x, y, ...] elif len(lst) == 3 and lst[2] is Ellipsis: return enumFromThen(lst[0], lst[1]) # L[x, ..., y] elif len(lst) == 3 and lst[1] is Ellipsis: return enumFromTo(lst[0], lst[2]) # L[x, y, ..., z] elif len(lst) == 4 and lst[2] is Ellipsis: return enumFromThenTo(lst[0], lst[1], lst[3]) raise SyntaxError("Invalid list comprehension: %s" % str(lst)) elif hasattr(lst, "next") or hasattr(lst, "__next__"): return List(tail=lst) return List(head=lst) L = __list_comprehension__("Invalid input to list constructor")