/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

  1. # -*- coding: utf-8 -*-
  2. '''
  3. Copyright (C) 2011 by Eike Welk
  4. Example implementation of algebraic data types for python.
  5. NOTE: Run with --- PYTHON 3 --- !
  6. Syntax for creating a function type:
  7. (Int, Float) >> Float
  8. Alternative syntax, which is close to Ocaml:
  9. # Int >> Float >> Float
  10. It would be nice to have the "->" operator available.
  11. See also
  12. --------
  13. http://en.wikipedia.org/wiki/Algebraic_data_type
  14. Article by Guido van van Rossum:
  15. http://www.artima.com/weblogs/viewpost.jsp?thread=86641
  16. '''
  17. import abc
  18. class TypeOperatorsMeta(type):
  19. '''
  20. Metaclass for all types that can be used with algebraic types.
  21. It implements the operators that act on the types.
  22. '''
  23. def __or__(cls, other):
  24. '''Create a sum type, also called union type.
  25. Called for ``type1 | type2`` operator.'''
  26. assert isinstance(other, type)
  27. class SumType(metaclass=SumTypeMeta):
  28. pass
  29. SumType.register(cls)
  30. SumType.register(other)
  31. return SumType
  32. def __getitem__(cls, el_types):
  33. '''Create a product or container type.
  34. Called for ``type1[type2, type3, ...]``.'''
  35. #The class creates the abstract type, which is used for type checking
  36. if hasattr(cls, '__param_type_hook__'):
  37. return cls.__param_type_hook__(el_types)
  38. else:
  39. raise TypeError('This type does not implement operator ``[..]``.')
  40. def __rshift__(cls, other):
  41. cls._create_function_type(other, cls)
  42. def __rrshift__(cls, other):
  43. cls._create_function_type(other, cls)
  44. @staticmethod
  45. def _create_function_type(arg_types, ret_type):#pylint: disable-msg=W0613
  46. '''Create a function type. Called for ">>" operator.'''
  47. assert isinstance(arg_types, tuple)
  48. assert isinstance(ret_type, type)
  49. class FunctionType(metaclass=FunctionTypeMeta):
  50. _arg_types = arg_types
  51. _ret_type = ret_type
  52. return FunctionType
  53. # --- Type checking algorithms --------------------------
  54. class SumTypeMeta(abc.ABCMeta, TypeOperatorsMeta):
  55. '''Metaclass for sum types. Performs the type checking in methods
  56. inherited from ABCMeta'''
  57. pass
  58. class ProductTypeMeta(abc.ABCMeta, TypeOperatorsMeta):
  59. '''Metaclass for product types (tuples). Performs the type checking.'''
  60. def __instancecheck__(cls, instance):
  61. '''Called for isinstance(instance, cls).'''
  62. if not isinstance(instance, tuple):
  63. return False
  64. if len(instance) != len(cls.element_types):
  65. return False
  66. for elem_inst, elem_type in zip(instance, cls.element_types):
  67. if not isinstance(elem_inst, elem_type):
  68. return False
  69. return True
  70. class UniformListTypeMeta(abc.ABCMeta, TypeOperatorsMeta):
  71. '''
  72. Metaclass for lists with only one element type.
  73. Performs the type checking.
  74. This implementation is obviously silly.
  75. '''
  76. def __instancecheck__(cls, instance):
  77. '''Called for isinstance(instance, cls).'''
  78. if not isinstance(instance, list):
  79. return False
  80. for elem_inst in instance:
  81. if not isinstance(elem_inst, cls.element_type):
  82. return False
  83. return True
  84. #TODO: implement
  85. class FunctionTypeMeta(abc.ABCMeta, TypeOperatorsMeta):
  86. '''Metaclass for function types. Performs the type checking.'''
  87. # --- Concrete types ----------------------------------------------------
  88. ##TODO: One can't inherit from bool????
  89. #class Bool(bool, metaclass=TypeOperatorsMeta):
  90. # pass
  91. class Int(int, metaclass=TypeOperatorsMeta):
  92. pass
  93. class Float(float, metaclass=TypeOperatorsMeta):
  94. pass
  95. class Str(str, metaclass=TypeOperatorsMeta):
  96. pass
  97. class Tuple(tuple, metaclass=TypeOperatorsMeta):
  98. @classmethod
  99. def __param_type_hook__(cls, el_types):
  100. '''Create a product type. Called for ``type1[type2, type3, ...]``.'''
  101. #Put single type object into tuple
  102. if not isinstance(el_types, tuple):
  103. el_types = (el_types,)
  104. class ProductType(metaclass=ProductTypeMeta):
  105. element_types = el_types
  106. return ProductType
  107. class List(list, metaclass=TypeOperatorsMeta):
  108. @classmethod
  109. def __param_type_hook__(cls, el_types):
  110. '''Create a product type. Called for ``type1[type2, type3, ...]``.'''
  111. #We accept only a single element type
  112. if not isinstance(el_types, type):
  113. raise ValueError('List must contain only one element type.')
  114. class UniformListType(metaclass=UniformListTypeMeta):
  115. element_type = el_types
  116. return UniformListType
  117. # --- Tests ------------------------------------------------------------------
  118. def test_sum_type():
  119. '''Test sum type functionality.'''
  120. IntStr = Int | Str
  121. #Demonstrate that the main functionality of a sum type works
  122. assert isinstance(Int(1), IntStr)
  123. assert isinstance(Str("hello"), IntStr)
  124. assert not isinstance(Float(1.0), IntStr)
  125. #Is the sum type a real type?
  126. assert isinstance(IntStr, type)
  127. print('type(IntStr) =', type(IntStr))
  128. def test_sum_type_3():
  129. '''Test sum type mechanism with more than two types.'''
  130. IntStrFloat = Int | Str | Float
  131. assert isinstance(Int(1), IntStrFloat)
  132. assert isinstance(Str("hello"), IntStrFloat)
  133. assert isinstance(Float(1.0), IntStrFloat)
  134. assert not isinstance(True, IntStrFloat)
  135. def test_sum_type_22():
  136. '''Sum types must have the "|" operator themselves too.'''
  137. #Crate two sum types
  138. IntStr = Int | Str
  139. IntFloat = Int | Float
  140. #Demonstrate that the individual sum types work
  141. assert isinstance(Int(1), IntStr)
  142. assert isinstance(Str("hello"), IntStr)
  143. assert not isinstance(Float(1.0), IntStr)
  144. assert isinstance(Int(1), IntFloat)
  145. assert isinstance(Float(1.0), IntFloat)
  146. assert not isinstance(Str("hello"), IntFloat)
  147. #Create sum type from two sum types
  148. IntStrFloat = IntStr | IntFloat
  149. #Test if the combined sum type works
  150. assert isinstance(Int(1), IntStrFloat)
  151. assert isinstance(Str("hello"), IntStrFloat)
  152. assert isinstance(Float(1.0), IntStrFloat)
  153. assert not isinstance(True, IntStrFloat)
  154. def test_product_type():
  155. '''Create tuples where each element has to have a specific type'''
  156. #Create abstract class for a tuple that contains: Int, Str, Float.
  157. TupIntStrFloat = Tuple[Int, Str, Float]
  158. tisf = Tuple((Int(1), Str("hello"), Float(1.)))
  159. assert isinstance(tisf, TupIntStrFloat)
  160. tiii = Tuple((Int(1), Int(1), Int(1)))
  161. tii = Tuple((Int(1), Int(1)))
  162. assert not isinstance(tiii, TupIntStrFloat)
  163. assert not isinstance(tii, TupIntStrFloat)
  164. def test_product_type_1():
  165. '''Create tuples where each element has to have a specific type.
  166. Test with standard python types.'''
  167. TupIntStrFloat = Tuple[int, str, float]
  168. tisf = (1, "hello", 1.)
  169. assert isinstance(tisf, TupIntStrFloat)
  170. tiii = (1, 2, 3)
  171. tii = (1, 2)
  172. assert not isinstance(tiii, TupIntStrFloat)
  173. assert not isinstance(tii, TupIntStrFloat)
  174. def test_list_type():
  175. '''Create lists where each element has the same type.'''
  176. #List with single element type.
  177. IntList = List[int]
  178. l_int = [1, 2, 3]
  179. assert isinstance(l_int, IntList)
  180. l_if = [1, 2.0, 3.0]
  181. assert not isinstance(l_if, IntList)
  182. #List with sum type as element type
  183. IntFloatList = List[Int | Str]
  184. l_is = [Int(1), Str('hello'), Int(2)]
  185. assert isinstance(l_is, IntFloatList)
  186. l_isf = [Int(1), Str('hello'), Float(2.0)]
  187. assert not isinstance(l_isf, IntFloatList)
  188. def test_principle():
  189. '''Demonstrate that the algorithm works in principle.'''
  190. class IntBool(metaclass=abc.ABCMeta):
  191. pass
  192. IntBool.register(int)
  193. IntBool.register(bool)
  194. #Demonstrate that the main functionality of a sum type works
  195. assert isinstance(1, IntBool)
  196. assert isinstance(True, IntBool)
  197. assert not isinstance(1.0, IntBool)
  198. #It works with inheritance too
  199. assert isinstance(Int(1), IntBool)
  200. #Is the sum type a real type? (Yes)
  201. assert isinstance(IntBool, type)
  202. print('type(IntBool) =', type(IntBool))
  203. if __name__ == '__main__':
  204. test_principle()
  205. test_sum_type()
  206. test_sum_type_22()
  207. test_product_type()
  208. test_product_type_1()
  209. test_list_type()
  210. print("Test finished!")