Perl was my go-to language for 8 years before Ruby so I fancied a comparison. And.. Ruby is rarely as brief, but even with my Perl hat on, it seems much easier to read, especially if you didn't write it:
Perl 6:
say " {.key}: {+.value} student{"s" if .value != 1}"
for %grade.classify(*.value.comb[0]).sort(*.key);
Ruby:
grades.group_by { |name, grade| grade[0] }
.sort
.each do |grade, names|
puts " #{grade}: #{names.length} student#{'s' if names.length != 1}"
end
Both can be improved, of course! :-) I'd be keen to see a Python version actually because I know so few of its idioms but it often does well with these sorts of things as well..
A lot of the standard lib constructs behave completely differently in different contexts. Often subtly. For instance,
@globvals = glob("*"); # returns all matches to @globvals
$glob1 = glob("*"); # returns the first match and stores it in $glob1
$glob2 = glob("*.c"); # returns the second match of "*" and stores it in $glob2
As you can see, the third call to glob returns the expected value.
Here's how glob is documented in the perlfunc document[1].
glob In list context, returns a (possibly empty) list of filename
expansions on the value of EXPR such as the standard Unix shell
/bin/csh would do. In scalar context, glob iterates through such
filename expansions, returning undef when the list is exhausted.
Context is a pretty important, pervasive concept in Perl. So much so that chromatic discusses it in the very first chapter[1] of his Modern Perl book[2].
[1]: http://modernperlbooks.com/books/modern_perl_2014/01-perl-philosophy.html#Q29udGV4dA
[2]: This very accessible, and free, book (updated very recently for 2014) is a must-read for any Perl developer.
Consistency is about expectations. It is possible to check if caller wants an array or a scalar and return either or a reference to either. And so nobody expects a single way of returning things across all the functions. Therefore such behavior is consistent.
Having implicit shared state dependent on the requested type is certainly not consistent. To a reasonably experienced perl programmer one would expect the following:
@globs = glob("*") # all matches
$glob1 = glob("*") # the first match of "*"
$glob2 = glob("*.c") # the first match of "*.c"
I agree that's the behavior you would want for that example. That is almost the behavior you get when use use File::Glob's bsd_glob function. For some reason it instead appears to return the last alphanumerically sorted match instead of the first. I've submitted a bug about that as well as the general lack of documentation on the true behavior of the CORE::glob() function, as outlined in my other comment.
I assume because you misread the example given there. If you note that $glob1 and $glob2 come from what should be different lists (all files in working dir and all files ending in .c in the working dir), then $glob1 and $glob2 should contain the first item each of their respective lists. That's exactly what the documentation says should happen.
> You would expect what it says in documentation, and it says it should iterate over the list.
Unfortunately it doesn't even do what it says in the documentation consistently. I have another comment here that outlines that fairly thoroughly. The behavior is very weird and specific to glob, and is not documented accurately.
I guess I misread the example, sorry. I thought we were talking in the context of inconsistent behavior of Perl as a language and therefore its syntax, but not in the context of an undefined behavior of glob().
We are and we aren't. elektronjunge chose an example that definitely is inconsistent, but I'm not sure that it's really indicative of Perl in general. IMHO, It's a fairly specific kind o broken, where we can't really fix the behavior because of backwards compatibility, but the documentation is just plain inadequate in this case as well.
You picked an extremely good built-in as an example. It doesn't function exactly as you've shown it, but there is very weird stuff going on. On the plus side, this is the only time I've seen something quite like this in Perl, so I'm not sure it's represents the state of using Perl as a whole very well.
glob appears to do something special, and unlike most other Perl functions where it's just a matter of knowing the context and the API the function exposes. In the versions I just tested (including 5.21.6), glob in fact doesn't quite do what it's documentation says, in the apparent effort to "do what you mean". It does not iterate through files in the result when used in scalar context, it iterates through files in the result when used in scalar context and in the exact same call-site in the program.
Here's all the files we'll test
# perl -E 'my @files = glob("*"); say for @files;
one.a
one.b
two.a
two.b
Glob acts like an iterator when used in scalar context. Here, each call returns another file.
# perl -E 'while ( my $file = glob("*") ) { say $file; } '
one.a
one.b
two.a
two.b
Glob gives the first file each time it's called in scalar context if it's not the same exact point in the source. This doesn't follow the docs, which says it will act like an iterator in scalar context.
# perl -E 'my $file1 = glob("*"); my $file2 = glob("*"); say $file1; say $file2;'
one.a
one.a
Again, we see it's not acting like an iterator. Each call is generating it's own list and returning the first file.
# perl -E 'my $file1 = glob("*"); my $file2 = glob("two*"); say $file1; say $file2;'
one.a
two.a
Here, we can see that since the same point in the code is getting hit, glob is acting like an iterator.
# perl -E 'sub myglob { my $mask = shift; glob($mask); } my $file1 = myglob("*"); my $file2 = myglob("*"); say $file1; say $file2;'
one.a
one.b
Here we see that since the same point in code is getting hit, glob is ignoring it's input and just acting like an iterator on the first input, which seems to be what you were trying to show in your example.
# perl -E 'sub myglob { my $mask = shift; glob($mask); } my $file1 = myglob("*"); my $file2 = myglob("two*"); say $file1; say $file2;'
one.a
one.b
That is very weird behavior. I agree it's not consistent with the rest of the language. I'm not sure it's indicative of the language as a whole though, as it appears to be due to weird historical implementation details that are kept for backwards compatibility. The perldoc for glob says it's implemented using the the standard File::Glob module. The perldoc for File::Glob mentions that it implements the code glob in terms of bsd_glob (the FreeBSD glob(3) routine, a superset of POSIX glob), which is function that can also be exported. In fact, the bsd_glob function, when used, acts as we would wish, without weird iterator behavior (without iterator behavior at all, in fact).
# perl -E 'use File::Glob qw/:bsd_glob/; my $file1 = bsd_glob("*"); my $file2 = bsd_glob("two*"); say $file1; say $file2;'
two.b
two.b
# perl -E 'use File::Glob qw/:bsd_glob/; sub myglob { my $mask = shift; bsd_glob($mask); } my $file1 = myglob("*"); my $file2 = myglob("one*"); say $file1; say $file2;'
two.b
one.b
Of course, this means the documentation that says the core glob routine is implemented using bsd_glob has some glaring omissions.
So, congratulations, you picked an extremely good example and unearthed some crazy Perl arcana, and possibly a bug (there's some open, longstanding tickets regarding glob bugs[1][2], which seem to boil down to "we're doing what we can to make it better, but we're hampered by backwards compatibility and weird semantics"). I think the documentation is woefully inadequate to explain what's going on in this case though.
Note: that bsd_glob seems to return the last item when used in scalar context, not the first.
You should, even if you don't use it. It's highly likely that ideas that come from Perl 6 will be adopted in some form in most other major languages at some point.
Perl's weird, it just takes a bit of everything (and not in a C++ kind of "be" everything), tosses it in a blender and then suddenly everybody has or wants smoothies in their language.
A short list of what I think we'll see percolating over to other places:
- some of the better syntactic sugar ideas, like blocks and closures, comma as a list constructor, chained comparisons
- what will probably be best-in-class Unicode support
- autothreading
- further improvements to the way regexes are used
- grammars
- junctions could become the next "thing" in lots of languages
- elements of the module system, like using two versions at once without conflict
Has it? If we're talking about Python or Ruby, I'd regard that as a small move sideways, not onwards. It's pretty much the same family of languages, and Perl6 almost seems like a new member of it, yet another alternative for Perl5, not necessarily the most likely upgrade path.
If we're talking about the world having moved on to either Haskell, Erlang or Go, Rust, well, I don't really see that being as pervasive as it often appears on trend-seeking sites like this.
And I really hope we're not talking about JavaScript…
Maybe, the alternative perl, "Perlb"? I'd also agree with the "perpetual playground" comment, and note that Perl6 originated not as something needed, but as a way to keep the developer band together.
Plus, I've always noticed a tendency in Perl in the last few years to be interesting/entertaining... sort of like it was designed by science fiction writers.
Minus the snarkiness, that's not a bad question. The linked article doesn't showcase most of the interesting stuff in Perl 6 like optional typing, junctions or macros, but there are some cool things about it.
That said, I think Python has eaten Perl's lunch, and who knows how things will evolve with the proliferation of statically-typed, non-verbose, fast functional languages? Personally, I'm not going back to dynamic languages as my primary workhorse.
Personally, the features I'm most interested in are ones you didn't actually list. They are
- grammars
- grapheme-based Unicode
- implicit parallelism
This is why Perl6 is still relevant today. Larry is hopeful that Rakudo can deliver sometime next year (not all of these features are quite there yet), but even if it doesn't, the language will remain of interest as long as there is no serious contender in sight.
I'm not following the Perl community, but I loved seeing some of Damian Conway's talk (mostly on very very fine and inspiring first class grammar) on P6, do you know some good articles about Perl6 new/weird ideas ?
OK, I thought you meant there were many more such languages - but "proliferated" means spread, so my mistake, yours is valid use.
Yes, Haskell is better known, but not proliferated in real-world usage (though jq was based on it). F# and Scala aren't seeing much adoption.
I do think type inference will be borrowed from them (and pr many other features), but not pure fp (it makes some tasks unnecessarily difficult). The growth of dynamic typed languages suggests to me that developers value ease of development over almost everthing.
I check in with what's going on in the Perl community every so often because I find I usually learn a lot more from them than, say, from the Ruby or Python community.
Perl 6 does seem to have become a perpetual playground for language experimentation, but that hardly makes it Duke Nukem Forever -- you can use it for that today.
Perl 5 is alive and well, seeing regular updates, and seems to have a future, even if its glory days as far as mindshare are over.
It's definitely leaning toward the functional side, but some things look very odd (like calling "filename".IO...). The new operators leave me dubious (eg, '*' to create a one-argument lambda function). And '»'? At least the equivalent '>>' is apparently available, but damn...
The * operator made me laugh out loud. Only Perl would have an operator called a "Whatever star".
Although, I actually think a lot of the stuff Perl does is really cool. I wouldn't want to have to maintain a codebase of it, but it never fails to be interesting. Like the whatever star. "*.value" basically saying, "Whatever value is here, gimme the value of it". It's different from a typical lambda declaration, but intuitive in its own way.
Yeah, but it would be neat if it was a word instead, for example "that". "*" is hilarious, though. How do you multiply in Perl if the star is taken? I also like that it's named "whatever star", reminds me of Scala's Pimp my Library feature[0]
It's not just one argument! I't consumes as many arguments as you like, there is also the HYPER WHATEVER which is two asterisc in a row * * (without white space a limitation of HN!) which is a dimensioned form of whatever for dealing with higher dimension data structures.
The following are identical with and without whatever:
It's worth pointing out we're lucky that 'first' is lexically before 'second' in the second one though. The $^ consumes parameters in the lexical order of the variable names. So it's used normally for $^a $^b in sorting. So .sort({$^a cmp $^b}) versus .sort({$^b cmp $^a}). But a general feature was made rather than an obscure exception. In general Perl6 is very consistent. It might also just be a bit too crazy at times, some of the stuff you can do like the (* * *)(2,3) below you obviously would just not do in real life.
So an example where the lexical ordering is kind of neat:
perl6> "ACAGATAAATTA".comb.map({$^third ~ $^second ~ $^first});
ACA TAG AAA ATT
Contrived since you could just split the string by three characters and flip it :P But hopefully you get the idea!
Could you elaborate a little? I ask because the Perl 5 code looks a lot like the Perl that I write. I'd like to improve, but the existing Perl code that I'm writing on top of is really bad. Like really, really bad. I don't have much quality Perl to compare against.
my @letters = map { substr $_, 0, 1 } values %grade;
$freq{$_}++ for @letters;
----------------------------
and
open my $fh, '<:utf8', "grades.txt"
or die "Failed to open file: $!";
my %grade;
while (<$fh>) {
m/^(\w+) \s+ ([A-F][+-]?)$/x
or die "Can't parse line '$_'";
$grade{$1} = $2;
};
better:
use 5.010;
use IO::All -binary, -utf8;
my $line_format = qr/
^ # line start
(?<student>\w+) # name
\s+ # gap
(?<grade>[A-F][+-]?) # grade
$ # line end
/x;
my $fh = io->file("grades.txt");
my %grade;
while (my $line = $io->getline) {
die "Can't parse line '$line'" if $line !~ $line_format;
$grade{$+{student}} = $+{grade};
}
Dammit, you're right. I was working in a buffer without perl instrumentation enabled because i was composing a comment, not a script. Now i can't edit the comment anymore. :/
Exactly. Perl's critics often completely overlook the degree to which liberal use of the map/reduce paradigm can simplify the graphical representation of your code and allow the powerful list processing features to be brought to bear. Consider:
I think Perl 6's killer feature is actually concurrency. P6 provides a number of ways to do reactive programming, channels, actor model semantics, etc. It even offers some new ways to approach modeling concurrency and asynchronous logic.
If you want to use a dynamic language (albeit also offering optional static typing) with powerful concurrency options that doesn't also involve s-expressions, I think P6 might be one of the few options available. I'm not familiar with Ruby, but from what I've heard the default runtime has some limitations when it comes to concurrency, and while it's been a while since I used Python, I recall hearing that it also has some problems with handling concurrency.
I don't currently use either but Perl6 grammars[0] are pretty dang awesome. If I were writing parsing code and my choices were Ruby or Perl6 I would choose Perl6 just for the grammars.
The short answer, is that the comparison isn't fair to either communities. Perl 6 has many killer features. Concurrency, Macros, Grammars, Regex goodness, functional programming features, OO features, Strong typing support, user defined operators, multiple VM backends, Continuations to name a few. But there are many many others.
Perhaps the most killer feature of Perl 6 is like lisp, you can use the language to grow itself without breaking backwards compatibility. This alone will ensure that the community can grow without worrying about breakage, regression and backwards compatibility.
Ruby and Perl 5 definitely have more libraries, users and a far more mature ecosystem of things. But I guess the sheer awesomeness of Perl 6 will attract a lot of developers and that will have a healthy community of its own.
In short I think there are plenty of reason why one would chose Perl 6 above Ruby.
I thought of a few troll-worthy comments, but then moved on to others. Few things came to me though later that are less abrasive so I guess I'll share these nicer sides of my opinion:
I don't know if it will be the same for perl6, but at least with perl5, backwards compatibility was rarely broken. Every time my debian-testing box upgraded a ruby version, it broke something. From 1.8,1.9,2.0, and I think 2.1 actually worked without any issues...
This is never an issue with perl5. And before you say "perl is dead it never changes", go look on the perl5porters mailing list. There are a ton of changes going on. I had every intent this year to join the dev team, but the commits come in so quick on that mailing list, I can never find a place to start. When perl5 updates to a new version (which has been happening once a year for like 5 years now), it is not just a few bugs that are fixed, but a whole lot of new features are added and plenty of new syntax to scare the heck out of anyone. So an argument of the ruby dev cycle is 'slower' probably won't hold up.
Onto more perl6 specifics, I think one of the original goals was perl6 being able to compile a script into a raw binary. Don't know if that feature will be implemented. Ruby can't do that, though it does have the JIT compile stuff. So I guess that could be one reason..
Perl6 is new, so I guess it is full of new "technology and stuff" right?
I personally never did much with ruby. I tried to use it as my first language to learn with. I signed up on some hacker forum wanting to learn to hack with my new found ruby skills... They just booted me with the message of "ruby aint really programming". This was back in 2007, but I never could get over that...
From then on I only used Ruby when I couldn't figure out how to do something in perl. So none of my ruby scripts have ever been seen by another pair of eyes. I keep them all hidden, knowing that they are cool and all in their own right, but knowing if I were just a little smarter... I could have written it in perl...
Not true for Perl6 at the moment. Perl6 is an entirely different language with its own implementation. At the moment it will be either on par or slower than Ruby. For some select numerical operations Perl6 is faster than Perl5. But for the rest it's between 100x and 10x slower than Perl5. This is what is being worked on now up to the official release of 6.0.0 though.
Ruby instead of Perl5 (not Perl6)? Because I found Perl5 really hard to write maintainable comprehensible code in, requiring weird magic boilerplate, and full of gotchas. Could just be me, maybe could have been overcome, was just my subjective opinion. And I like writing OO code in Ruby style. Also, like many others, what brought me to ruby was wanting to check out Rails (although now I'd say I have fonder feelings for ruby the language than I do for Rails the framework). All just my own opinions and experiences.
Those sorts of answers from other's perspectives is what i'm interested in for Perl6 (not 5) over ruby. I think I know many reasons to prefer perl5 over ruby (although I don't), but once you leave perl5 for perl6... I'm not sure why you wouldn't just ruby (or python). But could be just cause you're used to Perl and like Perl. Or could be because there's something about Perl6 you especially like over ruby/python. Etc. I think it's a reasonable question?
You wont get many opinions of people's experience of Perl6 from Hacker News. Just a lot of rage and assumptions it's somehow just like Perl5, which it's not. There are only a few of us (tens, maybe a hundred very quiet people) actually writing it daily. With no official "released" 6.0.0 version it's not supported that well by Linux distros yet either. Like if you install Rakudo on Debian stable right now it would be about two years out of date iirc. In the last two years a custom Perl6 VM was written with a JIT compiler and tonnes of concurrency stuff was added! You can however easily install it yourself and keep up to date with rakudobrew http://perl6advent.wordpress.com/2014/12/02/day-2-rakudobrew...
My $0.02 is you'd mostly want to learn Perl6 if you were after something like Ruby but perhaps with more room to express yourself and form a community and build new technologies. At least I really love the language and find it very expressive and intuitive to use and it has a lot of features useful for doing Bioinformatics built in (something I'm going to add to the P6 advent calendar on the 15th Dec). The extra level of detail ten years of design puts into a language like all the funky features working together and being consistent helps a lot. So much of the cooler expressive/functional stuff in Python feels out of place like lambda and list comprehensions. Maybe I'm wrong but I wouldn't write anything /that/ complicated or important using Python lambda or list comprehensions. They just don't play that well with the surrounding code you will have to write in a different style. I can't attest to Ruby since I don't know it well. I felt it was quite close to Perl and I'd be better placed learning Python instead, especially for a job writing code that wasn't for the web.
Perl 6 tries to address many of the Perl 5's gotchas, including OO. Throw in familiarity into that and a lot of new features that scratch various itches and you would understand why some Perl 5 people would want to try Perl 6, but not Ruby.
Maintainability, productivity, etc. of Perl 5 is on the same level with Ruby. And I don't think Perl 6 improves on that, so it's probably the same too.
Personally I don't see any benefit for anyone to switch between mentioned languages. They all have similar problems.
I feel like python provides a more readable solution.
#!/usr/bin/env python3
with open('data.txt', 'r') as f:
data = dict(x.split() for x in f if x.strip())
print("Zsófia's grade:", data["Zsófia"])
print("List of students with a failing grade:\n ",end="")
print(*[x for x in data.keys() if data[x] == 'F'], sep=", ")
print("Distribution of grades by letter:")
for k,g in groupby(sorted(data.values()), lambda x: x[0]):
print(" {}:".format(k), sum(1 for _ in g))
A working version with some stuff improved - groupby implemented, len() instead of sum(), a less squished version of printing, "student(s)" added:
#!/usr/bin/env python3
from collections import defaultdict
def invert_dict(data):
res = defaultdict(list)
for k, v in data.items():
res[v[0]].append(k)
return res
with open('data.txt', 'r') as f:
data = dict(x.split() for x in f if x.strip())
print("Zsófia's grade:", data["Zsófia"])
print("List of students with a failing grade:")
print(" " ,end="")
print(*[x for x in data.keys() if data[x] == 'F'], sep=", ")
print("Distribution of grades by letter:")
grouped = invert_dict(data)
for k, g in sorted(grouped.items()):
print(" {}: {} student{}".format(k, len(g), 's' if len(g) > 1 else ''))
No need for `invert_dict`; you're just counting so `collections.Counter` would work fine:
from collections import Counter
with open('data.txt', 'r') as datafile:
students = dict(line.rsplit(maxsplit=1) for line in datafile)
failing = [name for name, grade in students.items() if grade >= 'E']
grade_counts = Counter(grade[0] for grade in students.values())
print("Zsófia's grade:", students["Zsófia"])
print("List of students with a failing grade:")
print(" " + ", ".join(failing))
print("Distribution of grades by letter:")
for grade, count in sorted(grade_counts.items()):
print(" {}: {} student{}".format(grade, count, 's' * (count != 1)))
> treating regex like it's normal code despite the fact that regex could match a keyword easily
I'm confused by this statement. So you could match a Perl keyword with that regexp? What about it?
> It's mixing looping logic with exception logic
It depends on the context, but that's five lines of readable code. Exposure to 20th-century codebases may have lowered my standards, but I don't find it particularly shocking, especially for something that looks like a quick text file processing script. What do you suggest that makes for better code?
The postcircumfix {} is still there, and used. It just unambiguously takes an expression, and doesn't try to intuit string quotes in some cases. So you use {} for indexing with a general expression, and <> for string indexing.
Perl 6:
Ruby: Or if it had to be as 'functional' as the Perl.. Both can be improved, of course! :-) I'd be keen to see a Python version actually because I know so few of its idioms but it often does well with these sorts of things as well..