5 ms·
... because Ruby isn't terse enough yet
- mdemare 19y agoIn Paul Graham's Accumulator Generator comparison (http://www.paulgraham.com/accgen.html http://www.paulgraham.com/accgen.html) Ruby is now terser than any language except Arc. foo = 'n -> x -> n += x'.to_proc f = foo[4] f[2] # => 6 f[2] # => 8 (This would be even terser: foo = &'n -> x -> n += x' but the ampersand syntax is only allowed after a method call.)
- dfranke 19y agoI think Haskell is overall terser than Ruby. The trouble with the accgen test is that it's imperative, and Haskell isn't meant to be. Here's the Haskell implementation that I would submit: putStrLn "Don't do that, you nimwit!"
- yariv 19y agoThe Erlang version is unnecessarily verbose (and also semantically different) because it uses 2 processes. It can be rewritten as Foo = fun(N) -> put(var, N), fun(X) -> V = get(var) + X, put(var, V), V end end. It's not as concise as Ruby or Arc, but it's a one liner. However, similar to Haskell, this style of programming is discouraged in Erlang.
- rkts 19y agoIn ML you can get a similar terseness to that of Haskell, but with support for imperative programming. If we allow the increment function to be predefined (as the Arc example does): let inc a b = a := !a +/ b; !a then foo becomes: let foo n = inc (ref n) or, with the help of the composition operator: let (<<) f g x = f (g x) let foo = inc << ref
- kcl 19y agoThis is shorter than both the Ruby and the Arc versions: {x set y+eval x} It's in q, a verbose version of a language called k. Applying it to a symbol/variable `n will give you a functional projection you can apply to any i.
- mechanical_fish 19y agoFor comparison, the original Ruby version: def foo (n) lambda {|i| n += i } end It's five characters longer, but approximately 350% more readable. Of course, a lot of that is because I'm more familiar with standard Ruby syntax than I am with this hack... but doesn't that tend to prove my point?
- mdemare 19y agoWell, by definition it's more readable, since nobody is familiar with the new notation yet. But objectively it has problems: unnecessary def and lambda keywords, using () and || for argument lists, using def .. end and {} for scopes. There's room for improvement. Personally, I like many things from Haskell's syntax. I wish I could say this in Ruby: foo = n -> x -> n += x
- Goladus 19y agoOther measures of brevity: The original ruby definition is 15 tokens, the .to_proc version is 13 tokens. Orthogonality: to_proc 3 variables (foo, n, x) 1 method call (to_proc) 1 type of separator (') 2 assignment ops (=, +=) 2 binary operators (->, .) 9 different kinds of tokens, in 5 (arbitrary) categories. Original: 3 keywords (def, lambda, end) 3 variables (foo, n, i) 5 Separators (|, {, }, (, )) 1 assignment (+=) 12 different kinds of tokens, in 4 categories. So the only way the original ruby version comes out ahead by these measures is that it has "fewer categories" of token types that are required. Also I think that with the 'def foo' declaration, 'foo' is not technically a variable. I don't really know ruby well enough. But you get the idea.
- neelesh 19y agoThe python implementation looks overly complicated. I was thinking that the ruby and python implementations would be very similar. Here it goes def foo(n): return lambda i:n+i Tried it (Python 2.5) and it works.
- mdemare 19y agoActually it doesn't work - calling the returned lambda should increment n - your implementation doesn't.
- neelesh 19y agoThat holds true for the given ruby implementation as well, since n is incremented only in the scope of foo and not in the calling scope. def foo (n) lambda {|i| n += i } end n=5 a = foo(n) puts a.call(3) #prints 8 puts n # still prints 5. n is not incremented I agree that the python one is strictly not according to the problem definition, but for all practical purposes both python equivalents are the same, aren't they? or am I missing something?
- testapplication 19y agoThat should read: def foo(n): n = [n] def bar(i): n[0] += i return n[0] return bar Python really needs a scoping syntax to allow assignment to closed variables.
- mynameishere 19y agoThe Perl Golf anti-pattern.
- mechanical_fish 19y agoI tend to agree with you. It's obvious to me that the following code evaluates to 6: r = 2 foo = lambda {|x| x * r} foo.call(3) And it's obvious to me that the following code raises an ArgumentError: foo = lambda {|x,r| x * r} foo.call(3) But what does this do? r = 2 bar = 'x*r'.to_proc bar(3) Do I get the 6, or do I get the ArgumentError? UPDATE: I presume the answer is "ArgumentError" (because the alternative appears to be positively evil), and that what I really need to do is this: r = 2 bar = 'x -> x*r'.to_proc bar(3) Why is this such an improvement over lambda? Couldn't these folks just fix lambda (e.g. to remove the need for that annoying .call method every time I use the result) rather than deciding that every Ruby programmer needs to learn yet another argument syntax?
- raganwald 19y ago"these folks" There are at least two kinds of folks: those who are "fixing" Ruby the language, such as the Rubinius team, and those who are "using" the language. I am in the latter camp. So sometimes I complain about what is broken--http://weblog.raganwald.com/2007/02/why-ruby-is-not-acceptable.html--and http://weblog.raganwald.com/2007/02/why-ruby-is-not-acceptab... sometimes I hack around it. But no, I am in no position to change the language. If I was, I would actually implement the point-free and underscore conventions natively. If you want to use closures, you want to use lambda, not String#to_proc. If you dislike using .call, Ruby also allows [] syntax, so: r = 2 foo = lambda {|x| x * r} foo.[3] May be a little less objectionable.
- mechanical_fish 19y agoI cannot get foo.[3] to work: syntax error, unexpected '[' But please don't spend too much time explaining what I'm doing wrong, because I'm afraid I don't like foo.[3] anyway. :) Let me demonstrate my problem by taking a simulated tour through my brain as it encounters foo.[3] for the first time: "WTF? "Did someone mistype foo[3]? "No, wait, surely it's supposed to be foo(3). I'm pretty sure that foo is a function. Didn't we set it equal to a lambda expression three lines ago? "Maybe I need glasses. Let's adjust my monitor. No, those are square brackets, all right. And that really is a period between "foo" and the brackets. "Maybe Google can help. But I'm not sure how to google '.[]' "Oh, no -- is this one of those mysterious edge cases of the Ruby syntax? Do I hear _Why laughing at me? Does foo.[] magically target the eigenclass, or the metaclass, or the zetaclass, or something? Or did one of the libraries secretly redefine the core language behind my back? "omigod, I should have learned Python. "No, wait! The dot means that we're calling a method. And I know that [] is a method -- we redefine it all the time. So foo.[3] is just an obscure-ass way of calling foo[3] . "Which makes no sense, because I thought foo was a function. So I must be wrong. "WTF?" [ten minutes later] "Aargh! I went through ten minutes of confusion just to save three characters?" I may have exaggerated this a little. And, yet, I really did have no idea that .[] works, and I still have no idea how it works. (Of course, in my version of Ruby, it apparently doesn't work, which makes it hard to investigate.) Now, don't get me wrong -- Ruby is my favorite programming language. Quite often, after the tortured syntax, the monkey-patched class, or the strange new DSL stops you dead in your tracks, you'll struggle through the learning curve and discover that you have a powerful new technique. This starts on day zero of Ruby, when you see your first block. It looks like nothing on Earth, but it turns out to be insanely great. Symbol#to_proc is a borderline case. I like the idea, but I hate the syntax. The &: doesn't bother me as much as the lack of curly brackets. To me, idiomatic Ruby is about blocks -- I expect map() and inject() and delete_if() to take blocks, not arguments. I would really prefer this: my_array.map {_.get_funky} Reading this makes me happy. There's a map() call, so I expect a block, and look -- there are the curly brackets. And because it's a block, the stuff inside it must be a method call. And, in fact, the underscore and the dot mean that we're invoking a method on a placeholder. All is well with the world, and no higher brain functions need to be engaged. But, you know, life isn't perfect. Language is about communication, and ultimately the language you speak is determined by what other people are speaking. Symbol#to_proc is powerful enough to become popular, and popular enough to be idiomatic, so we all have to learn it. Perhaps I'll even start using it. But I'm afraid String#to_proc, like foo.[x], is past the point of diminishing returns. Do I really need a version of lambda that is not only utterly unlike Ruby's usual lambda, but is even less like Scheme's lambda? And are my colleagues really going to forgive me for asking them to learn Haskell syntax in order to read something as trivial as lambda {|x,y| x+y} ? I'm not even sure if I could forgive myself!
- donna 19y agohmm, i dunno... self-modifying code can lead to insanity... :)
- mnemonicsloth 19y agoWhat is a "point-free holomorphism" ? I study math, hack Scheme, and find myself totally confused by terminology. Maybe it's a riddle or something -- "What does complex-smoothness have to do with lambda calculus?"
- mdemare 19y agoThe point-free style means avoiding lambda expressions: http://rickyclarkson.blogspot.com/2007/09/point-free-programming-in-java-7-beyond.html http://rickyclarkson.blogspot.com/2007/09/point-free-program...
- raganwald 19y agoIt was actually a typo. Fixed, thank you.
- mnemonicsloth 19y agoNo, thank you. I've just been forced to learn about a new language proposal, a new (to me) set of morphisms, and a nonstandard approach to proving stuff in topology. That's a rich link.
- jsnx 19y agoThis is cute but first-class functions would be better. I've had to switch to Python to find them.