PyWart: The problem with "print"

R

Rick Johnson

You know, if you want a language with strict type declarations and
extreme run-time efficiency, there are some around.

I don't like declaring types everywhere, i hate it. I prefer duck
typed languages, HOWEVER, in order for duck typing to work
consistently you must have checks and balances that the programmer can
apply when he feels necessary. My "is_valid" built in will bridge the
gap. We won't be forced to declare types, but we should ALWAYS add
"type checks" to our "truth tests" unless we want to create subtle
bugs. "is_valid" IS the answer!
 
C

Chris Angelico

I don't like declaring types everywhere, i hate it. I prefer duck
typed languages, HOWEVER, in order for duck typing to work
consistently you must have checks and balances that the programmer can
apply when he feels necessary. My "is_valid" built in will bridge the
gap. We won't be forced to declare types, but we should ALWAYS add
"type checks" to our "truth tests" unless we want to create subtle
bugs. "is_valid" IS the answer!

Option 1:

void C_function(int x)

Option 2:

def Python_function(x):
assert isinstance(x,int)

Is there a fundamental difference? You're basically proposing Option 2
while detesting Option 1.

ChrisA
 
N

Ned Batchelder

I don't like declaring types everywhere, i hate it. I prefer duck
typed languages, HOWEVER, in order for duck typing to work
consistently you must have checks and balances that the programmer can
apply when he feels necessary. My "is_valid" built in will bridge the
gap. We won't be forced to declare types, but we should ALWAYS add
"type checks" to our "truth tests" unless we want to create subtle
bugs. "is_valid" IS the answer!

You are mis-using the term "duck typing." It doesn't mean just, "no type
declarations." It also means, "the type of the value is irrelevant, all
that matters is what it can do." Insisting that something be a list (or
a dict, ...) is unnecessary and counter to the duck-typing philosophy.
What's important is that you can iterate, or index it, or whatever it is
you want to do with the list. The abstract base classes in the
collections module were designed to help with determining these
capabilities:
http://docs.python.org/2/library/collections.html#collections-abstract-base-classes
Of course, often, it's best just to do what you want to do rather than
checking first.

Also, I have no idea why [] isn't a "valid" list. Surely different
applications will have different needs for what counts as valid once the
type-check is passed.

--Ned.
 
J

Jason Swails

This implicit conversion seems like a good idea at first,
and i was caught up in the hype myself for some time: "Hey,
i can save a few keystrokes, AWESOME!". However, i can tell
you with certainty that this implicit conversion is folly.
It is my firm belief that truth testing a value that is not
a Boolean should raise an exception. If you want to convert
a type to Boolean then pass it to the bool function:

lst = [1,2,3]
if bool(lst):
do_something

This would be "explicit enough"


i
f lst:
do_something

is equivalent to

if bool(lst):
do_something

why not just have your editor autobool so you can spend more time coding
and less time stamping around? That way the person that finds booled code
more readable can have what he wants and the people that find it less
readable can have what they want.

Win-win

BTW, you should do pointless comparisons like

if condition is True:
do_something

rather than

if condition == True
do_something
 
I

Ian Kelly

It is my firm belief that truth testing a value that is not
a Boolean should raise an exception. If you want to convert
a type to Boolean then pass it to the bool function:

lst = [1,2,3]
if bool(lst):
do_something

This would be "explicit enough"

That is *exactly* equivalent to the same test without the bool
function, and it gives the reader zero additional information about
what "lst" is, so it boggles me that you approve of this "if
bool(lst):" monstrosity while decrying the equivalent and slightly
more efficient "if lst:".

I think part of your complaint concerns the fact that the reader must
understand the rules by which a truth value is implicitly obtained
from lst in the statement "if lst:". But the reader must know the
*same* rules in order to understand the more explicit "if bool(lst):",
so there is no benefit to the latter in that regard either.
Yes i do care about the length or i would not have asked.
I'm asking Python to tell me if the iterable has members,
amd if it does, i want to execute a block of code, if it
does not, i want to do nothing. But i'm also informing the
reader of my source code that the symbol i am truth testing
is expected to be an iterable with a __len__ method.

Caring that the object has a length is not the same as caring about
what the object's length is. Steven's point stands, that your code
inefficiently asks the object for some property that you don't (yet)
care about.
"if lst" does not give me the same answer (or imply the same
meaning to a reader), it merely tells me that the implict
conversion has resulted in a True value, but what if the lst
symbol is pointing to a string? Then i will falsely believe
i have a list with members when i actually have a string
with length greater than zero.

Your "if len(lst) > 0" fails to differentiate lists from strings in
exactly the same way.
I agree. Summing the list members just to guarantee that the
iterable has members is foolish, however, python gives me no
other choice IF i want to be "explicit enough". In a
properly designed language, the base iterable object would
supply a "hasLength" or "hasMembers" method that would
return a much faster check of:

try:
iterable[0]
except IndexError:
return False
else:
return True

That check would guarantee the iterable contained at least
one member without counting them all.

You said earlier in your post that "bool(lst)" was "explicit enough",
and this is exactly what it does.
When i am writing code i prefer to be "explicit enough" so
that IF my assumptions about the exact type of an object are
incorrect, the code will fail quickly enough that i can
easily find and correct the problem.

In a duck-typing language you should not be making assumptions about
the "exact type" of an object in the first place. If I'm writing a
function that receives a list-like argument, then at the *most
specific* I will assume that the object passed in is a MutableSequence
(but since I prefer to keep my functions functional where practical,
more usually I will assume only that the object is an iterable or a
generic sequence). If the caller wants to pass in a deque or some
user-defined generic MutableSequence instead, then let them do so. I
will also clearly document that assumption; if the caller can't be
bothered to read the docs and passes in an object that breaks that
assumption, then that's their own damn problem when it doesn't work.
This is a programming language for consenting adults.
But we are really ignoring the elephant in the room. Implict
conversion to Boolean is just a drop in the bucket compared
to the constant "shell game" we are subjected to when
reading source code. We so naively believe that a symbol
named "lst" is a list object or a symbol "age" is a integer,
when we could be totally wrong! This is the source of many
subtle bugs!!!

I am more likely to believe that an object is a list based on the
documentation than on the mere fact that it is named "lst". The
variable *does* have documentation, doesn't it? If when debugging I
have reason to suspect that the documentation is incorrect or is being
ignored, then I'll add an assertion to test it.
There must be some method by which we can truth test an
iterable object and verify it has members, but do so in a
manner that is valid for all types AND exposes the "expected
type" in the method name. hmm...

This is nonsense. If it exposes the "expected type" in the name, then
it can only be valid for that expected type.
Adding a method like "is_valid" to every object can seem
logical, however, this can fail just as miserably as
Python's current implicit bool. And, more disastrously, an
"is_valid" method is not going to raise an error (where it
should) because it works for all types.

Actually it sounds completely illogical to me. What would be an
"invalid" object?
What we need is a method by which we can validate a symbol
and simultaneously do the vaidation in a manner that will
cast light on the type that is expected. In order for this
to work, you would need validators with unique "type names"

if var.is_validList():
elif var.is_validString():
elif var.is_vaildTuple():
elif var.is_validInteger():
elif var.is_validFloat():
elif var.is_validDict():
etc...

How are these any different from the more flexible isinstance? And
where exactly are you going to stop with these is_valid methods?

var.is_validObject()
var.is_validDate()
var.is_validTime()
var.is_validDatetime()
var.is_validSocket()
var.is_validDeque()
var.is_validMutableSequence()
var.is_validSequence()
var.is_validMutableSequencePlusAConcatenateMethod()
var.is_validUserDefinedObjectThatDoesNotExistInTheStandardLibraryAtAll()

And on and on and on. How do you plan to add every single possible
one of these methods to every single object? Remember that if you
miss one, e.g. if you leave is_validString() off of your socket
objects, then you'll get an AttributeError instead of a simple False
value when you try to test it.
By this manner, we can roll three common tests into one
method:

* boolean conversion
* member truthiness for iterables
* type checking

How exactly does this is_valid method perform the first two? Are you
suggesting that an empty sequence should not be considered "valid"?
 
R

Rick Johnson

How exactly does this is_valid method perform the first two? Are you
suggesting that an empty sequence should not be considered "valid"?

I'm suggesting that the rules for Python's current "implicit
conversion to Boolean" simply be moved into a "explicit function"
named "isvalid", that also does a type check. Here is some Python code
that might help you understand.

py> def isvalid(object_, type_):
.... if isinstance(object_, type_) and object_:
.... return True
.... return False
py> isvalid([], list)
False
py> isvalid([1], list)
True
py> isvalid(0, int)
False
py> isvalid(1, int)
True
py> isvalid({}, dict)
False
py> isvalid("", str)
False
py> isvalid(" ", str)
True

Now, let's go back to my earlier example of where i was expecting a
list but got a string instead. If i use Python's current implicit
conversion to Boolean my code will do something i don't want it to do.

py> lst = " "
py> if lst:
.... print("I'm a liar")
.... else:
.... print("I'm honest")
I'm a liar

But unlike this simple example (which failed quickly) in the real
world, it may not fail for a long time. And when it does fail, you
will be pulling your hair out tracking down the origin of the bug. If
however i use my "isvalid" function, my conditional will not lie to
me:

py> lst = " "
py> if isvalid(lst, list):
.... print("I'm a liar")
.... else:
.... print("I'm honest")
I'm honest

Now. You're not always going to need to "isvalid" function. Sometimes
you just need to test type, sometimes you just need convert to
Boolean, and sometimes you can just fly by the seat of your pants. The
point is, remove implicitly and inject explicitly.

Furthermore: If the current "implicit conversion to Boolean" can be
optimized by Python, then there is no reason why an explicit "isvalid"
function cannot -- any talk to the contrary is just BS.

If you still feel that this idea is garbage, then, keep on writing
your sloppy code. My proposal is the best method to handle the
problems that arise with duck typed languages in a manner that is not
restrictive or laborious -- it's actually quite elegant.

*school-bell-rings*
 
M

Mark Lawrence

On 05/06/2013 00:21, Rick Johnson wrote:
[snip]

Would you be kind enough not to smoke too much wacky baccy before
posting, thanks.

--
"Steve is going for the pink ball - and for those of you who are
watching in black and white, the pink is next to the green." Snooker
commentator 'Whispering' Ted Lowe.

Mark Lawrence
 
A

alex23

This is how you design a language for consistency and readability.

Great! Now you can shut up and get back to work on RickPython4000.
Come back and let us know all about it when it's done.
 
M

Michael Torrie

If you still feel that this idea is garbage, then, keep on writing
your sloppy code. My proposal is the best method to handle the
problems that arise with duck typed languages in a manner that is not
restrictive or laborious -- it's actually quite elegant.

Like most of your proposals, this does not have anything to do with the
python syntax itself. You just demonstrated code that does what you
want. So use it then. Adopt it in your own code, encourage others to
use it. No changes to Python are needed. If this technique proves its
value, then it will be adopted. If not, it will die. Start using it in
one of your major open source projects.
*school-bell-rings*

It's one thing to patronize people as you try to do to D'Aprano (and yes
I admit that most replies to your posts are often patronizing to you in
return), but you do realize this infantile attempt to try to make
yourself look smarter than others really reflects poorly on you? I for
one feel badly for you on this count, or at least embarrassed. So just
a suggestion... drop the school bell and teacher stuff (or at least
share your qualifications with us). It's really tiring, though to be
fair not quite as tiring as trying to help someone with an iron skull
get a CGI script working.
 
S

Steven D'Aprano

Option 1:

void C_function(int x)

Option 2:

def Python_function(x):
assert isinstance(x,int)

Is there a fundamental difference? You're basically proposing Option 2
while detesting Option 1.


How many years has Rick been coming here, proclaiming loudly how much he
loves Python's duck-typing? Ten years? And yet, he still has no clue what
it actually means.

If you're performing a type-check, IT ISN'T DUCK-TYPING.

Duck typing means you don't care whether you have an int, so long as
whatever object you get is usable where an int is usable.

Now, there are many places in my own code where I decide that I wish to
prohibit duck-typing. "Here I insist on an actual int, not just any old
number." There are, sometimes, good reasons for this. But every time I do
this, I am *not* duck-typing, I am doing a runtime type check which is
completely opposite in intent to duck-typing. (There's no short name for
this -- it's not quite static typing, because it happens at runtime and
isn't enforced by the language.)

It's almost like Rick declares that he's a great supporter of the free
market, and that everyone should be free to buy and trade in whatever
property they wish, while insisting that nothing can be bought or sold
without permission from the government first.
 
R

Russ P.

Yes, but the problem is not "my approach", rather the lack

of proper language design (my apologizes to the "anointed

one". ;-)

If you don't like implicit conversion to Boolean, then maybe you should be using another language -- and I mean that in a constructive sense. I'm not particularly fond of it either, but I switched from Python to another language a while back. The issue is not a lack of "proper language design" but rather a language design philosophy that values conciseness and simplicity over explicitness and rigor.

Implicit conversion to Boolean is only one of many language features that are questionable for critical production software. Another is the conventionof interpreting negative indices as counting backward from the end of a list or sequence. Yeah, I thought that was elegant... until it bit me. Is it a bad idea? Not necessarily. It certainly enhances programmer productivity,and it can be done correctly "almost" all the time. But that one time in ahundred or a thousand when you accidentally use a negative index can be a bitch.

But then, what would you expect of a language that allows you to write

x = 1
x = "Hello"

It's all loosey goosey -- which is fine for many applications but certainlynot for critical ones.
 
C

Chris Angelico

If you don't like implicit conversion to Boolean, then maybe you should be using another language -- and I mean that in a constructive sense. I'm not particularly fond of it either, but I switched from Python to another language a while back. The issue is not a lack of "proper language design" butrather a language design philosophy that values conciseness and simplicityover explicitness and rigor.

(Out of curiosity, which language? Feel free to not answer, or to
answer off-list, as that's probably not constructive to the thread.)

I cannot name a single modern programming language that does NOT have
some kind of implicit boolification. The only such language I know of
is REXX, which has a single data type for everything, but insists on
the exact strings "1" and "0" for True and False, anything else is an
error. Every other language has some definition of "these things are
true, these are false"; for instance:

* C-family languages treat all nonzero integers as true
* Shells treat 0 as "success" and nonzero as "error", and therefore as
"true" and "false" respectively (yes, this IS an inversion)
* Pike allows any variable to contain the integer 0, regardless of its
declared type, and thus is a sort of "null" value which is false; all
strings, arrays, mappings, etc are true
* Python treats an empty "thing" as false and a nonempty one as true

SQL is like REXX in that it's fairly strict; a condition must be a
boolean (example from PostgreSQL):

rosuav=> select 1+2 where 1;
ERROR: argument of WHERE must be type boolean, not type integer
LINE 1: select 1+2 where 1;
^

But an explicit conversion is permitted:

rosuav=> select 1+2 where cast (5 as boolean);
?column?
----------
3
(1 row)

It's all loosey goosey -- which is fine for many applications but certainly not for critical ones.

The looseness doesn't preclude critical applications. It's all a
question of what you're testing. Does your code care that this be a
list, and not something else? Then test! You have that option. What
happens if it isn't a list, and something is done that bombs with an
exception? Maybe that's not a problem.

Critical applications can often be built in layers. For instance, a
network server might listen for multiple socket connections, and for
each connection, process multiple requests. You would want to catch
exceptions at the two boundaries there; if a request handler crashes,
the connection should not be dropped, and if a connection handler
crashes, the server should keep running. With some basic defenses like
that, your code need no longer concern itself with trivialities - if
something goes wrong, there'll be an exception in the log. (BTW, this
is one of the places where a bare or very wide except clause is
appropriate. Log and move on.)

ChrisA
 
R

Russ P.

(Out of curiosity, which language? Feel free to not answer, or to

answer off-list, as that's probably not constructive to the thread.)

No problem. I'm using Scala. It has a sophisticated type system. The language is not perfect, but it seems to suit my needs fairly well.
I cannot name a single modern programming language that does NOT have

some kind of implicit boolification. The only such language I know of

is REXX, which has a single data type for everything, but insists on

the exact strings "1" and "0" for True and False, anything else is an

error. Every other language has some definition of "these things are

true, these are false"; for instance:


Scala (and Java) don't do that. Nor does Ada. That's because Ada is designed for no-nonsense critical systems. It is the standard higher-order language for flight control systems, for example.

The looseness doesn't preclude critical applications. It's all a

question of what you're testing. Does your code care that this be a

list, and not something else? Then test! You have that option. What

happens if it isn't a list, and something is done that bombs with an

exception? Maybe that's not a problem.



Critical applications can often be built in layers. For instance, a

network server might listen for multiple socket connections, and for

each connection, process multiple requests. You would want to catch

exceptions at the two boundaries there; if a request handler crashes,

the connection should not be dropped, and if a connection handler

crashes, the server should keep running. With some basic defenses like

that, your code need no longer concern itself with trivialities - if

something goes wrong, there'll be an exception in the log. (BTW, this

is one of the places where a bare or very wide except clause is

appropriate. Log and move on.)


Well, I don't really want to open the Pandora's box of static vs. dynamic typing. Yes, with enough testing, I'm sure you can get something good out ofa dynamically typed language for small to medium-sized applications, but Ihave my doubts about larger applications. However, I don't claim to be an expert. Someone somewhere has probably developed a solid large application in Python. But I'll bet a dollar to a dime that it took more work than it would have taken in a good statically typed language. Yes, extensive testingcan go a long way, but extensive testing combined with good static typing can go even further for the same level of effort.
 
M

Mark Lawrence

But then, what would you expect of a language that allows you to write

x = 1
x = "Hello"

It's all loosey goosey -- which is fine for many applications but certainly not for critical ones.

I want to launch this rocket with an expensive satellite on top. I know
it's safe as the code is written in ADA. Whoops :(

--
"Steve is going for the pink ball - and for those of you who are
watching in black and white, the pink is next to the green." Snooker
commentator 'Whispering' Ted Lowe.

Mark Lawrence
 
T

Terry Jan Reedy

But then, what would you expect of a language that allows you to
write

x = 1
x = "Hello"

It's all loosey goosey -- which is fine for many applications but
certainly not for critical ones.

I believe Shedskin, a Python *subset* compiler*, will reject that,
because it compiles ints to C ints. Some code checkers might too.
 
R

Russ P.

I want to launch this rocket with an expensive satellite on top. I know

it's safe as the code is written in ADA. Whoops :(


So Python would have been a better choice? Yeah, right. If you know anything about that rocket mishap, you should know that Ada was not the source of the problem. Ada won't keep airplane wings from breaking either, by the way. It's not magic.
 
R

Russ P.

Frankly, I don't think the language much matters. It's all down to the

skill of the programmers and testers. Ada wasn't the source of the

problem unless Ada has a bug in it... which is going to be true of

pretty much any language. Maybe Python would be a better choice, maybe

not; but let me tell you this, if the choice of language means the

difference between testable in three months and testable code in three

years, I'm going for the former.



ChrisA

I'm not an Ada guy, but Ada advocates claim that it reduces development time by half in the long run compared to C and C++ due to reduced debugging time and simpler maintenance. Then again, I think Java people make a similar claim. As for Python, my experience with it is that, as your application grows, you start getting confused about what the argument types are or are supposed to be. That requires the developer to keep much more of the design in his head, and that undesirable. Of course, you can always put the argument types in comments, but that won't be verified by the compiler.
 
M

Michael Torrie

But then, what would you expect of a language that allows you to
write

x = 1
x = "Hello"

It's all loosey goosey -- which is fine for many applications but
certainly not for critical ones.

This comment shows me that you don't understand the difference between
names, objects, and variables. May sound like a minor quibble, but
there're actually major differences between binding names to objects
(which is what python does) and variables (which is what languages like
C have). It's very clear Rick does not have an understanding of this
either.
 

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,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top