/types_new.py
https://bitbucket.org/eike_welk/algebraic-types-python · Python · 282 lines · 267 code · 0 blank · 15 comment · 0 complexity · 599af10603d4a661b804eb7771ed1837 MD5 · raw file
- # -*- coding: utf-8 -*-
- '''
- Copyright (C) 2011 by Eike Welk
- Example implementation of algebraic data types for python.
- NOTE: Run with --- PYTHON 3 --- !
- Syntax for creating a function type:
- (Int, Float) >> Float
- Alternative syntax, which is close to Ocaml:
- # Int >> Float >> Float
- It would be nice to have the "->" operator available.
- See also
- --------
- http://en.wikipedia.org/wiki/Algebraic_data_type
- Article by Guido van van Rossum:
- http://www.artima.com/weblogs/viewpost.jsp?thread=86641
- '''
- import abc
- class TypeOperatorsMeta(type):
- '''
- Metaclass for all types that can be used with algebraic types.
- It implements the operators that act on the types.
- '''
- def __or__(cls, other):
- '''Create a sum type, also called union type.
- Called for ``type1 | type2`` operator.'''
- assert isinstance(other, type)
- class SumType(metaclass=SumTypeMeta):
- pass
- SumType.register(cls)
- SumType.register(other)
- return SumType
- def __getitem__(cls, el_types):
- '''Create a product or container type.
- Called for ``type1[type2, type3, ...]``.'''
- #The class creates the abstract type, which is used for type checking
- if hasattr(cls, '__param_type_hook__'):
- return cls.__param_type_hook__(el_types)
- else:
- raise TypeError('This type does not implement operator ``[..]``.')
- def __rshift__(cls, other):
- cls._create_function_type(other, cls)
- def __rrshift__(cls, other):
- cls._create_function_type(other, cls)
- @staticmethod
- def _create_function_type(arg_types, ret_type):#pylint: disable-msg=W0613
- '''Create a function type. Called for ">>" operator.'''
- assert isinstance(arg_types, tuple)
- assert isinstance(ret_type, type)
- class FunctionType(metaclass=FunctionTypeMeta):
- _arg_types = arg_types
- _ret_type = ret_type
- return FunctionType
- # --- Type checking algorithms --------------------------
- class SumTypeMeta(abc.ABCMeta, TypeOperatorsMeta):
- '''Metaclass for sum types. Performs the type checking in methods
- inherited from ABCMeta'''
- pass
- class ProductTypeMeta(abc.ABCMeta, TypeOperatorsMeta):
- '''Metaclass for product types (tuples). Performs the type checking.'''
- def __instancecheck__(cls, instance):
- '''Called for isinstance(instance, cls).'''
- if not isinstance(instance, tuple):
- return False
- if len(instance) != len(cls.element_types):
- return False
- for elem_inst, elem_type in zip(instance, cls.element_types):
- if not isinstance(elem_inst, elem_type):
- return False
- return True
- class UniformListTypeMeta(abc.ABCMeta, TypeOperatorsMeta):
- '''
- Metaclass for lists with only one element type.
- Performs the type checking.
- This implementation is obviously silly.
- '''
- def __instancecheck__(cls, instance):
- '''Called for isinstance(instance, cls).'''
- if not isinstance(instance, list):
- return False
- for elem_inst in instance:
- if not isinstance(elem_inst, cls.element_type):
- return False
- return True
- #TODO: implement
- class FunctionTypeMeta(abc.ABCMeta, TypeOperatorsMeta):
- '''Metaclass for function types. Performs the type checking.'''
- # --- Concrete types ----------------------------------------------------
- ##TODO: One can't inherit from bool????
- #class Bool(bool, metaclass=TypeOperatorsMeta):
- # pass
- class Int(int, metaclass=TypeOperatorsMeta):
- pass
- class Float(float, metaclass=TypeOperatorsMeta):
- pass
- class Str(str, metaclass=TypeOperatorsMeta):
- pass
- class Tuple(tuple, metaclass=TypeOperatorsMeta):
- @classmethod
- def __param_type_hook__(cls, el_types):
- '''Create a product type. Called for ``type1[type2, type3, ...]``.'''
- #Put single type object into tuple
- if not isinstance(el_types, tuple):
- el_types = (el_types,)
- class ProductType(metaclass=ProductTypeMeta):
- element_types = el_types
- return ProductType
- class List(list, metaclass=TypeOperatorsMeta):
- @classmethod
- def __param_type_hook__(cls, el_types):
- '''Create a product type. Called for ``type1[type2, type3, ...]``.'''
- #We accept only a single element type
- if not isinstance(el_types, type):
- raise ValueError('List must contain only one element type.')
- class UniformListType(metaclass=UniformListTypeMeta):
- element_type = el_types
- return UniformListType
- # --- Tests ------------------------------------------------------------------
- def test_sum_type():
- '''Test sum type functionality.'''
- IntStr = Int | Str
- #Demonstrate that the main functionality of a sum type works
- assert isinstance(Int(1), IntStr)
- assert isinstance(Str("hello"), IntStr)
- assert not isinstance(Float(1.0), IntStr)
- #Is the sum type a real type?
- assert isinstance(IntStr, type)
- print('type(IntStr) =', type(IntStr))
- def test_sum_type_3():
- '''Test sum type mechanism with more than two types.'''
- IntStrFloat = Int | Str | Float
- assert isinstance(Int(1), IntStrFloat)
- assert isinstance(Str("hello"), IntStrFloat)
- assert isinstance(Float(1.0), IntStrFloat)
- assert not isinstance(True, IntStrFloat)
- def test_sum_type_22():
- '''Sum types must have the "|" operator themselves too.'''
- #Crate two sum types
- IntStr = Int | Str
- IntFloat = Int | Float
- #Demonstrate that the individual sum types work
- assert isinstance(Int(1), IntStr)
- assert isinstance(Str("hello"), IntStr)
- assert not isinstance(Float(1.0), IntStr)
- assert isinstance(Int(1), IntFloat)
- assert isinstance(Float(1.0), IntFloat)
- assert not isinstance(Str("hello"), IntFloat)
- #Create sum type from two sum types
- IntStrFloat = IntStr | IntFloat
- #Test if the combined sum type works
- assert isinstance(Int(1), IntStrFloat)
- assert isinstance(Str("hello"), IntStrFloat)
- assert isinstance(Float(1.0), IntStrFloat)
- assert not isinstance(True, IntStrFloat)
- def test_product_type():
- '''Create tuples where each element has to have a specific type'''
- #Create abstract class for a tuple that contains: Int, Str, Float.
- TupIntStrFloat = Tuple[Int, Str, Float]
- tisf = Tuple((Int(1), Str("hello"), Float(1.)))
- assert isinstance(tisf, TupIntStrFloat)
- tiii = Tuple((Int(1), Int(1), Int(1)))
- tii = Tuple((Int(1), Int(1)))
- assert not isinstance(tiii, TupIntStrFloat)
- assert not isinstance(tii, TupIntStrFloat)
- def test_product_type_1():
- '''Create tuples where each element has to have a specific type.
- Test with standard python types.'''
- TupIntStrFloat = Tuple[int, str, float]
- tisf = (1, "hello", 1.)
- assert isinstance(tisf, TupIntStrFloat)
- tiii = (1, 2, 3)
- tii = (1, 2)
- assert not isinstance(tiii, TupIntStrFloat)
- assert not isinstance(tii, TupIntStrFloat)
- def test_list_type():
- '''Create lists where each element has the same type.'''
- #List with single element type.
- IntList = List[int]
- l_int = [1, 2, 3]
- assert isinstance(l_int, IntList)
- l_if = [1, 2.0, 3.0]
- assert not isinstance(l_if, IntList)
- #List with sum type as element type
- IntFloatList = List[Int | Str]
- l_is = [Int(1), Str('hello'), Int(2)]
- assert isinstance(l_is, IntFloatList)
- l_isf = [Int(1), Str('hello'), Float(2.0)]
- assert not isinstance(l_isf, IntFloatList)
- def test_principle():
- '''Demonstrate that the algorithm works in principle.'''
- class IntBool(metaclass=abc.ABCMeta):
- pass
- IntBool.register(int)
- IntBool.register(bool)
- #Demonstrate that the main functionality of a sum type works
- assert isinstance(1, IntBool)
- assert isinstance(True, IntBool)
- assert not isinstance(1.0, IntBool)
- #It works with inheritance too
- assert isinstance(Int(1), IntBool)
-
- #Is the sum type a real type? (Yes)
- assert isinstance(IntBool, type)
- print('type(IntBool) =', type(IntBool))
- if __name__ == '__main__':
- test_principle()
- test_sum_type()
- test_sum_type_22()
- test_product_type()
- test_product_type_1()
- test_list_type()
- print("Test finished!")