4 ms·
One thing that puzzles me is the "expressiveness" factor. When I read code, in any language, I translate that code into blocks / models, not English. I don't re
by pbz 16y ago
One thing that puzzles me is the "expressiveness" factor. When I read code, in any language, I translate that code into blocks / models, not English. I don't read the code as I would read a book or an article. That would take too much effort / time because right after "reading" it, I would still have to parse it into the mental model.
For me, if you write "if (myvariable != null) { }" or "unless(myvariable.nil?) end" doesn't make a difference. In fact, if I were to read it as English "if my variable is not null do" sounds better than "unless my variable is null, question mark?" -- I'm actually puzzled how you would "read" that.
In any case, my argument is that a translation from code -> mental model is more efficient than code -> English -> mental model, in which case expressiveness is only helpful in as much as it helps in parsing the code and not "reading" it in English.
- joe_the_user 16y agoI find that display_warning() if problem_condition() feels much more expressive in some situations than if(problem_condition) display_warning() The later just emphasizes the primary activity to me - again, in some circumstances. It's something I miss now that I'm not doing Ruby anymore.
- mahmud 16y agoThat breaks efficient code reading. When you read code, you tend to skip through basic-blocks (i.e. consecutive statements without branches) and follow them to that intersection point where a decision has to be made. In traditional form, if you have: if (condition) consequence(); You're able to "run" the condition in your head and step into the consequence without loss of pace. But when you put the consequence before the condition, you will have to backtrack.
- v_ 16y agounless the consequence itself is something that signifies the end of a basic block, like a break or a next or a return.
- brianto2010 16y agobacktrack = true unless consequence.signifies? :block_end It doesn't seem that much harder to read.
- draegtun 16y agoUnfortunately though you are potentially adding subtle bugs here which can only be caught at run time. To avoid excessive test coverage it would be more pragmatic to do: backtrack = (consequence.signifies? :block_end) ? false : true But that's not as easy to read :(
- mahmud 16y agoI didn't reply to that one-liner because it is a one-liner. A lot of software engineering lore crumbles into tautologies or is completely obviated when you're working at the microscopic level.
- crayz 16y agoActually the ruby would just be "if myvariable" - I think it's more readable simply because it expresses the minimalist core of what that line of code is doing. I could also use Blub and say "IfConditionIsTrue(myVariable.booleanValueOfNullStatus())" Yes, you're right that my mental model of the 'if' construct will be basically the same in any language, but it's fundamentally less work and less error prone for me to translate ruby than some other language into that model, and the same is true going the other direction
- erikpukinskis 16y agoLet me just translate that post: include 'rspec' your_point.disagree do the_example.in_ruby = "if myvariable" me.think do this_thing(the_example.in_ruby).is(:more_readable) end.because do that_thing.expresses(the_example.in_ruby.what_its_doing.minimalist_core) end me.could do using "blub" do self.say "IfConditionIsTrue(myVariable.booleanValueOfNullStatus())" end end.implying do self.last_statement.is(:crazy_talk) end end your_point.agree do constructs = languages.map do |language| in language do me.mental_models.find_by_name("The If Construct") end end constructs.should be_identical end.with_caveat do languages.min do |language| in language do me.mental_models.find_all return me.mental_strain end end.should == "Ruby" end
- robconery 16y agoBrilliant! Except at the end... end.should be "Ruby"
- texel 16y agoThe given example of "unless myvariable.nil?" is a little bit awkard. In fact, the use of the "unless" keyword is, in my opinion, almost always awkward unless it's postfixed: "do_a_thing unless wait?" Not only that, but most of the time, the expression could simply be written "if myvariable; ...; end", seeing as the vast majority of the time, we only care if the thing we're testing is falsy. Ruby does give you the tools to be very expressive, indeed almost conversational in your coding style, but it also gives you enough rope to hang yourself.
- draegtun 16y agoDamian Conway in Perl Best Practices recommends not to use negative control statements at all (unless and until). So this would be a no-no: unless ($this > $that) { do_something(); } He's happy they're used to postfix statements however he says that these should only be used for flow control. For eg. for my $i (@list) { next unless $i % 2 == 0; do_something(); }
- draegtun 16y agoCorrection: Conway says don't use postfix unless, for, while or until So last example should be: for my $i (@list) { next if not $i % 2 == 0; do_something(); } Personally I don't follow the bible to the letter :) I do use prefix & postfix unless but only with simple truth conditions like this: unless ($this) { ... } next unless $this;
- GrandMasterBirt 16y agoExpressiveness is important. I remember many times reading logic and missing the little tinsy ! before a giant expression not making the quick flag in my mind raise indicating that this is a unless, not an if. In ruby the idea of the unless is not to save typing. In fact if !foo is easier to write than unless foo. However after a tad bit of getting used to it, you realize that it becomes immediately obvious when skimming the code what the operation is doing. The idea is lets leave the brainpower for the hard problems, and not require you to use all that brainpower to figure out the plumbing.
- erikpukinskis 16y agoPassing blocks is a much bigger deal to me in terms of expressiveness. Being able to pass around blocks lets you write APIs that are as powerful as language features. The new Routes API in Rails 3 is a great example of this: resources :people do resource :avatar member do get :republish_deep_links post :resend put :accessible_projects, :upload_preference end end That code sets up various URL paths in a Rails app. It maps URLs like /people/george/resend to the proper controller in the MVC framework. In many other languages, you could set up an API like this, but instead of taking a block, the "resources" function would have to accept some sort of nested data structure. That gets you pretty far, but what about when you want to do something that the parser of that data structure didn't expect? Usually you can't. But in Ruby, code is data and you can do whatever you want in those blocks. You could do this, for example: resources :people do resource :avatar member do ["apple", "orange", "peach"].each do |fruit| get "eat_" + fruit end end end That would map /people/george/eat_apple, /people/george/eat_orange, and /people/george/eat_peach. And maybe that's not that impressive to you, but you can inject arbitrary code in there. That's what people mean when they talk about expressiveness. Examples of this are everywhere in the Ruby world. Check out HAML and assert_select. APIs + Rigid data structures = Pain and suffering APIs + Ruby blocks = Flexibility and power
- erikpukinskis 16y agoI should say the first example I gave is from DHH's awesome presentation on his favorite Rails 3 features: http://www.youtube.com/watch?v=b0iKYRKtAsA http://www.youtube.com/watch?v=b0iKYRKtAsA