Hacker News new | ask | show | jobs
by derefr 4254 days ago
A lot of people are recommending IDE-like tooling--but in truly dynamic language (one with a "living image" with path-dependent monkey-patched behavior that can't be replicated during static analysis, like Smalltalk--or, sometimes, Ruby) there's a more idiomatic way.

In a dynamic language, if you're at all unsure of what code you need to write, then you don't write it in your editor in the first place. Instead, you build the expression you need, interactively, at the REPL—and then, once it works, you paste that into your editor.

In dynamic languages, the "dead" code in modules is effectively a coagulation of previously "live" REPL incantations; to trust code that was written "dead" to work correctly "live" is madness (or just a recipe for really long iterations involving unit tests.)

If you take this approach far enough, though, you do get a sort of IDE—something that manages your expression experiments and the context harnesses they need, and re-runs experiments when you edit their dependent formulae. I am, of course, talking about "notebook" interfaces like IPython's.

5 comments

And with Ruby, especially since you mention Smalltalk, we have a tool that is getting closer to the kind of live introspection and modification that Smalltalk is famous for: Pry [1].

Pry lets you call "binding.pry" anywhere in your program to dump you into a shell within that context, with full access to local variables etc.. And tab-completion and plenty of introspection features. I frequently find myself triggering Pry in the middle of handling http requests if something doesn't work, for example. Letting me inspect the environment, modify stuff, and when I exit the request is completed.

It can also do things (with some limitations) like bring up an editor to where the current method was defined, and let you edit and reload the code.

And you can attach to it remotely using Drb in case the app in question doesn't run attached to a terminal.

At this point it's almost criminal to do Ruby development without Pry.

[1] http://pryrepl.org/

I second Pry. It's amazing how fast you can use AR to add a little add-hoc business logic on-top of a DB and then drop a console with Pry to get a sort of custom interactive command line DSL :)

TBH if I was writing anything large enough for this kind of stuff to get out of hand, I would seriously consider a statically typed language like Golang or C#/F#(which both have absolutely fantastic tooling). REPL's don't help the next person to come along figure out what your methods are taking in and what those calls return. But I digress.

I don't always cut Ruby, but when I do I cut it with Pry... Yeah.

In pry is it possible to return something when I exit the REPL? Like this:

  def do_something
    return binding.pry

    do_something_but_does_not_work
  end

  result = do_something
  process result
It might be very convenient if I put 'return binging.pry' just before the broken code. I can interactively fix the broken code and continue run outside of current method.
If you are explicit: "return" in a Pry session will exit pry and return the value you pass to it, just as if you are in the method itself. So e.g. "return 42" would return 42 from do_something.
Pry changed my life for Rails development. Before it I was still using IRB and attempting to just log things to the terminal constantly to figure out what I could do. Pry has made me 2x as fast (if not more) as a Ruby developer.
Funny that you mention Smalltalk, because of its image model, completion usually works quite well.

I remember using it on my Smalltalk days at the university in 1996.

It works great if either A. The IDE lives inside the same live image the project being developed is loaded in; or B. there exists some sort of network/RPC-exposed "code server" running within the live image, which you can ask to do these sorts of completions.

Of non-Smalltalk languages, the only one I can think of with anything like that is Erlang. You could easily enough build a code server in Ruby, but you'd have to explicitly include the library that implements it in your project, and start up a server thread to expose it to an IDE that wanted to talk to it, etc. And because of that, it wouldn't be "part of the language" to the point where tooling (like IDEs) would be built to expect it.

The real problem, though, is that people are coding in dynamic languages with no live image connected to the editing session at all: instead, the code is just dead text until they want to test it, at which point it gets injected into a fresh session, run once, and then the session is immediately discarded (creating what is possibly a completely different path-dependent monkey-patch execution sequence than would happen in production, or in a REPL, or...).

For some languages this happens out of necessity, but for most, it's just an artifact of the batch-processing mentality. All e.g. Light Table gives you, when you think about it, is a text editor with a connection to a live Clojure or Python image; and yet to many it seems to be a completely foreign interaction paradigm for programming.

> You could easily enough build a code server in Ruby, but you'd have to explicitly include the library that implements it in your project, and start up a server thread to expose it to an IDE that wanted to talk to it, etc.

Take a look at Pry's remote sessions: https://github.com/pry/pry/wiki/Remote-sessions

Yeah, I can't help but think we've reinvented the wheel. Modern dynamic languages like Ruby, Python, JS, Clojure, are just now catching up to the state-of-the-art that Smalltalk achieved in the 1980s.
Yes you are quite right.

The image must always exist, either a real one like Smalltalk, or a virtual one like in your examples.

I think it is more of an issue for those that have decided to live outside an IDE though, as they usually have good support for such live sessions.

For example, Netbeans support for JavaScript and JRuby is quite good.

Is it possible that when you try and recreate your image from scratch, the code you have pasted into your editor is not sufficient to recreate the image?

Maybe you pasted some things in the wrong order, or maybe you redefined a function, but some data that was created using the old version of the function is still in the image.

If this is possible in your programming environment, then maybe the dead code is not worth keeping around, and we should only retain the live image.

In which case we need tools to version and diff and branch a live image.

Does a programming environment like that exist I wonder? Or do image based environments like Smalltalk let you export an image as text, like a DB can dump all the SQL required to recreate it?

The creators of Smalltalk encountered the difficulties you're thinking about while they were using Smalltalk for different projects at Xerox PARC in the '70s -- and addressed them.

Simple Smalltalk implementations are in 4 parts: the Smalltalk VM, the Smalltalk image file, the sources file and the changes file.

The image contains compiled code and objects, the sources file contains source code for some of the compiled code in the image (at least the source code provided by the vendor), and the changes file contains a log of changes made using the IDE.

Re-doing the changes recorded in the changes file should be sufficient to recreate the image up-to the last change.

In the late '80s multi person configuration management systems were developed for Smalltalk, see --

http://books.google.com/books?id=odx8WIDOcyIC&lpg=PA136&ots=...

Yes. Smalltalk environments export an image that's a snapshot of the entire state of the VM. It's like a reloadable core dump of the VM. It's a binary though, not plaintext. But there is also a sophisticated distributed versioning system for Smalltalk code, which can save versioned snapshots of slices of your code and state that can be imported into different images: http://wiresong.ca/monticello/
I like this approach, and I think that we're not doing it enough.

Does anyone know an effective way to do this in the browser, with JavaScript?

See http://www.lively-kernel.org/

Incidentally Dan Ingalls was a member of the original Smalltalk project.

I do this a lot in the JavaScript console. If I need DOM interaction, I'll usually just mock-up some HTML and access it via the file:// protocol.
"something that manages your expression experiments and the context harnesses they need"

This is the exact purpose of tests.

No: tests don't change, especially iteratively. If you're editing and re-running your tests in a loop (rather than editing and re-running the code in a loop against the tests), then your tests aren't doing anything related to "testing", because you're changing their pass/fail criteria with each edit. To do so is just to abuse a test suite+test runner as a REPL.

On the other hand, once you know what you're doing, the expectation can be codified into a test (dead code) to assert that the live code maintains an equivalent property with no regressions. A "finished" IPython notebook can indeed be replaced with a regression test to ensure the behavior remains the way you previously determined it to be via experiment. (Though note that this is subtly different from functional testing: you're not asserting any preconceived notion of how an API "should" respond; you're just asserting that the API seems to hold to a certain contract—the one you discovered from your experimentation—and that it's a regression if it then goes against any of the parts of the discovered contract that you had come to rely upon.)