Discussion:
Python syntax in Lisp and Scheme
(too old to reply)
Grzegorz Chrupala
2003-10-03 23:12:37 UTC
Permalink
I know at least one more person who came to LISP/Scheme over ruby. Maybe
it needs ruby and python to enlighten people without confusing them with
a syntax they are not used to first. ;)
I can't remember how *exactly* I came to use scheme (unfortuantely I
don't keep a diary), but trying to reconstruct it looks something like
this:

I am actually not a programmer, but mostly a linguist. About four
years ago I got interested in computational linguistics and decided to
learn a programming language. The first programming book I picked up
was "The Gentle Introduction..." (Common Lisp). I made sense to me but
I couldn't find a plug'n' play lisp implementation (I was pretty
computer-illiterate at the time: could only manage v. basic stuff on
Windoze). So I put that aside and decided to have a go at Perl (widely
used in NLP). That was much easier, I installed the ActiveState win32
port no problems and picked Perl up from online tutorials and the
multitude of other easily accessible Perl resources. After I've played
with perl for a while I heard about Python and Ruby, which to me
looked like more sophisticated versions of Perl, and I switched to
Ruby for most of my toy and not-so-toy scripts. While reading
ruby-talk and other ruby-stuff I kept coming across references to
Scheme and Lisp, which I was already vaguely familiar with from my
perusal of the "Gentle Introduction". At this point I was already
using Linux and so could easily install Clisp and half a dozen Scheme
implementations. Schemes such as Gauche, Bigloo and PLT seemed like
they were better suited to writing the sort of small programs or CGI
scripts that I was using Perl and Ruby for, so I sort of settled for
Scheme. (Sometime during this time I also learned Prolog in a
university course and it made me aware of the various advantages, as
well as some disadvantages, of using a very high level languages in
comp-ling).
At the moment I am quite happy with Scheme although I do miss the
large lively communities and the amout of libraries associated with
Perl, Python and Ruby. I hope the "revival" of Lisp-like languages
some of you have observed will gain momentum and that CL anb Scheme
will catch up with Python and Ruby in the areas where they are behind.

Cheers,
--
Grzegorz
Kenny Tilton
2003-10-04 00:52:44 UTC
Permalink
Post by Grzegorz Chrupala
I know at least one more person who came to LISP/Scheme over ruby. Maybe
it needs ruby and python to enlighten people without confusing them with
a syntax they are not used to first. ;)
I can't remember how *exactly* I came to use scheme (unfortuantely I
don't keep a diary), but trying to reconstruct it looks something like
It would be valuable to have what you wrote next in:

http://alu.cliki.net/The%20Road%20to%20Lisp%20Survey

Lisp there is defined as "any member of the Lisp family".

Aside: oh, great. Now the survey is going to get thrown off the ALU
Cliki by the Iki Police. um, could you all find something less
productive to focus on? Cutting and pasting thirty pages is /so/ helpful
to the Lisp community. Not!!!

You can be response #78...oops, #79.

Or e-mail me a go-ahead and I'll do the legwork.

kenny
Mark Wilson
2003-10-04 01:48:45 UTC
Permalink
I know at least one more person who came to LISP/Scheme over ruby.
Maybe
it needs ruby and python to enlighten people without confusing them
with
a syntax they are not used to first. ;)
[snip]
At the moment I am quite happy with Scheme although I do miss the
large lively communities and the amout of libraries associated with
Perl, Python and Ruby. I hope the "revival" of Lisp-like languages
some of you have observed will gain momentum and that CL anb Scheme
will catch up with Python and Ruby in the areas where they are behind.
[snip]
It's nice to see this thread from the Python group migrate over here to
Ruby. I recommend reading the thread for some interesting thoughts on
Lisp/Scheme, Python and programming languages in general.

I think Scheme is an excellent language and that learning both Scheme
and Ruby is a good idea.

Regards,

Mark
Alex Martelli
2003-10-04 17:15:18 UTC
Permalink
Grzegorz Chrupala wrote:
...
... box = [n]
... def foo(i): box[0]+=i; return box[0]
... return foo
...
It's still a hack that shows an area where Python has unnecessary
limitations, isn't it?
Debatable, and debated. See the "Rebinding names in enclosing
scopes" section of http://www.python.org/peps/pep-0227.html .

Essentially, Guido prefers classes (and instances thereof) to
closures as a way to bundle state and behavior; thus he most
emphatically does not want to add _any_ complication at all,
when the only benefit would be to have "more than one obvious
way to do it".

Guido's generally adamant stance for simplicity has been the
key determinant in the evolution of Python. Guido is also on
record as promising that the major focus in the next release
of Python where he can introduce backwards incompatibilities
(i.e. the next major-number-incrementing release, 3.0, perhaps,
say, 3 years from now) will be the _elimination_ of many of
the "more than one way to do it"s that have accumulated along
the years mostly for reasons of keeping backwards compatibility
(e.g., lambda, map, reduce, and filter, which Guido mildly
regrets ever having accepted into the language).
Python users might legitimately ask why they can't just write
return lambda i: return n += i
The rule Python currently use to determine whether a variable
is local is maximally simple: if the name gets bound (assigned
to) in local scope, it's a local variable. Making this rule
*any* more complicated (e.g. to allow assignments to names in
enclosing scopes) would just allow "more than one way to do
it" (making closures a viable alternative to classes in more
cases) and therefore it just won't happen. Python is about
offering one, and preferably only one, obvious way to do it,
for any value of "it". And another key principle of the Zen
of Python is "simple is better than complex".

Anybody who doesn't value simplicity and uniformity is quite
unlikely to be comfortable with Python -- and this should
amply answer the question about the motivations for reason
number 1 why the above foo is unacceptable in Python (the
lambda's body can't rebind name n in an enclosing scope).

Python draws a firm distinction between expressions and
statements. Again, the deep motivation behind this key
distinction can be found in several points in the Zen of
Python, such as "flat is better than nested" (doing away
with the expression/statement separation allows and indeed
encourages deep nesting) and "sparse is better than dense"
(that 'doing away' would encourage expression/statements
with a very high density of operations being performed).

This firm distinction should easily explain other reasons
why the above foo is unacceptable in Python: n+=i is a
statement (not an expression) and therefore it cannot be
held by a 'return' keyword; 'return' is a statement and
therefore cannot be in the body of a 'lambda' keyword.
or even
lambda i: n += i
And this touches on yet another point of the Zen of Python:
explicit is better than implicit. Having a function
implicitly return the last expression it computes would
violate this point (and is in fact somewhat error-prone,
in my experience, in the several languages that adopt
this rule).

Somebody who is unhappy with this drive for explicitness,
simplicity, uniformity, and so on, cannot be happy with
Python. If he wants a very similar language from most
points of view, BUT with very different philosophies, he
might well be quite happy with Ruby. Ruby does away with
any expression/statement distinction; it makes the 'return'
optional, as a method returns the last thing it computes;
it revels in "more than one way to do it", clever and cool
hacks, not perhaps to the extent of Perl, but close enough.

In Ruby, the spaces of methods and data are separate (i.e.,
most everything is "an object" -- but, differently from
Python, methods are not objects in Ruby), and I do not
think, therefore, that you can write a method that builds
and returns another method, and bind the latter to a name --
but you can return an object with a .call method, a la:

def outer(a) proc do |b| a+=b end end

x = outer(23)
puts x.call(100) # emits 123
puts x.call(100) # emits 223

[i.e., I can't think of any way you could just use x(100)
at the end of such a snippet in Ruby -- perhaps somebody
more expert of Ruby than I am can confirm or correct...?]
but apart from this it seems closer to what the above
quotes appear to be probing for. In particular, it lets
you be MUCH, MUCH denser, if that is your purpose in life,
easily squeezing that outer function into a (short) line.
Python is NOT about making code very dense, indeed, as
above mentioned, it sees _sparseness_ as a plus; a typical
Pythonista would cringe at the density of that 'outer'
and by contrast REVEL at the "sparsity" and "explicitness"
(due to the many names involved:-) of, e.g.:

def make_accumulator(initial_value):
accumulator = Bunch(value=initial_value)
def accumulate(addend):
accumulator.value += addend
return accumulator.value
return accumulate

accumulate = make_accumulator(23)
print accumulate(100) # emits 123
print accumulate(100) # emits 223


(using the popular Bunch class commonly defined as:
class Bunch(object):
def __init__(self, **kwds):
self.__dict__.update(kwds)
). There is, of course, a cultural gulf between this
verbose 6-liner [using an auxiliary class strictly for
reasons of better readability...!] and the terse Ruby
1-liner above, and no doubt most practitioners of both
languages would in practice choose intermediate levels,
such as un-densifying the Ruby function into:


def outer(a)
proc do |b|
a+b
end
end

or shortening/densifying the Python one into:

def make_accumulator(a):
value = [a]
def accumulate(b):
value[0] += b
return value[0]
return accumulate

but I think the "purer" (more extreme) versions are
interesting "tipizations" for the languages, anyway.


Alex
ts
2003-10-04 17:22:58 UTC
Permalink
A> [i.e., I can't think of any way you could just use x(100)
A> at the end of such a snippet in Ruby -- perhaps somebody
A> more expert of Ruby than I am can confirm or correct...?]

Module#define_method


Guy Decoux
Christoph
2003-10-04 22:56:33 UTC
Permalink
"Alex Martelli" wrote:
....
Post by Alex Martelli
def outer(a) proc do |b| a+=b end end
x = outer(23)
puts x.call(100) # emits 123
puts x.call(100) # emits 223
[i.e., I can't think of any way you could just use x(100)
at the end of such a snippet in Ruby -- perhaps somebody
more expert of Ruby than I am can confirm or correct...?]
Guy is probably thinking about something like this

---
def outer(sym,a)
Object.instance_eval {
private # define a private method
define_method(sym) {|b| a+=b }
}
end

outer(:x,24)

p x(100) # 124
p x(100) # 224
---


but there is no way to write a ``method returning
method ::outer in Ruby that could be used in the form

----
x = outer(24)
x(100)
----

On the other hand, using []-calling convention
and your original definition, you get - at least
visually - fairly close.

---
def outer(a) proc do |b| a+=b end end

x = outer(23)
puts x[100] # emits 123
puts x[100] # emits 223
---


/Christoph
Grzegorz Chrupała
2003-10-04 23:16:39 UTC
Permalink
Tut-tut. You are claiming, for example, that I mentioned the lack
of distinction between expressions and statements as "too complex for
Python to support": I assert your claim is demonstrably false, and
that I NEVER said that it would be COMPLEX for Python to support such
Sorry if I inadvertantly distorted your words. What I meant by my admittedly
rhetorical statement wa something like: "these features either introduce
too much complexity, or are messy, or otherwise incompatible with Python's
philosophy and for this reason the language refuses to support them." Not
necessarily too complex to *implement*. I do realize that
no-statements-just-expressions is not a particularly challenging design
issue.
It makes the _learner_'s job simple (the rule he must learn is simple),
That is plausible.
and it makes the _programmer_'s job simple (the rule he must apply to
understand what will happens if he codes in way X is simple)
This makes less sense. The rule may be simple but it also limits the
expressiveness of the language and forces the programmer to work around the
limitations in a contorted and far from "simple" way.
I thought the total inability to nest method definitions (while in Python
you get perfectly normal lexical closures, except that you can't _rebind_
outer-scope names -- hey, in functional programming languages you can't
rebind ANY name, yet nobody every claimed that this means they "don't have
true lexical closures"...!-), and more generally the deep split between
the space of objects and that of methods (a split that's simply not there
in Python), would have been show-stoppers for a Schemer, but it's always
nice to learn otherwise.
I don't really feel quite qualified discuss Ruby's design decisions wrt the
relation between methods, procedures and objects, but I don't think the
split between methods and objects is as deep as you claim:

irb(main):011:0> meth="f-o-o".method(:split)
=> #<Method: String#split>
irb(main):012:0> meth.class
=> Method
irb(main):013:0> meth.kind_of?(Object)
=> true
irb(main):014:0> meth.call('-')
=> ["f", "o", "o"]
irb(main):015:0>

I do tend to think that Ruby would be better off with a more unified
treatment of blocks, procedures and methods, but my understanding of the
issues involved is very incomplete. Perhaps Smalltalk experts would be more
qualified to comment on this.
--
Grzegorz
http://pithekos.net
Christoph
2003-10-05 01:56:59 UTC
Permalink
"Grzegorz ChrupaÅ,a" wrote:
....
Post by Grzegorz Chrupała
I don't really feel quite qualified discuss Ruby's design decisions wrt
the
Post by Grzegorz Chrupała
relation between methods, procedures and objects, but I don't think the
There is a huge difference between ``garden variety'' Ruby functions,
usually called methods, ala

def foo() end

and Method objects like

meth = "meth".method(:foo)

The former really ``live'' outside of Ruby's object space, the latter are
concrete living breathing objects - i.e. instances of the Method class.


---
def enumerate_method_objects
cnt = ObjectSpace.each_object(Method) {|mth|
p mth
}
puts "There are #{cnt} Method objects\n\n"
end

class String
def foo
self + " hello"
end
end


##
ary = [1,2]
ruby = "Ruby"

enumerate_method_objects() # no Method object exists

## create two Method objects
ary_length = ary.method(:length)
ruby_foo = ruby.method(:foo)

enumerate_method_objects() # 2 Method object exists

## manipulate the objects ``ary'' and ``ruby'' a bit
p ary << 3 # [1, 2, 3]
p ruby << " says" # "Ruby says"

## use the method objects
p ary_length[] # 3
p ruby_foo[] # "Ruby says hello"

## which is pretty much the same as writing

p ary.length() # 3
p ruby.foo() # "Ruby says hello"

---

resulting

---
There are 0 Method objects

#<Method: String#foo>
#<Method: Array#length>
There are 2 Method objects

[1, 2, 3]
"Ruby says"
3
"Ruby says hello"
3
"Ruby says hello"
---
Post by Grzegorz Chrupała
I do tend to think that Ruby would be better off with a more unified
treatment of blocks, procedures and methods, but my understanding of the
issues involved is very incomplete. Perhaps Smalltalk experts would be
more
Post by Grzegorz Chrupała
qualified to comment on this.
By not elevating function (methods) into real objects, you pretty much
avoided the problem of users complaining about the lack of higher order
function objects:-)


/Christoph

Continue reading on narkive:
Loading...