Settings

Theme

“Did you mean?” Experience in Ruby

yukinishijima.net

455 points by yuki2448 11 years ago · 186 comments

Reader

prairiedogg 11 years 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.

  • barrkel 11 years ago

    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.)

    • dopamean 11 years ago

      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!!).

      • barrkel 11 years ago

        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.

      • lhc- 11 years ago

        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.

  • jules 11 years ago

    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).
    • Swizec 11 years ago

      > 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.

      • jules 11 years ago

        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.

        • Swizec 11 years ago

          This was helpful! Now I have a bunch of new things to research and learn. Thanks!

      • tel 11 years ago

        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.

  • ajuc 11 years ago

    When I code in Python I always have ipython console handy to try snippets of code and to autocomplete methods and fields.

    • aidos 11 years ago

      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.

      • bhaak 11 years ago

        "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.

        • aidos 11 years ago

          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.

          • bhaak 11 years ago

            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.

        • spacemanmatt 11 years ago

          So true. Java was designed for tooling. Using it without tooling is actual using it against its design.

        • ajuc 11 years ago

          Also for J2EE you need jrebel.

      • sillysaurus3 11 years ago

        Would you mind making a little screencast demo of your workflow? (Or do you know of any?) That sounds so cool.

    • taude 11 years ago

      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.

    • aangjie 11 years ago

      Second that. That's why ipython is extremely handy. Also function/block wise history.

  • emsy 11 years ago

    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.

    • Argorak 11 years ago

      Most text editors have naive autocomplete (using a words list and words they see), which catches 99% of these errors.

      • emsy 11 years ago

        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.

        • Argorak 11 years ago

          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.

      • pmontra 11 years ago

        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.

  • zerr 11 years ago

    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.

    • jaegerpicker 11 years ago

      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.

    • lmm 11 years ago

      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)

    • loosescrews 11 years ago

      Try Go. It is a lot like C and compiles to concurrent machine code. It is also very easy to learn and use.

      • sirseal 11 years ago

        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.

      • zerr 11 years ago

        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.

    • jamesclonk 11 years ago

      If you're coming from mostly static languages, then this is a good choice for web dev: https://golang.org/doc/articles/wiki/

    • jimmaswell 11 years ago

      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.

      • zerr 11 years ago

        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.

    • loftsy 11 years ago

      Dart has great autocomplete.

  • rectangletangle 11 years ago

    Context sensitive auto-complete is a godsend.

    • jMyles 11 years ago

      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?

      • timothyb89 11 years ago

        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/

        • rectangletangle 11 years ago

          PyCharm's auto-complete is really nice. It seems to be the best Python editor going right now.

          • collyw 11 years ago

            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.

            • wylee 11 years ago

              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.

            • rectangletangle 11 years ago

              Admittedly I haven't tried every editor, but it seems nicer than most.

      • sjm 11 years ago

        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).

        • klibertp 11 years ago

          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.

      • jaegerpicker 11 years ago

        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.

        • jMyles 11 years ago

          Yeah, that has also been my experience.

          Why Intellij instead of Liclipse?

          • jaegerpicker 11 years ago

            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.

      • pjmlp 11 years ago

        Netbeans JavaScript support is quite good, and was originally based on JRuby support.

      • rectangletangle 11 years ago

        PyScripter supports it, but it's Windows only and seems abandoned by the devs.

  • jiggy2011 11 years ago

    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.

    • klibertp 11 years ago

      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.

      • jiggy2011 11 years ago

        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.

        • klibertp 11 years ago

          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.

          • jiggy2011 11 years ago

            What if the available methods are potentially different each time the code is run?

    • Rapzid 11 years ago

      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! :|

  • alexch 11 years ago

    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.

  • hawleyal 11 years ago

    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.

  • jfindley 11 years ago

    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.

    • weaksauce 11 years ago

      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.

  • ollysb 11 years ago

    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.

    • travem 11 years ago

      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.

      • ollysb 11 years ago

        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.

        • JamesBarney 11 years ago

          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.

derefr 11 years 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.

  • vidarh 11 years ago

    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/

    • Rapzid 11 years ago

      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.

    • gcao 11 years ago

      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.
      • vidarh 11 years ago

        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.

    • tibbon 11 years ago

      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.

    • KedarMhaswade 11 years ago
  • pjmlp 11 years ago

    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.

    • derefr 11 years ago

      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.

      • vidarh 11 years ago

        > 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

      • kyllo 11 years ago

        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.

      • pjmlp 11 years ago

        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.

  • bloat 11 years ago

    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?

    • igouy 11 years ago

      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=...

    • kyllo 11 years ago

      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/

  • skrebbel 11 years ago

    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?

  • prairiedogg 11 years ago

    "something that manages your expression experiments and the context harnesses they need"

    This is the exact purpose of tests.

    • derefr 11 years ago

      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.)

timr 11 years ago

Neat. In case you're wondering, it's implemented using a levenshtein distance algorithm:

https://www.omniref.com/ruby/gems/did_you_mean/0.6.0/symbols...

and it works by extending the NameError exception:

https://www.omniref.com/ruby/gems/did_you_mean/0.6.0/files/l...

WalterBright 11 years ago

Some years back I put this feature in both the Digital Mars C/C++ compiler and the D language compiler. It's turned out to be popular and very useful.

Compiler error messages are steadily improving from mere statements of what is wrong to suggestions for corrective action.

PaulJulius 11 years ago

Obviously this is a useful tool, and all the power to the author for finding what looks to be an excellent solution, but this line bothers me:

>>> Sometimes I wasted hours and hours just because there is one character difference. I hate it.

This shouldn't happen. Ever. This should not be a problem anymore. These are the sort of errors that we can catch immediately and should be caught immediately. From looking at the author's GitHub profile, it looks like he uses Emacs, presumably without a plugin that would give him IDE like features. I'm not going to tell him to go use a regular IDE, but it frustrates me that's we can't have those sort of tools available everywhere. (As a vim user myself I have high hopes for the neovim project and look forward to the day when it can be embedded inside a general sort of IDE.)

  • vidarh 11 years ago

    While you can catch some of these, for a language like Ruby, it's an incredibly hard problem to catch all of these errors immediately because the methods that can be called on a given named entity can not in the general case be known by statically inspecting the source, and you can not in general safely instantiate the application because even class definitions are executed and can have side effects, and the methods available can even vary based on environmental factors, such as whether or not you get a database connection, and what's in the database.

    Consider that it is a common pattern for Ruby ORM's to either use method_missing or dynamically define methods to correspond to the current (at connection time) set of columns present in your database.

    And "thanks" to the ability to monkey patch and redefining methods, even determining if something "obviously" safe like 42.to_s is not.

    There's no way for your editor to handle that unless you stand up a version of your app with an instrumented language environment and lets the editor poke around. Now that is possible with tools like Pry, etc., but it takes a lot more work to do safely (because your editor can't know if it can safely start your app).

  • jacques_chester 11 years ago

    Yuki works at Pivotal Labs in NYC. I work there too.

    We use whatever editor or IDE any given pair decides to use. In my day-to-day work I've used RubyMine (very popular in our west coast offices), Vim (popular in NYC), Emacs (popular with Mike Dalessio) and my own personal favourite, Sublime Text.

    I don't think solutions have to be either/or. Yuki's approach is simple and will cover lots of cases.

  • vinceguidry 11 years ago

    You're invoking "IDE" features like it's a panacea. In reality IDEs have only a few limited ways to catch these kinds of errors. With a statically-typed language it can run static analysis at intervals and make sure that all the messages being passed are actually defined on the objects they're being passed to. You can do this precisely because that's what static types gives you.

    In dynamic languages you can't do this because you don't know the type that any given identifier is pointing to. The type could be completely arbitrary, I.E. coming from external input. You might think, "oh, well you could just check against all possible methods", and you'd be right, except for Ruby. Ruby allows you to override what happens when you call an undefined method, (method_missing) letting you execute completely arbitrary code that lets undefined methods look just like methods. You could call car.start_and_drive_to_the_bank, and set up the Car type so that it can handle methods like that, (perhaps calling car.start, and then car.drive(bank)) along with .start_and_drive_to_the_mall and whatever. In practice that's bad Ruby, but you still have to be able to handle that, Bad Ruby is still Valid Ruby.

    So there's just no way to tell in Ruby whether any given method call is correct or not.

    I have an idea to conventionalize method_missing overrides so that they're easier to analyze. Maybe define a regular expression on the class so that the analyzer can sort out what's likely to work at runtime and which messages aren't. Not sure how well it would work until Matz manages to implement static typing as he wants to do in the future. Until you can tell whether any given identifier is likely to be of a type that overrides method_missing, you'd be running the method_missing analyzer for all types on all method calls.

  • rurounijones 11 years ago

    On the other hand, this gives a consistent experience and does not force people to mess around with plugins for their editor (if needed)

  • luikore 11 years ago

    A spelling error in http request can cost more hours if using IDE.

andrewchambers 11 years ago

The value of static typing is more and more apparent. Recently I've played with ocaml and F# and it felt great compared to Java or C++.

  • sjolsen 11 years ago

    C++ and Java are statically typed. If you're referring to dynamic polymorphism, then yes, it can be a pain in the ass.

    • andrewchambers 11 years ago

      I'm aware they are statically typed, I meant it in reference to people who hate static typing based on their opinions of Java and its relatives.

  • theGimp 11 years ago

    Aren't Java and C++ statically-typed as well?

    • cbd1984 11 years ago

      > Aren't Java and C++ statically-typed as well?

      Yeah, but not so you'd notice, coming from an ML or Haskell background. /sarcasm

      More seriously, Java and C++ (and C and Algol before them) fell into the trap of defining "data size specifications" as types: The discontinuity between "long long int" and "int" is one, as is the discontinuity between "float" and "double".

      The real type difference is between things which are semantically different, like "numeric value used to represent age in years, rounded to integral value", "numeric value used to represent age in days, rounded to integral value", and "numeric value used to represent weight in pounds, rounded to nearest hundredth". Foogol (Algol-derived) languages largely only got this when they embraced Smalltalk-style OO, and they kept their size specification pseudo-types, just to muddy the issue.

      Size specifications are useful when defining a wire protocol where the overhead of reading and writing an ASCII format with non-trivial syntax would kill your application, such that you need network-header style bits-on-the-wire protocol, and in file formats with similar constraints, but they're ultimately a low-level implementation detail.

      The way this interacts with tooling like editor autocomplete is that editors can do more for you when they know what kind of data you're passing around, as opposed to just how big it is and other trivialities.

  • jader201 11 years ago

    ... until you start trying to make your app testable.

    (I say this as a developer of primarily statically typed languages.)

inglor 11 years ago

This sort of stuff should, at least in the 'easy' case be done at the editor level. Doesn't ruby have linter tooling for this?

Still, props for the work.

  • ryanto 11 years ago

    Ruby has a linters, but this sort of problem is hard to solve with a linter in a dynamically typed language. It's even harder in a Rails environment since Rails adds many methods to your classes at run time.

    • kyllo 11 years ago

      Yeah, since it's a dynamically typed lang with inheritance and late binding, doesn't performing static analysis on Ruby code to determine whether a method is defined or not, basically require a solution to the halting problem?

      I'm under the impression that you have to actually execute Ruby code to find out with certainty where the methods it calls are defined.

      • vidarh 11 years ago

        You can solve the "90%" problem statically. My Ruby-compiler-in-progress warns of methods that have not been seen, and I may eventually add some (entirely optional) pragma to allow hinting to reduce false positives (though getting that non-intrusive will be a fun challenge).

        But you are right, the general case does require a solution to the halting problem, something which is trivial to demonstrate very explicitly:

            eval(STDIN.gets)
            42.will_i_halt?
        
        Put that in a file, and run it. Press enter, and it halts. Or cut and paste something like this, and it doesn't:

            class Fixnum; def will_i_halt?; loop {}; end; end
        
        And consider that even "require" is a practical equivalent to "eval", so trying to look for eval() calls and similar constructs and think you're safe doesn't work, unless you have a guarantee that the interpreter load paths will be the same when the code is run as what you think it'll be.

        But in more pragmatic terms, this is a real issue because many Ruby ORMs for example will add methods that depends on the current state of the databases they connect to, so many typical Ruby applications will actually validly use method names that are not explicitly defined anywhere.

    • realharo 11 years ago

      Such things are usually solved by parsing the structured javadoc-like API documentation for classes and methods in addition to the code itself. At least that's how it's done in the PHP world and it works quite well. Obviously it's not going to cover 100% of the cases, but much better than nothing.

      Plus it incentivizes you to write this documentation in the first place, which you can then instantly bring up inside the IDE from any place where the method is used.

  • syllogism 11 years ago

    Why though? Why not do it at the language level?

    When you're returning an error, you can be a bit slow. So why not do some extra computation and return a better error message?

joshdance 11 years ago

Blows my mind that IDEs and environments are not better at this. This is something that computers are good at, pattern recognition and scanning the whole file. Of course the it wouldn't work every time, an could make ridiculous suggestions, but I would love the computer to suggest something every time there is an issue. Even if it is wrong 90% of the time, if those guesses don't slow down the programmer the time saved would be huge.

  • lnanek2 11 years ago

    Java IDEs are better at this. JavaScript...well I saw NetBeans do it once and was amazed. Ruby...well it's impossible to do correctly as other posters have mentioned.

jevin 11 years ago

I think this is an amazing gem. And I'm eager to use it on my Rails projects.

On a side note, am I the only one who feels that autocomplete tend to get in the way when I'm coding?

  • ASneakyFox 11 years ago

    The trick is to actually use it. If you auto complete then your coding should be typing 2-3 characters, hit enter, 2-3 characters, enter and so on. It should feel like you're entering hotkeys not typing.

    Only real typing should be defining variables and methods.

    Your coding speed can go way up depending on how good the autocomplete is.

modarts 11 years ago

Or just use some semblance of static typing and completely eliminate this class of bug.

ChrisAntaki 11 years ago

> Sometimes I wasted hours and hours just becaue there is one charactor difference. I hate it.

Haha, humans are much more able to parse meaning, despite a character being off here or there. Well, you've taken computers one step closer to humans. And you've made programming with Ruby friendlier. Great job!

  • ndesaulniers 11 years ago

    When I read that line you quoted, I wondered "how did the author ever finish the gem, since it didn't exist yet to help them with their typos?"

ryan-allen 11 years ago

It's almost like... statically typed languages!

flowerpot 11 years ago

I see you used the Levenshtein algorithm to calculate the suggestions, very cool idea. I've noticed lately that it is being used quite more often than in the past. (may just be my perception)

general_failure 11 years ago

'Sometimes I wasted hours and hours just becaue there is one charactor difference. I hate it.'

Not sure if the typos (Yeah, there are two) are intentional but I loved it :-)

  • baddox 11 years ago

    > So what will happen when you misspell ActiveSupprt's Hash#with_indifferent_access?

    Turned down the too-obvious ironic opportunity and went for somewhere else in the sentence.

  • chipgap98 11 years ago

    There are far too many of them to be intentional

  • ZoF 11 years ago

    Yes. They are intentional.

mikecmpbll 11 years ago

Not sure how helpful this is, when it says there's no method called xyz, it's pretty obvious you called the wrong method, or you called it on the wrong thing. Your first thing should be checking that you didn't call the wrong method name.. which involves looking at the error which repeats the method name that you tried to call, and the object you called it on.

Bizarre that this is a genuine hang-up for people.

stretchwithme 11 years ago

I use Rubymine, which visually indicates when a variable is unused or doesn't exist.

It has tons of other features that save you time and hassle.

  • alexch 11 years ago

    RubyMine FTW. It's amazing to me how completely Dwarf Fortress -- um, I mean vim and tmux -- have conquered the Ruby dev world.

dankohn1 11 years ago

Could I also highly recommend making your project Rubocop-clean, and using pronto to run Rubocop on your CI server and make comments on your commits in Github. Rubocop warns against any methods you define that are not used at least once.

brvs 11 years ago

I would love it if instead of quitting my program with an error, it just went ahead and called the method it thinks I'm referring to. This would remove a lot of unneeded friction from web development.

  • rurounijones 11 years ago

    Baaad idea, hides the errors and you have no idea if it will call the correct thing in the future too.

  • zenlikethat 11 years ago

    You should implement a `yolo` gem to support this.

  • Solarsail 11 years ago

    One of the early PL/1 compilers IBM wrote in the 60's did something akin to this. It was a 'premium' feature, available in one of the more expensive compilers. Where it detected any error or wrong looking code, it would correct it. There's a story floating around somewhere on the net of one guy trying to use it... (Which I can't find at the moment.)

    The first time he tried to compile with it, he used a program of roughly "Hello from PL/1". When he ran it, he got a division by zero error, because his string had been turned into an arithmetic expression. With the 1 turned into an I, which was then an unitialized variable, which then defaulted to 0.

  • vidarh 11 years ago

    The person taking over your projects when you move on would put a bounty on your head if you were to do that.

  • Shervanator 11 years ago
  • Too 11 years ago

    There is actually a python module that does something like that. It goes to the extreme to keep the program running no matter the error you make, arithmetic error are replaced with random results, missing functions skipped, exceptions silently swallowed etc etc. It is obviously a joke but they could benefit greatly from this feature ;)

    • rectangletangle 11 years ago

      For those who are interested, I'm pretty sure this is what you're referring to https://github.com/ajalt/fuckitpy. It's worth looking into for the documentation alone.

      • Too 11 years ago

        YES! Thank you

            FuckIt.py uses state-of-the-art technology to make sure your Python 
            code runs whether it has any right to or not. Some code has an error? Fuck it.
        
            FuckIt.py uses a combination of dynamic compilation, Abstract Syntax Tree 
            rewriting, live call stack modification, and love to get rid of all those 
            pesky errors that make programming so hard.
        
            Still getting errors? Chain fuckit calls. This module is like violence: 
            if it doesn't work, you just need more of it.
    • vidarh 11 years ago

      Sounds like the Python equivalent of Visual BASIC's "On Error Resume Next" atrocity.

      Except you can find "On Error Resume Next" in "production" code. If I ever consider taking contracts doing Visual Basic, the contract will specify that any occurrence of "On Error Resume Next" automatically doubles my daily rate.

  • slavik81 11 years ago

    That has been tried before. Results were... poor.

    http://www.catb.org/~esr/jargon/html/D/DWIM.html

    • DonHopkins 11 years ago

      You are correct, but please don't refer to the bastardized version of the Jargon file. Wikipedia has a much better and more accurate article, with actual quotes and citations instead of unsourced politically motivated mythology attributed to "some victims":

      https://en.wikipedia.org/wiki/DWIM

      >Critics of DWIM claimed that it was "tuned to the particular typing mistakes to which Teitelman was prone, and no others" and called it "Do What Teitelman Means" or "Do What Interlisp Means." - Guy L. Steele Jr., Richard P. Gabriel, "The Evolution of Lisp"

      The person who bastardized and maintains the Jargon file is a racist, sexist nut job who built his career on attacking the Free Software movement, and who doesn't deserves anyone's respect or attention: http://rationalwiki.org/wiki/Eric_S._Raymond

      • negativeview 11 years ago

        That... makes me very sad. I remember the old ESR. Knew that I hadn't heard of him in a long time, did not know he went a bit nutty...

  • ajuc 11 years ago

    So, you want programming to be more like writing html in the Quirks Era

  • modarts 11 years ago

    I'm pretty sure this line of thinking led to us having horribly misguided features like ASI (automatic semicolon insertion) in javascript today

    • ASneakyFox 11 years ago

      Except for in for loops semicolons don't really do anything though. The job of the semicolon could easily be replaced by just a white space character.

  • Widdershin 11 years ago

    This gem does just that:

    https://github.com/markburns/fuzzy

  • mattgreenrocks 11 years ago

    Ah, the mentality of the web: "I don't have time to get it right, just keep spewing code!"

  • rco8786 11 years ago

    That would be hideously bad

skatenerd 11 years ago

Who's gonna be the first person to hook this into method_missing()

bradgessler 11 years ago

I hope somebody forks this and creates a version that automatically corrects the method for you at runtime. Why even show an error or throw an exception?

Bonus if the corrections are cached for performance.

silveira 11 years ago

A python implementation of "Did you Mean?" by Peter Norvig http://norvig.com/spell-correct.html

sleepingspider 11 years ago

Strange. As an ruby programmer, I never had such a need.

imacomputer2 11 years ago

"Sometimes I wasted hours and hours just becaue there is one charactor difference." Oh thank God! I thought I was the only one.

RVuRnvbM2e 11 years ago

Wow. Talk about the wrong approach. When you're having to play trial-and-error to get the right method, you definitely have a problem with your tools - not the language!

I don't know about other editors, but vim has great autocomplete support for ruby built right in. Because of this I don't often even type methods out in full anymore.

And there's a great plugin for doco too: https://github.com/danchoi/ri.vim

annnnd 11 years ago

> Here is a good exmaple...

Looks like "Did you mean" could be a nice extension to browser textboxes too. ;)

mangecoeur 11 years ago

This is really smart, really the sort of thing you wonder why no one thought of it before.

thesz 11 years ago

Glorious Haskell Compiler suggests names for typos too.

whizzkid 11 years ago

How about implementing this in a way that it starts guessing method names while typing a method?

In this case, you would solve the error while typing it.

A simple dropdown with suggestion(s) would be great in Sublime Text.

KedarMhaswade 11 years ago

"Did you mean?" is great, but "Do you mean?" would be better. Achieving latter in Ruby/JS/... certainly feels harder.

octref 11 years ago

>>> Sometimes I wasted hours and hours just becaue there is one charactor difference. I hate it.

"because" misspelt.

germs12 11 years ago

Seems like a bandaid approach to fixing a severed artery. The real problem is the lack of desire to read documentation. This also has the problem of sending someone down the wrong path when the wrong "solution" is suggested. Read more documentation and pay attention while you're programming.

  • amckenna 11 years ago

    I don't think this has to do with a lack of knowledge of documentation, it has to do with simple spelling errors. Quite a number of times I type a method name wrong and upon scanning the code don't see the missing, duplicated, or mistyped letter. This would help a lot with that.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection