Live data from Hacker News

Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

cemerick.com

131–140 of 141 posts

Re: Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

#131
post #129

Earlier quoted context omitted.

I don't get why people hate on starting with JavaScript. Since ES6, JS strikes a really nice balance between practical usage and theoretical value. Like Scheme it's a dynamically typed language focused around a single data structure (list for Scheme, object for JS), with first class functions. Sure, it has weak typing and there's some scoping complexity. But, that's a completely reasonable tradeoff for being one of t…

Because programming should be descriptive to what you want the computer to do , and the way that humans explain things to each other is usually linear. For instance: brush your teeth, then put on your clothes, then get in the car, then start it. The "JavaScript" way to do this is that starting your car is somehow nested inside of the brush your teeth event. Everything is a callback of everything else, so trying to ex…

Modern JS fixes this (mostly). Promises and async/await (ES5 and 6) address those issues. What was previously

    function first(cb) {
        console.log('first');
        cb();
    }

    function second() {
        console.log('second');
    }

    first(second);
can now be expressed as

    new Promise().then(console.log('first'))
                 .then(console.log('second');
or

    async function first() {
        console.log('first');
    }
    async function second() {
        console.log('second');
    }
    async () => {
        await first();
        await second();
    }();
depending on exactly what your goals are. The inversion of order ("callback hell") can be avoided if you stick to modern concepts.

Re: Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

#132

Earlier quoted context omitted.

> But Python encourages the direct opposite - writing long files of code to avoid introducing too many "modules" and this is a debilitating headache. How does Python encourage mega-files? AFAIK there's nothing that prevents nor discourages proper organization of code into files. I frequently use multiple files to organize my library implementations.

Just some clues: A class definition can not be split in many files (e.g. in C# you can do this easily with "partial class" declaration and that's amazing, needless to say you can and almost always do split one module (a namespace) into many flies there). Whatever you put in a separate file you will have to import manually everywhere you use it and you have to care about circular imports. You have to invent names all…

>A class definition can not be split in many files (e.g. in C# you can do this easily with "partial class" declaration and that's amazing, needless to say you can and almost always do split one module (a namespace) into many flies there).

I think most would consider this a good thing. WYSIWYG. If a class is so gargantuan as to require multiple files, it should probably be multiple classes that communicate via a well defined, public API, not implicitly.

>You have to invent names all the time, a package, a module and a function having the same name smells a headache.

This is only an issue if you are importing *. If you import in a namespace (`from foo import module`, `from bar import other_module`), you can have `module.module`, `other_module.module` and anything else all living in harmony. If you work in python as python intends and not C/C++ (namespaced imports, not text-prepended ones), it's much, much cleaner.

It's also possible to, generally speaking, have private submodules within one larger module. You can do this with `__init__.py` by importing the various submodules, explicitly re-exporting the things you wish to be top-level public, and setting `__all__` to include them. This is why I can do something like `import numpy as np; np.ndarray` even though ndarray is defined in an extension module referenced by `numpy.core.multiarray`.

See multiarray (https://github.com/numpy/numpy/blob/master/numpy/core/multia...), which pulls things from `numpy.core._multiarray_umath`, then exported by core via `numeric` (https://github.com/numpy/numpy/blob/master/numpy/core/__init...), then again by the top level __init__ (https://github.com/numpy/numpy/blob/master/numpy/__init__.py...).

Re: Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

#133
post #129

Earlier quoted context omitted.

Because programming should be descriptive to what you want the computer to do , and the way that humans explain things to each other is usually linear. For instance: brush your teeth, then put on your clothes, then get in the car, then start it. The "JavaScript" way to do this is that starting your car is somehow nested inside of the brush your teeth event. Everything is a callback of everything else, so trying to ex…

Modern JS fixes this (mostly). Promises and async/await (ES5 and 6) address those issues. What was previously function first(cb) { console.log('first'); cb(); } function second() { console.log('second'); } first(second); can now be expressed as new Promise().then(console.log('first')) .then(console.log('second'); or async function first() { console.log('first'); } async function second() { console.log('second'); } as…

Okay, in python this is:

     print "first"
     print "second"
I mean...imagine explaining your example to somebody. That's difficult for some programmers to fully understand.

Re: Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

#134
post #133

Earlier quoted context omitted.

Modern JS fixes this (mostly). Promises and async/await (ES5 and 6) address those issues. What was previously function first(cb) { console.log('first'); cb(); } function second() { console.log('second'); } first(second); can now be expressed as new Promise().then(console.log('first')) .then(console.log('second'); or async function first() { console.log('first'); } async function second() { console.log('second'); } as…

Okay, in python this is: print "first" print "second" I mean...imagine explaining your example to somebody. That's difficult for some programmers to fully understand.

No. In python(3.7+) this is

    import asyncio

    async def first():
        print("first")

    async def second():
       print("second")

    async def _():
        await first()
        await second()

    asyncio.run(_())
prior to python3.7, python didn't have similarly clean asynchronous programming tools. With python 3.5+, you could use the old event loop syntax [1] to do it, and prior to that, you needed to use `yield` and `yield from` for similar semantics.

If you want synchronous stuff in JS, this suffices:

    console.log('first');
    console.log('second');

EDIT:

The problem that JS had (prior to promises in ES5) was that the only way to do deferred/asynchronous things (like "run this after I get data from a network call") was to provide a callback, something like

    function(resource_url, callback) {
        data = get(resource_url);
        callback(data);
    }
This gets very trick very fast if you want to have chained calls (imagine that `callback` also conditionally requests a resource, and you want to do something with that resource, and based on that you may want to redraw the DOM and then...).

There wasn't a good pattern for describing that in JS. The "common" pattern was to just have callbacks within callbacks, and unlike in python you'd often use anonymous functions, so you end up with nested anonymous functions which inverts the way you think, it's really hard to grok.

Promises and async/await linearize that, but that's a problem that python never really had to address because up until very recently, python didn't get used for async stuff.

[1]: https://docs.python.org/3.6/library/asyncio-task.html#exampl...

Re: Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

#135

Earlier quoted context omitted.

> Compared to C, Python is the better choice. However, it seems like you haven't learned a Lisp dialect. I would love to know if you would still prefer Python once you know Scheme. the school I went used to teach C, Python and Scheme in the first year (nowadays it's C, Python and Racket). I don't think I remember more than one or two people actually liking the LISP experience, how bad it was when comparing to other l…

The language known as Scheme has done a lot of damage to Lisp's image. When undergrads are exposed to Scheme, they tend to forever carry a negative image of Lisp by association. A lot of the time when you meet someone who had a bad experience with Lisp, if you interview them a bit, you soon discover it was actually Scheme. Scheme twenty years ago, R5RS was even worse than now. It had nothing practical in the spec. No…

I agree that there's a problem of people (even the people who rave about how enlightening it is) misunderstanding that a toy Scheme interpreter is what Lisp is, but I don't think that's an issue with Scheme. Introductory FP classes that use Common Lisp don't leave any better a taste in students' mouths, and real world Scheme is a lot more capable than a toy implementation designed for pedagogical purposes. I don't think the standard's conservatism is very much a factor.

Re: Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

#136

Earlier quoted context omitted.

Just some clues: A class definition can not be split in many files (e.g. in C# you can do this easily with "partial class" declaration and that's amazing, needless to say you can and almost always do split one module (a namespace) into many flies there). Whatever you put in a separate file you will have to import manually everywhere you use it and you have to care about circular imports. You have to invent names all…

>A class definition can not be split in many files (e.g. in C# you can do this easily with "partial class" declaration and that's amazing, needless to say you can and almost always do split one module (a namespace) into many flies there). I think most would consider this a good thing. WYSIWYG. If a class is so gargantuan as to require multiple files, it should probably be multiple classes that communicate via a well…

> If a class is so gargantuan as to require multiple files

IMHO anything that doesn't fit in one screen is "gargantuan"

> If you import in a namespace (`from foo import module`, `from bar import other_module`), you can have `module.module`, `other_module.module`

Which looks ugly and feels a nasty headache if you want to maintain intuitive vision of your code and dependencies structure.

> It's also possible to, generally speaking, have private submodules within one larger module. You can do this with `__init__.py` by importing the various submodules, explicitly re-exporting

I know but even reading this paragraph hurts. Too much mess to manage manually.

Re: Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

#137

Earlier quoted context omitted.

>A class definition can not be split in many files (e.g. in C# you can do this easily with "partial class" declaration and that's amazing, needless to say you can and almost always do split one module (a namespace) into many flies there). I think most would consider this a good thing. WYSIWYG. If a class is so gargantuan as to require multiple files, it should probably be multiple classes that communicate via a well…

> If a class is so gargantuan as to require multiple files IMHO anything that doesn't fit in one screen is "gargantuan" > If you import in a namespace (`from foo import module`, `from bar import other_module`), you can have `module.module`, `other_module.module` Which looks ugly and feels a nasty headache if you want to maintain intuitive vision of your code and dependencies structure. > It's also possible to, genera…

>IMHO anything that doesn't fit in one screen is "gargantuan"

This is a bit of an odd definition, but sure (I've seen C++ files whose imports alone were gargantuan under your definition). I just looked through a production python application, and it there were 2 non-test classes that were more than 100 lines long. Both were relatively verbose (well commented, use type hints so argument lists use significant vertical space, etc.)

In general, it seems like you're limiting yourself to, with the necessary boilerplate, one or two functions in any file that isn't "gargantuan". This makes it needlessly difficult to understand the structure of your applications since you are forced to split related logic up among multiple files. This, more than screen length or the import syntax, makes it much, much harder to "maintain intuitive vision of code and dependencies structure" as you say.

>Which looks ugly and feels a nasty headache if you want to maintain intuitive vision of your code and dependencies structure.

Not at all. It's more explicit about the dependency structure (`module.method(args)` at a call site gives you a much better idea of the structure than just `method(args)`), and so makes it significantly easier to maintain an intuitive vision of the code and dependencies.

It's incalculably easier to understand dependency structure when using the `import module` syntax than `from module import Class` or `from module import *`. So much so that the former allows reliable, large scale refactorings without any runtime information. The others, as you correctly recognize, do not.

>I know but even reading this paragraph hurts. Too much mess to manage manually.

Then don't! You don't need to. It's really only necessary for truly public/widely used APIs (like numpy) where understanding the internal structure of the module is not worth it for the average user.

Re: Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

#138
post #98

Earlier quoted context omitted.

~70k today. Programming python in 92 - nice :)

@xte - I agree with you for the most part; however, I think there are some supply-and-demand considerations to be made. For example, companies who use esoteric or obsolete languages, where the workforce who knows that language inside and out is particularly small (a la Cobol programmers circa late 1990s), should expect to pay more. Where I live, because of the area being inundated with technology boot camps, there is…

If you adjust only for inflation then its about 82% cumulative 1992-2018.

Re: Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

#139

Earlier quoted context omitted.

> If a class is so gargantuan as to require multiple files IMHO anything that doesn't fit in one screen is "gargantuan" > If you import in a namespace (`from foo import module`, `from bar import other_module`), you can have `module.module`, `other_module.module` Which looks ugly and feels a nasty headache if you want to maintain intuitive vision of your code and dependencies structure. > It's also possible to, genera…

>IMHO anything that doesn't fit in one screen is "gargantuan" This is a bit of an odd definition, but sure (I've seen C++ files whose imports alone were gargantuan under your definition). I just looked through a production python application, and it there were 2 non-test classes that were more than 100 lines long. Both were relatively verbose (well commented, use type hints so argument lists use significant vertical…

> Then don't! You don't need to. It's really only necessary for truly public/widely used APIs (like numpy) where understanding the internal structure of the module is not worth it for the average user.

I mean for the library developer, not for the user.

Re: Why MIT uses Python instead of Scheme for its undergraduate CS program (2009)

#140

I love Python for a lot of things but I’ve come to realize how complex it can really be. I regularly see others write Python code that I have to fix to add robustness. I also run "pylint" or equivalent tools before committing major changes to scripts because it is quite easy to make mistakes that won’t otherwise be found right away. Some of my least favorite pitfalls: - If you call a function that happens to refer to…

>If you call a function that happens to refer to a variable that only exists in the calling code, it will work

This is not true. This code gives a NameError instead of printing "1":

  def f1():
      print(var)
  
  def f2():
      var = 1
      f1()
  
  f2()
If it can't find a name in the local scope, it will look in enclosing functions, then the module scope, then the builtins. But it will never look inside the caller's scope unless the function is defined in the caller's scope.

Python's implicit scoping can be confusing, but it's not quite that bad.

Post reply on HN