proposals for having more fun with map

T

Tom Anderson

hi all,

i've got a few proposals to do, in one way or another, with the map
builtin. i'd like to hear what people think.

firstly, collections, and things that look like collections, should
support the call operator; the implementation should look like:

def __call__(self, arg):
return self[arg]

then, you could use collections as functions. after all, collections and
functions are just two different ways of representing mappings (dicts are
general mappings, and lists are mappings of the natural numbers which have
a contiguous domain), so it makes sense for them to be somewhat
interchangeable. specifically, this would make it possible to use dicts
and lists as first arguments to map, eg:

details = {
"surnname": "Cleese",
"firstname": "John",
"dob": 19391027,
"birthplace": "Weston-super-Mare",
"height: 1.95,
"favourite work": "the documentary about lemurs"
}
template = ("firstname", "surname", "height")
info = map(details, template)

okay, so maybe you're not convinced, but this is something i find myself
wanting to do all the time, so i either end up throwing lambdas all over
the place to glue the dicts into map, or writing a little helper function
like so:

def functor(dict):
return lambda arg: dict[arg]

this isn't hard, of course, but it's the sort of thing that ought to be
done once and only once.

there's a case to be made that function objects should also have
__getitem__, so they look like mappings, but that's harder, since they
can't really supply a keys method, so they can't be full mappings.
however, i do wonder if sequences could have a keys method, which would
look like:

def keys(self):
return range(len(self))

so they look like mappings of a range of the natural numbers. the problem
with that is that the sequence and mapping versions of __iter__ are
incompatible. oh well.

secondly, map should work on dicts as well as sequences. map has semantics
that look like this:

map(fn, x) = fn(x)

at the moment, x has to be a sequence; i'd like to allow x to be a
mapping. the implementation is a doddle:

def mapdict(fn, dict):
mdict = []
for key in dict:
mdict[key] = fn(dict[key])
return mdict

again, trivial, but if i'm writing this out in almost every program i
write, other people must be too, so it's worth putting in the core.

now, that function is a version of map for dicts; i'd like to see one
function which supports sequences and mappings. i'm not entirely sure how
you'd decide whether an input was a sequence or a mapping, though; the
best i've come up with is using hasattr(x, "keys") to see if it looks like
a mapping (which falls down if we give sequences a keys method!).

thirdly, i'd like map to try to preserve the type of the sequence; if you
feed it a tuple, you get a tuple back, and if you feed it a string, you
get a string back. this isn't hard to do, but is hard to do well. the
simple way forward is to pull out the type of the parameter and try to use
it to construct a return value from a list:

def preservingmap(fn, seq):
mseq = map(fn, seq)
try:
mseq = type(seq)(mseq)
except TypeError:
pass
return mseq

however, this requires the constructor of sequence-like types to
essentially be a copy constructor; this may be too great a restriction on
programmers.

also, we'd need to redefine the implementation of __str__ for sequences;
at present, it's the same as __repr__, but that doesn't work in the above
scheme, since it's far from being a copy constructor. in particular, we
want it to be such that, for a string s, str(list(s)) == s. i think it has
to look like:

def __str__(self):
return reduce(lambda a, b: a + b, map(str, self))

so that it just returns the concatenation of the stringification of its
elements. this isn't much use for the general task of displaying lists to
humans, but that's what repr is for. str is about converting an object to
a string, and this is surely the most natural way for sequences.

anyway, that's it. am i bonkers, or does any of that sound like a good
idea?

tom
 
V

Ville Vainio

Tom> wanting to do all the time, so i either end up throwing lambdas all over
Tom> the place to glue the dicts into map, or writing a little helper function
Tom> like so:

Tom> def functor(dict):
Tom> return lambda arg: dict[arg]

See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/305318

#itemgetter: also of course works with dictionaries
data= ( {'name':'fred','score':50},{'name':'betty','score':75})
print map(operator.itemgetter('name'),data)

Tom> this isn't hard, of course, but it's the sort of thing that
Tom> ought to be done once and only once.

Indeed ;-).

Tom> again, trivial, but if i'm writing this out in almost every
Tom> program i write, other people must be too, so it's worth
Tom> putting in the core.

Actually, the more likely future of map is moving *out* of the core,
to a "functional" module of some sort. List comprehensions (and soon
genexps) are the recommended approach.
 
J

Jeremy Bowers

hi all,

i've got a few proposals to do, in one way or another, with the map
builtin. i'd like to hear what people think.

Most of your proposals are implementable today with a pure Python module,
albeit with some subclasses of built-ins.

If you're serious about this, I'd suggest bundling all your suggestions
into a module and pointing people at them to try them out, including
several examples of the advantages you want to point out.

The best argument for proposals is to try it and find it useful, and since
yours are easy to implement, I'd suggest taking advantage of that.
 
T

Tom Anderson

Most of your proposals are implementable today with a pure Python module,
albeit with some subclasses of built-ins.

true - in fact, i wrote such a module to make sure that this was true!

the problem is the 'albeit'; for it all to work properly, it needs some
cooperation from core types, without users going round wrapping things all
the time. still, you can probably get at least 80% of it without doing
anything exciting.
If you're serious about this, I'd suggest bundling all your suggestions
into a module and pointing people at them to try them out, including
several examples of the advantages you want to point out.

The best argument for proposals is to try it and find it useful, and
since yours are easy to implement, I'd suggest taking advantage of that.

well said. i'll bundle them up and stick them somewhere; it'll have to
wait for my broadband to come through, though ...

tom
 
T

Tom Anderson

Tom> wanting to do all the time, so i either end up throwing lambdas all over
Tom> the place to glue the dicts into map, or writing a little helper function
Tom> like so:

Tom> def functor(dict):
Tom> return lambda arg: dict[arg]

See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/305318

of course! i really should use the operator package more; it just feels
kind of clunky to me.
Tom> again, trivial, but if i'm writing this out in almost every
Tom> program i write, other people must be too, so it's worth
Tom> putting in the core.

Actually, the more likely future of map is moving *out* of the core, to
a "functional" module of some sort. List comprehensions (and soon
genexps) are the recommended approach.

NOOOOOOO!!!!!!!!!!!!!!!!!! one by one, my favourite languages are being
mutilated beyond recognition ... </overreaction>

tom
 
A

Alex Martelli

Tom Anderson said:
NOOOOOOO!!!!!!!!!!!!!!!!!! one by one, my favourite languages are being
mutilated beyond recognition ... </overreaction>

Yep, an overreaction. Moving a function among different modules of the
standard library (e.g., from builtins to a new module 'functional') is
very different from changing the language.


Alex
 
A

Alex Martelli

Tom Anderson said:
details = {
"surnname": "Cleese",
"firstname": "John",
"dob": 19391027,
"birthplace": "Weston-super-Mare",
"height: 1.95,
"favourite work": "the documentary about lemurs"
}
template = ("firstname", "surname", "height")
info = map(details, template)

Use map(details.get, template) and you can get this today. No reason to
change dicts for this purpose.
okay, so maybe you're not convinced, but this is something i find myself
wanting to do all the time, so i either end up throwing lambdas all over
the place to glue the dicts into map, or writing a little helper function
like so:

def functor(dict):
return lambda arg: dict[arg]

this isn't hard, of course, but it's the sort of thing that ought to be
done once and only once.

It is -- you simply need the bound method somedict.get (in 2.4 it might
be slightly faster to call somedict.__getitem__, I think in 2.3
somedict.get is faster and in any case it _is_ nicer to type!-).

however, i do wonder if sequences could have a keys method, which would
look like:

def keys(self):
return range(len(self))

so they look like mappings of a range of the natural numbers. the problem
with that is that the sequence and mapping versions of __iter__ are
incompatible. oh well.

Yep, not worth doing.

secondly, map should work on dicts as well as sequences. map has semantics
that look like this:

map(fn, x) = fn(x)

at the moment, x has to be a sequence; i'd like to allow x to be a
mapping. the implementation is a doddle:


Nope, a dict is accepted as x today:
d=dict(a=23,b=42,c=69)
map(lambda x:x, d) ['a', 'c', 'b']

This cannot be changed w/o backwards incompatibilities (so, propose it
for Python 3000 -- won't get in before then, as it would break some
current, perfectly correct programs).

anyway, that's it. am i bonkers, or does any of that sound like a good
idea?

Why should the two be mutually incompatible?-)

Seriously: some of your ideas may have merit but you should realize no
changes that break currently working programs are gonna get in until 3.0
at the earliest, and even in 3.0 the focus will be on simplification.
If you're keep on your backwards-incompatible proposals you might be
luckier with some of the various python-like languages which are
springing up _without_ the backwards compatibility requirements of
Python, such as bobo or Prothon. For Python itself, you must deal with
the additional burden of backwards compatibility on top of everything...


Alex
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top