Hacker News new | ask | show | jobs
by prairiedogg 4254 days ago
I've been pairing with ruby / rails developers since 2010 and coming from statically typed languages, it's unbelievable how much time gets spent playing "guess the method name". Even in rich IDEs like RubyMine, the utter lack of context in any given file in rails leaves programmers typing their best guess of a method name, running the tests, rinse, repeat.

This solution, while creative and laudable, solves a problem that shouldn't exist. It should either be solved by IDE/tooling in a dynamically typed language or by the compiler in a statically typed language. Stop guessing and let your tools do the hard work of remembering method names for you.

11 comments

I stick a 'binding.pry' (pry gem) in the body of the method I'm trying to write, and I trigger it with a test that has all the correct parameters (i.e. the happy path).

I then write my code in the REPL. I can use 'ls' to look up available methods dynamically. Complex ActiveRecord queries are much easier to prototype. Finding the right method isn't usually too difficult. And pry supports prefix completion at the first level. Assign the result to a variable, and you can do more completion at the next level.

For loops and if-branches, I typically take the bit of data that is relevant to the loop body or that if branch, and work with it separately, to develop that branch. But it's more usual for me to use map and select than explicit loops in any case.

I copy each finished line of code from the repl into my editor, as and when I'm happy with it.

This method of working is also why I don't see any benefit from using RubyMine. I use emacs, and primarily I rely on global project regex (using helm-git-grep and helm-projectile) to navigate the system.

(A happy coincidence is that the default readline key bindings are emacs style, so a lot of what you learn in emacs maps straight over to the repl terminal.)

I actually follow a very similar pattern when developing in both Ruby and Clojure. I particularly like it in Clojure because I can run `(doc function-name)` to get some documentation on the function I intend to use (I can never remember the order of arguments!!).
The pry equivalents are show-doc and show-source.

They don't always work depending on configuration, but it's very nice when they do. They can even go far enough to show the C source for builtins.

I do this a lot in the ipython REPL. You can type "method_name?" and see the docstring and some other basic info about the method (like args), its extremely helpful.
It's a hard problem to solve. In general inferring which methods are valid on any given variable or expression is undecidable. So you have to make an approximation, which is hard and the end results usually still disappointing. Here's a little test case:

    def foo(x): return x
    
    a = foo("hello")
    b = foo(34)
To infer the methods that you can call on `a` and `b` you have to trace through the `foo` function. You can't just run `foo` because what if the input is unknown or what if `foo` contains a loop or is recursive?

    def foo(x, i):
       if i == 0: return x
       else: return foo(x, i-1)
    
    i = int(read_input())
    a = foo("hello",i)
    b = foo(34,i)
It gets even worse when you have a huge codebase, monkey patching, eval, and other features like that (which Rails uses in copious amounts).
> In general inferring which methods are valid on any given variable or expression is undecidable.

And yet, Hindley-Milner can do it. In general.

I've been working with dynamic and nontyped and weak typed and duck typed languages for over half of my life and you know what, it's getting ridiculous. They were never meant to support the team and project sizes we're forcing them into now.

Hindley-Milner and other type systems do it by restricting the class of programs you are allowed to write. Essentially what they do is if they can't infer the type of a variable they terminate with an error. The class of programs that you are allowed to write is still reasonably big and Turing complete, but as research to make the type system more expressive shows, people want to go beyond that (higher kinds, GADTs, dependent types, polymorphic variants / structural records, subtyping, etc.). Type inference for a dynamically typed language can't terminate with an error if it can't infer the type, it has to be conservative in the other direction: if it can't infer the type of a variable it must assume that it can be anything.
This was helpful! Now I have a bunch of new things to research and learn. Thanks!
Of course you understand this, but for the benefit of the audience:

HM can do this because it dramatically restricts the allowable complexity of every single name in the program. This is rarely a problem, though, as typically you, as a programmer, only ever intend for names in the program to be so complex. The result is that HM simply ensures that your program is one which makes enough sense all of the time.

Which is sometimes painful for people when they first start.

When I code in Python I always have ipython console handy to try snippets of code and to autocomplete methods and fields.
I also work that way. I use the autoreload feature of ipython so that any code I change in my editor is instantly replicated in my ipython objects. I've really never been more productive in development in my life.

Contrast with some Java I was doing recently (granted, without a Java IDE) and I found the whole cycle painfully slow. Even things the compiler picked up were a magnitude slower to find than when I was in ipython. And then there were plenty of things the compiler wouldn't get, which could take a good minute for me to discover from the time I made the edit. It's insane to me that people don't work in a REPL.

"without a Java IDE"

This was your problem. Programming Java without an IDE that recompiles your changes on the fly is just so arcane and really slows you down as you noticed.

Which is great, but that's almost not the main issue. In the ipython REPL I can work with my code and data at the same time. It's a huge disadvantage to not be be able to probe your data to create a working process.
The Java VM has hot code swapping builtin (IIRC limited to method bodies but the commercial JRebel drops that limitation).

Start your program in your IDE's debugger (or point it to your application server where your program is running) and you can change the code immediately, rerun your methods, change variables, inspect the objects, etc.

It's more than a simple REPL, it's a full-blown debugger and it comes really close to what you have in a SmallTalk image where your code, data, and environment is all the same.

So true. Java was designed for tooling. Using it without tooling is actual using it against its design.
Also for J2EE you need jrebel.
Would you mind making a little screencast demo of your workflow? (Or do you know of any?) That sounds so cool.
Sure. I'm not near my machine for a couple of days but I'll post something when I get the chance. I see your contact info is in your profile so will ping an email when done.
UPDATE: video here https://www.youtube.com/watch?v=k-mAuNY9szI

Sound quality isn't great but hopefully it demonstrates what I'm talking about.

Me as well, if you're offering.

I find iPython interesting but haven't figured out how to integrate it into my current workflow of Sublime/Terminal.

Thank you very much! I look forward to it!
I'd be curious to see this as well
Ping me too, please. Thanks.
I do something similar with JavaScript and code functions in a REPL and then move the code into my actual file as soon as I get it working. Usually I do this with Google Dev tools console.
Second that. That's why ipython is extremely handy. Also function/block wise history.
It's kind of silly, the major benefit of using Ruby (or JavaScript nowadays) is to iterate quickly. But then, some of their users spend months turning a text editor into an IDE just to waste precious hours on spelling errors.
Most text editors have naive autocomplete (using a words list and words they see), which catches 99% of these errors.
I have experienced some problems with this kind of autocomplete in a JS project.

-Two methods shouldn't have a similar name, so you try to avoid it, possibly leaving you with a worse abstraction.

-You usually need to have all files open or at the same location, so modularization is discouraged.

-Libraries are usually not picked up by it.

-If you make a spelling error on the first usage you now have this spelling error everywhere.

Naive autocomplete is much better than none, but it's still not in the same league as context sensitive autocomplete.

YMMV, but I don't experience any of those problems. Also, I don't see where same names pose any problems.

Most advanced language plugins pick up libraries.

For example I use pabbrev mode in emacs, which learns what I type. It hints me away from most misspellings because it suggests the correct alternatives before I type them. Most of the long method and variable names for me are a prefix plus a TAB.

Nevertheless I'll try this gem.

I have exactly this frustration as a person coming from static languages (mostly C++) to web dev. Anybody can recommend something more comfortable? I like the concept of Vaadin, but I'd prefer something non-Java.
I'm personally very happy with golang. We are a python shop with some bit of Java on the backend and we decided to try golang to get past a few python performance hurdles. So I wrote a smallish API/data importer as a first application. The results have been fantastic. Nearly as productive as python, better in my opinion support for a functional paradigm, much faster, and of course really easy and great concurrency support. Sure python and django have a good bit more tooling and third party libs but go is gaining ground. One major hurtle I faced was the lack of a good visual debugging IDE. I'm using the go plugin for Intellij but it's not really that good. If go keeps gaining popularity and Intellij releases first class support, like they have with python ruby and node, it will really help the language ecosystem.
Scala is a wonderful language for web dev, particularly if you've found a JVM library you like (the interop is very good). The conciseness and readability (if you stick to the right libraries) of Python with the safety and IDE support of Java. (Can't speak to Vaadin, I'm a big Wicket fan myself)
Try Go. It is a lot like C and compiles to concurrent machine code. It is also very easy to learn and use.
Er, you mean to say "compiles to machine code." There is no such thing as concurrent machine code... Now the Go language has some fantastic data structures baked-into the syntax that provide a language with which to construct concurrent programs.
Go has some potential (I'm still waiting for Gen/censored/ics :)). Also, I'm particularly interested in components based web frameworks, and there seems to be none so far - last time I found GWT like library for Go, but it is undeveloped/dead I think.
If you're coming from mostly static languages, then this is a good choice for web dev: https://golang.org/doc/articles/wiki/
ASP.NET maybe? C# is one of the best-designed languages suitable to server-side dev in modern popular use. Using it with VS is the most convenient and efficient IDE experience I've encountered.
I actually enjoyed WebForms ~10 years ago. But for personal SaaS projects, free/cheap hosting space seems somewhat unrepresented. And I really don't want to go Mono-way...

As for ASP.NET itself - nowadays it seems ASP.NET MVC is recommended, but I find it way too bloat.

Dart has great autocomplete.
Context sensitive auto-complete is a godsend.
Seriously. Is there a good list of tools that provide it? In Python, the only one I've managed to love is PyDev. Supposedly Atom does it with the "script" package?
I've been working with PyCharm [1] a lot lately and I've come to like it a lot. Probably the best context awareness I've seen for any dynamic language - it (almost) feels like working in something more tool-friendly like Java in that regard.

[1]: https://www.jetbrains.com/pycharm/

PyCharm's auto-complete is really nice. It seems to be the best Python editor going right now.
Really? I used Aptana, which includes the Pydev plugin. I notice everyone seems to give PyCharm lots of love, and have been tempted to try it. Autocomplete is one of the features I rely on heavily.
I used Eclispe/Aptana/PyDev at a previous job and it was pretty nice, but PyCharm is superior in my opinion. In addition to great completion and code navigation, it also gets Vim emulation mostly right. PyCharm is also a mostly out-of-the-box experience, whereas it took a while for me to get Eclipse set up just right.
Admittedly I haven't tried every editor, but it seems nicer than most.
For Python I use jedi.el for Emacs (http://tkf.github.io/emacs-jedi/latest/). There's also anaconda-mode (https://github.com/proofit404/anaconda-mode).
Jedi is really good. There are plugins for Vim (of course) too.

Personally I believe that things like Jedi - external static code analysers in form of a library - are what we should be doing. It's not good for IDE writers - it lessens vendor lock-in - but for the users it's a win. Having the same, very good, completions in both Vim and Emacs made my life much simpler.

I use Intellij with the PyDev plugin as my IDE and Atom (with the script package) as my text editor. In general unless it's a really quick edit PyDev is MUCH better in the general IDE functions, like autocomplete, jump to definitions, visual breakpoint debugging, etc.. I love atom but Intellij is much better for serious dev/debugging sessions.
Yeah, that has also been my experience.

Why Intellij instead of Liclipse?

I really REALLY dislike eclipse. In fact it would nearly always be my last choice for and ide. In my experience it's slow, buggy, bloated, and in general the features that I need work FAR better in Intellij, Visual Studio, Atom, Sublime Text 2, Emacs, or VIM. I've used pretty much all of them and I would rather go back to them than use an eclipse based project.
Netbeans JavaScript support is quite good, and was originally based on JRuby support.
PyScripter supports it, but it's Windows only and seems abandoned by the devs.
It's not a problem that can be solved for a language like ruby without adding type hinting (and probably interfaces) to the language. FWIW php actually does support this.
You don't need (static) type hinting, you can do the same with running a dynamic environment and querying it. That's how it's done in Smalltalk (see Pharo), some Lisps and so on.
You could guess, but it wouldn't work in cases where, for example methods are added to a class dynamically and non deterministically as a side effect of some other operation.
Do you know how "image based" environments work? In short you live in the same space as your code, so when you write some new method you have access to all the "dynamically and non deterministically" code artifacts. With Smalltalk the code is always being run and you get many useful tools for querying the state of a running system. I know it's hard to believe, but you can get much better completion (not to mention refactoring support) with this approach than you get from static analysis.
What if the available methods are potentially different each time the code is run?
Which becomes ironic because you end up writing way more text into the file for phpdoc then you would have if it were a static language to begin with; and you end up treating the variables as static type anyway! :|
PHP5 has real type hinting in the language, so you don't need to write any more than you would in Java.
And soon it'll even have return typing, should that RFC pass.
phpdoc handles much more than just typing function parameters.
By far the most common usage is parameters and return values. Even moreso than comments.
RubyMine is pretty damn good, especially because you can immediately jump to the source code, even of libraries, and just read the list of available methods. It's amazing to me how completely Dwarf Fortress -- um, I mean vim and tmux -- has conquered the Ruby dev world.
The tool (Ruby) does the hard work: foo.methods. Writing some code, running the tests, rinse, repeat. That describes the REPL way of doing things. Also, the way I prefer to code. Do something, examine the results, do something else.
I personally also find ruby to have uniquely poor naming - lots of similar methods with overlapping functionality, no consistent style, etc.

That's not to say it's a bad language per se, but as someone who works in lots of different languages, I find ruby to be the one where I'm most likely to fail to guess correctly and have to spend time looking up the method I want.

Since it was written by a Japanese fellow originally i think that's where the unique names came from. Upcase and downcase were two that stuck out to me. Overall I like ruby quite a bit though.
This is honestly an issue I don't experience, if I have a typo my test fails and the compiler tells me the line and method call that failed. I correct it, carry on. I can't think of a scenario where you would get stuck with this type of problem.
Compare this with an IDE highlighting this immediately when you write the offending line. In your workflow you have to run a test to identify this issue, then go back and fix it. It certainly doesn't leave you stuck but you are incurring additional overhead in that you actually have to run tests to discover there's a problem and then remediate it.
It's a marginal overhead and it also has the benefit of encouraging clear naming. Autocomplete is convenient but also removes some of the cost of poor naming. This is fine when you're writing code (the autocomplete just kicks in and you search for a method that seems about right) but it does nothing for the readability of your code.
I would argue that readability and writability of class members are not the same. Many time verbosity will improve the readability but hurt the writability of the code, and auto-complete reduce the cost of this verbosity.