Python syntax in Lisp and Scheme

D

David Eppstein

Isn't it true though that the lambda can only contain a single expression
and no statements? That seems to limit closures somewhat.

It limits lambdas. It doesn't limit named functions. Unlike lisp, a
Python function definition can be nested within a function call, and the
inner function can access variables in the outer function's closure.[/QUOTE]

To clarify, by "unlike lisp" I meant only that defun doesn't nest (at
least in the lisps I've programmed) -- of course you could use flet, or
bind a variable to a lambda, or whatever.
 
D

David Mertz

|> Isn't it true though that the lambda can only contain a single
|> expression and no statements? That seems to limit closures somewhat.

|It limits lambdas. It doesn't limit named functions. Unlike lisp, a
|Python function definition can be nested within a function call, and the
|inner function can access variables in the outer function's closure.

I don't really know Lisp, so I could be wrong. But my understanding is
that CL has a 'let' special form that works fine within a function
definition. In particular, you should be able to define inner functions
by binding a name to a lambda, using 'let'.

So there's nothing really special about the fact that Python (or
Haskell, ML, etc) can nest function definition. Of course, Haskell's
'let' and 'where' are quite wonderful... even better, syntaxwise, than
Python's nested 'def's.

Yours, David...

P.S. On the prior poster's misunderstanding: Lambdas in Python are
actually completely general. There is nothing you cannot express using
a single expression, even side effects--it just gets ugly doing it.
Basically, just like in CL, a list or tuple is a single expression, and
it evaluates its elements in predictable left-to-right order... you can
work out the rest of the ugly details from that fact. Or you can look
at my articles on "FP in Python"--however, my intent in those is NOT to
enable obfuscated Python, but to point to actual useful techniques.
It's a fine line though.
 
R

Raffael Cavallaro

Sometimes a function is so simple that its body is more clear than any
name. A name is an extra level of indirection. You must follow it to be
100% sure what the function means, or to understand what does it really
mean that it does what it's named after.

Your argument is based on the assumption that whenever people express
_what_ a function does, they do so badly, with an inappropriate name.

We should choose our mode of expression based on how things work when
used correctly, not based on what might happen when used foolishly. We
don't write novels based on how they might be misread by the
semi-litterate.

Anonymous functions add no clarity except to our understaning of
_implementation_, i.e., _how_ not _what_. Higher level abstractions
should express _what_. Implementation details should remain separate,
both for clarity of exposition, and for maintanence and change of
implementation.
The code also gets longer

No, it gets shorter, because you don't repeat your use of the same
abstraction over and over. You define it once, then reference it by name
everywhere you use it.
- not
only more verbose but the structure of the code gets more complex with
more interdependent parts.

No, there are _fewer_ interdependent parts, because the parts that
correspond to the anonymous function bodies are _completely gone_ from
the main exposition of what is happening. These formerly anonymous
function bodies are now elswhere, where they will only be consulted when
it is necessary to modify them.

You seem to take the view that client code can't trust the interfaces it
uses, that you have to see how things are implemented to make sure they
do what they represent to do.

This is a very counterproductive attitude. Code should provide high
level abstractions, and clients of this code should be able to tell what
it does just by looking at the interface, and maybe a line of
documentation. It shouldn't be necessary to understand the internals of
code one uses just to use it. And it certainly isn't necessary to
include the internals of code one uses where one is using it (i.e.,
anonymous functions). That's what named functions and macros are for.
Inlining should be done by compilers, not programmers.

Anonymous functions are a form of unnecessary information overload. If I
don't need to see how something works right here, in this particular
context, then don't put its implementation here. Just refer to it by
name.
When you have lots of short functions, it's
harder to find them. There are many names to invent for the writer and
many names to rememner for a reader.

Which is why names should be descriptive. Then, there's little to
remember. I don't need to remember what add-offset does, nor look up
it's definition, to understand its use in some client code. Anonymous
functions are sometimes used as a crutch by those who can't be bothered
to take the time to attend to the social and communicative aspects of
programming. Ditto for overly long function bodies. How to express
intent to human readers is just as important as getting the compiler to
do what you want. These same programmers seem enamored of
crack-smokingly short and cryptic identifier and function names, as if
typing 10 characters instead of 3 is the real bottleneck in modern
software development. (Don't these people know how to touch type?)


Function headers are administrative
stuff, it's harder to find real code among abstractions being introduced
and used.


Seemingly to you, the only "real code" is low level implementation. In
any non-trivial software, however, the "real code" is the interplay of
high level abstractions. At each level, we craft a _what_ from some less
abstract _how_, and the _what_ we have just defined, is, in turn used as
part of the _how_ for an even higher level of abstraction or
functionality.

Why do you insist on naming *functions*? You could equally well say that
every list should be named, so you would see its purpose rather than its
contents.

I think this concept is called variable bindings ;^)

Perhaps every number should be named, so you can see what it
represents rather than its value.

Actually, they are _already_ named. The numerals we use _are_ names, not
numbers themselves. I'm surprised you aren't advocating the use of
Church Numerals for all numerical calculation.
You could say that each statement of
a compound statement should be moved to a separate function, so you can
see what it does by its name, not how it does it by its contents. It's
all equally absurd.

In the Smalltalk community the rule of thumb is that if a method body
gets to be more than a few lines, you've failed to break it down into
smaller abstractions (i.e., methods).
A program should balance named and unnamed objects. Both are useful,
there is a continuum between cases where one or the other is more clear
and it's subjective in border cases, but there is place for unnamed
functions - they are not that special. Most high level languages have
anonymous functions for a reason.

Yes, but they should live inside the bodies of named functions. Not lie
exposed in the middle of higher level abstractions. Please also see my
reply to Joe Marshall/Prunesquallor a few posts up in this thread.
 
P

Pascal Bourguignon

[...]
I thought that whitespace was significant to Python.

My computer does not display whitespace. I understand that most
computers do not. There are few fonts that have glyphs at the space
character.

Since having the correct amount of whitespace is *vital* to the
correct operation of a Python program, it seems that the task of
maintaining it is made that much more difficult because it is only
conspicuous by its absence.

:)

That remembers me that when the languages had significant spaces, the
programming was done with forms, sheets of physical paper preprinted
with empty spaces:


|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

and you could then fill the blanks and have the significant spaces and
columns nicely visualized.

I assume python editors now show this kind of form as a background
bitmap, don't they.
 
J

james anderson

David said:
It limits lambdas. It doesn't limit named functions. Unlike lisp, a
Python function definition can be nested within a function call, and the
inner function can access variables in the outer function's closure.

To clarify, by "unlike lisp" I meant only that defun doesn't nest (at
least in the lisps I've programmed) -- of course you could use flet, or
bind a variable to a lambda, or whatever.[/QUOTE]

? which lisps have you been using?

i do not observe the spec for defun
(http://www.lispworks.com/reference/HyperSpec/Body/m_defun.htm)
to place any restrictions on its context. anscillary discussion introduces the
issue of compile-time side-effects, eg whether the definition of the function
is known to exist for the purpose of error-checking, but there are no
constraints apparent to this reader on where the form may appear.

to wit


? (defun function1 (a)
(defun function2 (b) (+ a b)))
FUNCTION1
? (function1 2)
FUNCTION2
? (function2 3)
5
? (function1 5)
FUNCTION2
? (function2 3)
8
? (defun function* (a list)
(apply #'+ (mapcar (defun function2 (b) (+ a b)) list)))
FUNCTION*
? (function* 1 '(1 2 3))
9
?

that is, both the invocation for side-effect and for the returned value appear
to have the intended effect. on the other hand, it's not clear why one would
want to do this, so perhaps you mean something else?

....
 
P

prunesquallor

Pascal Bourguignon said:
[...]
I thought that whitespace was significant to Python.

My computer does not display whitespace. I understand that most
computers do not. There are few fonts that have glyphs at the space
character.

Since having the correct amount of whitespace is *vital* to the
correct operation of a Python program, it seems that the task of
maintaining it is made that much more difficult because it is only
conspicuous by its absence.

:)

That remembers me that when the languages had significant spaces, the
programming was done with forms, sheets of physical paper preprinted
with empty spaces:


|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
1 7 12

and you could then fill the blanks and have the significant spaces and
columns nicely visualized.

I assume python editors now show this kind of form as a background
bitmap, don't they.

The Lisp Machine had a visual Hollerith editor.
 
B

Brian McNamara!

Raffael Cavallaro said:
Your argument is based on the assumption that whenever people express
_what_ a function does, they do so badly, with an inappropriate name. ....
Anonymous functions add no clarity except to our understaning of
_implementation_, i.e., _how_ not _what_. Higher level abstractions
should express _what_. Implementation details should remain separate,
both for clarity of exposition, and for maintanence and change of
implementation.

I am in total agreement with Marcin. What you (Raffael) say here
sounds simply like dogma, rather than practical advice for constructing
software.

As a short practical example of what I'm saying, consider code like

-- I am translating this from another language; apologies if I have
-- screwed up any of the Haskell details
nat :: Parser Int
nat = do {c <- digit; return charToInt c}
`chainl1` (return \x y -> 10*x+y)

The code here parses natural numbers; it does do by parsing individual
digit characters, translating the digit characters into the
corresponding integers, and then combining the digit-integers into the
overall number.

If I understand you correctly, you would insist I write something like

nat :: Parser Int
nat = do {c <- digit; return charToInt c}
`chainl1` combineDigits
where combineDigits = return \x y -> 10*x+y

In my opinion, the code here is worse than the original.
"combineDigits" (or whatever we might choose to name it) is a one-time
function. It is not going to be reused (it's local to "nat"; moving it
to a more-top-level scope would just cause more problems finding a good
name for it).

Introducing a name for this function does nothing more than clutter the
code. The name provides no extra understanding. Of course the
function is "combining digits"! If the programmer is using chainl1, he
already knows that

chainl1 :: Parser a -> Parser (a->a->a) -> Parser a
-- parses a series of items separated by left-associative operators
-- which are used to combine those items

and he can infer "combining digits" just by seeing the call to chainl1
and inspecting its left argument.

Anonymous functions are a form of unnecessary information overload. If I
don't need to see how something works right here, in this particular
context, then don't put its implementation here. Just refer to it by
name.

There are more functional abstractions than there are reasonable names.

Forcing programmers to name every abstraction they'll ever encounter is
tantamount to forcing them to memorizing a huge vocabulary of names for
these abstractions. Perhaps you can remember the precise meanings of
ten-thousand function names off the top of your head. I, on the other
hand, can probably recall only a hundred or so. Thus, I must write,
e.g.

zipWith (\x y -> (x+y,x-y)) list1 list2

rather than

zipWith makePairOfSumAndDifference list1 list2

By only naming the most reusable abstractions (and, ideally, selecting
a set which are mostly orthogonal to one another), we provide a "core
vocabulary" which captures the essential basis of the domain. Lambda
then takes us the rest of the way. In my opinion, a core vocabulary of
named functions plus lambda is better than a separate name for every
abstraction. In natural language, such a scheme would be considered
double-plus-un-good, but in programming, I think it lends itself to the
simplest and most precise specifications.


I agree with your one of your overall theses, which is that we should
focus on the "what" rather than the "how". Where our opinions diverge,
however, is that I think sometimes the best way to communicate an
abstraction is to show the "how" inline, rather than creating a new
name in an attempt to capture the "what".
 
K

Ken Shan

Raffael Cavallaro said:
I think this concept is called variable bindings ;^)

Yes, but what Marcin is talking about outlawing anonymous variables, so
for example you can't write

amount_due = sum(item_prices) * (1 + tax_rate)

in his hypothetical programming language down the slippery slope (or is
it up the slippery slope?), but must write

subtotal = sum(item_prices)
tax_multiplier = 1 + tax_rate
amount_due = subtotal * tax_multiplier

just as programmers did before FORTRAN.

I would go one step further and point out that even in the code above,
there are still unnamed things whose functionality is possibly unclear.
These things are source code locations to which computations return,
aka continuations. For instance, what is the purpose of executing this
code above? The purpose is not to go through a bunch of arithmetic
operations for fun, but to compute the amount due.

def compute_amount_due:
subtotal = sum(item_prices)
tax_multiplier = 1 + tax_rate
amount_due = subtotal * tax_multiplier

There, clearer already. But what is the purpose of having the subtotal?
It is to add the tax to get the amount due.

def compute_amount_due:
sum(item_prices, add_tax_for_amount_due)

def add_tax_for_amount_due(subtotal):
tax_multiplier = 1 + tax_rate
amount_due = subtotal * tax_multiplier

What is the purpose of computing the tax_multiplier? To multiply with
the subtotal in order to get the amount due. (I assume a Python-ish
syntax with partial function application (currying) here.)

def add_tax_for_amount_due(subtotal):
add(1, tax_rate, subtotal_and_tax_multiplier_to_amount_due(subtotal))

def subtotal_and_tax_multiplier_to_amount_due(subtotal, tax_multiplier):
amount_due = subtotal * tax_multiplier

But what's the purpose of having the amount due at all? Maybe it is to
print a bill:

def subtotal_and_tax_multiplier_to_amount_due(subtotal, tax_multiplier):
multiply(subtotal, tax_multiplier, print_bill_with_amount_due)

def print_bill_with_amount_due(amount_due):
...
Actually, they are _already_ named. The numerals we use _are_ names, not
numbers themselves. I'm surprised you aren't advocating the use of
Church Numerals for all numerical calculation.

Functions are _already_ named as well. The lambda expressions we use
_are_ names, not functions themselves.
In the Smalltalk community the rule of thumb is that if a method body
gets to be more than a few lines, you've failed to break it down into
smaller abstractions (i.e., methods).

So perhaps we should have the programming language outlaw anonymous
functions that are more than 4 lines long...
 
A

Adrian Hey

james anderson wrote:
i'm just wondering if you're serious about all of this.

I think Marcin is serious, and any Haskeller would agree I suspect.

A good example from Haskell would be the use of lambda abstractions
as the second argument of the monadic >>= operator. Supplying names
for all those abstractions would be really awkward and yield highly
unreadable code IMO.

For those that don't like all those lambdas, Haskell's imperative style
"do" expressions can be used instead, but only for monads. There are
still occassions where explicit lambda's are an easier and clearer
alternative than defining named functions.

Regards
 
R

Rainer Deyke

One of the most compelling arguments for anonymous functions comes from
Smalltalk, where the 'if' statement is implemented as a higher order method.
The equivalent in Python would look something like this:

class true_class:
def if_true(self, fn): return fn()
def if_false(self, fn): pass

class false_class:
def if_true(self, fn): pass
def if_false(self, fn): return fn()

true = true_class()
false = false_class()

(x == 0).if_true(anonymous_code_block)

Sure, Python already has an 'if' statement. That does not mean that there
are no other control structures worth implementing as HOFs.

However, I think I can make an even stronger case for anonymous functions
from my own code, which is littered with lambda expressions such as 'lambda:
1', 'lambda: 0', and 'lambda: None'. Giving those mini functions individual
names would be an atrocity, and even a factory function that returns these
mini functions would only hurt readability.
 
A

Albert Lai

I agree with your one of your overall theses, which is that we should
focus on the "what" rather than the "how". Where our opinions diverge,
however, is that I think sometimes the best way to communicate an
abstraction is to show the "how" inline, rather than creating a new
name in an attempt to capture the "what".

There are also times when "what" and "how" are literally the same,
which is when the "what" is a very trivial task.

It is a very slippery slope to insist coding styles in the name of abstraction:
- every value must have a name
- every function or procedure must be christened
- everything must be put in a class or object (I call this OOP - object
obfuscation pomposity)
- every line of code must be commented

There is an adverse social effect (since social effect was mentioned)
whenever an abstraction mechanism like the above ones is doctrinized.
Programmers will begin devaluing the mechanism. If you insist on
comments, they start writing silly comments; if you insist on naming
things, they start giving silly names; and if you insist on classes,
well, they start putting everything into one single class and making
all members static and public. They start seeing the abstraction
mechanism as a bother and stop seeing its potential benefits.

It is when you allow some freedom of not using a mechanism that the
the mechanism becomes valuable. (The political parallel: some people
like to stay in their countries precisely because their laws permit
them to leave. Once you remove that freedom, people begin to smuggle
themselves out.)

Now no one is suggesting to make all functions anonymous, all code
unstructured, and all aspects uncommented. Nothing of that sort! In
practice we give names to all non-trivial functions. Someone asserted
that we should judge a mode of expression for what it does when put in
good use, not what it could do when abused. That person should not
argue against anonymous functions by noting all the gory details of
implementation it might expose in the wrong hands.

And now I would like to sidetrack a bit and give an unrelated example
of Brian's point that sometimes "what" without "how" is completely
unclear.

I have an abstract data type to present you. This exercise is to test
how well you understand the "what" presentation, so the names of the
operations will not give you a clue. They are meaningful names though
- meaningful to me, a Cantonese speaker. (Just to show you how
meaningless the whole notion of "meaningful names" is.) But I will
present you "what" it does without mentioning "how".

It is a functional (immutable) abstract data type, so most operations
take an existing instance as a parameter and return a new instance.
The name of the abstract data type is LiDui.

Hung : LiDui
MoYun : LiDui -> Bool
PaiDui: (LiDui,Object) -> LiDui
DuiTau: LiDui -> Object
LeiDui: LiDui -> LiDui

MoYun(Hung) is true.
For any LiDui d:
If MoYun(d) is true, we have LeiDui(PaiDui(d,o))=d and DuiTau(PaiDui(d,o))=o.
If MoYun(d) is false, we have LeiDui(PaiDui(d,o))=PaiDui(LeiDui(d),o).

That's it.

I have never seen any first-year CS class present this abstract data
type this way. All presentations either show a simple pseudocode
implementation, or appeal to a mental model derived from a real life
experience, which is still a kind of simple pseudocode implementation,
except it is in real life terms instead of CS terms.
 
T

Terry Reedy

That remembers me that when the languages had significant spaces, the
programming was done with forms, sheets of physical paper preprinted
with empty spaces:
[further idiocy snipped]

I do believe that several Lispers have suggested that people should
give Lisp a fair trial before rejecting it on account of parentheses
or macros. The same goes, of course, for Python and significant
indents/dedents. For most people who try Python, freedom from
visually redundant fences is a feature. Those who find it a bother
after trying are welcome to chose another language.

What makes the comments above doubly absurd is that Lisp has as much
or more need for 'significant spaces' as Python. Compare (1,2,3)
versus (1 2 3). Having the "correct amount of whitespace is *vital*
to the correct operation of a" Lisp program as much as for any other.
Do Lispers therefore use forms? I suspect not ;-)

Terry J. Reedy
 
A

Albert Lai

Albert Lai said:
Hung : LiDui
MoYun : LiDui -> Bool
PaiDui: (LiDui,Object) -> LiDui
DuiTau: LiDui -> Object
LeiDui: LiDui -> LiDui

MoYun(Hung) is true.
For any LiDui d:
If MoYun(d) is true, we have LeiDui(PaiDui(d,o))=d and DuiTau(PaiDui(d,o))=o.
If MoYun(d) is false, we have LeiDui(PaiDui(d,o))=PaiDui(LeiDui(d),o).

That's it.

I knew I forgot something.

If MoYun(d) is false, we have LeiDui(PaiDui(d,o))=PaiDui(LeiDui(d),o)
and DuiTau(PaiDui(d,o))=DuiTau(d).

MoYun(PaiDui(d,o)) is false.
 
L

Lulu of the Lotus-Eaters

|However, I think I can make an even stronger case for anonymous functions
|from my own code, which is littered with lambda expressions such as 'lambda:
|1', 'lambda: 0', and 'lambda: None'. Giving those mini functions individual
|names would be an atrocity

Let's say I created a module lambdas.py (and generalized your functions):

OneFunc = lambda *a,**kw: 1
ZeroFunc = lambda *a,**kw: 0
NoneFunc = lambda *a,**kw: None
...etc...

In my application, I might have:

from lambdas import *
...
widget(param1, param2, callback=isCond and myFunc or NoneFunc)

Where exactly does the atrocity arise?! If anything, it's slightly more
readable than Deyke's little lambdas (and a little more concise too).

Yours, Lulu...
 
A

Alex Martelli

Rainer Deyke wrote:
...
(x == 0).if_true(anonymous_code_block)

Sure, Python already has an 'if' statement. That does not mean that there
are no other control structures worth implementing as HOFs.

True: if we had good code block literals, they'd help. However, the
issue of having to name the code block (with a def) is perhaps not as
crucial as it appears -- as important (and not implicit in the code
block being a literal) is in what scope the code block gets and sets
variables. If an anonymous code block, like a lambda, is totally
equivalent to a named function (sets variable locally, gets them in
lexical scope), then being able to write the above rather than

def guarded(): anonymous_code_block
(x == 0).if_true(guarded)

is a somewhat minor. The problem is, what could I possibly write in the
code block in order to obtain the same effect as with:
if x == 0:
x = 23
y += 1
and that's an issue of scope, not one of naming or not naming the block.
(Mertz's proposal for a 'block' keyword addresses this, but by wanting
the block's scope to be that of the caller rather than that of the
definer, it of course solves none of the problems Smalltalks block solve).

However, I think I can make an even stronger case for anonymous functions
from my own code, which is littered with lambda expressions such as
'lambda:
1', 'lambda: 0', and 'lambda: None'. Giving those mini functions
individual names would be an atrocity, and even a factory function that
returns these mini functions would only hurt readability.

I disagree: I find nullary(1), nullary(0) and the like very readable.
"lambda: x" is IMHO much more of a problem -- again because of scope.


Alex
 
D

David Eppstein

It is a functional (immutable) abstract data type, so most operations
take an existing instance as a parameter and return a new instance.
The name of the abstract data type is LiDui.

Hung : LiDui
MoYun : LiDui -> Bool
PaiDui: (LiDui,Object) -> LiDui
DuiTau: LiDui -> Object
LeiDui: LiDui -> LiDui

MoYun(Hung) is true.
For any LiDui d:
If MoYun(d) is true, we have LeiDui(PaiDui(d,o))=d and
DuiTau(PaiDui(d,o))=o.
If MoYun(d) is false, we have LeiDui(PaiDui(d,o))=PaiDui(LeiDui(d),o).

That's it.

You sure you don't need a little more? This seems to fit both stacks
(MoYun always = True) and queues (MoYun true only for Hung).

That's one trouble with axiomatic definitions -- it's hard to be sure
you've expressed all of the behavior you intend to specify.
Another is that they can be difficult to understand.
 
J

Jacek Generowicz

Paul Rubin said:
Jacek Generowicz said:
However, please _do_ tell me if you hear of anyone implementing Python
in Lisp[*].

Having Python as a front-end to Lisp[*] (as it is now a front-end to
C, C++ and Java) would be very interesting indeed.

That would not be simple to do, because of weird Python semantics.

Could you elaborate on how Python semantics are weird (any more weird
than, say, Common Lisp)? and on why making a Common Lisp
implementation of Python would be any less simple than making a C
or Java implementation of Python?
But doing a Python dialect (a little bit different from CPython) would
be worthwhile.

In what sense would it differ? (I would not insist that it conform to
the Python/C API :)

Would it differ from CPython any more than Jython differs from
CPython?
 
J

james anderson

Adrian said:
I think Marcin is serious, and any Haskeller would agree I suspect.

i have no argument with the utility of lambda abstractions.
i am trying only to understand the implications of an argument which, at least
as stated, rather unequivocally deprecates bindings.
the position which was proposed in the forgoing post was rather extreme.
i enquired to see of m.kowalczyk might offer some practical examples from
which it might be possible to learn something.
A good example from Haskell would be the use of lambda abstractions
as the second argument of the monadic >>= operator. Supplying names
for all those abstractions would be really awkward and yield highly
unreadable code IMO.

yes, i am good friends with lambda abstractions. we get along well and respect
each other. i am, however, neither married to one, nor an initiate in a sect
which deems it a transgression to utter the name of another deity.

....
 
P

prunesquallor

Terry Reedy said:
That remembers me that when the languages had significant spaces, the
programming was done with forms, sheets of physical paper preprinted
with empty spaces:
[further idiocy snipped]

I do believe that several Lispers have suggested that people should
give Lisp a fair trial before rejecting it on account of parentheses
or macros. The same goes, of course, for Python and significant
indents/dedents. For most people who try Python, freedom from
visually redundant fences is a feature. Those who find it a bother
after trying are welcome to chose another language.

What makes the comments above doubly absurd is that Lisp has as much
or more need for 'significant spaces' as Python.

That is simply incorrect. It is a small matter to set the readtable
such that things like (+x3) are interpreted as (+ x 3). This would
make it much more difficult to use complicated variable names, but
that's the tradeoff.
Compare (1,2,3) versus (1 2 3). Having the "correct amount of
whitespace is *vital* to the correct operation of a" Lisp program as
much as for any other. Do Lispers therefore use forms? I suspect
not ;-)

There's a bit of a difference between the whitespace separating tokens
and the whitespace that delineates blocks. The former is a member
of the half-open interval (0, +inf), the latter is a singleton.
 
D

Donald 'Paddy' McCarthy

John said:
I know. I played around with the idea a bit after it came up a couple
of weeks ago, and identified a number of issues.
So let's say I want to use a code block instead of
a lambda or a named function in a map:

foobar = map(def (x, y, z):
astatement
anotherstatement
list1, list2, list3)
A third item is that I don't really care if we use 'def'
or not. Borrowing the vertical bar from Ruby, the map
example becomes:

foobar = map(| x, y, z |
astatement
anotherstatement
list1, list2, list3)

I kind of like this better, except for one really unfortunate
issue: it's going to raise havoc with code coloring algorithms
for a while.

John Roth

Hmm,
How about just using the presence of (...):: as starting a code block?
A call to a function that might take two function args and something else:
foo = bar(func1, func2, nonfunc)
would become:
foo = bar( (a,b)::
astatement
somemorestatements
,(c,d,e)::
anotherstatement
yetmorestatements
,nonfunc)

I used the double colon after the closing bracket as the statements of
the enclosed statement block must be double indented w.r.t foo

I guess the indentation of the arguments of bar (not including the
statement blocks could have less restrictive indentation.
Just as you could write
foo = bar(
func1,
func2,
nonfunc)
the indentation rules would have to be that the statement block is
double indented on the line after ):: and the statement block ends on
the next line with LESS indentation (more indentation would be tart of
the statement block).

Pad.
 

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,780
Messages
2,569,611
Members
45,276
Latest member
Sawatmakal

Latest Threads

Top