Hacker News new | ask | show | jobs
by Veedrac 4130 days ago
I was curious about this, so here is the result of my digging.

`core` has inline lambdas in Ruby:

    :prn =>       lambda {|*a| puts(a.map {|e| _pr_str(e, true)}.join(" "))},
but separately defined ones in Python

    def prn(*args):
        print(" ".join(map(lambda exp: printer._pr_str(exp, True), args)))
        return None

    ...

    'prn': prn,
Note that `return None` isn't needed - Ruby doesn't have it, so that's a "wasted" line. The function should also be more like

    def prn(*args):
        print(" ".join(printer._pr_str(exp, True) for exp in args))
or, if Python 2 support wasn't needed,

    def prn(*args):
        print(*(printer._pr_str(exp, True) for exp in args))
Defining these out-of-line is a good idea, though (it improves introspection in Python).

`mal_readline` has a bit of support for Python 2 which takes a few lines, but mostly it's code like

    try:
        with open(histfile, "r") as hf:
            for line in hf.readlines():
                pyreadline.add_history(line.rstrip("\r\n"))
                pass
    except IOError:
        print("Could not open %s" % histfile)
        pass
versus

    File.readlines($histfile).each {|l| Readline::HISTORY.push(l.chomp)}
The Python code handles errors but has two pointless `pass` statements and a couple of odd choices. It should better be:

    try:
        with open(histfile) as hf:
            for line in hf:
                pyreadline.add_history(line.rstrip("\n"))
    except IOError:
        print("Could not open %s" % histfile)
Note that the Python uses lazy reading which is why it needs explicit closing; Ruby likely would too if it read lazily.

I have no idea what's up with `mal_types`/`types`; Ruby does simple stuff and then Python does... something. I will say that the Python can be a lot simpler at minimum, although I'd need to grok what it does before I can say what. For example,

    elif _hash_map_Q(a):
        akeys = a.keys()
        akeys.sort()
        bkeys = b.keys()
        bkeys.sort()
        if len(akeys) != len(bkeys): return False
        for i in range(len(akeys)):
            if akeys[i] != bkeys[i]: return False
            if not equal_Q(a[akeys[i]], b[bkeys[i]]): return False
        return True
can be replaced with

    elif type(a) is Hash_Map:
        return a.keys() == b.keys() and all(equal_Q(val, b[key]) for key, val in a.items())
I think the `_Q` suffix is a the writer missing Ruby's `?`-suffixes on identifiers.

There's also a lot of mess that I think arises because the writer might not be aware that you can make callable objects in Python.

I think in the end it comes down to language experience. My Ruby would probably look as hacky as Joel Martin's Python. I doubt there would be as large a difference if they were both written with the same language familiarity.

2 comments

I guess it's really hard to be idiomatic in 23 languages.
Python is one of the oldest implementations and I ought to do a another pass through to clean it up sometime.

I'm happy to take pull requests that make an implementation shorter and/or more idiomatic as long as it doesn't change the overall order/flow (and doesn't break any of the tests of course).