Python from Wise Guy's Viewpoint

B

Brian McNamara!

(e-mail address removed) (Jon S. Anthony) once said:
That's not actually good enough. You also have to have overloads for
all the possible types for 1-arg, ..., N-arg. Actually it's worse
than that - the set of types is not closed, so even in principle this
won't work.

I'm not sure I understand you, but if I do, then "templates" take care
of this. That is, we'd write (e.g. for the 3-arg case):

template <class A, class B, class C>
Result someFunc( A a, B b, C c ); // fudging "Result" for simplicity

which means that someFunc works "forall" types A, B, and C.
 
D

David Mertz

In this thread, a number of folks have claimed (falsely) that static
typing never gets in the way. An example from my own Gnosis Utilities
comes to my mind as a place where I benefit greatly from dynamic
(strong) typing.

The package gnosis.xml.objectify takes an XML source, and turns it into
a "native" Python object. The function make_instance() can accept a
wide range of different things that might sensibly relate to XML: a DOM
object, a filename, an XML string, any object with a .read() method.
Just one function deals happily with whatever you throw at it--without
any deep commitments about what type of thing it is (i.e. some novel
file-like object, or some new DOM implementation work without any
problem). The code is simple (the above function is actually a proxy to
a class):

class XML_Objectify:
def __init__(self, xml_src=None, parser=EXPAT):
self._parser = parser
if parser==DOM and (hasattr(xml_src,'documentElement'): ...
elif type(xml_src) in (StringType, UnicodeType):
if xml_src[0]=='<': ... # looks like XML
else: ... # looks like filename
elif hasattr(xml_src,'read'): ...
else:
raise ValueError, ...

I would challenge any enthusiast of Haskell, SML, Mozart, Clean, or the
like to come up with something similarly direct. I know, of course that
the task is *possible*, but I bet you'll need a WHOLE LOT of extra
scaffolding to make something work.

Actually, my strong hunch is that Lisp will also not make things quite
as easy either... but obviously, I know similar ad hoc capability
checking is possible there.

Yours, David...
 
P

prunesquallor

If I have read that correctly, it looks like it admits these Haskell
types:

lookup ::
(Eq k)=> k -> Map k v -> (v -> a) -> a -> a
lookupDefault ::
(Eq k)=> k -> Map k v -> Map k v -> (v -> a) -> a -> a
transformList ::
(Eq k)=> k -> Map k v -> Map k v -> ([a] -> b) -> (k -> a) ->

Actually, rewriting all of these functions using the Error monad would
make it all more elegant.


True.

Monads are relative newcomers to the Lisp world. You won't see them
very often.

The other problem is that monads don't compose.
 
J

Joachim Durchholz

Marshall said:
Does the existence of downcasts point out a place where
Java is lame? Absolutely. Does extra effort result from
this lameness? Certainly. Does this extra effort cause
bugs? Nope.

Bugs are not the only thing that I would term a "serious problem".
Actually, I'd call everything a "serious problem" that eats up
productivity. Of course, some problems are more serious than others; the
absence of a good way to catch exceptions in C++ constructors (and,
hence, that an exception thrown somewhere within a C++ constructor will
almost inevitably leak memory) is worse than Java's ubiquitous need for
downcasts.
But I also consider the unavailability of type inference in Java a
"serious problem". Actually I fear that my preferences have changed from
"static typing > run-time typing" to "type inference > run-time typing >
explicit static typing".
(Anyway, the situation is much better with Java generics,
available now in prerelease form; mainsteam in the next
major version.)

Good thing, that.
Let's just hope that having to wait a decade for Java generics was worth
it... Java is probably the inevitable next step in my career. Well,
there are worse languages, that's what I always tell myself (and it
almost suffices to calm me down...)

Regards,
Jo
 
J

james anderson

David said:
...

Actually, my strong hunch is that Lisp will also not make things quite
as easy either... but obviously, I know similar ad hoc capability
checking is possible there.

generic functions serve quite nicely to define type / value constrained
data-flow graphs. if one needs "hasattr" specificity, one needs an extra
parameter, but that's all.

....
 
J

Jesse Tov

Remi Vanicat said:
$ ocaml -rectypes
Objective Caml version 3.07+2

# let rec f x = f;;
val f : 'b -> 'a as 'a = <fun>

Eww. Something like this would be much nicer:

f : mu (\'b. 'a -> 'b)

Still, I didn't know Ocaml did that--cool.

Maybe the point here should be we know how to make a type system that
allows that, but typically it's not worth it because we don't need
functions with those types. (The ML solution--an implicit fix-point in
data constructors--makes it work pretty much wherever I'd care.)
Actually, I'd be interested to see a function that needs a recursive
type that couldn't be trivially rewritten to make it type in ML/Haskell.

Jesse



Jesse
 
B

Brian McNamara!

(e-mail address removed) once said:
The package gnosis.xml.objectify takes an XML source, and turns it into
a "native" Python object. The function make_instance() can accept a
wide range of different things that might sensibly relate to XML: a DOM
object, a filename, an XML string, any object with a .read() method.
Just one function deals happily with whatever you throw at it--without
any deep commitments about what type of thing it is (i.e. some novel
file-like object, or some new DOM implementation work without any
problem).

I have no chance at a full implementation, but here is a sketch in
Haskell. I know that a mere sketch is never as good as a working
implementation, so I hope someone else will take up the challenge.

Anyway:

type XMLRep = ... -- "internal" representation of XML objects

class ConvertibleToXML a where
convertToXML :: a -> Maybe XMLRep

instance ConvertibleToXML DomObject where
convertToXML :: DomObject -> Maybe XMLRep
convertToXML aDomObj = ...

instance ConvertibleToXML String where
convertToXML :: String -> Maybe XMLRep
convertToXML s = if (head s) = '<'
then XMLStringToXML s -- assume an XML string
else readXMLFromFileNamed s
-- yes, we'd need to be in the IO monad here

-- Later in the program
someFunc x y =
...
let xml = convertToXML x in ...
-- which will infer the constraint "ConvertibleToXML x"

As far as I can tell, the only "extra scaffolding" is the type class
ConvertibleToXML. Each time some new data type comes along which can be
converted to XML, we add a new instance declaration which shows how.
 
J

Joachim Durchholz

My point is that type systems can reject valid programs.

And the point of the guys with FPL experience is that, given a good type
system [*], there are few if any practial programs that would be wrongly
rejected.

[*] With "good type system", I mean a type system that has both
parametric polymorphism (i.e. templates) and type inference (i.e. you
don't have to write down any types, they are automatically determined by
the compiler from usage).
Each of these facilities is mildly useful in a static-type context, but
when they are combined in, say, Hindley-Milner (HM) fashion, the effect
is awesome. Some of my awe came from the realization that the "how do
you typecheck this?" challenges can actually be typechecked (though the
technical details of the solutions were often wrong - nothing is perfect
in this world.)

Regards,
Jo
 
J

Joachim Durchholz

The other problem is that monads don't compose.

Which is why Haskell has monad transformer libraries (and I'd assume
that other languages with a monad libraries has transformers as well).

Regards,
Jo
 
J

Joachim Durchholz

Brian said:
So C++ can only mimic "noisy_apply" so well. I expect that Haskell
can mimic it better in this respect.

Definitely.
Haskell uses currying, which mimicks multi-parameter functions using
single-parameter ones.

Regards,
Jo
 
J

Joachim Durchholz

Are they happy with something like this?

(defun black-hole (x)
#'black-hole)

(for non lispers, the funny #' is a namespace operator.
The black-hole function gobbles an argument and returns
the black-hole function.)

Now *that* is a real challenge, and Haskell indeed doesn't allow this
(it says "black_hole has an infinite type", which is not a surprise: the
literal transliteration of the above function would be
black_hole _ = black_hole
and the only solution to the above equation would require that
black_hole has a countably infinite number of _ parameters).

However, what purpose would the function serve? I'm pretty sure that
there's an equivalent idiom in Haskell, but I can't tell unless I know
what black_hole is good for.

Regards,
Jo
 
R

Remi Vanicat

(e-mail address removed) once said:

I have no chance at a full implementation, but here is a sketch in
Haskell. I know that a mere sketch is never as good as a working
implementation, so I hope someone else will take up the challenge.

Anyway:

type XMLRep = ... -- "internal" representation of XML objects

class ConvertibleToXML a where
convertToXML :: a -> Maybe XMLRep

instance ConvertibleToXML DomObject where
convertToXML :: DomObject -> Maybe XMLRep
convertToXML aDomObj = ...

instance ConvertibleToXML String where
convertToXML :: String -> Maybe XMLRep
convertToXML s = if (head s) = '<'
then XMLStringToXML s -- assume an XML string
else readXMLFromFileNamed s
-- yes, we'd need to be in the IO monad here

-- Later in the program
someFunc x y =
...
let xml = convertToXML x in ...
-- which will infer the constraint "ConvertibleToXML x"

As far as I can tell, the only "extra scaffolding" is the type class
ConvertibleToXML. Each time some new data type comes along which can be
converted to XML, we add a new instance declaration which shows how.

By the way, this system have one big advantage with regard to the
David Mertz example : Every one can had a new type to the
ConvertibleToXML class, when with David example, one have to change
the class XML_Objectify to have the same effect.
 
A

Alex Martelli

Brian said:
(e-mail address removed) once said:

....but since Python doesn't (yet) have protocol adaptation machinery,
you don't really have a good systematic way to extend make_instance
(while respecting the open/closed principle) for another completely
new type of "XML source" that doesn't naturally conform (though it
might be adapted) to any of the required protocols.

I.e., if a third-party library XX generates instances of XX.YY and
feeds them into a callable which happens to be that make_instance,
you have no way of providing adapters of XX.YY instances to the
requirements of make_instance which is not "invasive" in some way
(and, presumably, similar problems with adapting the results of
make_instance to whatever protocol third-party library XX requires).

One gets by, mind you, by being "somewhat" invasive and wrapping
the daylights out of something or other. But, it's definitely not
a point of strength or elegance -- it feels like those plumbing
repairs made with duct tape, and as such it's somewhat of a let-down
compared with many other areas of strength of Python.

Now, compare with Haskell's approach...:
class ConvertibleToXML a where
convertToXML :: a -> Maybe XMLRep

instance ConvertibleToXML DomObject where
convertToXML :: DomObject -> Maybe XMLRep
convertToXML aDomObj = ... ...
As far as I can tell, the only "extra scaffolding" is the type class
ConvertibleToXML. Each time some new data type comes along which can be
converted to XML, we add a new instance declaration which shows how.

Amen. In other terms, WITHOUT modifying some third-party library (nor
in fact this one), you can supply a *PROTOCOL ADAPTER* that is able
to convert whatever novel protocol the 3rd party library supports to
whatever protocol make_instance requires -- the "typeclass", which is
QUITE A BIT more powerful than just an "interface", represents the
protocol, and the instance represents the adapter from some type or
typeclass into the required protocol.

Now, Haskell does it all at compile-time, but that's just because
that's the way Haskell works; just the same thing could be done at
runtime by explicitly registering protocols and adapters. But of
course, having the tools available would be not very useful unless
protocol adaptation was REQUESTED and/or OFFERED by a reasonably
vast subset of standard and third-party libraries -- if you have to
do it all yourself by hand you might as well code more down-to-earth
wrappers as above mentioned.

The best current implementation of the protocol adaptation ideas in
PEP 246, to the best of my knowledge, is PyProtocols,
http://peak.telecommunity.com/PyProtocols.html .

No, it ain't Haskell, and in particular PyProtocols' interfaces
are NOT as powerful as Haskell's typeclasses (we'd need a LITTLE
extra semantic support from the Python compiler to get those). But,
as the above ConvertibleToXML example shows, you don't ALWAYS need
the full power of typeclasses -- pretty often, using them as little more
than interfaces is sufficient. Moreover, PyProtocols supports
adaptation (including transitive adaptation) even on "alien" interfaces
(which it sees as "opaque protocols", if you wish -- of course you'll
have to code the adapters WITH knowledge of the alien interfaces'
semantics, PyProtocols cannot do that for you, but it can support you
with registry and dispatching to suitable adapters), NOT just its own.


Alex
 
P

prunesquallor

(e-mail address removed) once said:

I disagree. See the discussion of "monad transformers" in part 3 of

http://www.nomaware.com/monads/html/index.html

I have even implemented a few of the monad transformers in C++.
Composing monads is terrific!

The problem is when you combine several monads in a stack. You have
to decide on an order, even if the monads are orthogonal. Then you
have to create lifting code to get at the monad you want. If you
re-order the stack, you have to re-order the lifting code, too.

I think monads are pretty cool, but they still have their problems.
 
N

Neelakantan Krishnaswami

Ha!

Although this doesn't get me any closer to my goal of simple,
useful, correct program that cannot be proven typesafe. I don't
believe the feature this function illustrates could be useful; you
have to have a handle on black-hole before you can invoke it, so
getting it back as a return value doesn't get me anything. But it's
a nice example.

The feature this program demonstrates is useful! It's a function of
recursive type; in Ocaml (invoked with the -rectypes option) it would
type as:

# let rec blackhole x = blackhole;;
val blackhole : 'b -> 'a as 'a

With a little bit more work, you can use recursive types to represent
(for example) infinite streams:

type 'a stream = unit -> 'a * 'b as 'b

let head stream = fst(stream())
let tail stream = snd(stream())
let cons h t = fun() -> h, t

let rec unfold head tail cons seed =
let rec f() =
let h = head seed in
let t = tail seed in
cons h (unfold head tail cons t)
in f

So now you can write the infinite stream of ones as

let id x = x

let ones = unfold id id cons 1

and the natural numbers as

let nats = unfold id ((+) 1) 0 cons

You can write a function that gives you every even or odd element of a
stream as:

let ($) f g x = g(f x)

let evens s = unfold head (tail $ tail) cons s

let odds s = unfold head (tail $ tail) cons (tail s)
 
M

Marshall Spight

Joachim Durchholz said:
the
absence of a good way to catch exceptions in C++ constructors (and,
hence, that an exception thrown somewhere within a C++ constructor will
almost inevitably leak memory)

Java nails this one.

is worse than Java's ubiquitous need for
downcasts.
But I also consider the unavailability of type inference in Java a
"serious problem".

Me, too, sigh.

Java is probably the inevitable next step in my career. Well,
there are worse languages, that's what I always tell myself (and it
almost suffices to calm me down...)

I got assigned to a Java project in 1996 and went kicking and
screaming. Within 3 months, I thought it was great. YMMV.


Marshall
 
J

james anderson

Brian McNamara! said:
(e-mail address removed) once said:

I have no chance at a full implementation, but here is a sketch in
Haskell. I know that a mere sketch is never as good as a working
implementation, so I hope someone else will take up the challenge.

...

As far as I can tell, the only "extra scaffolding" is the type class
ConvertibleToXML. Each time some new data type comes along which can be
converted to XML, we add a new instance declaration which shows how.

? would haskell suggest to handle the attribute-based specialization in a
manner similar to the original post, or does it offer some other mechanism?

....
 
R

Rainer Deyke

Joachim said:
Of course, some problems are more serious than others;
the absence of a good way to catch exceptions in C++ constructors
(and, hence, that an exception thrown somewhere within a C++
constructor will almost inevitably leak memory)

This is simply not the case. There is no problem with catching exceptions
in constructors in C++, and 99% of the time it isn't necessary anyway.
 
B

Brian McNamara!

(e-mail address removed) once said:
? would haskell suggest to handle the attribute-based specialization in a
manner similar to the original post, or does it offer some other mechanism?

There are two ways to go. If it's reasonable to know statically about
this attribute, then you could encode it in the type system (e.g. a type
like DOMWithDocumentElement). My hunch (based on my very limited
knowledge of the domain) is that this is a dynamic attribute. So
instead you'd 'fail' if the attribute didn't match at run-time. Note
that my Haskell code returned "Maybe XMLRep"s, so the corresponding code
would look something like

-- has type Maybe XMLRep
if (hasAttr domObj "documentElement")
then Just ... -- do the conversion of the domObj
else Nothing

The same strategy would be used to cope with other 'dynamic failures'
in the other branches, such as a malformed XML string, or a
non-existent file when converting from a filename.
 

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

No members online now.

Forum statistics

Threads
474,172
Messages
2,570,933
Members
47,472
Latest member
blackwatermelon

Latest Threads

Top