
Rust is surprisingly expressive - steveklabnik
http://words.steveklabnik.com/rust-is-surprisingly-expressive
======
stiff
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.

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

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

* Category functions in Objective-C

* .NET extension methods

------
hamburglar
Sometimes I think I must be a humongous idiot because adding a "days" method
to Fixnum smells like the stupidest, most counterproductive, cutesy bullshit
to me, but some pretty smart folks think it's great (that's a compliment btw).

Stuff like this is the opposite of expressive to me because cute expressions
that read like english scream "please take a moment to analyze this and make
sure it does what it sounds like." I am instantly suspicious of code that is
so expressive that you can just intuit out its purpose without thinking it
through. Code is too subtle for treating it that way.

~~~
kybernetikos
I think I agree with the thrust of what you are saying here. Just as English
has rules with what you do with nouns and verbs etc, code has rules too that
are different to English.

2.days().from_now()

says that we are performing the action 'days' on the object '2'. What could
that possibly do? Well there're a bunch of options and none of them are
obvious. It's weird and counter-intuitive. And then we go and perform the
action 'from_now' on whatever the result of that was.

I don't mind 'fluent' style interfaces as long as they don't mess up the
normal meanings of what objects and methods are, but sadly they usually do.
Another classic sign of a bad interface is that with many of these fluent
interfaces it's not obvious whether a particular thing should be a chained
method call or argument, and if it's an argument, whether it should be got by
calling a method, using a field or what.

Code should read like clear _code_. English should read like clear _English_.
These are not the same things, and efforts to make them the same usually make
a mess of both.

~~~
matthewmacleod
_Code should read like clear code. English should read like clear English.
These are not the same things, and efforts to make them the same usually make
a mess of both._

This stinks of principle over pragmatism.

Using the sugar offered by expressions like this requires a bit of knowledge
of the development environment. I accept that it's a bit of a different syntax
from some other languages, but that doesn't make it unclear at all.

Speaking to Ruby (where ActiveSupport is from), we all know that it's a
language that focuses on providing flexible syntax and "multiple ways of doing
things", in contrast to some other languages which restrict flexibility to
provide more consistently structured code. I don't think it's a bad thing at
all to have these options available, and I often find my intentions are much
clearer when writing, say:

    
    
      if post.created_at < 5.minutes.ago
    

as opposed to Python:

    
    
      if(post.created_at < (datetime.now() - timedelta(minutes=5)))
    

or Objective C:

    
    
      NSDate *now = [NSDate date];
      NSDateComponents *delta = [[NSDateComponents alloc] init];
      [delta setMinute:-5];
      NSDate *fiveMinutesAgo = [[NSCalendar currentCalendar] dateByAddingComponents:delta toDate:now options:0];
      if ([[post created_at] compare: fiveMinutesAgo] == NSOrderedAscending)
    

That's where the magic comes in, and it makes Ruby ideal for things like web
templating, while perhaps making it less suitable for other uses.

TLDR: various constructs like this have their place, they're not inherently
bad, but do require knowledge of the tools.

~~~
Camillo

        if post.created_at < 5.minutes.ago
    

That reads like "if post was created less than five minutes ago", i.e. if the
post is more recent than five minutes. However, if "5.minutes.ago" gives you a
timestamp, as is reasonable, then that actually means "if post was created
_more_ than five minutes ago", i.e. the very opposite of what it would mean in
English. Whoops!

A more sensible syntax might be:

    
    
        if post.creation_date < now - 5*minutes
    

"ago", "from_now", "since_now" etc. are especially despicable in a language
where you can just use + and -.

~~~
mattgreenrocks
I'd prefer:

    
    
        if post.editable?
    

to everything I've seen. There's a concept behind the if statement that should
go in the model. I realize this is sort of off-topic, but it kind of negates
the whole discussion since you can wrap up the cute/ugly and test it alone. :)

~~~
klodolph
And what would the definition of `editable?` look like?

    
    
        fn editable(&self) -> bool {
            self.created_at < 5.minutes().ago()
        }
    

Yes, it's wrong. No amount of unit testing or isolation is going to change the
fact that whenever you read code like that, your brain will come up with two
conflicting meanings, because these cutesy methods make it look too much like
English, and English has _different rules_.

English parse tree is: ((less than (five minutes)) ago), whereas code parse
tree is (less than ((five minutes) ago)).

------
mattmanser
Extension methods in C# do the same thing and have been around, what 5ish
years now? Since .Net 3.5.

Here's an example implementation:

    
    
        public static class DateExtension
        {
            public static TimeSpan Days(this int daysCount)
            {
                return new TimeSpan(daysCount, 0, 0, 0); //days, hours, mins, secs for those wondering
            }
    
            public static DateTime FromNow(this TimeSpan addTime) 
            {
                return DateTime.Now.Add(addTime);
            }
        }
    

usage is the same as Rust:

    
    
        2.Days().FromNow();
    

Basically if this impresses you, you really should give modern C# a whirl as
there's a lot more you can do than that.

Much of how it does stuff now is how my perfect language would do it. There
are bits of Ruby I love, and Rust is looking more exciting to me than Go, but
the C# team really have done some amazing work.

~~~
steveklabnik
I am certainly not implying that Rust is the only language that supports this
kind of thing. Haskell is where I first saw some of this. I'm just happy that
Rust is doing it well.

C# is a great language, but I don't use Microsoft technology, and it's
effectively Windows only.

~~~
majorsc2noob
I run C# in production on Mac OSX, IOS and Android phones. What do you mean by
Windows only?

~~~
steveklabnik
If you work hard enough, you can do anything. Mono is slow, doesn't support
everything, and most of the help you'll get is Windows specific.

~~~
majorsc2noob
That is just FUD im my opinion. I havent har any issues with getting my
applications working on multiple platforms. Have you har performance issues
with C# on IOS?

~~~
mattmanser
Be practical. I've had to support wordpress on IIS before, it's always a lot
of hassle not using the tech in its native environment. Same with MySQL +
.Net. The EF, for example, played really poorly with MySQL.

I can't imagine using mono is for the faint hearted. For example in the new MS
programming language thread [1] today someone mentioned they ended up giving
up on the .Net GUI controls and using GTK# instead because it was so
unreliable. Another mentioned that the transition from one SQL driver to
another had left a lot of projects hanging in the wind with SQL breaking on
mono but working on Windows.

[1]
[https://news.ycombinator.com/item?id=6974494](https://news.ycombinator.com/item?id=6974494)

~~~
mfn
I don't think that's a fair comparison. Rust doesn't come with a set of
functioning, well-tested and cross-platform GUI libraries and database drivers
either.

Running C# the language with the BCL on mono is pretty painless, and offers a
ton of functionality.

------
maninalift
The example presented in this post is neither a great example of what Rust can
do nor an elegant solution to the particular problem of dealing with dates and
times.

For a much further-reaching solution to this problem look at C++ user-defined
literals, combined with what Bjarne Stroustrup calls "type rich programming".

now() + 3days + 21s + 102ms

This is not to say that c++ is the only language that can do this (although it
does do it very well).

See the following video for a simple overview (apologies for toe-curling
introduction):
[http://channel9.msdn.com/Events/GoingNative/GoingNative-2012...](http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Keynote-
Bjarne-Stroustrup-Cpp11-Style)

...also the video above is worth watching just for the vector vs. list
comparison.

------
mpweiher
Good examples:

    
    
      2.days().from_now()   (Rust)
    

vs.

    
    
      2.days.from_now       (Ruby)
    

I'd probably still prefer Smalltalk:

    
    
      2 days fromNow.
    

It seems to me that many distinctions we still suffer from are quite
arbitrary. Yes, having numbers be objects (boxed or tagged) is one way of
achieving this sort of uniform handling, but you can also achieve it
statically.

Just like you don't have to switch to a "scripting" language for expressive
file manipulation:

    
    
      file:hello.world := 'Hello World!'.
    

EDIT: typo.

~~~
MichaelGG
I guess we'd call these "type extensions"? F# supports this just fine, and C#
can do a bit, too.

Overall, I haven't found I need type extensions that much, if I have flexible
function handling. It's the same level of expressiveness to write "fromNow 2
days". (fromNow having signature (int -> (int -> TimeSpan) -> DateTime). Or
even "2 |> days |> fromNow" (fromNow being TimeSpan -> DateTime).

Actually, this example is pretty terrible, as even C#'s basic format would be
"DateTime.Now.AddDays(2)" which seems pretty fine to me - add a top-level
binding for now and it's on the same level of conciseness. Also, adding type
extensions to integers for time seems quite ugly. Most 3rd party libraries
I've seen don't use type extensions in a graceful manner, and end up slapping
members on everything and it's just silly. (And I'm ignoring the fact that
using .Now in .NET is rarely correct since it's rare when you don't want to
use whatever random timezone the server's configured for, instead of UTC.)

~~~
bad_user
"2.days.from_now" is different from DateTime.Now.AddDays(2), because it can be
broken in two parts:

\- 2.days is a duration, or a time delta, or time span, or whatever you want
to call it

\- from_now is a method called on this duration value

This construct "2.days" is useful on its own. It's actually more useful than
"DateTime.Now", as durations are often part of public interfaces, whereas
usage of concrete timestamps is very often hidden. The equivalent in C# for it
would be "new TimeSpan(2, 0, 0, 0)". That's much more unreadable than just
"2.days" and often, instead of thinking about the problem, developers fallback
to specifying time spans as Ints in seconds or similar.

On TimeZone, "2.days.from_now" is a timestamp. If the underlying library is
not totally fucked up, like "java.util.Date" is, then it does have an attached
timezone. Or in case this yields a Unix timestamp, it's really not a Unix
timestamp unless it was calculated since 1970-01-01 00:00:00 UTC.

~~~
MichaelGG
You wouldn't write new TimeSpan(2,0,0,0), you'd write TimeSpan.FromDays(2).

There's nothing stopping you from binding a function "days" to wrap
TimeSpan.FromDays.

If this is what people consider to be some pinnacle or notable point in
expressiveness, yikes.

~~~
bad_user
Oh, but we can expand on that ...

    
    
        scala> 2.days + 10.minutes
        res: FiniteDuration = 2890 minutes
    

I just don't get it. So a literal like "0.2f" to denote a Float (and not a
Long) is acceptable. A literal like "300.5m" to denote a decimal and not a
floating-point is acceptable.

But somehow other kinds of literals for other units of measurement are not,
because Dear God, magic, magic!!!

------
wycats
I wrote the original implementation.

One thing worth noting is that the implementation isn't globally extending
`uint` with `.days()`. In order to write `2.days()`, you need to first import
the trait that provides the implementation.

From that perspective, it's not much different from importing a function like
`days_from_now` that you call as `days_from_now(2)`.

------
dahlberg
Why is the string representation of:

    
    
      2.months().from_now()
    

printing a date one year in the past?

    
    
      date: active_support::date::Date{year: 2013, month: 2u, day: 28u}, hours: 0u, minutes: 59u, seconds: 6u, nanos: 243388962u

------
sklivvz1971
In other news C# is exactly as expressive as Rust and Ruby...

    
    
        void Main()
        {
    	Console.WriteLine(2.Days().Ago());
    	Console.WriteLine(4.Days().FromNow());
        }
         
        //This can be a library, mmkay?
    
        public static class Extensions 
        {
    	public static TimeSpan Days(this int days){ return new TimeSpan(days, 0,0,0); }
    	public static DateTime FromNow(this TimeSpan howMuch){ return DateTime.Now + howMuch; }
    	public static DateTime Ago(this TimeSpan howMuch){ return DateTime.Now - howMuch; }
        }
    
    

Results in:

    
    
        26/12/2013 22:05:10
        01/01/2014 22:05:10
    

Is there a better example?

~~~
mythz

        public static class Extensions {
            public static void Print(this object o) { Console.WriteLine(o); }
        }
    
        2.Days().Ago().Print();
        4.Days().FromNow().Print();

------
jimmaswell
Why would days be a method of an integer? It feels out of scope shoving
date/time stuff into integral datatypes.

~~~
klibertp
The basic idea is that objects should know how to represent themselves. In
Smalltalk every Object implments writeOn: method, which gets a stream and
prints self on this stream.

By extension, if an object knows how to convert itself into another object, it
implements a method for doing do. In Smalltalk there is a convention of
putting all such methods inside "converting" protocol. For great example of
how is this useful you can look at the Collection and it's subclasses in
Smalltalk - all collections have many "asOtherKindOfCollection" methds, which
are either inherited or implemented, depending on a collection. It's great to
be able to send "asSet" to _any_ collection that comes your way and the way
Smalltalk does it is natural in purely object oriented languages.

That and the fact that creating IntsToRange or StringToDayOfWeek "static"
classes is actually even worse leads to the design where Integers have `to:`
and `days` methods.

Actually, there is one more probable cause: in Smalltalk and some other
languages extending already defined types is _easy_ , really easy, easier even
than creating a new, empty class. The methods are grouped into "protocols",
which are grouped into packages and it's - not quite - largely orthogonal to
the classes. Combine that with powerful IDE, VC support (Monticello,
Metacello) etc. and you have no reason whatsoever _not to_ extend even the
most basic classes.

~~~
philwelch
The question is what "2.days" actually returns. IIRC, in ActiveSupport it
returns an integer number of seconds. Is that a sensible convention? If you
had another class representing an span of time (with or without any absolute
calendar reference) that an Integer could convert into, that might be a
cleaner design. But even in that case you wouldn't necessarily have a method
on Integer to confer it to a TimeSpan; you'd initialize a TimeSpan with an
integer argument.

In general I find it cleaner when less primitive classes can represent
themselves as more or equally primitive classes, but not vice-versa.
to_integer, to_float, to_list, to_boolean, etc. But on the other extreme, if
your application entails establishing database connections, implementing
String#to_database_connection would be ridiculous.

For me, integers are much more primitive than date and time objects. `2.days`
would be equivalent to implementing `2.meters` in a CAD application, but I
suppose you might be OK with that.

But even granting that an integer can transform itself into a representation
of a time span, i.e. `2.days` is okay, `2.days.ago` is right out. That's not
just an Integer representing itself as a span of time. Now all of a sudden
Integer has to have knowledge and, even worse, opinions about the current date
and time (which is one of the most hazardous boundary cases in all of
software).

~~~
klibertp
> integers are much more primitive than date and time objects

In terms of concepts, yes, but in terms of language syntax and semantics they
are the same (in Smalltalk).

> `2.days` is okay, `2.days.ago` is right out

Yeah. But not because it "looks bad", and not because Integer would need to
know anything more than it does already (it wouldn't), but because `2 days
ago` is actually less flexible and harder to maintain.

Consider:

    
    
        Date today - 2 days. "27 December 2013"
        DateAndTime now - 2 days. "2013-12-27T04:43:42.679+01:00"
    

We can get either Date or DateAndTime without changing anything. With proper
planning the superclass of both Date and DateAndTime can have a generic -
method which will work for all the cases. With the `2.days.ago` we're tied to
one result type. We need to code around it, giving it a default argument with
a class or remembering the "kind" of Duration.

Anyway, if it's ok to have a function which takes integer it's equally ok to
have an Integer have a method. Especially when you just _can 't_ have a
function, like in Smalltalk and the _only_ alternative is writing yet another
class with one method in it.

~~~
philwelch
> Anyway, if it's ok to have a function which takes integer it's equally ok to
> have an Integer have a method.

Seriously? Any function that takes an integer argument? Like, "exit". You want
to call 1.exit to terminate a program with a return code of 1? Is there any
difference between a function that takes an argument and a method on an
object? Shall we implement all functions that take n arguments as
multimethods? Are you actually going to implement
String#to_database_connection? That was meant as a reductio ad absurdum, I
didn't expect anyone to literally follow me there.

If you want some particular Integer to get decorated with additional methods
because you're using it in some particular domain context but you're not
globally changing the interface of all Integers that's a different story.

> Especially when you just can't have a function, like in Smalltalk and the
> only alternative is writing yet another class with one method in it.

There are perfectly same object oriented ways to solve this problem, like
instantiating some kind of TimeSpan object, or having a singleton for process
status in the case of the "exit" function.

~~~
klibertp
> You want to call 1.exit to terminate a program with a return code of 1?

1.exit is a bad idea because it has a side effect outside of it's receiver, I
think.

> Are you actually going to implement String#to_database_connection?

Yeah. Or at the very least I won't die when I see this (working code in Pharo
2.0):

    
    
        'http://example.com' asZnUrl retrieveContents. "connect to given url and fetch it's contents"
    

or this:

    
    
        'HelloWorld' asMorph openInHand. "create a bitmap representation of string and display it on screen where the cursor is"
    

or this:

    
    
        #asSet value: (Array with: 1 with: 1 with: 3). "Symbol>>value:anObject looks like this: anObject perform: self"
    

which is especially interesting: how come a Symbol knows how to perform an
action it can represent on some given object? I dunno. It just does.

I can't tell you exactly when adding a method to some class begins to be a bad
idea and when it's still ok to do. I don't know any hard rule for this. I
agree that `1.exit` is a bad idea and I provided a rationale for why I think
so. But I'm still convinced that there are many (many more than you seem to
think) cases where it's ok, it's acceptable and convenient to extend String,
or Object, or any class you don't "own".

I'm actually looking at the system which is built with very many methods like
the examples above (although it doesn't have 1.exit) and I see that it works.
One of the most basic classes, Object, has this many external (with extension
methods) protocols in it:

    
    
        *Collections-Abstract-splitjoin, *Fuel, *Fuel-Collections, *Glamour-Helpers, *Graph-ET-Core, *Graphics-Display Objects, *Kernel-Exceptions-debugging, *Monticello-Storing, *Morphic, *NativeBoost-Examples, *NativeBoost-core, *Nautilus, *Polymorph-EventEnhancements, *Polymorph-TaskbarIcons, *Polymorph-Widgets, *Ring-Core-Kernel, *Shout-Parsing, *Soup-core-testing, *Spec-Core, *Spec-Tools, *System-Settings-Browser, *System-Support, *Tools-Base, *Tools-Browser, *Tools-Explorer, *Tools-Finder, *Tools-Inspector, *UIManager, *VMMaker-translation support, *collectionextensions, *deprecated20, *eyesee-support, *grease-core, *grease-pharo20-core, *magritte-model-accessing, *magritte-model-actions, *magritte-model-model, *magritte-model-testing, *magritte-morph-converting, *metacello-core, *mondrian-complexshape, *mondrian-core-accessing, *mondrian-fadelayout, *necompletion-extensions, *neo-json-core, *petitparser-core-converting, *petitparser-core-testing, *roassal-core, *rubric, *tools-debugger
    

And the system still works. And it's easy to navigate. And to understand. And
to use, even. So, once again - I'm not arguing that 1.exit is ok, just that
there are many "saner" extension methods which are ok. To get the above list I
had to write some code:

    
    
        |allProtocolNames externalProtocols|
        allProtocolNames := (Object allMethods collect: [ :x | x category asString]) asSet asSortedCollection. 
        externalProtocols := (allProtocolNames select: [ :x | x beginsWith: '*' ]) 
            inject: Character cr asString 
            into: [:acc :x | 
                acc , x , Character cr asString
            ].
        Transcript show: externalProtocols.
    

I may not be very experienced Smalltalker, but I believe this is more or less
idiomatic Smalltalk code. Even in this short snippet there are 3 asSomething
methods - and the code still works and I still think it's readable. And cute.

Is it Smalltalk specifically which enables this technique or what other rules
extension methods need to follow to be sane I don't know. Which is why I'm
hesitant to implement those methods myself. But I accept their existence and
the fact that they have their place in a sound, sane design, at least if done
right (for any acceptable value of "right" we can agree on).

But well, my day to day job is writing Python, where "monkey patching" is
considered a sin and the community consensus is pretty much the same as your
opinion: don't extend classes, write functions instead. So I kind of
understand this position. Although I still think it's not inherently bad idea
to extend base classes and that it's the matter of language design, project
design and tooling. But I can't say for sure. I'm just telling you what I saw
when I went and learned Smalltalk, that's all.

~~~
philwelch
This is surprising enough I'm not sure how to respond anymore.

> which is especially interesting: how come a Symbol knows how to perform an
> action it can represent on some given object? I dunno. It just does.

Presumably it just sends itself to the object you pass in? This is not
something I'm actually bothered by.

I guess it depends on how you extend classes you don't own. If the adapters
themselves are modular and are only loaded with the class that they're trying
to adapt to, that might be OK. In other words, if I have a DatabaseConnection
class that extends other classes with asDatabaseConnection methods, and that
extension only happens when I load the DatabaseConnection class, that still
satisfies the intention of SRP even though I'm technically implementing
methods on other classes.

------
jimmyhmiller
In clojure you could write this.

(-> 2 days from-now)

The great thing is that you don't have the weird semantic problems that the
rust or ruby examples have. There are just two functions here, days and from-
now.

~~~
wycats
That's essentially how traits work in Rust. You need to import the trait into
your scope, and 2.days().from_now() is essentially sugar for
days(2).from_now() where days() returns a TimeChange object.

~~~
jimmyhmiller
Don't get wrong, rust looks fascinating. I realize that the syntax is
basically sugar, but from what I understand is that you are still adding a
method to a type. Here's a snippet from the code.

    
    
        fn days(&self) -> TimeChange {
            TimeChange::new().days(*self as f32)
        }
    

So while you are right, it is basically sugar for days(2).from_now(), but you
couldn't call the function that way. It must be attached to an instance of an
uint.

Also in order to get the English looking version you have to implement those
function specially using traits. Whereas with the clojure version they are
just normal functions, but the threading macro gives you that English
readability you are looking for.

~~~
pcwalton
> So while you are right, it is basically sugar for days(2).from_now(), but
> you couldn't call the function that way. It must be attached to an instance
> of an uint.

We plan to implement that, it's just not implemented yet:
[http://www.reddit.com/r/rust/comments/14pucx/rfc_method_call...](http://www.reddit.com/r/rust/comments/14pucx/rfc_method_call_syntax_as_sugar/)

> Whereas with the clojure version they are just normal functions, but the
> threading macro gives you that English readability you are looking for.

You could do that with a macro in Rust as well. We don't make ordinary
functions acquire that sugar for namespacing reasons: you can have multiple
methods called e.g. "get" in scope as long as they're attached to different
types, but that isn't true for functions.

------
RodgerTheGreat
In Factor:

    
    
      2 days hence
    
      4 weeks ago
    

etc. [http://docs.factorcode.org/content/article-
calendar.html](http://docs.factorcode.org/content/article-calendar.html)

------
seth1010
While I don't think that syntax is _that_ important, I am fond of sugar.

Because I've been working in D for a while, here's how you'd do it in D.

    
    
        import core.time: Duration, dur;
        import std.datetime: Clock, SysTime;
    
        Duration days(int number) {
          return dur!"days"(number);
        }
    
        SysTime from_now(Duration duration) {
          return Clock.currTime + duration;
        }
    
        void main() {
          import std.stdio: writeln;
          writeln(2.days.from_now);
        }
    
    

Runnable pastebin link:
[http://dpaste.dzfl.pl/9b84f820](http://dpaste.dzfl.pl/9b84f820)

~~~
mcguire
I haven't really looked at D in a long time, so I'm not clear on what's going
on here.

Is D allowing you to write the function call "days(2)" as "2.days"?

(As an aside, Nimrod allows you to do specifically that; first-arg.function-
name(...) == function-name(first-arg,...).)

------
jemeshsu
So it is a demo that you can use Rust metaprogramming to make Rust look like
Ruby. I hope this would not become idiomatic Rust.

------
stcredzero
_For the most part, I’d found the complaints about dynamic typing from the
static typing camp to be very FUD-y. I very rarely get TypeErrors in my Ruby
code. Refactoring generally isn’t very difficult._

 _But slowly, this has started to change. I think that it’s that I tend to do
more infrastructure and plumbing work now. I’ve run into more and more
situations where types would be helpful, or picking the mutable state path has
caused headaches._

Explaining the attraction of dynamic debugging to static type die-hards is a
lot like explaining the attractions of static typing to dynamic programming
die-hards. You can only get a sense of what the other side is talking about
through lots of hands-on experience. Where it's really valuable is often an
edge-case, but often it's a really expensive edge-case! (Goes both ways.)

This from an old Smalltalker. If you do enough Smalltalk, especially in a
large established code base, you eventually see situations where you wish
there were type specifications. However, you still wish that more statically
compiled language people were more open minded and less knee jerk when you
talk about the magic you can do. (Which also means that the open minded ones
are particularly valuable.)

------
eranation
My personal choice for "statically typed Ruby" is actually Scala or Kotlin.
Scala has its drawbacks, especially the compile time, (which is getting
better) but both have similar expressiveness when it comes to mind blowing
library usage. (Although sometimes arguably in the expense of potentially
complexity of the language when it comes to being able to writing such elegant
libraries) I found it similar to the OP's argument regarding python vs ruby.
Scala as a language might be a very subjective and divided topic, but using it
with the right libraries can be very expressive just with added static typing.

------
nwmcsween
I dislike all these 'expressive' or how abstract rust as a language can be
from what it's trying to achieve which is to be a 'systems programming
language' on par with c (c++ is not the same as c). Show the resulting
assembly for both a C version and a Rust version, now map both the rust
version and c version to the resulting assembly.

~~~
copx
Rust is meant to replace C++, not C. The original motivation was that the
Mozilla devs were sick and tired of all the subtle ways bugs can sneak into a
large, complex C++ code base.

The ultimate aim is to write the nextgen browser engine not an operating
system kernel or some embedded code for a system with 4kB RAM.

~~~
steveklabnik
... though you can absolutely do the kernel stuff too.

------
enneff
If I read it right and the article cites the ability to add a method to the
primitive number type as "expressive", then that's a pretty narrow view of
expressivity. More examples would help.

------
allochthon
Static v. dynamic typing aside, one thing I love about Ruby is the syntactic
ambiguity of a local variable versus a method call, in which parentheses are
not needed.

------
seivan
Objective-C

@(2).days.fromNow;

or

@(2).SH_days.SH_fromNow;

