Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I don't think the ability to write expressions similar to natural language like "2.days.from_now" should be called "expresiveness". Real expresiveness is about what SICP calls "means of abstraction" and "means of combination", not necessarily about having very nice syntax. In general I think those considerations would benefit from distinguishing a bit more between syntax and semantics. As another example, I have been doing Ruby professionally for almost 10 years now, and despite this I have to strongly disagree with the conclusion of this quotation:

Python is a beautiful, clean language. But the same restrictions that make it nice and clean mean that it’s hard to write beautiful, clean libraries. Ruby, on the other hand, is a complicated, ugly language. But that complexity allows you to write really clean, nice, easy-to-use libraries.

The Ruby metaprogramming magic makes for really nice syntax, which is what made me choose it over Python all those years ago, but it complicates understanding of what's going on - as I matured as an engineer, maintained a codebase over several years, several times spend long hours debugging issues coming from metaprogramming pitfalls, I shift more and more to the Python approach of building APIs in the simplest possible way, as much as possible sticking to simple function calls and no really fancy metaprogramming just to get a cleaner syntax, e.g. compare Rails has_many :foo with Django's models.ForeignKey(Foo) and the internal implementations of those. Same thing with using "BDD" testing frameworks like RSpec vs. traditional assert stuff, what is the nice syntax worth if tricky bugs hit you where you most want to avoid it, in tests. Libraries like RSpec might be "nice", but they are certainly not "easy-to-use", and I don't think I would call them quite "clean", if I look at their source code.

In other words, while clean syntax is luring, in the long run clean and simple semantics is what I think is really important.



Cannot upvote enough!

Rubyists try too hard to emulate natural language syntax, but in the process forget that natural languages are not particularly clean - in fact, more often than not they are highly dependent on contextual information, not just semantically, but even syntactically.

Ultimately, natural languages and programming languages serve very different purposes: communication in natural languages is highly dependent on context for resolving ambiguity (e.g., if we took this discussion to a non-programming forum, everybody else would be like "WTF is going on?"), while programming languages are all about making it possible to be pedantically precise without reducing convenience.


What kind of people still get excited over a language's ability to do this?

* Category functions in Objective-C

* .NET extension methods


> In other words, while clean syntax is luring, in the long run clean and simple semantics is what I think is really important.

I agree. And I think that Rust has one of the simplest semantics around in programming languages: the method resolution rules are quite simple, because there is no method overloading and all extension methods have to be explicitly imported to be used. These two restrictions are important for maintainability: the former means that the compiler "never guesses"—multiple applicable methods are a compile time error—and the latter means that you never have to guess about where a method comes from, because it was always defined either alongside the type or defined as part of a trait that was explicitly imported at the call site.


A former developer at our company was someone who was really excited about using Ruby metaprogramming features to the fullest extent possible to enable concise code. He wrote a bunch of subsystems based around a few custom frameworks.

Whenever a project takes me into modifying that codebase I get that sinking feeling you get when you realize you left your wallet at a restaurant or that your car has been towed.


You should really watch Ola Bini's talk "Expressing Abstraction - Abstracting Expression" from Strange Loop 2012:

http://www.infoq.com/presentations/Language-Expressiveness

tl;dw: there are many types of expressiveness in programming languages, and they're all important.

Likewise syntax is important too, but often downplayed by the PLT community. Case in point: your post!


Django's models are entirely fancy metaprogramming behind the scenes. When it works, it works, but the code that implements it is not easy to understand by any means.

Django models are definitely more self-documenting than Rails models, which seems like a result of the Python legibility culture.


Yes, but Django model metaclass magic is context-independent; the logic resides entirely in the model and does not affect the rest of the environment, which is something that Ruby's includes can do (and give you no warning when method overloads break stuff).


Exactly, in Rust you can't (easily) pass in a named function into a map because of all of the pointery stuff going on. Rust clearly has its uses but if you can't write high order functions then expressive it is not.


You can write higher-order functions in Rust - in fact, much of the standard library relies on them. What you cannot do is use named and anonymous functions interchangeably.


you cannot [..] use named and anonymous functions interchangeably.

That seems like a odd restriction/asymmetry. What's the rationale?


It is a bit weird, and it's one of the parts of the language still being worked on [1]. What it comes down to is that while they're both bits of machine code being executed, their environments are different. I'm a bit out of date as there's been movement in this area since I've written much "hard core" Rust code, but the basic problem is that what the grandparent poster calls an "anonymous function" is a closure, with a captured environment. Thus said closure has different interactions with lifetimes, memory management, and program safety.

Since safety and soundness is one of the primary goals of Rust, problems like this do crop up and are surely what I see as the hardest part of designing a usable language.

[1] http://smallcultfollowing.com/babysteps/blog/2013/10/10/fn-t...


To be frank, I am not sure of the specifics. At first I thought it was because anonymous functions have a notion of the lifetime of the variables they close over, which named functions do not. However, taking into account that named functions do not close over anything in first place, this suggests that one should be able to use a named function wherever an anonymous function is expected.


You can use named functions interchangeably with anonymous functions, it's not just practical because of pointers.


I stand corrected. Indeed, this program worked just fine:

    fn foo() {
      println("hai")
    }
    
    fn main() {
      let xs = ~[1,2,3];
      for _ in xs.iter() {
        std::task::spawn(foo)
      }
    }


Yes, you can. Try it:

    fn add1(x: &int) -> int {
        *x + 1
    }

    fn main() {
        println!("{:?}", [ 1, 2, 3, 4, 5 ].map(add1));
    }


I don't think it's the similarity to natural language, but rather expressing things directly.

I'd agree that unconstrained metaprogramming can make code hard to read, and Ruby especially tends to go overboard. One of the goals of Objective-Smalltalk is to create a language with cleaner extensions points, making semantically consistent extension/adaptation easier. We'll see how it works out :-)


> Real expresiveness is about what SICP calls "means of abstraction" and "means of combination", not necessarily about having very nice syntax.

I was thinking specifically of the expression problem:

> The Expression Problem is a new name for an old problem. The goal is to define a datatype by cases, where one can add new cases to the datatype and new functions over the datatype, without recompiling existing code, and while retaining static type safety (e.g., no casts).

http://en.wikipedia.org/wiki/Expression_problem

Now, I'm not saying Rust solves the expression problem, nor that what I'm calling "expressiveness" here maps to it, even. Just that we can easily argue over what "expressive" means.

> Libraries like RSpec might be "nice", but they are certainly not "easy-to-use", and I don't think I would call them quite "clean", if I look at their source code.

The whole point of the quote is that the source code is ugly. I think we're in violent agreement here.


This paraphrased quote was quite ambiguous to me, but the conclusion of having preferred Ruby in the end without further commentary makes it seem like the nice syntax is worth the complications, while I would say those "intuitions" of initial liking/disliking a language because of syntax are completely misleading and should not be trusted.

Also, it's not only about ugly internals, I wrote several RSpec tests that looked like a perfectly reasonable "natural-languigish" expression of what I wanted to, but did not work at all, because of the tricky mechanics underneath happened not to support this particular combination of incantations. In the end you have to understand all the ugly internals to know which of the "nice" calls will work and which not, and I all the time see people shooting themselves in the foot because of not having this understanding, e.g. not wrapping some bit in a lambda in Ruby and having something evaluated at the wrong time etc.


> This paraphrased quote was quite ambiguous to me, but the conclusion of having preferred Ruby in the end without further commentary makes it seem like the nice syntax is worth the complications

It doesn't actually complicate the language to allow stuff like this: it just naturally fell out of having traits and methods, which we needed for other reasons.


Correct me if I'm wrong but isn't the successful imitation of natural language an example of "means of abstraction" in practice?


You can write 2.days().from_now() in almost any OO language which allows extending primitive types, so it isn't really very impressive. There are lots of example of languages with very powerful means of abstraction / composition and not syntax people commonly consider pretty or natural: Lisp, Erlang, Prolog. There are also programming languages which imitate natural languages to some degree and aren't very expressive, like COBOL.


> so it isn't really very impressive.

You can say this about any programming language feature or concept.


I think his declaring something as impressive or not in an objective way is fraught with peril since impression is always a subjective feeling. If it impressed you, it is impressive.

However that being said 2.days.ago is not a particularly differentiating bit of magic because as has been stated elsewhere every object oriented language that allows extensions of primitive types will allow this.

The main question here though is whether 2.days.ago is a good design and does it really demonstrate expressiveness, which as has also been stated above related more to the concept of "small pieces, loosely joined"

2.days.ago breaks encapsulation and abstraction and inverts the flow of control (parameter calls function).


It's not the same as that, since it's scoped and must be implicitly imported.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: