Skip to content

Instantly share code, notes, and snippets.

@adityagodbole
Last active August 29, 2015 14:00
Show Gist options
  • Save adityagodbole/11152703 to your computer and use it in GitHub Desktop.
Save adityagodbole/11152703 to your computer and use it in GitHub Desktop.
from collections import namedtuple
from types import MethodType
def _init(self, *args, **kwargs):
if not self._typespec:
return
if len(self._fields) != len(self._typespec):
raise TypeError("Incorrect type in constructor")
for field, ts in zip(self._fields, self._typespec):
if ts and not isinstance(getattr(self, field), ts):
raise TypeError("Incorrect type in constructor")
def _extend(base, name, *args, **kwargs):
fields = base._fields + args
typespec = base._typespec + kwargs.pop('typespec', None)
argsdict = kwargs.copy()
argsdict.update({ 'typespec': typespec })
return newtype(name, fields, **argsdict)
def newtype(name, *args, **kwargs):
typespec = kwargs.pop('typespec', None)
struct = namedtuple(name, *args, **kwargs)
struct.__init__ = MethodType(_init, None, struct)
struct.extend = MethodType(_extend, struct)
struct._typespec = typespec
return struct
from records import newtype
# New immputable tuple type with rudimentary typecheck
Point = newtype('Point', 'x y', typespec=(int, int))
# Extend an existing type
Vector = Point.extend('Vector', 'z', typespec=(int,))
# Compose a new type out of existing
VectorE = newtype('VectorE', 'pt z', typespec=(Point, int))
# Define functionality without the data (interface)
class Geom(object):
def distance2D(self):
return self.x + self.y
def distance3D(self):
return self.x + self.y + self.z
# "include" the Geom functionality as well as implement
# some new functionality
class VectorOps(Vector, Geom):
def show(self):
print "VectorOps: ", self.x, self.y, self.z
#p = Point(1, 2.0) # Raises typeerror
#v = Vector(1, 2, 3.0) # Raises typeerror
v = Vector(1, 2, 3)
print "Vector: ", v.x, v.y, v.z
ve = VectorE(Point(1,2), 3)
print "VectorE: ", ve.pt.x, ve.pt.y, ve.z
vo = VectorOps(1, 2, 3)
vo.xshow()
print "2D: ", vo.distance2D()
print "3D: ", vo.distance3D()
# This will cause an exception
vo.z = 10
$ python test.py
Vector: 1 2 3
VectorE: 1 2 3
VectorOps: 1 2 3
2D: 3
3D: 6
Traceback (most recent call last):
File "test.py", line 30, in <module>
vo.z = 10
AttributeError: can't set attribute
This exercise is to demonstrate that assignment is not necessary
# for a general purpose language. Assignment creates local mutable state.
# While local state is necessary, it need not be mutable.
# Immutable local state can be implemented using functions
from records import newtype
from math import sqrt
Point = newtype('Point', 'x y', typespec=(int, int))
# This is an example of a function which uses assignment
def _distances(pt1, pt2):
xdiff = pt1.x - pt2.x
ydiff = pt1.y - pt2.y
simpledist = xdiff + ydiff
fulldist = sqrt(xdiff ** 2 + ydiff ** 2)
return (simpledist, fulldist)
# This is the equivalent function without assignment
# Concept - local state can be achieved with closures
# and in some languages is implemented that way.
# In scheme let can be implemented as a macro using
# lambdas
def distances(pt1, pt2):
def dists(xdiff, ydiff):
def simpledist():
return xdiff + ydiff
def fulldist():
return sqrt(xdiff ** 2 + ydiff ** 2)
return (simpledist(), fulldist())
return dists(pt1.x - pt2.x, pt1.y - pt2.y)
def main(p1, p2):
print distances(p1, p2)
print _distances(p1, p2)
main(Point(10, 10), Point(20, 20))
$ python localstate.py
(-20, 14.142135623730951)
(-20, 14.142135623730951)
# This demostrates how we can model typeclasses or interfaces
# on top of our types
from records import newtype
from math import sqrt
Point = newtype('Point', 'x y', typespec=(int, int))
# This class only defines behaviour
class Vector(object):
def magnitude():
return sqrt(self.x ** 2 + self.y ** 2)
# Class Point "includes" the Vector behaviour
# We can say that Point "implements" Vector
class Point(Point, Vector):
pass
# Note that we have passed the type Vector, which is just
# an interface or a typeclass in the typespec. Below we
# construct a Segment using Point values. This is all cool :)
Segment = newtype('Segment', 'p1 p2', typespec=(Vector, Vector))
class Segment(Segment):
def length(self):
def diffsq(x, y):
return (x - y) ** 2
return sqrt(diffsq(self.p1.x, self.p2.x) +
diffsq(self.p1.y, self.p2.y))
def main(p1, p2):
print Segment(p1, p2).length()
main(Point(10, 10), Point(20, 20))
$ python typeclasses.py
14.1421356237
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment