Please create an account to participate in the Slashdot moderation system

 



Forgot your password?
typodupeerror
×
Programming IT Technology

Interviews With The Creators of Vyper and Stackless 73

Frank writes: "What most programmers probably think of when they talk about 'Python' is the specific implementation sometimes called 'CPython' (because it is implemented in C). However, Python as a language specification has been implemented several times in parallel with the evolution of Guido van Rossum's reference implementation. This article consists of annotated interviews with the creators of two of the non-standard Pythons -- Stackless and Vyper." A pair of interviews to make your head spin with talk of Literate Programming and the odd but neat concept of "continuations."
This discussion has been archived. No new comments can be posted.

Interviews With The Creators of Vyper and Stackless

Comments Filter:
  • I feel pretty much the same way. Ruby seems to be a step forward compared to Python in some respects, and a step backward in others. Overall it's a wash, and since Python already works very well for me I don't think I'd gain much by learning Ruby. That doesn't mean, though, that I would discourage anyone who doesn't already have a scripting language under their belt from choosing Ruby over Python. From what I can tell, they would learn better habits from Ruby than from Perl or Tcl.

  • by ndfa ( 71139 ) on Monday November 13, 2000 @03:07PM (#626473)
    ---- Opps... COMPLETE COMMENT BELOW

    When you have programmed using a functional language (Scheme for me) and used this way
    of working it takes a bit to get back to Java/C!

    The basic idea for those of you who are confused is to use recursion and get away with not using the stack for the calls. How you might ask... well first of all its easy when you are using a language like Scheme/Lisp which treats functions as first class data members! There is no difference b/w (well almost none) b/w data types and functions, each can be passed into a function and returned!

    SO Normally when you make a recursive call you are just pushing down your current data into the stack and when you reach the base case you go back up the stack!
    In case of Continuation Passing Style the idea is really very very neat! You basically make a recursive call and pass a function to that will be able to compute the answer with the data it needs! There is no stack. For example (Factorial of course)

    (define fac
    (lambda (n)
    (if (&lt= n 0) 1
    (* n (fac (- n 1)))))

    Note you had to keep the info of where you were so you can recurse back doing the multiplications!
    This data went into the stack!!! Now if you want to do this without stacks you can use a method that you build each time you go through the loop and so you build the answer on your way!!!

    (define fact-cps
    (lambda (n fun)
    (if (\>= n 1)
    (fun 1)
    (fact-cps
    (+ n -1)
    (lambda(z)(fun (* n z))))))

    This way of course you just build a formulae that will give you the answer! No stack.
    There is a formal algo that you can take any recursive funtion and make it into CPS which is pretty cool till your professor thinks that a scheme interpreter in written in scheme should use CPS!! BUT i digress!! So you can seee that this is really a very powerful idea! It introduces a lot of neat tricks you can do! One of which is hte Y-Combinator... but taht I cant talk about cause i sit in amazement for sometime when i see it:)

    Hmmm these are things i think you should sometime learn if you are in the computer world cause most of the time we stick with the real programming issues and dont see some of the cooler things! So this wont make you much money but you will see how sexy some of these ideas really are!! SCHEME RULZ!

  • Hi AC,

    Yes, Perl is an extremely popular language. This is because it solves a problem for a lot of people. You can have all of this dispite the fact that its design leaves something to be desired.

    Perl is not the end of language evolution and certainly Larry Wall has never claimed that it was. You really shouldn't have that much difficulty accepting this.

    Thanks

    Bruce

  • sorry, write-only languages aren't where it's at. grow up, logo boy.
  • Try browsing the new book from Addison Wesley, it is much better than the translated docs.

    Don't let the perl-like variables fool you, they are merely a convenience. If you wish, you can do things such as get a regular expression object and access its features instead of doing things the perl way. In fact the perl way is simply accessing features of the current object...

  • jpython.org [jpython.org]

    Come on folks, I know there's an anti-Java contingent out there but when you discuss non-C implementations of Python you really shouldn't omit JPython.

    Some key differences between JPython and CPython:

    Syntax

    • JPython has a different interpretation of floating point literals. CPython doesn't allow 001.1 CPython should be fixed.

    • JPython supports continue in a try clause. CPython should be fixed - but don't hold your breath.
    Standard types, functions and behavior
    • JPython string objects support full two-byte Unicode characters and the functions in the string module are Unicode-aware. CPython should add Unicode support in the future, though the details of this are still unclear.

    • In JPython, 0.1**4 is printed as 1.0000000000000002E-4. In CPython, it is printed 0.0001, even though it is not actually equal to 0.0001. Both behaviors are acceptable for now -- this is still being discussed.

    • JPython sequences support three argument slices. i.e. range(3)[::-1] == [2,1,0]. CPython should be fixed.

    • Every object in JPython is an instance of a class -- there are no types in JPython. i.e. [].__class__ is a sensible thing to write in JPython. CPython should be fixed - but don't hold your breath.
  • I am the author of the interviews in question here. As pointed to in the first interview, I also did interviews about J(P)ython and Python for .NET developers. Those have not made it to IBM developerWorks yet (they are a bit slow with publication schedules), but it all can be found at my own website already. (As well, certain manglings by IBM editors are not present in my home versions [says the spoiled artist :-)])

    Take a look at:

    http://gnosis.cx/publish/tech_index.html
  • A Python compiler written in Python? It could use a GCC backend to compile C code emitted by the Python compiler.

    The stage 1 compiler would be a simple distribution of Python source files, and it would be run with the regular CPython installation. That would be used to compile itself into a binary Python compiler.

    Why not? Develop Python programs with CPython. When you're done, compile it for speed.

  • We all love GvR, but with that same reasoning, the language would not have threads. I'm actually hoping the digital creation's guys ruffle his feathers a bit and get him more proactive in removing some of the barriers to making python a better software engineering tool. sure it is a great glue, but this language has the capability to morph into much more.

    jim
  • Larry Wall, on the other hand, has no discernible background or ability in language design, and it shows in Perl.

    But, since nobody uses Perl, due to its atrocious language design, this doesn't matter. :-)

    Seriously, Perl the language has grown a lot since it was first released, and you can find yourself tripping (quite literally) over its roots. But an interesting part of Perl's design is that it's author, Larry Wall, has an academic background in Linguistics rather than CS, and had a pressing need to get something like perl working rather than a prime requirement that everything be perfect at the start.

    But today's perl has lexical scoping and closures and many of the nice things people wish other scripting languages had (or had had sooner). Now that Perl6 is really going to happen (since Damian Conway is motivated to get all his language features implemented before the magic spell wears off :-)), I wouldn't be surprised to see a lot of the dubious stuff finally go away.

    And there will be much rejoicing. But the claim that Perl's non-CS roots have somehow doomed it to obscurity or uselessness really seems quite far-fetched.

  • Stackless Python is not about distributed processing at all.
    Stackless Python only means that the state info for the current thread of execution is not saved on the C stack as you go along, but maintained elsewhere. The best selling point (to us here at CCP [ccpgames.com] at least) is the microthread support this allows, rather than create an operating system thread (and a seperate C stack) for every seperate task, we just allocate tiny Python microthreads, with little or no overhead.
  • i'm not so sure you are using continuations in your example.

    from what i understand, a continuation is a semantic element that allows you to save a place in the future execution of a block of code. the continuation can then be called (with local vars as parameters) at a later point in time.

    http://www.tismer.com/research/stackless/spcpape r.htm

    jim
  • what exactly does the author of vyper mean when he says that python doesn't support lexical scoping properly? I've done a bit of work with python and always assumed that the scope of variables and classes was the same as for C or C++. Is he saying that variables don't pass out of scope at the end of a block? Or that it has dynamic scoping and that variables pass into scope depending on how the function was called? I guess I haven't delved deeply enough into python to be able to see what he's talking about.
  • Well, I'd say that ML is mostly a functional language. You can write code in an imperative style using references, but it gets painful really quickly. In the end it's easier to write functional code, and let ML do all the work of turning it into something imperative, what with it's tail-recursion elimination, automatic replacement of code that has repeated identical recursive calls (eg. to compute the nth fibonacci number, f(n) -> if n

  • Oh, arse. Yeah, I know, I should have used the preview button...

    Anyway, as I was saying, to compute the nth fibonacci number,
    f(n) -> if n <= 2 then n else f(n-1) + f(n-2)
    ML will replace this with either a memoisation technique, or some dynamic programming technique.

  • most of the people coming in here going on about how ugly perl is typically haven't spent more than an hour using it

    And I suppose this is in contrast to the Perl weenies who come into Python articles to bash Python, based on their many many hours spent studying the pros and cons of each? Yeah right. Most Perl folks spend about three minutes looking at Python, notice maybe one thing about the language itself - usually the indentation thing - but mostly just notice that it doesn't look like Perl, and then spend the rest of their lives making the illogical claim that Perl must be better because more people use it.

    Python has its weaknesses, which I have gone out of my way to describe in my last post on the subject (a copy of which pretty easy for any non-moron to find on my website), but overall it's a fine language. As with Ruby, and Pike, and several other languages, on technical merit it deserves at least equal consideration to Perl.

  • Python has two scopes: the current local scope and the global scope. Given the following useless code:

    foo = 'This'

    def frotz():
    foo = 'The other'
    def bar():
    print foo
    def foobar():
    foo = 'Yet another'
    print foo
    def gbar():
    global foo
    print foo
    def bar_kludge(foo = foo):
    print foo
    bar()
    foobar()
    gbar()
    bar_kludge()

    There is now way for a function defined in frotz to see the definition of foo in frotz. The losest you can get is the bar_kludge trick which results in a local variable of bar_kludge named foo that is initialized to the value of the foo in frotz.

    Is it all clear as mud now?
  • IIRC, Guy Steele (one of the designers of the Java spec) wrote his thesis on converting Scheme code into continuation-passing style, then compiling that. This gives you tail-recursion (briefly, writing code in a recursive style, but making it behave iteratively) for free!

    Continuations are really nice for *formally* defining how languages should behave under forking (with data dependencies among threads), and other wacky control features that may or may not be useful. I've only seen this stuff done rigorously for Lisp-like languages, in the lambda calculus. I doubt it's used at all for imperative languages.
  • The language Ruby [ruby-lang.org] is a fully object-oriented interpretive language with good regular expression support. It's inspired by Perl, but a good deal cleaner than Perl IMO. I'm afraid I've always had an esthetic problem with Perl. Unlike Python, Ruby's object model isn't an afterthought. Ruby is dual-licensed under the GPL and an Artistic-like license. What would be the Perl Conference in the U.S. is the Perl and Ruby Conference in Japan. Ruby's released implementation is interpreted, but there is an experimental JIT compiler. Packages for various libraries exist and it's shipped with Debian.

    It seems to be a pretty nice language. Nobody here seems to have heard of it. Why? I think it needs a bigger community outside of Japan.

    Thanks

    Bruce

  • Might the main CPython branch adopt these innovations? Vyper's scoping and functional features, and Stackless' continuations? The article says that Stackless isn't a huge fork--could it get brought back into the trunk?

    Just something to ponder.


    ---
    Zardoz has spoken!
  • It seems to be a pretty nice language. Nobody here seems to have heard of it. Why?
    Maybe it suffers from the same curse that (unfortunately) afflicts Smalltalk...
  • I'm intrigued by the concept of continuations, but can't find an example I can relate to (having never done Prolog, Scheme, Lisp, ML, etc.)

    Does anyone have an example in something like X86 assember, Pascal, Delphi or even C?

    --Mike--

  • There are discussions going on amongst the Python developers right now. Coroutines, generators and microthreads will probably make it into CPython based on Christian Tismer's ideas. Guido is pretty cool on continuations however.
  • Well, there are some implementations of ML that I've seen which have continuations, but who can really say whether ML is functional or imperative?
  • In JPython, 0.1**4 is printed as 1.0000000000000002E-4. In CPython, it is printed 0.0001, even though it is not actually equal to 0.0001.

    uhm.... Someone please explain this one to me.

  • I prefer it this way:
    (define fact
    (letrec
    ((facto
    (lambda (n t)
    (if (< n 2)
    t
    (facto (- n 1) (* n t))))))
    (lambda (n) (facto n 1))))

    Note that by coding in this manner, one is coding tail-recursively--Nothing has to be saved on the stack from one recursive call to the next (in Scheme functions evaluate their arguments before applying the operator so (- n 1) and (* n t) are evaluated before recursion).

    I've tried this in Python and got interesting results: A nontail recursive factorial will stack overflow. A tail-recursive implementation still overflows, but after more calls.

    Most coders couldn't care less, but then most coders prefer an iterative approach to a recursive one. Which is a bleeding shame, because often recursion is so much cleaner. You can always make it iterative later.

    A lot of people who grew up with imperative languages like C, seem to bear a lot of malice towards more functional languages like Python (it's almost first order and pretty functional) or Scheme. I don't really see why. The common whine is that it's the difference between theory and practice. But hey, this is computer science: Theory is practice.

    --
    Lagos
  • The easier the extensions are to add, the harder it is to do things with them that were not envisioned by the folks who designed the installation process. I don't ask that horors like my all in one static binary of a fancy python environment be easy to produce; but don't preclude the possibility in the name of ease of use. Disutils in my limited experiance with it seems like much work to little effect; I have little interest in the goals it sets out to meet and haven't given a fair look.

    As for complexity; as long as it's not required where's the harm? Advanced perl melts my brain (around halfway through the camel book) but the advanced features aren't necessarily required to make functional perl code. I'm beginning to get the idea of continuations and am probably going to look at applying the stackless patches to my own customized python tomorrow just to play with it. If I can do it and not break anything, even should I decide not to use new capabilites I won't go to the effort of reverting back to stock unless there's a functionality based reason.

  • It would be nice if the lexical scoping from Vyper went into Python, but it's so much closer to it's implementation language Ocaml than it is to Python, that I think it would alienate many potential Python adopters.

    It's a shame that so many programmers, especially those from a more engineering background than a math background, are so hostile to functional programming. Like the man says, it's not the 1970s any more, the amount of clever thinking that's gone into functional programming means you can regularly beat the engineering results from an imperative language, in many fewer lines of code, and less hours of work.

    However, Stackless Python is a different matter. The continuation passing is totally transparent to the programmer (unless they want to use it), that it wont have matter to those engineers, and will even please the mathematicians. Microthreads in particular are gorgeous, no engineer would turn their nose up at them, just because of the math background. This is a win for everybody, it's just obviously the way Python should go.

  • It's only write-only if you're too stupid to understand Scheme.
  • "no ability" is a strong word. What Larry created was a language that worked excellently for what he wanted it for, and it grew from there. It wasn't until Perl's later years that people started using it for things he never designed it for (and probably could have never predicted).

    Perl gets the job done. It really gets the job done. If that's not the mark of a good language, well, I'll leave you to your language designing.

  • You have to imagine how you create fractions (which .0001 is) with integers, since that's all that 32 or 64 (or whatever number) bits can create. Python is forced to live with a set precision, which isn't always perfect.

  • Heh, and someone got modded up for posting Scheme code. I think someone has it out for you, Bruce. I shudder to think what would happen if RMS were to happen along with some elisp examples.
  • Python is quite an ok language, even though I understand that quite a lot of developers are less than fond of the concept of "significant whitespace".

    If you start off with the idea that one "significant" tab is equivalent to 4 "significant" spaces, things can turn out differently than you may have thought when the emacs setting of the day happens to be: 1 tab is traded for 5 significant spaces.

    The real issues to me, however, are:

    (1) Oosterhout says that the inner loops are to be coded in C, while the outer, administrative loops can be done in -Script-. I could have agreed with this idea, as it sounds good, if it were not that integrating C and -Script- becomes a project in itself. I dare you to figure out tools like SWIG, or even to use the python.h files manually. You're in there for some major undertaking. God save the queen!

    (2) Now that you're finished SWIGging and re-SWIGging, compiling, re-compiling your libs, scripts, glue code, you can create your deployment package: an atrocious bunch of dlls, .py, .pyc, .pyd, dot anything files, and as a bonus, you will probably be forced to pollute the \system32 folder extensively with global dll-hell-raising dependencies. A python deployment is the definition itself of "system pollution". The McMillan installer is a conceptual heavyweight, gathering complex dependency trees; and watch it go wrong regardless: not really your mother's deployment tool.

    (3) Python depends on a virtual machine, say the pyxxyy.dll, which shares the problems of all shared objects. While Guido was going from v1.52 to v1.6b to v1.6 to v2.0b to v2.0, which is fine, because that improves the system, it had one major drawback: continuous redeployment of almost identical but slightly different virtual machines. You will find that your virtual machine gets updated more rapidly than the applications that depend on it. What's more, virtual machines create a horrendous need for backward compatibility of the APIs, with its trail of deprecated classes, methods, and entire packages. All the cruft will remain there forever, and you will be deploying an everincreasing pile of mostly unneeded dog shit to your users. In my opinion, *virtual machining* is bound to end in tears.

    I'm looking for a high-level language, regardless of its syntax that:
    (1) understands seemlessly C/C++ header files and the APIs that it exposes. No SWIGgin! (But also, no IDL or stuff like that, just direct invocation!)

    (2) grabs its language run-time (virtual machine) (whichever version that you indicate), native methods (.obj files), and your application, uncrufts it (removes every class or method not used) and nicely links it into one, single clean executable (optionally compressed with, for example, UPX).

    No shared objects, no \system32 pollution, no dlls, no folders full of .pyd, .py, .pyc, .dll files! Just one .exe!
  • Personally, I'm not much of a programmer (I gave it up before I was ever done getting a Bacchelor's and went for journalism) and didn't learn much about language design, but I agree; Perl solves problems but it's atrocious. I've learned very little Python but what I have I really like. =) Perl's one of those things that just does a lot of really neat stuff but is uglier than a configure file.
  • I'd also like to point out that it's possible to write GNOME apps using Ruby.

    There, idiot moderator, mod this down.
  • Oh man, I ran that code and now I see what you mean....that's just nasty....why wouldn't they have made it behave like Pascal? If you nest functions in Pasquale, it still behaves properly. And the 'global' thing - it's useful but just having a scope operator like c++ (::foo) would save you having to clobber local variables as well. Ick. Oh well, python is still a pretty awesome language, even if you DO have to constantly use 'this.' in all your classes.
  • I took your code and tried something else - even more nasty:

    foo = 'This'

    def frotz():
    foo = 'The other'
    if 1 :
    print foo
    if 1 :
    foo = 'Yet another'
    print foo
    if 1 :
    global foo
    print foo
    if 1 :
    print foo

    frotz()

    output:
    The other
    Yet another
    This
    This

    ICK!!! the global variable clobbers the local from that point on....
  • Correct me if I'm wrong, but isn't there simply a syntactic difference, then, between a tail-recursive function and an iterative function, provided that tail-recursion is implemented by the compiler/interpreter in the optimized way you mention?
  • by Anonymous Coward on Monday November 13, 2000 @02:17PM (#626510)
    aslo available in PDF [ibm.com] format
  • Just look at this picture [foretec.com]. Is that the face of a man with a dick?
  • Yes Bruce, people have heard of it. The FreeBSD ports tree is chock full of excellent Ruby software.

    Ruby really is a great language, and I suspect that by time Perl6 is done, it will resemble Ruby in many ways.

    Moving to Ruby now will probably give you everything Perl6 is promising, without the three year wait.

  • Thanks for mentioning the FreeBSD ports tree - I went and looked at it, and there are a number of Ruby-related packages in there that have not made it to Debian yet.

    I might do some serious web programming with this.

    Thanks

    Bruce

  • I'll believe it when I see it. There are alot of system specifics that would need to be taken into account; for example, I might have a file-descriptior stored in a variable. That file descriptor is not going to survive a move to another machine.

    Likewise, any C-land objects that are pointed to by the state are going to be hard to move. It seems to be the sort of thing you really want to implement at the VM level using Distributed Shared Memory, not at the language level.

    But, I expect that anyone smart enough to implement stackless wouldn't go around saying these sorts of things if he didn't know something I obviously don't.
  • Python has been successful because it's an easy-to-use, simple language that integrates well with UNIX systems. That's the "market" I think Python should stick to.

    And, nice as Python is, there is some room for improvement in its role as a scripting and VHLL for UNIX. Module configuration even in 2.0 is somewhat messy (configure, compile, edit Setup, compile some more). There is still nothing quite like CPAN and the CPAN shell module for Perl. Documentation is still more difficult to get to than "perldoc". Despite distutils, Python extensions still sometimes don't "just" install. And building C extensions could also be greatly simplified with a bit more support from the Python installation. Just about the only language enhancement I would like to see for now is lexical closures, not because I think its functionality is desparately needed, but because it would simplify scoping rules if done right.

    I think Python is at risk of going down the same path as other dynamic languages before it. Adding a lot of new features (continuations, type declarations, list comprehensions, etc.) complicates the language to the point where people may not feel comfortable just picking the language up anymore. And the effort that goes into those fun features is effort that doesn't go into making the language even easier to install and interface with.

  • Well Condor [wisc.edu] does (and has been doing for quite a while) exactly this with C. Assuming you follow the link, now you can believe it. Basically you link with condor's library and it intercepts i/o calls and sends them back to a central controller. Keep in mind this is distributed processing not distributed i/o.

  • One way to understand continuations is to look at how it's implemented. Check out the source code to one implementation of scheme, SCM [mit.edu]. In effect it doesn't interpret continuations in scheme but implements continuations in C and then implements the scheme continuations using the C ones. The actual code works by deviously using setjmp, longjmp and messing with the stack but it's not that hard to understand (although it's a bit messy on architectures that have a non-contiguous stack). The last time I looked at this code was about 5 years ago so apologies if it's been reimplemented differently since then. Many years ago I used a similar implementation of continuations in C myself in order to implement a version of the very complex recursive pattern matching that Mathematica supports - in effect by using coroutines to generate streams of possible matches. At the time most people thought this was a gross solution to the problem but now I see that continuations are considered to be respectable I shall have to start using the method again.
    --
  • by Scarblac ( 122480 ) <slashdot@gerlich.nl> on Monday November 13, 2000 @05:14PM (#626518) Homepage
    The main problem with adding Stackless patches to the main trunk is that it's hard to implement in Java, so this would probably bring about incompatibilites between CPython and JPython (currently called Jython because of more unholy trademark shite).

    But since the Stackless stuff brings real performance improvements, microthreads, and nifty new libraries, it is likely to go into the main trunk at maybe 2.1 or probably 2.2.

  • JPython was in fact mentioned as another implementation. But this particular article was about Vyper & Stackless.
  • by alienmole ( 15522 ) on Tuesday November 14, 2000 @01:59AM (#626520)
    <SCRIPT>
    // See explanation at end.
    // This code works and will run in a web browser -
    // just save as .html and load in your browser.
    // Will work in Mozilla or IE.

    // sample calls
    printFacVal( "fact", 12, fact(12) );
    printFacVal( "factail", 12, factail(12, 1) );
    printFacVal( "factfn", 12, factfn(12, function (x) { return x; } ) );

    // write the function name, value of n and result to the browser window
    function printFacVal( fname, n, x )
    {
    document.write( fname + "(" + n + ") = " + x + "<P>" );
    }

    // Plain ol' recursive implementation of factorial
    function fact( n )
    {
    if ( n == 0 )
    return 1;
    else
    return n * fact( n - 1 );
    }

    // Version which supports tail recursion: final value is
    // known when recurse bottom is reached, so stack can be
    // optimized away in a language which supports this.
    // See http://www.cs.utexas.edu/users/novak/cs30770.html -
    // or for fun, http://info.astrian.net/jargon/terms/t/tail_recurs ion.html
    function factail( n, total )
    {
    if ( n == 0 )
    return total;
    else
    return factail( n - 1, n * total );
    }

    /*
    factfn()

    Version which builds a function to calculate factorial(n)
    This only really makes sense in a functional language.
    Although syntactically it can be mapped directly onto its
    functional language equivalent, and although it does
    actually work in Javascript, CS students around the
    country are recoiling in amazement tinged with horror
    as they read this.
    */
    function factfn( n, fn )
    {
    if ( n == 0 )
    return fn( 1 );
    else
    return factfn( n - 1, function ( p ) { return fn( n * p ) } );
    }

    /*

    factfn() would be rather hard to implement in a language like Pascal or C,
    except if you were using one of those languages to build a functional language
    (in which case, that would be easy! ;)

    As factfn() descends through its recursive calls, the anonymous function which
    is passed as the second parameter becomes a function built specifically to
    calculate the factorial of the specified n, i.e. the final value of fn(1)
    equals the factorial of n.

    However, since current implementations of Javascript don't support tail
    recursion, factfn() relies on a stack to achieve its recursion, and is
    thus bounded by stack size in what it can compute (actually, in this case,
    the factorial calculation is limited numerically: factorial(170) or so
    will overflow a typical Javascript implementation, long before the stack
    fills up.) A tail-recursion implementation doesn't need a stack to recurse,
    thus is not limited by stack depth.

    Another difference between this and a tail-recursive implementation is that
    when fn(1) is finally evaluated, in the Javascript implementation, it will
    call itself recursively, again using the stack. At each level, the value
    of n is taken from the function's context at the appropriate level (which
    may be implemented on the stack).

    A fully tail-recursive cps implementation would do all this without the
    need for a stack. The final evaluation of fn(1) would simply evaluate the
    final result of n * (n-1) * (n-2)... and get the right answer.

    */
    </SCRIPT>
  • A lot of continuation-based research was done in the Standard ML project at AT&T and Princeton in the late 80's/early 90's. Andrew Appel (who wrote the native-code generator for this particular SML compiler) has a book, Compiling with Continuations [amazon.com], which goes into this stuff. Continuation-passing is odd at first but pretty easy to code up in a functional language.
  • I'm a bit up in the air myself, and will eagerly pursue any other replies... in the meantime, maybe take a look at Coroutines in C [greenend.org.uk]. It's by PuTTY's author, and there's an example of such coding in it's SSH code apparantly.
  • I'm looking for a high-level language, regardless of its syntax that:

    (1) understands seemlessly C/C++ header files and the APIs that it exposes. No SWIGgin! (But also, no IDL or stuff like that, just direct invocation!)


    Sounds like any language that supports FFI (Foreign Function Interface).

    (2) grabs its language run-time (virtual machine) (whichever version that you indicate), native methods (.obj files), and your application, uncrufts it (removes every class or method not used) and nicely links it into one, single clean executable (optionally compressed with, for example, UPX).

    Sounds like Smalltalk. Squeak is a free cross-platform Smalltalk that supports FFI:
    http://www.squeak.org
  • I meant that Smalltalk is the best language under the sun, but that the people promoting it don't know how to market 1/100th as well as the Sun.
  • JPython sequences support three argument slices. i.e. range(3)[::-1] == [2,1,0].

    And people claim Perl is hard to read!

    :-)

  • Doing this in easy-to-read Python is why the IBM interview is sooooo significant. I really want to use the lazy functional language Haskell for everything (I'm a power freak). I wouldn't mind using Scheme but I'm having enough trouble talking my boss into letting me use Python. Extended functional language and/or ('C') stackless capabilities in Python is just what I want so I can have even more fun programming.

    Perl, C, FORTRAN, Pascal and AWK are a purely procedural (not even OO) languages. Functional languages are to large reflecting telescopes what these languages are to binoculars and it does boil down to vision preceded by imagination.
  • Nice.

    People tend to dislike and resent what they don't understand.
  • Python suffers the same problems that TCL and Perl do. They are all ad hoc languages which were not designed but hacked together and "developed" through wasted years of pure brute force Monte Carlo methods of design and debugging.

    Exactly the same methods that work for human languages, human biology and capitalism. A central plan works well when you've got a closed, well-defined problem to solve: "reimplement UNIX" or "make a fast C compiler". The trouble with planned solutions to open-ended problems is that sooner or later, the planner will miss a trick which Monte Carlo methods would have hit on, which is why evolution wins in the long run.
  • Here's a quote from the link you referenced, under point 2.1:

    In essence, every single line of code has a continuation that represents the entire future execution of the program.

    The last expression in my third function, factfn():

    factfn( n - 1, function ( p ) { return fn( n * p ) } );

    ...is exactly that. This expression represents the entire future execution of the program. The only qualification is that to evaluate that expression, a lot of stack activity of various kinds is required, but that would be true of ordinary Lisp, too, as opposed to a tail-recursive implementation.

    The explanation h ere [oberlin.edu] is more explicit, IMO:

    Let us agree to write all programs so that whenever a subexpression is being evaluated, a procedure representing the current context is always applied to the answer produced by that subexpression. This one-argument procedure is called a continuation. Whenever any function in the program is executed, it is passed the continuation parameter representing the current context. Deep-recursive calls can be made tail recursive by augmenting the current continuation parameter with any actions required by the context of the current call.

    The example on that page is the canonical example of factorial in cps style in Lisp-like languages:

    (define fact-k
    (lambda (n k)
    (if (zero? n) (k 1)
    (fact-k (1- n) (lambda (v) (k (* n v)))))))

    This program maps syntactically to my Javascript example, exactly: their n is my n, their k is my fn, and their v is my p.

    To further demonstrate the apparent "continuation-ness" of the Javascript implementation, if you change the line in factfn() which reads "return fn(1)" to simply "return fn", factfn() will now return an unevaluated function to its caller. That function encapsulates the factorial of the value of n specified by the caller. You can then evaluate it, after the factfn() function has returned. The calling code would then look like this:

    aFacFn = factfn( 5, function (x) { return x; } );

    document.write( aFacFn( 1 ) );

    When aFacFn(1) executes, it returns 120. It happens to arrive at this result by recursion. Nevertheless, the values of n which it uses internally come from function contexts (what that Python page is referring to as "frames") which are not (or no longer) on the traditional call stack. Since functions are first-class objects in Javascript, it pretty much has to support the concept of a frame detached from the call stack, and it does.

    Having defended my position, I will say that no-one should seriously try to learn about these things using Javascript, or Python for that matter. When learning, it's much easier to work with these concepts in the context in which they're most effective, i.e. a true functional language. I picked Javascript for these examples because of its 1st-class function support and the fact that just about anyone reading this can run the code in their browser.

  • python, i way too dynamic to be tamed by a compiler. there are no static types definitions and many python programs make heavy use of the eval functionality.

    Best to wait for python3k for such endeavors when python will most likely get optional static types defintions, interfaces, static scoping, and other features that will make python compiler optimization friendly.

    jim
  • Tail recursion and iteration are equivalent, computationally, but the difference between the two can be a little more than syntactical. I'd suggest reading this page [ksu.edu], which talks about tail recursion and cases in which recursion is preferable. A relevant quote:

    So why would you want to write a program recursively when you can write it using a loop? Well, the main answer is that recursion is a more general mechanism, so it can express some solutions simply that are awkward to write as a loop. Some programmers also feel that recursion is a stylistically preferable way to write loops because it avoids assigning variables.
  • In my other response to you, I forgot to mention that there's a potentially important difference between tail recursion and iteration: ideally, tail recursion actually simply allows optimal use of the stack, and doesn't necessarily involve recursion (or iteration) at all. It's just that the context in which it's most discussed is that of recursion & iteration. However, the tail call at the end of a function does not need to recursively call the current function: it could call something else, and the result would simply be that the stack depth is not increased and when the called function returns, it returns directly to the original caller rather than popping through a stack of intermediate calls. Think of it like a GOTO written in a function calling style with parameters.
  • Smalltalk uses too much memory. And loading the (huge) image at startup just sucks.
    Ruby, on the other hand, has good characteristics of Smalltalk, like pure oo, is faster, the syntax is sweet, you load the modules as you need them, etc. . I suggest everyone to at least take a look at it. It's syntax is very easy (much easier than Python, IMHO), and once you get the grasp, you start to just do right guesses of how to do things.
  • I think Matz tried to make things a little comfortable for Perl users, due to it's wide user base.
    But I understand that Ruby is much simpler than Python. Ruby don't have many (any?) special types, and that's a big thing about simplicity, IMHO.
  • integrating C and -Script- becomes a project in itself. I dare you to figure out tools like SWIG, or even to use the python.h files manually. You're in there for some major undertaking.

    And how, exactly, would you make it easier? In particular, how would you make it easier without sacrificing the full power and flexibility of extensions?

    I think the real problem is with terminology. It's not a matter of "inner loops" vs. "outer loops". It's about performance-critical parts vs. non-performance-critical parts. For most scripts and tools and anything else short of full-blown industrial strength applications, coding any part of your Python application as an extension module is just stupid. In those cases where it might make sense, it's really not that hard to apply standard software-engineering approaches to the design of one or more extension modules which neatly encapsulate the performance-critical parts of your app behind a fairly simple interface. I've done it several times, and been quite satisfied with the results...and it's not because I'm particularly easy to satisfy. ;-)

    I'm looking for a high-level language, regardless of its syntax that:
    (1) understands seemlessly C/C++ header files and the APIs that it exposes. No SWIGgin! (But also, no IDL or stuff like that, just direct invocation!)

    That's a pretty tall order. C/C++ interfaces aren't exactly self-describing, even as far as number of arguments in many cases, let alone type and meaning etc. There's actually a Python extension module - forgot the name - that lets you do pretty much the sort of direct invocation you're talking about, putting all of the burden on the programmer to make sure they're passing the right number of arguments etc. It turns out not to be all that useful, though, because it really only provides part of a real extension interface - the "interpreter calls X" part. A full extension interface also allows X to call the interpreter, or X to interpret or manipulate or even create complex structures - objects, lists, dictionaries, aggregates of same - comprehensible to the interpreted code, and so on. Without all of that, which is not available in the model you propose, what you have is a crippled extension interface lacking many benefits inherent in the real thing.

    2) grabs its language run-time...and nicely links it into one, single clean executable

    This sounds pretty much like what "freeze" does, or perhaps a minimally-enhanced version of it. Could you explain what exactly you want that goes beyond that?

    BTW, keep in mind that creation of a single monolithic executable is not always the goal. Many ways of doing what you describe would preclude component models or interactive use. It's just one more example of how Python represents a certain set of tradeoffs. It's not perfect for every usage, but the omission of certain features you might want might well be a conscious decision based on the fact that their inclusion might have precluded some other feature that is more generally useful.

  • by AMK ( 3114 ) on Monday November 13, 2000 @05:33PM (#626536) Homepage
    I wouldn't worry too much about overcomplexity, since GvR is quite careful in adding new features these days; for example, he's against adding continuations since they're complicated to understand and it seems likely few people will use them. Instead, it's more likely that useful abstractions such as coroutines will be added, providing the abstractions people usually use continuations to build. In fact, I wish GvR was a bit less cautious; there are several problems where I think new keywords would be the best solution, but anything that calls for a new keyword has a significant obstacle to overcome.

    As for improving module building and other things, I hope to make significant steps in that area for 2.1.

  • Couldn't something similar be done (within statement lists) with an iterative parse tree evaluator, to jump between parse nodes?

    I understand he didn't want to rewrite the core, and was working off an existing codebase, but if one were to start from scratch, it seems like that might be the simplest way to do this.
    ---

  • by Ars-Fartsica ( 166957 ) on Monday November 13, 2000 @05:35PM (#626538)
    A new, and quite good book has been released regarding Ruby, "Programming Ruby" published by Addison Wesley. Yo ucan find it at many online booksellers.

    The online docs are also good.

  • by AMK ( 3114 ) on Monday November 13, 2000 @05:56PM (#626539) Homepage
    I'm always on the lookout for languages that match Python in simplicity and power. From what I know of it, which comes mostly just from reading the docs, Ruby's the closest thing I've seen yet. However, it seems to look more at Perl for its syntax, which means it has implicit arguments for functions, magical character prefixes in certain spots, and cryptic names for some global such as $$. It's also a much younger language, and given that Matz doesn't seem very parsimonious in his design; I wonder if the interpreter will become painted into a corner by growing complexity. This tendency is hard to avoid; look at Perl or Zope for examples of the snarl that can result. On the other hand, I think Ruby's syntax and semantics may be essentially complete at this point, meaning there's nothing left to add; I don't know what Matz's thoughts are.

    In short, I think I agree with another poster's comment that instead of waiting for Perl6, you should look at Ruby instead. Not as pleasant to read Ruby code as Python code, though, and its syntax isn't as clean, so it won't be replacing Python for me just yet.

  • Ok, that's distributed processing.

    However, the implementor in the articl discusses "pickling" program state, which allows it to be stored to disk, for example. This implies that it is not using a secondary server to manage state, but somehow hoping to store all of it. My comment was basically that this seemed impossible, because some state is stored in kernel space and only accessed by a handle.

    But I can see how my comment about DSM was misleading. Sorry about that.

  • Both Vyper and Stackless Python offer some extremely cool features that I would definitely like to see in CPython.

    Python 2.0 saw the introduction of assignment-operators ( +=, -=, *=, etc.), and list comprehensions (a quick steal from Haskell). Both of these are cool, but there are still nagging usability problems:

    The fact that lambdas are fundamentally broken because you cannot do anything really powerful with them. Adding lexical scoping to python would alleviate this problem, and let me do nifty 'map' and 'filter' stuff in python a LOT easier.

    And continuations are damned powerful. Right now you can hack up a semblance of continuations using the fact that you can divorce instance-methods from their instances, and use them as normal methods, which means methods can have a static state, and use a 'if/elif/else' statement block to choose a continuation.. but hat is a horrible hack.

    Python is an excellent language, but the cool thing about it is that there are so many obvious things that need to be fixed, it can only get better :)

    -Laxitive
  • TCL, Python, and Perl were not the products of someone who knew anything about language design. They were more like on-the-job training exercises for less than qualified individuals

    Wrong. John Ousterhout, creator of Tcl, is a CS professor. Guido van Rossum, creator of Python, is well versed in language design. You may not like their decisions - I personally detest Tcl - but I would bet either of them is about 10x more qualified to design a language than anyone here.

    Larry Wall, on the other hand, has no discernible background or ability in language design, and it shows in Perl.

  • by _Quinn ( 44979 ) on Monday November 13, 2000 @02:55PM (#626543)
    Stackless Python is supposed to let you wrap up the program and its state, and send it to a friend who can continue where you left of. Sounds alot like process migration in a system like MOSIX, to me. Could Stackless Python be set up as a daemon and auto-migrate based on load?

    -_Quinn
  • When you have programmed using a functional language (Scheme for me) and used this way
    of working it takes a bit to get back to Java/C!

    The basic idea for those of you who are confused is to use recursion and get away with not using the stack for the calls. How you might ask... well first of all its easy when you are using a language like Scheme/Lisp which treats functions as first class data members! There is no difference b/w (well almost none) b/w data types and functions, each can be passed into a function and returned!

    SO Normally when you make a recursive call you are just pushing down your current data into the stack and when you reach the base case you go back up the stack!
    In case of Continuation Passing Style the idea is really very very neat! You basically make a recursive call and pass a function to that will be able to compute the answer with the data it needs! There is no stack. For example (Factorial of course)

    (define fac
    (lambda (n)
    (if (
    Note you had to keep the info of where you were so you can recurse back doing the multiplications!
    This data went into the stack!!! Now if you want to do this without stacks you can use a method that you build each time you go through the loop and so you build the answer on your way!!!

The optimum committee has no members. -- Norman Augustine

Working...