[Types-sig] types, functional stuff.

Edward Welbourne Edward Welbourne <eddy@chaos.org.uk>
Thu, 3 Dec 1998 19:54:53 GMT


A quick suggestion on the `how to specify static typing' debate ... then
a little contribution on functional tools.

Once upon a time, so I'm told, folk started putting

__doc__ = """The documentation of this entity"""

at the starts of entities, and it was seen to be good.

OK, so a function (or method) can start with

def myfunc(first, flexible, optional=7):
    __statictypingdata__ = {
	'': types.IntegerType,	# The return type
	'first': types.FloatType,	# The type of this arg
	'flexible': (types.StringType, types.TupleType, types.ListType),
	# that's offering a choice
	'optional': (None, types.IntType),	# NB None != NoneType
	# and there's nothing to stop us putting in info about local
	# variables, too.
	}

and a class implementing .__call__() could do similar.
The big problem is specifying that a dictionary argument is to be a
dictionary mapping integers to objects of class Hubert.
This just seems much more practical than whacky syntax.


Now, John Skaller asked for:

>        compose(f1, f2, f3, f4 ..) # chain in series, 1 arg only
>        product(f1, f2, f3, f4, ..) # apply componentwise (in parallel)
>        sum(f1, f2, .. ) # select one of
>        delta(x,i) = (x,x,x,x,x ..) # i times

so, John: there follow implementations for 3 of these in python (you can
do currying too, it's not hard).  Use them.  If the uses persuade folk
they are useful, maybe someone'll bother to implement them as a C
module.  The language doesn't need them as built-ins.  I haven't done
sum: it would involve working out what types the functions f1, ... take
as their arguments and I don't have time or the right docs to hand.
But hey, I'll throw in a close relative (union).

def delta(what, count=2): return (what,) * count

class compose:
	def __init__(self, *args):
		row = []
		# this will be in reverse order so that
		# compose(f1,f2)(x) = f1(f2(x))
		for item in args:
			if isinstance(item, _Composite):
				# gratuitous optimisation
				row = item.__components + row
			else:
				row[:0] = [ item ]

		self.__components = row

	def __call__(self, *args):
		for item in self.__components:
			args = apply(item, args)
			if type(args) != types.TupleType: args = (args,)

		return args
		# yeah, sorry about returning a tuple always.

class product:
	def __init__(self, *args):
		self.__factors = args

	def __call__(self, *args):
		if len(args) != len(self.__factors):
			raise TypeError, 'Wrong number of arguments'

		return map(lambda f,x: f(x), self.__factors, args)

class union:
	def __init__(self, parts):
		"""parts is a dict, mapping types to functions"""
		self.__parts = parts

	def __call__(self, what):
		try: part = self.__parts(type(what))
		except KeyError:
			raise TypeError, 'Argument not supported by this direct sum'
		return part(what)

and no, I haven't tested them: I just typed them out of my head.  The
basic lesson is that python is a perfectly good implementation domain
for all the functional things I ever want.  They practically write
themselves, so don't complain about their absence from the language.
(But lambda is a necessity !)

I'm not subscribing to this list, just reading web archive when I have time.
So don't be offended if I'm slow responding to any replies ;^)
Dig the crazy meta-stuff, but it confuses me !

	Eddy.