Why I’m Learning Perl 6(evanmiller.org) |
Why I’m Learning Perl 6(evanmiller.org) |
Well, as a matter of fact I really got no output at all :(
http://blogs.perl.org/users/zoffix_znet/2017/07/the-hot-new-...
https://github.com/perl6/marketing/blob/master/TablePosters/...
Why mention .NET here? Async code in .NET works with a dispatcher thread (-pool) under the hood, which is nothing like the greenish threads that the other systems offer. Am I missing something?
You can also just call Windows fibers directly, if feeling adventurous.
It might not be under the hood, but as perceived to the programmer it is?
As far as web development in perl..well have fun convincing everyone on the node.js and python bandwagons to move 'back' to perl. Glad it works for you.
Python comes with a C module for interacting with an embedded Tcl interpreter (Meaning Tcl and Python interpreters are running within the same process). It's mainly used for GUI programming from Python but the interface is generic enough to be able to write some nice iterop code.
For example, I have a library that seamlessly lets you use Python classes and objects from Tcl and vice versa.
From there we merged the Tcl and Qt event loops so that we could have PyQt widgets playing nicely with Tcl/Tk widgets.
This was all part of an effort to migrate from a Tcl/Tk codebase to Python/PyQt.
I wish I could share the code because it's kind of neat. Only around 600 lines of Python code to do all that magic.
TCL has an interesting history, it's creator is also known for Ousthout's Dichotomy[2] which is useful when disambiguating between "scripting" and "other" languages (an argument that occurs frequently on the internet).
Antirez has written about TCL, if you're an admirer of him or his work (redis) you may find his take on it interesting[3].
[1] https://en.wikipedia.org/wiki/Tcl#Uplevel
One of its great strengths was being extensible. It's pretty easy to bake some functionality into Tcl and provide a new shell with it. This is what expect (http://expect.sourceforge.net/) is, for instance.
Another early advantage of Tcl was Tk, which provided Tcl extended with GUI stuff. Tk was cross-platform and established very early on, so if you wanted to provide one GUI for Unix, Mac and Windows, it was a good choice. It was very ugly for a long time; I hear it has gotten better lately but I don't know that myself. Tk is still built-in to Python as Tkinter. This is another specialty shell provided by Tcl, wish ("window shell").
Tcl was supposed to be good for embedding. The original author was supposedly using it for robotics. I haven't seen much energy there; I think Tcl's lunch probably got eaten by Lua some time ago. Lua is smaller, more like a modern programming language and easier to embed, but Tcl is more like shell scripting, so I guess there might be style reasons why you might prefer Tcl despite Lua's advantages.
Tcl is easy to disparage, but there's a lot of misinformation out there. At one time, it really only had one data type (strings). Quotation is a big topic in Tcl, but like Lisp, you can supply your own control structures. Everything is a command and commands have a lot of freedom about how to interpret their arguments. It is rather Lispy, unlike Lua. Tcl does have modern data structures and it does pretty snazzy things behind the scenes to optimize access to them while making things still seem to be strings for the cases where that matters.
Tcl is still around in some surprising places. I wouldn't expect there being a lot of new development starting with Tcl but it's kind of a fun language to know about. I got the newest edition of the book and it's a neat book, but I confess I haven't found ideal use cases for it.
Some of the things I've written are: a simple standby order input app, a Forms/Reports compiler (sends file to server, waits for response and informs user), and a Ghostscript wrapper for merging lots of PDFs into one file.
I don't want to go back.
M:N is what you do when you don't have real kernel thread support. You hacked N user space threads per process (N:1) but then 1990 rolled in and cheap SMP with M processors started to spread across the land, so you needed N:M to take a bit of advantage of that.
>There’s no GIL, so unlike Those Other Languages
There is no GIL in EVM as well, but playing with the words you make it look like "all are the same". This does not deserve top HN and just stupid.
If your Perl6 VM is _so_ great, why didn't you mention anything about what's really important like preemptive scheduling which has been Erlang's unbeatable feature since the beginning? Less hype and more details, please.
I'm only peripherally involved in the community, but at this point, it seems people are using it primarily for small utilities and such.
The Perl 5 porters development list has a decent amount of traffic ( http://www.nntp.perl.org/group/perl.perl5.porters/2017.html ). And Perl 5 still has a good number of active contributors ( https://www.openhub.net/p/perl/contributors ). Also, The CPAN has plenty of modules with daily activity ( http://search.cpan.org/recent ).
Even if you would not choose to do new development in Perl 5 (because of other language preferences, or whatever), that does not mean it is unreasonable (or "insane") for others to do so.
Whether Perl 6 will ever catch on, I don't know. The language has a lot of features that look interesting, and I think it could also be very useful. I personally have not had time to look at it much yet, but probably will when I don't have higher priority things in life preventing me from doing so.
The reason is the swig bindings for Tcl are sufficient to generate decent package cores for just about anything I care to extend and I can optimize later.
Yes it is, and that's a good thing. Over the years Tcl has become increasingly Scheme-like in some respects. In fact without too much trouble I've ported some programs I'd written in Scheme to Tcl because of Tcl's portability and easy access to GUI when needed. OTOH adapting some code from Javascript to Tcl was harder.
Tcl is very useful for utilities. Library support is enormous so there aren't a whole lot of things that are impossible in that domain. For ~20 years I used a a client-server scheduling program that I wrote entirely in Tcl/Tk. It required only minimal maintenance, in fact hadn't touched in over the last 10 years it was deployed.
The main difficulty has been marginal debugging support, but there are ways around it so it's not insurmountable.
I've seen it embedded in load balancers and used for test harnesses for embedded devices.
https://devcentral.f5.com/articles/irules-101-01-introductio...
Putting aside the "web scale" jokes (http://www.mongodb-is-web-scale.com/), this statement is still absurd.
Every major language, or at least the ones that matter for backend development, has support for thread multiplexing / coroutines / fibers, whatever. Perhaps not in the core language syntax or standard library. But it's easy to implement with native code, and so SOMEONE has implemented it in a library if the language has an FFI.
Java, and all of the other JVM-based languages in turn, have Quasar (http://docs.paralleluniverse.co/quasar/).
Ruby has support for primitive fibers baked into the standard language (https://ruby-doc.org/core-2.1.1/Fiber.html), and likewise community gems with more robust functionality like Quasar.
Python 3 likewise has this out of the box (https://www.python.org/dev/peps/pep-0492/).
The list goes on and on: https://en.wikipedia.org/wiki/Coroutine#Programming_language....
M:N threading is not, from a developer perspective, the same thing as coroutines/fibers. Coroutines are lower-level; it's possible to build something like M:N threading on top of coroutines, but it means doing a lot of work that's already done for you in a language that provides M:N threading out of the box, and if you have to do it yourself, that also means code you use from others isn't going to integrate well with it generally.
Not sure I understand what you're trying to say here, this sounds exactly backwards to my reading. A green threading library can be written in assembly language with no reference to anything but the hardware ISA specification and the ABI that defines the relevant calling convention. It's as "low level" as a software construct can be. The only lower level implementation I can think of are hardware-assisted context switching features like you see on some embedded architectures.
Coroutines, on the other hand, are artifacts of the language runtime and depend in sensitive ways on that whole stack. Much higher level.
If it's about serving a great number of concurrent connections, I'm not sure why M:N is the only way to go.
I much prefer the go/erlang version of this. Playing the "what color is your function" game or "will it block" is no fun.
https://github.com/ruby-concurrency/concurrent-ruby for those wondering. Rails uses it as of version 5.
Now, I preferred correction to downvoting, but I can easily see why others would choose downvoting.
It's probably just my age, as I started out online when most discussion forums lacked explicit voting mechanisms. Before social media came along, and "gamified" human conversation. Maybe it helps police trolls, but at the cost of encouraging group-think and excessive meme-referencing, and just making a lot of people more neurotic and insecure in general. Overall, I think that online discussion is worse than it was 15 years ago, due to the psychological effects of all the imaginary Internet points.
But anyhoo, at the time I saw this (45 minutes after my original comment), my comment had 25 votes and was the top-most thing on this page. More often than not, if you wait an hour, the crowd straightens out the impact of any early outliers.
Like dragonwriter said, having coroutines in your language or a fiber library is not close to the same thing as the language having a scheduler that maps those fibers onto native threads for you. Some of the languages mentioned can't even execute multiple threads on multiple cores in parallel.
Because for some people, it's not enough to think of others as misinformed, or not so well-spoken.
They need to be thought of as blundering idiots, and castigated accordingly.
I learned ruby, perl5, python, lisp, forth, ml, ocaml, scheme, haskell, r, c#, java, lua, c++, factor, idris, asm, erlang, prolog, rust, d. But that wasn't quite enough because haskell and idris kept on talking about complicated type theory stuff. So I also learned lambda calculus, type theory, set theory, domain theory, topology, category theory, information theory, sub-structural logic.
What I'm trying to say is that I'm not afraid of learning new things. Even if they seem hard or esoteric.
When I heard that perl6 was ready, I took a look. I like the idea of a lot of what is present in the language (hey look, a grammar engine, that's neat). But ultimately, I decided that it was too much stuff that I would have to learn. Maybe that's just a perception problem on my part, but I have to think that they have some sort of problem if someone like me feels overwhelmed by all of the things that you have to grok in order to understand the language.
But it has been a decade and I am truly impressed with what it has turned into. Unfortunately, it has to re-gain mindshare as if it was starting from scratch. It might be a little bit harder actually, because there are a variety of scripting languages these days that are easy to learn, and there are still more than a few people who actively don't like Perl.
I really liked this slideshow: http://tpm2016.zoffix.com/#/
It gives a good review of Perl6 from early 2016. The video is an hour and a half, but it only takes a few minutes to scan through the slides and find the interesting pieces. (left and right arrow to navigate the slides)
EDIT: as seen in other responses, options abound: Python 3, Elixir, D, Dart and more all have built-in concurrency primitives, not to mention that discarding nodejs because of "callback hell" is at this point laughable.
[0]: http://vibed.org/features
Note: Vibe.d is a web frameworks as well as a networking stack for D. If you're even remotely curious about D or do any web development I recommend you check out Vibe.d it is quite impressive (at the very least to me).
Edit:
There's apparently jin.go / Go.d:
I guess the author doesn't know about Haskell? The concurrency story for Haskell is great, and using the right library, you can literally just define types for the routes for your backend and then ask it to generate JS for your frontend.
It is of course possible to gain slowly over time, but with numerous languages competing, that path may be a dead-end.
But such gems tend to be unique, also in completely different fields. And it probably wouldn't have been possible if the original developer hadn't been involved and gained experience through Catalyst, which is roughly Perl's take on Ruby on Rails.
If it got a bit more press in its early days, outside of the Perl community I think the world of web development could look very different today.
As I said I am not a Perl developer anymore (and don't focus on the web part anymore either), and I am sure there is a bit of nostalgia in those lines, but even though there are great frameworks in other languages, no doubt, there is impressive projects, I still didn't get something that allowed such a kind of rapid development and transitioning prototype applications into production services in such a seamless manner.
I think it's also a great example of Perl being very readable despite all the ugly scripts you find out there - of course also because a big bunch of it being written by non perl developers who just needed to get a script going. Just like with JavaScript and PHP.
It's funny though how the presence of a well done web application framework nowadays is considered the killer application in a pretty consistent way. I've seen that in various languages that weren't really made for web application programming as well. There is obvious reason, as it's the agreed upon API for most applications, however I wonder if this might not bring stagnation and kind of a cycle that only allows a certain way of thinking and developing to succeed, as standards and programming languages/frameworks co-evolve.
For long-running tasks, if they are I/O bound you can use non-blocking I/O and event loops. If they are CPU bound, then use threads or separate processes. The two techniques can be combined to scale well across multiple cores.
The OS is designed to schedule workloads, it has decades of development in doing this, and has all the system-wide information needed to schedule tasks well across the CPUs. why re-implement the wheel in your programming language?
But imagine how cool it would have been - write a class in Python, sub-class it from Ruby, use that subclass in a function written in Perl, call that function from Lua code...
Okay, I see it now. It was just too awesome for its time. Maybe in a hundred years or so...
But regardless, having fun with P6, which is specifically designed to be readable, maintainable, composable, refactorable, etc. is fundamentally different from having fun with a language like P5.
I'm enjoying using it for command-line tools and web applications - it's expressive and scales with the problem space.
1. https://perl6advent.wordpress.com/2015/12/20/perl-6-christma...
Right now I feel the major reason that keeps me from investing time in Perl 6 - besides adoption by distros - is the lack of a good book, like the Lama and the Camel book for Perl 5. It's kind of frustrating after having waited so long.
Looking at the Perl 6 website, there are a few more coming along as well. https://perl6.org/resources/
This has improved, and I think most distros now ship Rakudo (Perl6 on MoarVM). At least: Debian, Ubuntu, Fedora, and Arch do.
Rakudo is the only currently supported implementation (ignoring several specialized partial implementations), it is also open-source.
Here's an example where a naive Perl 6 version is going to beat the pants off of the naive version in another language:
Find the sum of all integers from 10 to 1000000 inclusive.
In Perl 6:
say [+] 10 .. 1000000
the [+] is a left fold using the &infix:«+» operator (&infix:«+» is left associative)This finishes almost immediately. (It also does so if the endpoint is 10¹⁰⁰⁰ or more)
The reason is if you use the built in version of the &infix:«+» operator, it calls the sum method on the Range object. That particular method knows how to calculate the sum on the Range without iterating through the values. If you modify the &infix:«+» operator lexically it won't take that shortcut.
This also works the same if you wrote it like this:
say Range.new(10,1000000).reduce(&[+])
(There is syntax to omit either or both endpoints with both ways to create a Range)There are plenty of areas where Rakudo Perl 6 is slower than I would like, but for many uses it is fast enough.
It's remarkably slow right now.. It takes 1.5 minutes to generate an image. I tried to do the same thing in Julia and it takes 1.5 seconds. So I guess I must be doing something wrong!
> or Nginx or Node.js without the callback cacciatore.
What's a cacciatore in this context? It means hunter in Italian. Probably something messy if it means the same as callback hell.
Kinda like "gabagool" which is effectively an american only name for an italian product we call "capocollo".
pmontra is italian, hence the confusion.
In #devops is turtle all way down but at bottom is perl script. - @devopsborat
Way ... way back in the day, the argument was it would save memory, encourage reuse, etc. While some of this may have been true, it also gave rise to dynamic library hell. Reuse was overshadowed by incompatible versions, or API changes between versions that became the stuff of legends.
This would make a great study in how the ROI was completely overshadowed by risk considerations not considered at the outset.
Of course, saving memory was a huge issue in the days when 1GB RAM was considered gigantic. So maybe the cost was worth it.
[1] https://scalability.org/2017/03/what-is-old-is-new-again/
The Perl community has spent a couple decades "enjoying" taking (or being forced to take) a long hard look at that.
One result in P6 is :auth (authority), :api (api version) and :ver (package version) qualifiers, eg:
use Some::Module:ver<1.2>:api<3.4>;
with corresponding support throughout the language and packaging tools.Aiui this aspect of the language and tooling needs work but shows promise.
Could you expand a bit on how static linking would cause either of those things? It seems to me that they are both consequences of dynamic linking.
Perl gives the programmer a lot of rope to work with. Whether one ties that rope into a drawbridge or a noose is the programmer's choice.
Also, CPAN is fantastic and widely supported. I really wish more languages would follow its example, especially when it comes to package namespaces.
In Python I can expect the standard library to be consistent and be maintained as part of the core language. Now I don't mean that everything has to be in the standard library (I'm fine with the database access libraries being third-party for example), but in Perl even the most basic stuff like exceptions is only on CPAN and there are again multiple packages to choose from..
Mostly an urban myth. Where it isn't, at least 8 of those ways are actually important.
Exercise: write a python program that prints Python's quoting documentation, without external files, without editing the documentation. Spoiler alert: it's impossible.
sub add1 {
my ($arg1, $arg2) = @_;
$arg1 + $arg2;
}
sub add2 {
my $arg1 = shift;
# possibly two screens of dense code here, then
my $arg2 = shift;
$arg1 + $arg2;
}
sub add3 {
$_[0] + $_[1]
}
# There may be some other ways to extract arguments
# that I am not aware of.
# It's possible to combine any of the methods!
And this is just argument access, one of the core language primitives.Last year I attended a conference and saw Larry Wall speak. It was an overview of Perl 6 and I was completely underwhelmed. Larry spent about half the time talking about unicode support. It wasn't a boring talk, but I never felt a moment when I said, "Awesome! This is a pain point in some other languages and I would pick up perl6 if this ever happened again."
I don't want to write perl6 completely off, but I have found that perl6 advocates have not done a great job on why you would actually want to use it. It's hard to justify learning "different" syntax just because someone says, "hey, it's fun!".
From what I've seen perl6 makes some operations more convenient. Cool, I guess, but not a great reason to reach for perl6.
Grammars are really cool, and one reason I might reach for perl6, but you also have really great special-purpose tools like Antlr. I suppose I don't feel the need grammars that often to need them reified in my language of choice, but next time I need to do some parsing, however, I will probably play around with perl6.
Perl: Good at regular expressions, and proper per-processor core threading, Bad because what you wrote will be indecipherable to future you
Python: Very expressive (without being too much), lots of library support, good drop-to-c/do-stuff-fast support, sensible support for object and functional programming paradigms that evolved over time, super useful stdlib. Bad because GIL
Ruby: ???? at least it's beautiful? seems to only be used in codebases that are rails-based which is basically a red flag for me now due to personal preference
It looks like Perl just got another killer feature
Ruby has been referred to as Perl 5 with a better object system.
Perl 5 also has Moose an object system based on Perl 6's which has been ported to Python and to Ruby twice.
The way to allow calling into C with NativeCall is almost seamless most of the time. It has also been used to make using modules/libraries from other languages even more seamless. (usually the only way you know the module is from a different language is the :from<Python> in the use directive.)
No other reason, that's all. Just the syntax.
I haven't looked at Perl 6 yet, don't know if that's better or worse. Too many other things to do, and I don't think I'll ever work with Perl professionally again, that ship has probably sailed.
Larry Wall came and made a language inspired by awk that doesn't give feedback on when you're out of the comfort zone. This lets you make a mess before you know what you've done (aka technical debt). The same problem 'that killed Smalltalk and could have killed ruby' too (thanks, Bob Martin).
Perl tries to be everything at once, still mysteriously lacking interactive REPL out of the box. Yes, Python is more heavyweight for scripting, but it pays in the long term with better maintenance and tooling for big scripts.
It sounds like you are justifying an emotional attachment rather than making consistent arguments.
Now, there is nothing wrong with having an emotional attachment to a language or other tool, as long as you know it for what it is.
Python is a great language and has some really nice features, it's C extension system is miles better than Perl 5's horrible mess called XS. But to me, using it feels like wearing my shoes on the wrong feet, I really prefer Perl 5.
From my experience and from listening to colleagues, Python has one edge on Perl 5. Perl 5 and Python have very similar tooling abilities. Long term maintenance of large systems is pretty much the same. Python has the edge in ability to find and hire qualified people because the community has done a better job articulating Python's strengths and recruiting new people.
In fact there are two flip-flop operators that exist in Perl 5, one which works like the one in awk `..` , and the other like the one in sed `...`. Perl 6 renames them to `ff` and `fff`.
I remember hearing/reading that the release of Perl was delayed until a2p and s2p were written, specifically for people who used awk and sed to be able to get going quickly in Perl.
Some people (such as me), prefer designating variable scope and easily identifying where a variable was defined. By the way, that isn't required by Perl 5. It was decided by the community that it was beneficial in that it solved far more problems debugging than the small bit of extra effort required, so the common refrain to "use strict;" was adopted because with that pragma it then is required.
> Why can't an advanced language like Perl 6 scope variables without littering "my" everywhere?
It could, but the community has decided it's not really beneficial. Have you actually examined why you think it's better?
> obligatory "use v6;" at the top of every script
That's not obligatory. You can omit at your leisure. If you do in include it and your script is accidentally called from a Perl 5 interpreter, you will either get an error, or if supported it will try to load a Perl 6 interpreter to parse the code.
I strongly disagree: From a languge design perspective, block-level lexical scoping with explicit declarations is superior to all alternatives I'm aware of.
var creates variables that are function scoped. my creates variables that are block scoped.
let in JavaScript would be pretty much the same as my.
Both are good things as they prevent typos from being new variables.
So, if you want to learn Perl6, don't worry about writing "baby talk" Perl 6. Write something that works and solves your problem. Later on you may learn that there are multiple other ways to express the same ideas you wrote already. Maybe one of them better expresses the heart of your algorithm, then you update your code.
Now, you may think, how the heck am I going to maintain code written by a bunch of people at different phases of learning Perl6?
The Perl community has responded to this challenge by putting a huge amount of work into writing excellent documentation. Perl6 docs are readily searchable and carefully indexed. It's a big, new language with lots to write about, so the docs are not yet complete, but they are already fantastic and keep getting better.
So, please, just pick a problem to solve and jump on in. The water's fine.
As a result, a language like Perl is very nice and maybe even artistic but in many situations downright "dangerous".
In a way that's correct, because ultimately Perl6 is designed as the ultimate "kitchen sink" language; it has all the little features you could think of already baked in, which will include a lot of features you won't use.
The main reason for this stems from the overarching design philosophy; "There is more than one way to do something". The language ultimately tries to be as flexible as possible, going so far as to support modification the the core grammar, the object system, etc.
This is meant to make the developer as comfortable as possible, but it can lead the common case of perl-itis, also known as "write-once, read-nonce" code.
Conversely, whereas the P5 motto was TIMTOWTDI, the P6 motto is closer to TIMTOWTDIBSCINABTE, so there's usually relatively few idiomatic ways to do something.
With all due respect, you didn't "learn" these languages. You got a superficial understanding of these languages. It's like someone who learns to say "hello, name is John, what is your name" in 17 human languages saying they learned 17 languages.
Being able to write "hello world" in all these languages doesn't mean you "learned" it.
Learn You A Haskell for great good (http://learnyouahaskell.com) is an excellent introductory book to read to get started with Haskell.
A drier but more in-depth read is http://book.realworldhaskell.org/
Also, when you hit monads again, the best advice I can give you to understanding them (99% of Haskell guides out there seem to be about monads) is to take this path:
Functors -> Applicatives -> Monads
And check out this link: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_...
I'm not sure those resources were around when last you tried haskell, but they're worth giving a read.
Haskell has M:N threading, and a strictness about types and semantics that's very refreshing to me. Yes, I could have written my most recent web project in some other language that would have been quicker (because it would have been simpler and likely more imperative), but I trust my codebase so much more with Haskell. Ironically it's my own understanding and clarity of thinking that I sometimes doubt now, but that's a good problem as far as I'm concerned, makes me think clearer.
[EDIT] - Can't believe I forgot, but Erik Meijer's series on Haskell is actually one of the first resources that actually got me really into it, it is absolutely fantastic, I actually watched the whole series before I read learn you a Haskell.
https://channel9.msdn.com/Series/C9-Lectures-Erik-Meijer-Fun...
tl;dr: 1) If you want to buy a book, http://haskellbook.com/ is supposed to be good
2) If you instead want to learn from free online sources, a) cis194 spring 2013 followed by b) The data61 course (links in the github link above).
Parrot was implemented in C, and to my recollection implemented various iterations of bytecode syntaxes, notably PBC (parrot byte code).
Rakudo Perl 6 developers took the route of developing Perl 6 through an intermediary language NQP, which is a language aimed at making it easy to implement interpreted languages.
Rakudo (the implementation of Perl 6 that run on MoarVM) is implemented in NQP, and started out on Parrot through an implemention of NQP for Parrot.
NQP[1] stands for Not Quite Perl, and shares various features with Perl, but is much more constrained in features as capabilities, making it easier to implement in a VM.
Because it's mostly implemented in NQP, it's fairly easy to port to a new VM, as all you have to do is get NQP running on that VM, and Perl 6 is mostly ported. For example, this is how the Java port happened within a few months of announcement. NQP was implemented, and then 95% of Perl 6 was already working.
When a new VM instead of Parrot was desired, MoarVM was conceived of as a VM to natively run NQP (rather than NQP on top of the native VM bytecode). Porting Rakudo to it was relatively easy, since Rakudo targets NQP, not a specific VM.
Pugs, conversely, was an interpreter/VM for Perl back in the days of Parrot, and ran its own Perl 6 implementation directly (no intermediary language, and it wasn't Rakudo). It was writtem in Haskell.
Niecza was another direct implementation of Perl 6, but implemented in C# for .Net. I don't believe it used NQP.
Truly an interesting feature
Here is a working grammar for INI config files: https://github.com/tadzik/perl6-Config-INI/blob/master/lib/C...
I could not even find in the standard documentation which kind of parsers it supports, is it LR(k)? LL(k)? Any CFG? What parsing method does it use: recursive descent, shift-reduce, something more general like CYK/Earley algorithms? How does it handle ambiguities?
It turns out that `grammar` keyword generates recursive descent parsers and anything even a bit fancier still requires a dedicated parsing library.
Disclaimer: I do not hate Haskell, I just don't "get it". If you love the language, fine, have as much fun with it as you possibly can. I for one have given up, at least for now. Maybe I'll try again next year.
[1] NB that the word "stubbornness" has three pairs of double consonants. How fun is that?
If you feel like trying a pure language again someday you could consider PureScript (which just has the first 2 things above) or Elm (which just has the 1st). If you do try I'd like to hear how it goes, email in profile.
I understand the tutorials and have read though the books available, but when I try to use the language on a small real project, say a 2d barcode generator, I have trouble getting off the ground. Its not even clear to me which set of features are recommended for the language. It seems like different users program in different, incompatible, languages! Here is a list of optional language extensions supported by GHC:
Cpp
OverlappingInstances
UndecidableInstances
IncoherentInstances
UndecidableSuperClasses
MonomorphismRestriction
MonoPatBinds
MonoLocalBinds
RelaxedPolyRec
ExtendedDefaultRules
ForeignFunctionInterface
UnliftedFFITypes
InterruptibleFFI
CApiFFI
GHCForeignImportPrim
JavaScriptFFI
ParallelArrays
Arrows
TemplateHaskell
TemplateHaskellQuotes
QuasiQuotes
ImplicitParams
ImplicitPrelude
ScopedTypeVariables
AllowAmbiguousTypes
UnboxedTuples
UnboxedSums
BangPatterns
TypeFamilies
TypeFamilyDependencies
TypeInType
OverloadedStrings
OverloadedLists
NumDecimals
DisambiguateRecordFields
RecordWildCards
RecordPuns
ViewPatterns
GADTs
GADTSyntax
NPlusKPatterns
DoAndIfThenElse
RebindableSyntax
ConstraintKinds
PolyKinds
DataKinds
InstanceSigs
ApplicativeDo
StandaloneDeriving
DeriveDataTypeable
AutoDeriveTypeable
DeriveFunctor
DeriveTraversable
DeriveFoldable
DeriveGeneric
DefaultSignatures
DeriveAnyClass
DeriveLift
DerivingStrategies
TypeSynonymInstances
FlexibleContexts
FlexibleInstances
ConstrainedClassMethods
MultiParamTypeClasses
NullaryTypeClasses
FunctionalDependencies
UnicodeSyntax
ExistentialQuantification
MagicHash
EmptyDataDecls
KindSignatures
RoleAnnotations
ParallelListComp
TransformListComp
MonadComprehensions
GeneralizedNewtypeDeriving
RecursiveDo
PostfixOperators
TupleSections
PatternGuards
LiberalTypeSynonyms
RankNTypes
ImpredicativeTypes
TypeOperators
ExplicitNamespaces
PackageImports
ExplicitForAll
AlternativeLayoutRule
AlternativeLayoutRuleTransitional
DatatypeContexts
NondecreasingIndentation
RelaxedLayout
TraditionalRecordSyntax
LambdaCase
MultiWayIf
BinaryLiterals
NegativeLiterals
DuplicateRecordFields
OverloadedLabels
EmptyCase
PatternSynonyms
PartialTypeSignatures
NamedWildCards
StaticPointers
TypeApplications
Strict
StrictData
MonadFailDesugaring
I've seen different subsets of these extensions recommended. Is there some canonical subset recommended by anyone doing real work in Haskell?
Even more off topic: in reference to your note about the word "stubbornness", the word "bookkeeping" has three consecutive doubled letters. I believe that there are no examples of four consecutive doubled letters in any ordinary English dictionary, but I ought to grep the longer lexicons I've got at home.
That combining is what M:N threading is. Green threads are application level threads. "threads" are OS level threads. M:N threading is the ability to take M application level ("green") threads, and schedule them on N actual threads.
> why re-implement the wheel in your programming language?
Because for some things, OS level threads are far too heavy. For others, application level threads don't achieve the parallelism required to efficiently use the hardware available. Combining them should allow scaling well across multiple cores, as you noted yourself. The difference here is that they are part of the language, which means they can be easily and efficiently used without setup by everyone using the language.
More or less. I would say that manually threading continuations through async calls doesn't count as M:N. Not even when done via semi-automatically via the stack-less coroutines which are popular on recent languages as they are a very leaky abstraction.
I would even argue that a proper M:N system should have, if not full preemption, at least a best effort attempt at guaranteeing forward progress by implicit insertion of yields.
> For others, application level threads don't achieve the parallelism requires to efficiently use the hardware available.
You've got this backwards: OS-level threads execute in parallel (or, rather, may be executed in parallel), while green threads spawned within the same OS-level thread execute sequentially on a single processor core (but in no predetermined order).
It's at the purported 'web scale' in the article (I'm guessing this means lots of tasks, or lots of work) that green threads least useful, because you know there that the workload will be big enough to keep all CPU cores busy, and multi-threading/multi-process is always going to be a win. Adding green threading to an already maxed out computer is just extra overhead for no benefit.
But for long-lived workloads, or lots & lots of tiny requests, (the OP talks about 'web-scale', whatever that is), you would be creating the processes and threads once, at startup, and then they just all keep busy with little overhead.
A universal M:N threading system with coroutines underlying everything means that both styles of code can happily coexist.
One might suppose this to be the case, but to my knowledge only network IO is truly non blocking, although there are async io api's in some languages for disk reads/writes, they're still blocking behind the scenes. One of the coolest things about go (imo) is it's scheduling of many go routines onto relatively few machine threads (and processes) for blocking IO. I'm not clear if Perl 6 has such a facility.
Windows does apparently offer the necessary building blocks; this is the most relevant HN sub-discussion: https://news.ycombinator.com/item?id=9584269
jnthn (one of the primary contributors to MoarVM) has a write up about recent work done to improve the system here: https://6guts.wordpress.com/2017/06/08/sorting-out-synchrono...
There must be some reason why no OS offers non-blocking disk I/O in this way, but I don't know what it is.
To be serious, in my circle of developer-friends, we feel that it tries to be TOO simple, and disagree with some of the decisions of how the language works (ex. seemingly endless `if err != nil { return nil, err }` type things)
How would your friends label "brainfuck" ? Too simple because it has very limited number of identifiers or Too hard because accomplishing anything is nightmare ?
I have written code in go/java/php/python/js and error handling has been a majority chunk of lines in most if not all cases(in other cases the errors are just not handled). The best ideal case flow is always easiest to build.
I can understand other people's complaints about the language though.
Go to me feels like Java did back in the 90s (in the sense that Java only offered some of the power of a language like Smalltalk while making you jump through hoops, and thus having to write more boiler plate).
no generics
It's good that you won't be picky, since you're arguing against decades of "cacciatore" for Italian Americans. ;)
And how do I know I have to rebuild in the first place?
You'll probably tell me that there are tools to help deal with this. I'm sure there are. My point is that it isn't as simple as "scp".
Which means that perhaps for many of them there is a better module out in the ecosystem that almost nobody uses because they have one extra step to install them.
If you want something close to that in Perl 6, just install Inline::Python and you can use all of those “batteries included” with Python as if they were modules written in Perl 6.
Exceptions in Perl 6 are much easier to deal with than they are in Perl 5.
Several pretty big recommendations (particularly regarding OOP in Perl) from PBP aren't really regarded as having stood the test of time very well.
What M:N threading does, at least in theory, is allows you to create 100,000 threads cheaply AND keep a slow function call in one thread from blocking others. In a language with those features you can build it out of continuations (NOT coroutines) and real threads. But that requires building your own scheduler that can move a user thread to a different process at need. Which is non-trivial, and is unlikely to cooperate well with any third party libraries.
That said, whether M:N threading is a real optimization depends on a ton of things. A big one is the underlying performance of the scheduler and the language. Which is improving for Perl 6..but has a long way to go.
Whether optimizing at this level matters is another good question. Most companies shouldn't care. Those that should, should be cautious about deploying Perl 6 into a mature infrastructure.
So that's why people can think that M:N is important, and why you might legitimately disagree.
When the author (as is the case here with M:N threading) identifies the specific feature of interest, we do know exactly what it means, independently of whether they also reference something less precise.
> If it's about serving a great number of concurrent connections, I'm not sure why M:N is the only way to go.
If you want to posit an argument challenging the idea that M:N threading is an advantage, that's a different thing that disagreeing with the authors contention that choices are fairly limited if you want a language with language-level integration of M:N threading.
Guilty as charged. Perl is happily ugly, and happily derivative.
Usenet article <1992Aug26.184221.29627@netlabs.com> (1992) -- Larry WallGood leaders identify and propagate conventions that can keep their teams functioning in a complex millieu.
You can't get rid of the irreducible complexity of any system. You can only move it around.
In my experience, if you use an expressive language well, you can fit the code to the problem domain in a way that better communicates the underlying approach you have taken to solving the problem.
Relying on strict, small languages, like Java, means that you gain a consistency of syntax by sacrificing flexibility.
I've seen monstrous turds written in many languages, the problem in all these cases was poor or absent leadership.
They are preemptive, and many things you do which will block effectively call Thread.yield() to give other things a chance to run.
I once wrote some code which took my 4 core CPU to about 500% utilization, and it was very simple code. (on a hyperthreaded Core I7) It was purposefully written to be fairly daft, as there were plenty of better ways to write it. I just wanted to see what would happen, and how long it would take to complete.
The monadic pattern is just extremely common (and desirable), here's what the wiki (https://wiki.haskell.org/Monad) says:
> Monads in Haskell can be thought of as composable computation descriptions. The essence of monad is thus separation of composition timeline from the composed computation's execution timeline, as well as the ability of computation to implicitly carry extra data, as pertaining to the computation itself, in addition to its one (hence the name) output, that it will produce when run (or queried, or called upon). This lends monads to supplementing pure calculations with features like I/O, common environment or state, etc.
If I were to try and paraphrase that, it's because it is such a good abstraction for all the things you have to do that aren't as simple as pure functions. In other languages, just about any function can do anything, so they're understandably less important
So while other languages can use threads and async-IO perfectly well to keep all a systems resources utilized, that is not the only point of a programming language. Programming languages are primarily a means of communication.
To an extent this is a design rooted in a world where disks are much faster than network access. But perhaps that world is coming back with SSD's/NVME/etc..
Now, with NT, you have ReadFileEx() and WriteFileEx(). However, a user can call them in such a way such that the semantics are: "hey, try and read this, if you can do it immediately without blocking, great... if you have to block, then do whatever you need to do in the background to make that happen, but still return to me without blocking".
That, and that alone, is the key difference between the inherently synchronous I/O model of UNIX, and the inherently asynchronous I/O model of NT. The entire NT I/O subsystem, cache manager, driver API, memory management, APCs, scheduling et al is predicated around the notion of every I/O request being asynchronous.
If everything happens to be in the right spot at the right time, sometimes an I/O call can be synchronous (i.e. user->kernel->user without a context switch due to a required wait). In every other case, the kernel won't be able to complete it there and then, so, it checks to see if the user still wants that read or write call to return immediately -- which implies "asynchronous I/O" (referred to as "overlapped" I/O in NT parlance, because you're overlapping an I/O request with more compute).
Windows kernel drivers are fundamentally more complex than corresponding Linux drivers because the kernel's I/O model is fundamentally more sophisticated -- everything is packet driven (the "I/O request packet", or Irp), your driver's read/write entry points need to be able to query the incoming I/O request and determine if the user wants sync/async, how you need to return the call so that the I/O manager can furnish the correct behavior to all the other pieces of the subsystem (and potentially other drivers that are layered higher and lower), and a huge number of other subtle details.
The added complexity is required because the fundamental I/O model is asynchronous. In the UNIX synchronous I/O model, there's simply no semantic concept -- at both the driver level, kernel level, and APIs exposed to the user -- to say "here, read() this and return immediately -- if it can be done synchronously, great, if not, kick it off in the background and give me some opaque structure back I can use in the future to check on the completion of the operation".
The other huge advantage of NT is the notion of thread-agnostic I/O. That is, the thread that initiates one of these asynchronous read requests doesn't have to be the same thread that completes it. Although it sounds simple, that's one of those tip-of-the-iceberg technical things where there are so many pieces behind the scenes that need to cooperate to facilitate the functionality. I talk a little bit about thread-agnostic I/O here: https://speakerdeck.com/trent/pyparallel-how-we-removed-the-....
So, to summarize, all discussions regarding asynchronous I/O and M:N threading on UNIX are sort of fundamentally flawed because the underlying primitives can't express what is actually needed (an asynchronous I/O subsystem at the kernel level, thread-agnostic completion-oriented I/O, and ideally, thread pools + completion ports) to achieve the end goal: optimally using your underlying hardware :-)
(Optimal hardware usage necessitates one thread running per core, and the ability for any one of these threads to continue program logic upon completion of an I/O request, regardless of whether or not they were the thread to initiate that request.)
The exact sense of the phrase you excerpted is explained in the next clause of the same sentence of the comment.
Honestly I was asking the question because I thought you had some deeper point in mind I couldn't see. But you just had it backwards.
This was quite painful with.js Node 1-2yrs back when Promises and coroutines started picking up steam [such as https://github.com/tj/co]. It was always a shot in the dark.
Has this situation changed now? (basically is the stdlib all async friendly now?)
The concurrency stories for Erlang and Go are front and center, very well defined, and obvious. How do you get similar functionality in Haskell (specifically, what libraries do what you are talking about)?
GHC Haskell basically have the same concurrency story (since ghc 7.0.1 - 16 November 2010 [1]):
- You can start ten or even hundred thousands of threads
- Inside them you can program like having synchronous I/O
- And the runtime handles it efficiently (with epoll/kqueue/...)
The difference is that haskell is less opinionated on the high level concurrency primitives, so you have many choices (sometimes with multiple different library implementations) like: locking, explicit async code, transactional memory, channels... https://hackage.haskell.org/packages/#cat:Concurrency
[1] Release Notes: "On POSIX platforms, there is a new I/O manager based on epoll/kqueue/poll, which allows multithreaded I/O code to scale to a much larger number (100k+) of threads" https://downloads.haskell.org/~ghc/7.0.1/docs/html/users_gui...
The go-to library for nice Haskell concurrency is probably async[2], which you can combine with stm[3] for very nice guarantees. If you're interested, there is a very nice, free book[4] on the subject.
[1] https://haskell-servant.github.io/ [2] https://www.stackage.org/package/async [3] https://www.stackage.org/package/stm [4] http://chimera.labs.oreilly.com/books/1230000000929/index.ht...
The article claim is so ridiculous it's not even wrong and you go on to snark about a Haskell counterexample. What a complete asshole.
String literals are described by the following lexical definitions:
stringliteral: shortstring | longstring
shortstring: "'" shortstringitem* "'" | '"' shortstringitem* '"'
longstring: "'''" longstringitem* "'''" | '"""' longstringitem* '"""'
shortstringitem: shortstringchar | escapeseq
longstringitem: longstringchar | escapeseq
shortstringchar: <any ASCII character except "\" or newline or the quote>
longstringchar: <any ASCII character except "\">
escapeseq: "\" <any ASCII character>
You can't paste that text into a file and write a Python program around it that outputs the same text. You need to encode the quote characters.More generally, there is no way in (many languages including) Python to treat an arbitrary (yet known) block of data as data.
If you needed to have `」` in there you can fall back to `Q{{{{{{…}}}}}}` or `Q:to<END>;…END`
how would it not
Granted, in my area, there are exactly none, but I don't live in the best location for the use of modern tool/language/technology stacks. If I were to move an hour or two north, I'd probably find many.
* Are you saying you don't use it because you consider it niche?
* Or are you saying you believe people don't use it because they believe it is niche?
* Or is your point that Go is niche because people consider it to be niche so it never gains critical mass?
In any case, it's not niche. Period. It's being used lots by both start ups and more established businesses alike. I'm not saying it has the kind of penetration that Java or Python does, but Go is still definitely "mainstream" (for want a better description) these days.
https://www.apress.com/us/book/9781484228982
http://shop.oreilly.com/product/0636920065883.do
https://deeptext.media/perl6-at-a-glance/
https://deeptext.media/migrating-to-perl6
https://www.amazon.com/Learning-program-Perl-Getting-program...
https://lwn.net/Articles/724198/ (May 30, 2017)
In truth, an AIO operation can block in the kernel for a number of reasons, making AIO difficult to use in situations where the calling thread truly cannot afford to block. A longstanding patch set aiming to improve this situation would appear to be nearing completion, but it is more of a step in the right direction than a true solution to the problem.
In other words, AIO seems likely to remain useful only for the handful of applications that perform direct I/O to files.
If it works in the sense that the kernel supports the API and implements aoi internally using async mechanisms, then that would be awesome. Is that the case?
What I am saying is that I have not encountered a real-life case where this would have made my life easier.
Again, it's neat, but not a real reason to move to a new language.
What MoarVM got better was everything else. The GC, the calling convention, the OO (6model), the jit. I was on my way to fix all the parrot damage done from the previous decade, because this would have enabled all architectures and proper threading for perl6, but then perl6 decided to kill it and go with Moar. And spread a lot of lies on threads.
I was always partial to the variants that could do citations well. They would automatically link-ify items postfixed by [1] etc with the link included at the citation at the bottom. I implemented a customer facig newsletter system at one job using that, where it automatically made the emails multipart when markdown was submitted, with the text part being raw Markdown. When using citations, it's still very readable.
> But anyhoo, at the time I saw this (45 minutes after my original comment), my comment had 25 votes and was the top-most thing on this page.
It was grayed out at the bottom of the page when I saw it.
Sometimes there is some really weird voting dynamic here and it would be interesting to know what goes on.
For a lack of a better word you can call it intellectual curiosity or something.
Many times it's because the user was just asking a question, or made what they thought was a perfectly reasonable comment.
HN is pretty horrible with voting etiquette.
And I'll still hold that this is acceptable curiosity.
Because in the second (add2), you might do something interesting with the second (or third) arguments depending on the first. You might have optional arguments. Your arguments might be a list of items you don't know the size of. It allows you to develop what you need out of the extensible core mechanism. Modules can and have built on that.
> # There may be some other ways to extract arguments
There are. Now you can just do
sub add( $arg1, $arg2 ) {
$arg1 + $arg2;
}
My personal favorite is to use Function::Parameters[1] fun add( Num $arg1, Num $arg2 ) {
$arg1 + $arg2;
}
which allows named params, type constraints (runtime), default params, etc.The first example uses the explicitly named default array @_. This is a common pattern, and easy to read.
The second one omits the "default". Note that whilst it is possible to write some dense code between the $arg1 and $arg2, it won't work as expected if the dense code bit has array access - ie., the default array can change in the code.
The third example uses the typical sigils for accessing individual elements within an array (default or otherwise).
I wish the core language had saner defaults, but over time, I've seen some reasonable uses for the different styles:
1. General subs, the same as you example
2. shift removes the first element of the array and returns it. This can make the code more readable in certain cases:
use Params::Validate 'validate';
sub add2 {
my $self = shift;
my $args = validate (@_, { ... } );
}
3. Slightly less verbose code, for simple one-liners and/or anonymous functions: my $calc_functions = {
'add' => sub { $_[0] + $_[1] },
'subtract' => sub { $_[0] - $_[1] },
...
};
my $func = 'add';
$calc_functions->{$func}->($args);add3 shows how to access the aliased arguments directly.
# break the aliasing of @_ and make undef into 0.
sub undef_to_zero { @_ = map { $_ // 0 } @_; }
sub math_stuff {
# call with identical @_
&undef_to_zero;
# Do math without undef warnings.
my $sum = 0;
$sum += $_ for @_;
}
# Alter all arguments in calls by repeating them
sub fatten {
$_ = "$_" x 2 for @_;
}
$foo = "abc";
@bar = ( 1, 2, 4 );
foo($foo, @bar);
say $foo; # abcabc
say for @bar # 11
# 22
# 44 def add1(*_):
_ = list(_)
arg1, arg2 = _
return arg1 + arg2
def add2(*_):
_ = list(_)
arg1 = _.pop(0)
arg2 = _.pop(0)
return arg1 + arg2
def add3(*_):
_ = list(_)
return _[0] + _[1] def add(x, y):
return x + y
whereas the Perl codebase that I work with has every possible combination of these methods.e: Just clarifying that your complaint here is that every sub in Perl 5 just receives a list of its arguments; this isn't a "more than one way to do it" issue.
The old P5 motto TIMTOWTDI was long ago updated to TIMTOWTDIBSCINABTE and P6 adopts the latter. While P6 supports most of the options shown for P5, most folk writing P6 will just write something like:
sub add($x, $y) { $x + $y }How much actual cognitive overhead is there associated with three of four different ways of unpacking args?
Speaking for myself (as a long-time Perl/Python/GoLang/etc programmer), when I jump into a language I don't know, it doesn't take me long to get 'muscle memory' for how such things work.
It doesn't matter which way you unpack your args unless you are trying to do something fancy (the sub 1% cases). Just pick one.
Hell, it's easy enough to make a script to rewrite multiple shifts into destructured list assignment or vice versa.
If I were ever to go there again, I would probably give OCaml a try. It seems to offer many of the benefits of Haskell, while also offering "a way out" into the familiar world of objects and side effects. But don't hold your breath, I don't think I will get there this year.
If it's the latter, I can definitely tell you that part of the zen of Haskell is firming up the boundaries between side-effecting code and pure code -- for example, IMHO the better you get at Haskell, the less `do` statements show up in your code (for better or for worse, because >>=, <*>, and co make it harder for newcomers to enter a codebase)
Writing a "purely" side effect-based one, say, read a string from stdin, parse an age, and say, "What, you are %d years old?!?! Wow, you're old!" completely eluded me.
Maybe it was just that all the tutorials I encountered sucked. Maybe I am just too dumb for Haskell. Given that I currently really love Go, I kind of suspect the latter. But who knows? I'll find out next year, I guess. ;-)
Alternatively, give Erlang or Elixir a shot: their primary mode of error handling is "don't, let it crash", which is possible due to the relatively unique process model.
Right. Like Perl 6.
> Very often, your error handling through a chain of operations will be to use a slightly different operator (say '?.' Instead of '.') and any errors will be automatically propagated to the end of that section of code where you can handle them all in one place.
P6 does this stuff particularly well.
It makes option types opt out. You have to explicitly specify the equivalents of Just or None, otherwise you're automatically dealing with an option type if you're dealing with a type.
Conditional constructs are designed to work well in this context.
One can use ML style pattern matching as those conditional constructs.
And other styles of matching.
Error exceptions are unified with error values, allowing codebase/author A to use one style, and B to use the other but pretend that A's code used B's style, and for C to use B's code in whichever style they prefer, seamlessly promoting/demoting between warnings, errors, and exceptions, including across language boundaries, as fits a use case.
And so on. Perl 6 learned from Perl 5 but it also learned from Haskell and many other languages.
> Alternatively, give Erlang or Elixir a shot
Note that the author of the OP is well known in the Erlang community.
Honest question, as I haven't had a chance to look into P6 as much as I would like. It looks pretty cool in a lot of ways, but I'm worried that all of the options available would create a confusing mess.
> Note that the author of the OP is well known in the Erlang community.
Yeah, my comment was directed at the parent who asked:
> I don't understand how some other language would not need to handle all the errors ?
And was intending to give a brief description of how other languages (possibly including P6, though I don't have the experience to say) handle errors differently from the languages they listed (go/java/php/python/js).
Erlang and it's VM doesn't need error handling (edit: like the ones you listed).
You use pattern match and you match for the outcome you want. So every other state is a let it crash and burn cause you don't care. Because of this mentality you don't have to be God and figure out all the possible fail state to Error check.
The VM enable this type of thinking because it's preemptive. And also there are supervisor trees to resent your program state. The error check is in form of monitor and robustness I guess but it's not in the usual form of other popular languages like the ones you listed.
This is why I prefer Erlang over Go in concurrency. The BEAM VM is good. Also no generics.
None of them are incompatible as far as I am aware. If they are, I'm sure it's in very minor ways that don't occur in practice.
As far as I can tell from blogs like [1], the other language extensions also change the language in ways that are not forward and backward compatible with out-of-the-box Haskell.
You are correct that Strict is a language extension that significantly changes the semantics of a Haskell program. It's a new extension so it didn't cross my mind. But besides Strict the number of extensions that change semantics of existing programs in a significant but non-forward and -backward compatible way is very, very small.
Basically, the summary of Haskell extensions is:
Around half of them are new features that should be in the language standard but aren't because a new standard hasn't been produced for years. Around half of them extend (not change) the language in powerful ways and are best avoided unless you really need them. Around Ɛ% are things that significantly change the semantics, and 0% are things that mean you're programming in a "different, incompatible, language" (all extensions are compatible across module boundaries).
I'm not sure how I feel about this, because a single program doesn't own the machine. I don't necessarily want a language to automatically use all the cores any more than I want the language to automatically use all my memory or bandwidth.
I think the GOMAXPROCS approach kind of makes sense. You can spin up as many serial independent tasks as you want, and go will use up to GOMAXPROCS threads.
@array = 1, 2 ... 1,000,000 ;
@array>>++ ;
where the `>>++` operation increments each of a million integers with the workload distributed across multiple real CPUs?Yes, P6 grammars generate recursive ascent/descent parsers.
(It allows arbitrary mixes of ordinary P6 closures, using the 'Main' DSL's syntax, with parsing P6 closures, using the 'Regex' DSL's parsing syntax. Purely declarative parts of the latter are true regular expressions and are mapped to automata by the compiler.)
> and anything even a bit fancier still requires a dedicated parsing library.
Huh?
Please explain what it is about the Perl 6 grammar (the grammar for Perl 6 that is itself a Perl 6 grammar) that isn't fancy.
In my recent SO post [1] I wrote "In most cases, the practical answer when you want to parse anything beyond the most trivial of file formats -- especially a well known format that's several decades old -- is to find and use an existing parser.". Is this what led to your "bit fancier" conclusion about P6 parsing?
> Is the language itself a good place for parsing/grammar handling?
Yes.
Not only does this allow devs to more easily write and use parsing code, just as P5 made it easier to write and use regexes, but it also makes it easier to mutate Perl 6 and write DSLs.
[1] https://stackoverflow.com/questions/45172113/extracting-from...
Basically, regular expressions have grown to impossible to understand, write and modify. Mostly due to perl.
Yet, they are used daily by millions of developers, because they are a ton better than manual character checks.
Perl6 grammars are basically an attempt to bring developers another step forward.
So, if you are doing "test if string has whitespace" you'd use a regex, while for "parse an apache log line" you'd probably use a grammar instead of a very nasty regex.
IANA perl6 dev though, just a casual observer.
PBC was the bytecode format, the human readable low-level syntax was called PASM and the slightly higher-level synax was called PIR.
Besides PIR, other languages still close to the metal were NQP versions (though I don't remember the exact relationship between NQP/NQP-rx/parrot-nqp) and eventually Winxed.
> Besides PIR, other languages still close to the metal were NQP versions (though I don't remember the exact relationship between NQP/NQP-rx/parrot-nqp) and eventually Winxed.
I vaguely remember NQP/NQP-rx etc as being branches with reimplementations or new features, but I could be way off. I remember Winxed, but I'm not sure I realized Whiteknight implemented it at the same layer as NQP instead of on top of it. That would explain why some other new language implementations started using it instead of NQP, which I recall (e.g. I think someone started up a new Ruby on Parrot implementation after Winxed that used Winxed). I miss reading Whiteknight's blog posts on massive changes he was working on.
The way I remember it, the issue was that later versions of NQP moved towards greater backend independence and no longer mapped directly to Parrot VM semantics (eg 6model), so Parrot had to rely on older NQP version, making Winxed an attractive alternative.
Google for lock-free vs wait-free
Thanks for causing me to look again (I used babelmark[1] to check what versions supported it and noticed all of them do, including Markdown.pl). I dislike putting out wrong information. :/
1: http://johnmacfarlane.net/babelmark2/?text=%5Btext%5D(url)
I wish more languages would do this and I happily applaud Perl 6 and go for allowing sane operations in the real space without needing separate packages.
But there are high quality packages for all modern languages that allow you to get precision if you need it.
I'm not debating the utility of this feature, but I am saying it's not--by itself--going to make me want to pick up Perl 6 for my next project.
What I am saying is that this isn't a killer feature.
It makes me go, "Oh, neat", and not, "Wow, this will make my life so much easier".
Aiui the Java compiler and language constructs generally don't know the difference.
The P6 Nothing isn't a null, it's a "type object" with memory- and type- safe behavior.
There's a tiny bit more about this at: https://en.wikipedia.org/wiki/Option_type#Perl_6
The first relevant version of compiler tools were called PGE/TGE, written in PIR and based on the notion of attribute grammars and grammar engines. The first versions of NQP grew out of those.
I believe early versions of NQP were hosted in the Parrot repo, but they started to be developed out of tree and eventually were completely hosted out of the tree.
NQP was an improvement over PGE/TGE in many ways, so some of Parrot's internal tools were created in or ported to NQP.
Eventually (I believe with NQP-rx), NQP forked into an incompatible version out of tree. As you mention, when the next incompatible revision of NQP was being planned, it was no longer an attractive target for Parrot, because it was:
* developed out of tree
* developed out of Parrot's release cycle
* backwards incompatible
* "backend independent"
In particular, migrating existing, working Parrot tools to a new language which offered little beyond busywork to gain "backend independence" wasn't worth the effort. (Having NQP go unsupported for existing tools wasn't great either, especially given that one explanation for "backend independence" was that Parrot tools weren't evolving fast enough to support Rakudo features -- still feels like a circular argument.)I still maintain that it made little sense for Parrot to depend on an external project, evolving rapidly, offering little stability, with the express intent to obviate Parrot.
That's interesting. I'm not sure why I remembered it so strongly as Whiteknight's invention, but it probably has to do with how much he talked about it, and that my initial exposure to it was through his blog.
> The way I remember it, the issue was that later versions of NQP moved towards greater backend independence and no longer mapped directly to Parrot VM semantics (eg 6model), so Parrot had to rely on older NQP version, making Winxed an attractive alternaive.
That sounds likely. The whole situation was a clusterfuck. To my knowledge, some people are still really upset about it and how it played out (but at this point I don't really see how it could have gone any other way).
That's actually a fairly easy endeavor with perl regex capabilities, especially named captures. Parsing HTML, or a programming language, or other much more complex structures is much easier with a grammar. For example, JSON parsing[1].
That said, there are ways to approximate some features of grammars with Perl5 regexes[2].
1: https://github.com/moritz/json/blob/master/lib/JSON/Tiny/Gra...
2: http://blogs.perl.org/users/brian_d_foy/2013/10/parsing-json...
It's SIMD, exactly as you say, but potentially on much larger blocks of code than single arithmetic operations. A compiler that can pick where to use SIMD is doing the same kind of analysis. The difficulty in doing it at compile time is knowing whether or not it will be worth the cost.
In your example, the compiler could see that the array has a million entries and so spreading the work across threads might be a win. But in general, the compiler probably can't tell roughly how many iterations a loop is going to have, so it doesn't know where to thread and where not to.
In P6 (potential) parallelism is explicit. You have to use an explicit construct such as the `<<` or `>>` meta operators.
I used a simple arithmetic operation but it can be any code. Of course, it had better be parallel safe.
The code-gen at compile time makes an AOT call on whether to attempt parallelization. If the JIT thinks that's not working out during a particular run it may decide to de-optimize.
> more generally, any loop where ... each iteration of the loop is independent of the others
Right. The `<<` and `>>` meta operators aren't the only relevant features. For example there's a `.race` method that not only (potentially) parallelizes a map, grep or whatever, but provides further explicit control such as batching size.
(And to add some precision, some extensions may be visible across boundaries, but almost all are not, and the extent to which they are is very small).
A more accurate statement is: "python bytecode can't execute concurrently w/o something like multiprocessing". The key difference? You can do multithreaded I/O all day long, which is a pretty important use case of threading. You can also hand off work to C and release the GIL, although this isn't as common. Example: XML parsing, regex, numeric work.
FWIW all these gotchas are one of the reasons I love just having complete threading support built in to my language.
No this isn't any more accurate. Python bytecode does execute concurrently if you have multiple threads. It just doesn't execute in parallel.
> [Erlang] is a concurrent language – by that I mean that threads are part of the programming language, they do not belong to the operating system. That's really what's wrong with programming languages like Java and C++. It's threads aren't in the programming language, threads are something in the operating system – and they inherit all the problems that they have in the operating system. One of the problems is granularity of the memory management system. The memory management in the operating system protects whole pages of memory, so the smallest size that a thread can be is the smallest size of a page. That's actually too big.
> If you add more memory to your machine – you have the same number of bits that protects the memory so the granularity of the page tables goes up – you end up using say 64kB for a process you know running in a few hundred bytes.
With Erlang, there are threads running concurrently across multiple CPU's and a preemptive scheduler within each of those threads. This is all transparent through use of Erlang processes. Essentially, many concurrent schedulers managing hundreds of thousands of processes that start out at 0.5kb each. A JVM thread is 1024kb by comparison and a goroutine is 2kb.
You're getting legitimate concurrency with Erlang, as well as isolation. There are only so many things that a single CPU can do at the same time and that's where the scheduler becomes necessary. The scheduler also ensure that if you fire off 100,000 processes and one of them is CPU intensive that it can't hog the processor and preventing the others allocated to that processor from executing.
From what I've seen, the difference between Python 2.x and 3.x is tiny compared to the Perl 5/6 change.
The difference between Perl 5 and Perl 6 is probably about 10x the difference between Perl 5.10 and 5.26
I like to explain it this way:
Perl 4 is to C
as Perl 5 is to C++
as Perl 6 is to Haskell/Smalltalk/C#/Swift/Julia…
So I might agree actually on the "bigger", but the "noticeability" does have a lot to do with the general feeling.
(to be clear, I agree with all of your comment)
From what I know about Python 2/3, it's that you can write maybe 90% on average/in general of your code the same way without much thought about the differences. However, if you do wish to adopt the new features, there's plenty to keep you busy writing scripts for at least a few months, maybe even a year.
It's basically a different language that shares some things, with few users. It would be nice to see it shopped, but you might as likely wish distribution shipped with crystal or nim.
$ cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)
$ yum info rakudo
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: mirror.team-cymru.org
* epel: mirror.oss.ou.edu
* extras: mirror.cloud-bricks.net
* updates: mirror.atlantic.net
Available Packages
Name : rakudo
Arch : x86_64
Version : 0.2017.04.2
Release : 1.el7
Size : 3.4 M
Repo : epel/x86_64
Summary : Perl 6 compiler implementation that runs on MoarVM
URL : http://rakudo.org/
License : Artistic 2.0
Description : Rakudo Perl 6, or just Rakudo, is an implementation of the
: Perl 6 language specification. More information about Perl 6 is available
: from <http://perl6.org/>. This package provides a Perl 6 compiler built for
: MoarVM virtual machine. import Text.Printf
main :: IO ()
main = do
age <- readLn
print (formatAge age)
formatAge :: Int -> String
formatAge age = printf "What, you are %d years old?!?!?! Wow, you are old" age
might look nice and imperative. But then you realize it is just syntactic sugar for `main = readLn >>= printf ...`, which is incredibly confusing when coming from another language. And then you learn that libraries can just invent their own meanings for `>>=` as long as the type signatures match and all bets are off.
The upshot of this is that the code like the one above is easy to test and could be async or parallelized without issue. The downside is that people usually fail several times before finally getting it.The haskell book[1] is apparently really helpful with this but it also costs 60$ and is over a thousand pages so it's a pretty large investment in both time and money.
For a really good book on programming, I'll happily pay 60 bucks. I will put that on my Christmas wish list, and give it another try next year.
main :: IO ()
main = do
putStrLn "Please input your age:"
age <- getLine
when (age < 10) (putStrLn "..." ++ (show age))
when (10 =< age < 40) (putStrLn "..." ++ (show age))
when (age >= 40) (putStrLn "..." ++ (show age))
But as you get more comfortable, you get something like: type Age = Int
-- Imagine there are constants here for youngAgeMessage, middleAgeMessage, and oldAgeMessage,
-- for simplicity they're just strings, though you could add even more descriptive aliases
-- like "OldMessage" with a quick union type like AgeMesssage = YoungAgeMessage | MiddleAgeMessage | OldAgeMessage, etc etc
makeAgeMessage :: Age -> String
makeAgeMessage age
| age < 10 = youngAgeMessage ++ show age
| 10 =< age < 40 = middleAgeMessage ++ show age
| age >= 40 = oldAgeMessage ++ show age
main :: IO ()
main = putStrLn "Please enter your age:"
>> getLine
>>= putStrLn . makeAgeMessage . read
This isn't even the final form of this code either, there are some more things you could do to make this code more axiomatic haskell. This code is approximate (like you probably can't copy and paste it, probably won't compile) but should at least show what I mean.It's similar to early clojure and the use (and eventual love, usually, of the threading macro "->") and the unix philosophy -- once you start writing those clean functions that pass whatever they need right along, it starts getting easier (and more desirable) to pluck out the parts that don't have to be side-effecting.
Also, the type system expresiveness is just amazing -- it's what Java should have been but never got the chance to be:
data Thing = OneThing | AnotherThing | ThirdThing
Guess what a `Thing` can be? literally just those things, not even a null or anything. A lot of people say they really love/need/only use "strongly typed" languages (which means a ton of things to a ton of people), but the biggest slap in the face to me is how a language like Java (that people will reach for when they want to compare some usually weaker language to a "strongly typed" one), is a literal minefield of Null Pointer Exceptions (NPEs). "Anything can be anything or sometimes nil" is a hard pill to swallow once you've used Haskell. Also, trying to use Option<> everywhere feels wrong if you do it in Java, because it starts to bleed everywhere, but actually... it's absolutely right (IMO) -- imagine how much better java code could be if the default was to Option.map over things, and the second you decided to try and pull a value out, you knew you were opening yourself up to something bad, instead of just passing things around and hoping they're there or writing repetitive checks.Now, if you see a `Maybe Thing`, you instantly know that that thing is either JUST the thing, or it's Nothing, and maybe's type is:
data Maybe a = Just a | Nothing
Type classes are also amazing, they're basically just as ergonomic as Go's interfaces, without some of the weird hangups and interface{}All that said, I definitely get the hangup, Haskell is difficult to get started with, even to this day, but man, that hurdle is so worth. Maybe I'm just addicted to high-learning-curve things but Haskell feels good.
Just one thing that stands out to me:
> "Anything can be anything or sometimes nil" is a hard pill to swallow once you've used Haskell.
In a language like Java yes, nearly every type is inhabited by null. In fairness, a similar problem exists in Haskell: every type is inhabited by "bottom" which is the computation that never terminates. This is a side effect of being lazy by default.
I've heard that Idris is basically Haskell but strict by default -- I've never tried it but maybe I should? I'm hesitant to go to a language with even less libraries/support/mindshare than Haskell though
For example, I have to explicitly install 2.7 to be able to use Cocos2d-x build scripts.
Jython and IronPython are both limited to Python 2.x, ZipPy is WIP without a stable release.