In fact, this has been the basis for much writing on Lisp too. Paul Graham has written entire essays along the same lines.
If you work in a job that forces the use of a less powerful language than what you've been exposed to, you can, I think, go through a sort of depression. You simply long to use the tools that you know hold much more power yet must resign yourself to the tools you have.
You can overcome the problem by being present in your work. The language might be ugly. It might be totally underpowered with limited means of abstraction and a type system that snmacks you and, sometimes, your client over the head. What you can do is, despite that, commit to writing great software with the tools you have. Commit to improving your work. Perhaps expand the ecosystem with tools borne of insights from your adventures with high-powered tools.
You will have a much more enjoyable time in most languages this way. Perhaps except MUMPS but there might be hope.
I ask as an old SML guy, I like the math theory, I like the promise of better, more accurate and reliable software, I think you can do some beautiful things with algebraic types but I've been dumbfounded by the lack of great examples out there. Ada suffered/suffers the same fate, it has this "academic" pedigree for robustness but not many examples that you can get your hands on. so much expressiveness that doesn't seem to result in much expression.
I'm tempted to compare it to PHP, which exploded in popularity despite (unarguably, IMHO) better languages existing all around it at the time. Go doesn't seem like a bad language in the way that PHP was a bad language for its first 4~5 major versions, but it does find itself surrounded by many more powerful/expressive languages, and is seemingly getting much faster adoption.
I've been learning Go myself, and while I miss some of the features of my normal primary languages (Perl, lately, but also Ruby and Python), I like that I can read other people's Go code without having to look anything up, even though Go is very new to me and I have years experience reading/writing Perl/Ruby/Python. Of course, Perl/Python/Ruby aren't a major paradigm shift away from Go as Haskell is...but, it's still a question of whether it's better to write ten simplistic LOC or two maybe opaque LOC. I tend to prefer concision over verbosity (and thus hate reading Java; I get lost in the trees before I can find the forest).
Maybe Go is a sweet spot for a lot of developers. Rob Pike did a talk called Simplicity is Complicated, about this very subject...I found it a pretty convincing argument. https://www.youtube.com/watch?v=rFejpH_tAHM
I think the number of useful tools is pretty closely correlated to popularity, and obviously Haskell has a relatively small user base. It's not like Haskell gives you super-powers and makes you 10x more productive; it mostly lets you write better software, not necessarily more software. I do feel more productive with Haskell, but it's on the order of 50-100% (depending on the task), not enough to let Haskellers match the open-source output of a very popular language like C.
On the practical side, I love having types that let me refactor so mercilessly, along with a very mature compiler with a decent suite of warnings.
Perhaps the type of problems Haskell is apt to solve (those requiring strict correctness, etc.) are not typically open source software projects.
The great thing about Haskell and other functional programming languages is that the functional style forces the program to be created in a form that is modular and elegant. Programs become modular and composable and re-composable in the functional world because the programmer has No Choice but to build his programs in an elegant way. It's hard to explain this to a typical programmer who hasn't dived deep into functional programming but code reuse, modularity and elegance are significantly higher in functional programming languages than in procedural.
This elegance, beauty and forced organization comes at a high cost though. And the cost is thinking in a way that is outside the normal paradigm of what humans are use to. I know a lot of haskellers think that humans can be taught to think in both ways equally, I disagree with them and believe that it is far Easier for a human to think about an algorithm procedurally than it is for him to think about it functionally.
If you have several tasks to execute, what is your first thought on how to plan out those tasks? A task list (aka list of functions) or a series of composed tasks placed in a single sentence (aka expression)? The answer is obvious: humans think about the list first because procedural programming comes more naturally.
This I believe is the main reason why Haskell or lisps haven't taken over the programming world. Functional programming is just HARDER. It takes me 10 times longer to hack out a simple program in haskell than it does in python because haskell doesn't allow you to 'hack' together a program. In haskell the application has to be an elegant diamond or the thing won't even compile! Python allows me to hack something together quickly which is both a good and a bad thing because as we all know hacking together software is quick but easily leads to unmaintainable software.
I've found that "powerful" languages allow me to do clever things(TM) and I hate clever things(TM) retroactively when I come back to them.
This gets even worse when you have multiple people on the team doing clever things(TM).
They also allow you to do sensible things(TM) like use the one and single, battle tested, standard library's generic sort, btree, etc implementation, instead of having to roll (and then read) 1000s of your own over the span of a year...
And it's a false dichotomy that you can't have the sensible things(TM) without the too-clever-for-their-own-good-things (TM) you allude too.
“The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise.”
~ Edsger Dijkstra
I think i can honestly say i did not fully get that quote until i learned Haskell
A language like Haskell lets me write extremely complicated code, but it also lets me concisely express the constraints I want in the form of types without getting too verbose. Go lets me do neither.
Less powerful languages force the programmer to come up with their own clever abstractions so that if you have multiple programmers you'll end up with 20 different clever ways of doing the same thing.
Dynamic languages - ruby, python, bash, objective C etc - allow all sorts of cleverness - such as monkey patching, attribute injection, dynamic lookup - because of their dynamic runtime. All the checks about whether the code makes sense is performed at runtime.
Languages with more mature/strong/static typesystems are initially restrictive - but the emphasis in cleverness is to be able to know upfront - and at compile time - if the code expressed is consistent in the way data and effects are represented and expressed.
Of course - there are other kinds of code complexity -in terms of ease of understanding, and maintainability - that will always involve tradeoffs considering the task at hand and nature of the programming environment (solo, community, commercial, academic etc).
I was forced to use go for a job, it actually made me feel stupider, so much that I didn't know how to use the right types anymore when i moved back to a more sophisticated language. I was afraid the language wouldn't let me do what I want, just like Go.
There will be a "go programmer generation" and it's not going to be pretty. If people deem "generics too complicated to understand", what can you expect from programmers who don't want to learn anything? The only thing I find nice about the language is implicit interfaces. But not at all cost.
Yes, you are a little bit limmited sometimes because of the lack of generics and the like, but at the same time, if you learn to use Go interfaces well, you end up using them most of the time happily. The parallelism primitives are great too.
As much as I love complex type systems, pattern matching, algebraic data types etc., I know that if Go had those things, it wouldn't be the language I love to write everyday anymore.
Somehow, Go is a really practical language and you end up being very productive in it, at least from my experience.
Saying that people who write Go should be stupid is shallow thinking. Complex languages aren't the only complexity that you should have. Some people concentrate on sophisticated architectural design (like Kubernetes).
There is one thing in Go, that I haven't met anywhere else. I can really understand any codebase I stumble upon in a matter of seconds. This is a huge boon to productivity.
Anyways, I never had any problems switching between Go and languages like F# or Scala on a daily basis. Rust looks interesting, though I think it will end up being too complex for a lot of people to understand, which will hinder its growth.
That said if I look back on the past 6 months, I believe I have improved. In fact, a core principle I hold dear goes as follows:
"If I look back 6 months and I'm not any better, there's a big problem".
At one point, it seemed to annoy my non-technical boss because he couldn't understand why some of the code I wrote 6 months ago is perhaps not as good as it could be. He has kept me around so maybe it isn't a bad thing :)
We will, I think, always have programmers who don't want to learn anything new. It's interesting that there exists "Continuing Professional Development points" in the UK for healthcare: you get a rebate for each hour spent on courses to further your education. Maybe software engineering would benefit from this given the amount of, quite significant, cost sunk into large scale projects. Their universal credit programme should be incentive enough.
Entirely different problem though. If not Go, then what?
So I end up stuck working with crappier tech, crappier tools, and dealing with the effects of fewer and fewer resources going into improving the last 5% of annoying things about Erlang. While a deluge of resources go into partially reinventing the first 50% of it over and over again in some other random language.
Erlang would have been a great fit for Pusher's problem space.
Blargh! :-(
Only you can easily change languages...
Stoicism was referring to actual things one cannot change. Like, say, death, or their status as a roman slave.
So it's less stoicism and more "our way or the highway".
Other times, though, being forced to work with obtuse tools feels like trying to construct a skyscraper out of sticks and dog crap. No matter how elegant a structure you manage to construct, doing so is a miserable experience because you're never able to transcend the feeling that you're working with twigs and poo.
As someone whos written code in Haskell (even some medium sized programs) and moved on just fine, no.
Frankly, Haskell can be an excercise in frustration.
Choosing a language that makes these things difficult, is a poor choice. We should not accept subpar languages to be used just because.
That said FP/Lisp are very hard to leave, I can understand that.
Just the other day I was struggling with the hopeless nightmare of Java's lack of true first class functions (but still having some first class function like semantics) combined with embedded classes restrictions combined with generics thrown in for good measure (AsyncTask embedding in an activity and updating result in the activity).
Go features are clearly specified in a very readable and fairly concise spec and guide and its features fit seamlessly together virtually every time
So have you checked out rust?
Rust doesn't have lazy evaluation, or a particularly extensive standard library. I'm not aware that Rust has heap or thread profiling any better than Go's.
For everything else, though, yes. I found Rust pretty easy to learn (coming from Java with a bit of Python background); i haven't mastered lifetimes or coherence, but i manage my day-to-day work without needing to. No GC means no GC pauses, and if there are big slow drop cascades, they are at least confined to one thread. Rustfmt seems to be mostly reasonable. Library versioning is done properly. The type system provides generics, sum types, and strong, if unconventional, control over side effects. Godoc uses source order and Markdown. Struct fields have to be initialized. Boilerplate is minimal; in particular, checking errors is one character, and sorting detects and uses an existing. ordering, or takes a single function to define one.
But "promising" doesn't mean I'd replace the few use-cases where Go currently shines for me (wouldn't recommend it for any-and-all development at all, but there are select cases where I currently absolutely wouldn't use anything else) as these are those few programs where I really want to write (and later, read) exactly step-by-step "almost machine instructions" and where the only "expressiveness" needed is already fully afforded by Go and quite well at that. Dev-related mostly bulk-file-processing-based custom tooling (not bad to fire off sizable amounts of automatic background transformations from one weird format to another on file-system events etc.) .... filters through which to pipe vast/continuous data streams with just-some-processing over it .... providing out-of-process certain "low-level, must be lightning fast" tasks and logic with IPC for a higher-level codebase .... it's a great tool for such cases, especially given how simple the syntax is. Ie. the basics are grasped very easily very quickly and you don't have to recall "its pattern matching syntax and quirks, its generics syntax and quirks, its etc etc". =) I'm perplexed when teams choose to express their entire domain model or startup in Go, however.. fair game, maybe I'm missing something there
Needs more context. It's more mature for some things than others, like many young languages.
> how are compile times
Not Go, for sure, but not the worst either.
> how much of a priority are they
Getting them down is a high priority, but has taken some time to make a dent in. The last few releases have all posted speed ups, and "cargo check" in the last release helps. Incremental recompilation is coming, you can try its initial implementation on nightly today. There's a lot more to do, but users really want improvements, so it is and will continue to be actively worked on.
I never get the compile time argument. Well I code a big Scala application and the most time it spents is integration testing. the 10 minute test suite would probably still run 9 minutes and 30 seconds on go. we heavily rely on the database and cover a lot of concurrency/parallelism things in our tests. some things which we would need to test in go, too.
the compile time argument always looks good on first, but later on it's just a dumb way to prefer a language since it never matters. (Actually even the go maintainers didn't cared for a while about the compiler performance).
On the contrary, you haven't pointed out an objective issue that you have with Go. You offered some harsh remarks about the language but backed them up with praise.
I'd say you love the language, but apologetically so. You shouldn't it's a great language.
If ever you decide it's not for you then look at other alternatives, rust being one them.
Right now I'm keen to get more personal experience with Rust, and hopefully in the wider team. I'm particularly interested in [Tokio](https://tokio.rs/), which should address some of our concerns about the existing concurrency support.
Also, in same reddit comments there's a longer (quite argumentative) discussion on Rust concurrency that you might be interested in: https://www.reddit.com/r/programming/comments/5fyhjb/golangs...
Personally, as a frequent Haskell user, I'm looking forward to replacing embedded C/C++ with Rust as soon as they get the AVR toolchain figured out and there's better support for common ARM uCs.
I see statements like this a lot from Haskellers and I think its overstated. Anecdotally, after going from Python to spending 3-4 years in Haskell then going back to a dynamic language (Elixir) I've come to the conclusion that how you think when programming is very much a learned trait that works for that language. It's neither good or bad, but it's educational nonetheless. Haskell and other languages like it forces you to have a very unique mindset that can overpower previously learned languages not like it. And it's in no way permanent.
After I stopped using Haskell I was like "ugh, I need types! wth!". I wanted to go back to Haskell only out of familiarity, but as I continued after a short while I wasn't even thinking like that anymore. The thought rarely occurred. I stopped thinking in Haskell and started thinking in Elixir.
I don't think it's entirely placebo or emotional drive too, because I really liked the Java OOP world at first, and went deep into it; but got nothing.
I have come to think that it is precisely when the language switch feels the worst, the learning is the most beneficial for long term. If it is popular and you feel like it does not really work, it means you have conceptual learning to do.
https://youtu.be/yyzK6e6py9A?t=7
Seriously though, I think Elixir (with its guards and pattern-matching facilities) occupies a nice middle ground between strict static typing and the dynamic typing of procedural/OO langs.
However I switched from Python to Scala and besides the performance issues and the poor handling of async I/O that I had with Python, by far the biggest problem with Python was all the insecurity while developing with it. It drove me insane, because we had a production system that always had problems due to how poorly Python's ecosystem treats errors.
Just to give an example, at that time we were using BeautifulSoup to do HTML parsing, a library resembling JQuery, except that BeautifulSoup can return nulls all over the place and no matter how defensive I got in my coding, null pointer exceptions still happened. And what was the solution for async I/O in Python? Monkey patching the socket module by means of Gevent or Eventlet of course, which didn't work for native clients (e.g. MySQL's) and if this wasn't problematic enough, you had to guess what worked or not and when. The worst kind of magic possible.
When I switched to Scala, all of these problems vanished. This isn't about having tests either, because tests only test for the behavior you can foresee, unless you do property-based testing with auto-generated random data and in this regard static languages like Scala and Haskell also rule due to the availability of QuickCheck / ScalaCheck.
You see, my Python codebase got really bad because of fear of refactoring. Yes, even with plenty of tests and good code coverage. Because actually the tests themselves became a barrier for refactoring, because in a language like Python there is no such thing as information hiding and coupled with dynamic typing, it means that your tests end up tightly coupled with implementation details, will break on correct refactoring and will be hard to fix.
Nowadays when I'm doing refactoring with Scala, most of the time it simply works as soon as the compiler is happy. Some people bitch and moan about that compiler being kind of slow and it is, but the things that it can prove about your code would require specialized and expensive tools for other languages and that wouldn't do such a good job.
Going back to dynamic languages, there are also languages like Clojure, which get by with sane defaults. For example in practice Clojure doesn't do OOP-style dynamic dispatching most of the time and functions are usually multi-variadic and capable of handling nil values gracefully. This is not enforced by the language, being simply a hygienic rule accepted by the community. However, relying on such conventions requires (a) capable developers that (b) agree on what the best practices should be.
And the issue that people miss when thinking of the merits of such languages is that smaller communities are always homogeneous. So when giving as example a language like Elixir, if it hasn't drove you insane yet, well, you have to take into account that Elixir might not be popular enough.
So basically I prefer strong static typing because I'm not smart enough or wise enough to seek and follow best practices as often as I'd like and because I don't have much faith that other people in the community can do that either, especially after that community grows. The biggest irony imo is that Python is right now the anti-thesis of TOOWTDI.
> because in a language like Python there is no such thing as
> information hiding and coupled with dynamic typing, it
> means that your tests end up tightly coupled with
> implementation details, will break on correct refactoring
> and will be hard to fix.
Would you mind to provide more details about this point? I thought dynamic languages make testing easier, because you don't care about the type of the object as long as it works with the same API (if it quacks…).
This also supposedly makes it way easier to mock objects. Now there you are basically claiming (unless I misunderstand your claims) the opposite.Maybe one could write Python and turn a "strict" flag for the module (project?) where you'd have to fill in the types and it becomes Scala-like.
I would agree that the Elixir community is currently very homogeneous and therefore very pleasant. To some extent, that is true of many new langs and their early communities. I disagree that this is a premature strike against it. I say enjoy it (and work within it) while it lasts, if you can.
I get that we all have favourite languages, but it is not amazingly helpful to try and compare them like this, for me. I'm sure if you're a Haskeller and you're eyeing up Go, being forewarned might be helpful, but here's another idea:
Don't compare. Just use. Take it at face value. Figure out what becomes easy, what becomes hard.
I came at Go from 10+ years of professional Ruby development, and it was a tough punch to the stomach at times. I still think in Ruby more often than I think in Go. But I know the two aren't really comparable for most things.
Isn't this precisely what the author did? The bad parts he mentioned were mostly "this is stuff that the compiler could've catched, but it didn't and thus gave me a hard time".
If we were going to carry out this argument, it would probably just boil down to the familiar debate of how strict a type system should be. The author is just a proponent of expressive type systems, and he plainly puts it in the title: "a Haskeller's perspective".
For example, compared to MRI, Go has better GC and execution speed, much less flexibility, better concurrency primitives but not better concurrency libraries (EventMachine), and is incredibly verbose.
If you refuse to compare languages, then you are asserting that at least one of the languages is somehow magically incomparable and thus criticism-free, and Go is not so special or good that it should get any pedestal-driven treatment.
I came from Java, but it should be said that the Java style I've been using since the mid 2005s (some functional influences and a lot of experience-driven minimalism) made learning Go really easy. I should be missing immutable types and types that can only be instantiated if the state is consistent, but for some reason I didn't. Perhaps it is that the Go mindset is just very easy to adapt to?
One thing I would say, though, is that you can feel that Go was a more deliberate language. Unlike most popular languages it wasn't really a language that grew out of inexperience and as a learning exercise for the language implementers. The authors already had a fairly good idea of what they wanted to accomplish and they had the attention to important detail that others tend to miss due to lack of experience.
Something about Haskell strikes me as different. Despite the buzz about it, I don't see many projects for it other than shellcheck, pandoc and xmonad, and for two of those, there's better solutions around (sphinx, awesome/i3).
The other thing is the general flow I've see with Haskell programmers, many really tie down their identity do it. They see programming as a crossword puzzle for them to solve in short term, not as something other programmers have to read later on. They're not very empathetic to the idea the runways dwindling and you have to ship sooner rather than later.
In addition, I found that the Scala / Haskell developers I knew took golang to be quite the nuisance. They find gophers pesky. I think the reason why is years of Haskell teaches them to overengineer and complicate things needlessly. It's frustrating because they're not aware of it themselves and take offense, even blame you when you point it out to them.
Maybe I've just been unlucky. In 10 years, I've never had people who consistently failed to ship, been mean and arrogant as scala / haskell programmers. They take the slight criticism as an assault on their identity.
I tried awesome for two weeks and had one crash. I've run XMonad for 5+ years and had no crashes. Just because they are supposed to accomplish the same things does not mean that they're equal. One is better and it's because of the choice of language.
It's very hard to take your post seriously when you include something like this in it and you don't bother to qualify it even in the slightest.
I looked in your history and yup, you are partial to Haskell. Why do you feel a need to immediately judge. Why did you say "hard to take seriously". Why didn't you just ask politely for more details? In my opinion, you already had your mind made up. Just like the blog poster did.
> I tried awesome for two weeks and had one crash. I've run XMonad for 5+ years and had no crashes.
That's your personal anecdote. There's no evidence that the language could have prevented the crash.
And that doesn't make the window manager "better", which is subjective. What are more people using? Awesome and i3. Primarily because they don't want to deal with Haskell when they could be doing lua or a simple config.
I did submit a bug report and it did get fixed, but there was another crash I couldn't be bothered to try to track down, and just switched back to the standard ubuntu shell.
> The tooling is bad
I feel like the tooling is really impressive, considering the age of the language. Remember that Haskell is something like 25+ years old. Go has done quite a bit in short time - I can only hope it will get better too.
> Zero values are almost never what you want
I've always felt the defaults to be spot on. Anyway, it's my responsibility to initialise these values, even on struct fields.
> A culture of “backwards compatibility at all costs”
I agree the current (package management) landscape is dire, but this should change, hopefully this year, with godep. As it is now, I have found success using glide for dependency management, so that's what I would recommend for now.
Apart from that, I can agree on a lot of things - I'm specifically annoyed by the whole generics thing, the way I interpret the people involved with the project is "it would be nice to have, but the implementation is awkward, so we won't admit to it being nice to have".
Yet, I'd argue that tooling is still one of the worst aspects of the language. The whole cabal/stack thing is a mess.
That's not the impression I'm getting
That's like hiring a toddler who can do sums for an accounting position. Showing promise is good, but not the same as being good right now.
Most useful typesystems with generics are Turing-complete. Essentially they introduce own language for types with often very weired rules and syntax that one has to master on top of the basic language. With code generation I can program my types using the same language I use for code with less things to learn.
No, it's the same language with a bigger vocabulary and grammar, on which everybody agrees.
And generics can be quite sane, as exemplified by the languages in the ML family, which have been around for decades.
Java also didn't have generics. They added them eventually, much later in version 5, but then due to backwards compatibility concerns they added them with invariance at the declaration site and complex wildcard rules at use site.
So the irony of this situation is that Go will add generics, it's inevitable for a (quasi) static language once it grows in usage and ecosystem. But when they'll do add those generics, they'll be broken due to backwards compatibility concerns, becoming yet another counter example for generics, picked up by the next Go / Java that will reinvent the wheels again.
With code generation you are introducing your own compiler, DSL and all that bullshit that becomes yet another dependency you have to manage. That's busy work, that's bureaucracy, that's brittle. Now your codebase depends on pragma statements, manifests and obviously a specific syntax that aren't even managed by the default compiler, that's the opposite of simplicity.
At least C has standard macros, and most developers uses the same syntax for them.
Go is an extremely divisive language, a fact which will hinder its adoption as criticism of that language will get harsher since more people get exposed to it.
The good thing is, as Google relies more and more on Go, peer pressure from other googlers might force changes in the language.
The easy solution for that is to put parametric polymorphism in the module language, not the type system. ML, Ada, Modula-3 all did it successfully.
>"Strict evaluation is typically better for performance than lazy evaluation (thunks cause allocation, so you’re gambling that the computation saved offsets the memory cost), but it does make things less composable. "
Can anyone tell me what a "thunk" is in this context and also why it causes performance problems? The article linked to in the sentence results in a 404.
For instance, you can write
x = [1..]
which gives you a list of all the positive integers. Because of laziness (which thunking enables), this line runs just fine. The compiler allocates a thunk for x, saying "okay, I know what x is now". But x is never fully evaluated (because we don't really have enough memory!): it's only evaluated as far as needed. Indeed, typing head x -- this is 1
makes the interpreter print the first element of the list. The rest is still in a thunk. In memory, we have something like [1,<unevaluated thunk>]
Now you can try doing head (tail x) -- this is 2
which evaluates the list only as far as the second element. Right now, in memory, the list looks something like [1,2,<unevaluated thunk>]
You can do similar things with basically any other datatype, enabling cool stuff like [1].However, having to evaluate (or "force") thunks repeatedly in tight loops can obviously degrade performance: printing a Haskell list like [1..10] requires you to evaluate the first element, print it, then force the next thunk to print the 2, and so on. Comparing this to the equivalent in basically any other (strict) language, we see that a lot of indirection is removed, because the endless wrapping/unwrapping is replaced with a simple linked list of 10 elements. (I'm sure GHC is smart enough to optimize the thunking away in this toy case sufficiently.)
One might also say that a thunk is a "promise" (I'm not familiar with the JS concept of the same name, which I think is related to async things instead) to give you a certain value by calculating it only when you need it (if you do at all: a thunk stays unevaluated when you're done running the program, it's GC'd away).
So you're skipping the cost of computation for a value you may never use, but you still have to represent the expression in memory somewhere. This can lead to large thunks building up, eating up memory.
a = 1 + 2
if (condition) {
print(a)
}
In a lazy system, if `condition` is not true, then `a` is never evaluated.This contrived example would be a poor place to have lazy evaluation though, as the overhead of deferring would exceed the cost of computing 1 + 2 up-front.
This is pretty much unacceptable in today's world of low-latency (web) apps.
How active is GHC's development?
Would it be possible to efficiently run Haskell using Go's runtime environment, i.e. by changing the compiler backend?
As an example, see here: https://bazqux.com/ and the discussion is here: https://news.ycombinator.com/item?id=5961570
He was able to survive unexpected slashdot effect from being on the front page of Hacker news without even noticing it. He told me he went up one evening and found that there were tens of thousands of new users actively trying his site. So he added a couple of servers and went to sleep.
GHC development is very active.
And the answer to your last question is No.
There are two active areas of development which could solve this problem (long pause times with a large working set):
* Compact regions -- which will provide off-heap storage that can be manually memory managed. These are shipping with GHC 8.2. See the paper here if you're interested: http://ezyang.com/compact.html
* Linear types -- A type system extension which allows the programmer to specify values which can only be used a single time. This allows a king of type-safe manual memory management where the compiler statically check that values are freed. This work is further off, but there is currently a prototype being developed in GHC. Check out this blog post for more information: http://blog.tweag.io/posts/2017-03-13-linear-types.html
> Would it be possible to efficiently run Haskell using Go's runtime environment, i.e. by changing the compiler backend?
It's an interesting thought, although I suspect this would be very challenging in practice. Haskell's lazy evaluation, with thunks instead of concrete values, probably wouldn't play nicely with Go's heap layout. I would also suspect Go's runtime has been quite special-cased to Go the language. It would nice if the Go runtime system was opened up in the same way the JVM is.
At this point it might be simpler to translate Haskell source syntax into Go.
My overall feeling is that putting more work into the standard GHC runtime system would be a more efficient use of time.
Why? Because Haskell is an interpreted language while Go is a compiled one. Interpreted language doesn't care much about performance as it isn't designed for that purpose, while in the other hand, compiled language does. As a result, interpreted language tends to be more 'elegant' and has lots of convenient features at the cost of performance. A concrete example is when you talk about preventing unacceptable data type in Haskell. They could make it so in Go, but the performance cost would be undesirable.
IIRC, I read that they designed Go to be practical instead of 'elegant', the reason is so that people can learn it easily, making it a good alternative for other compiled languages like C++ whose learning curve is hugeeee and ugly!
I started out liking Haskell too! But has I moved along with my small evaluation api project it felt more and more like I was trying to cut wood with gyroscopic laser saw. It worked but it was a lot of fan fair for sawing some wood.
Picked up erlang/elixir. Looked pretty decent. Felt like cutting wood with a Japanese pull saw, so I had to use the vise clamps that came with it. It cut some fucking wood.
~ Ron Swanson out.
Also since erlang has excellent pattern matching (even on binary data!) and a supervisor system (OTP) errors are easy to recover from. Erlang takes a very pragmatic approach of building tools that deal with errors by recovering from them instead of trying to exhaustively trying to prevent them.
but as someone striking out into the elixir camp/world, this is also what I'm seeing
Those are things that suck about go; it's not specifically that they suck about go when compared to haskell; they just generally suck (particularly the type system stuff).
However, I don't think that the situation is so bad I would go as far as to say, "Other than that, I will probably never choose to use Go for anything ever again..."
I maintain that while go might not give smart programmers the flexibility to express their FP dreams, it has a grungy practicality both for developing and maintaining code that is quite effective.
Introducing go and building applications with it in a team is easy because its quick to pick up for the whole team, regardless of background, its simple to use and its (relatively) difficult to shoot yourself in the foot in terms of distributing binaries or developing applications that run at modest scale.
When gogland (the IDE by jetbrains) comes out of EAP, it'll even have a modestly good professional IDE with integrated debugger (no atom, you don't count when your debugger never works on any platform).
...but hey, if you don't like it, don't use it.
Haskell is pretty great too.
Sure there were languages with such type systems before, and we managed to deliver our work with them.
However I don't want to work in 2017 as I used to work in the mid-90's, when templates were an experimental feature in C++, or the only MLs we knew were Caml Light, Miranda and SML.
The world has moved on.
Man, how long will this meme survive? Go only initially originated with some Google developers, it's not in any way "Google's answer to Apple's and Microsoft's strategically-important and accordingly-subsidized-and-evangelized-and-invested-in languages". Just picture a handful of (previously "accomplished" in the field, as it turns out though) guys thinking "this company has a wide mess of Python etc scripts that should really be C programs, except for the problems this would pose"..
You can do this in your version-control system (e.g. git), via a process called "vendoring". It's ugly but, using one of the popular tools, quite workable.
What do you find ugly about it?
One of the few dependency systems I’ve seen that work well is Gradle with Maven dependencies, using Java 9’s versioned modules.
Proper versioning, proper dependencies, etc.
I have used git submodules to avoid copying, but if one library has its own vendor'd copies you can end up with two versions of the same dependency. Which is even more ugly.
Doesn't this mean you could implement a generic tree type if you fix the underlying data structure to be an array/map? (Not a go programmer yet, but honestly curious)
Nope. There are only a few functions that accept type parameters, and they must be called with concrete types, and they only work with certain aggregate types. For example, "make" can only be used to create slices, maps or channels, so you could create a slice of 10 bytes with "make([]bytes, 10)" but you cannot write "make(T)" or even "make([]T, 10)" where T is a type variable, and you also can't do "make(Tree)" where Tree is, e.g, some struct type.
Other than that, I will probably never choose to use Go for anything ever again, unless I’m being paid for it. Go is just too different to how I think: when I approach a programming problem, I first think about the types and abstractions that will be useful; I think about statically enforcing behaviour; and I don’t worry about the cost of intermediary data structures, because that price is almost never paid in full.
Wow.
The "clean look" you refer to is mostly thanks to custom CSS though - nothing specifically related to Hakyll.
If you're thinking about building a similarly-styled blog or personal site, you could start by studying the styles this author used on their site: https://github.com/barrucadu/barrucadu.co.uk/tree/master/css
The article mentions the impressive worst case pause times of Go's GC. Since then we have performed some additional benchmarking. The conclusion was: it is impressive, but there are still a couple of issues that break the sub 1ms claims. We blogged about this here: https://making.pusher.com/golangs-real-time-gc-in-theory-and.... It's hard to guarantee low latency in all cases...
Michael also mentions that there is nothing like ThreadScope, or at least nothing that's easy to find. The latter is true. There is an impressive runtime system event visualiser which can be opened with `go tool trace` https://golang.org/cmd/trace/. You can see a screenshot of this in the GC blog post I linked to above. Unfortunately the only documentation is this Google Doc: https://docs.google.com/document/d/1FP5apqzBgr7ahCCgFO-yoVhk... which is tricky to find, and could be more in-depth.
I'm in the middle of writing a blog post on how to use this too. Watch out for it on our engineering blog in the next month or two. https://making.pusher.com/
The nice thing about Haskell and the Lisps is that they are consistently the same thing all the way down through every layer of abstraction, even at the bottom. There is no point where you reach the "magic" or "forbidden" layer where the paradigm changes and it turns into a different language.
The problem with Go is that the code we programmers get to write feels like a DSL on top of the "real" Go, which uses constructs and "magic" we aren't allowed to take advantage of.
It is in a way a mirror image of my experience with Go: It is not so much that Go is a great language, it has its fair share of flaws, but it is quite compatible with the way I think.
> I will probably never choose to use Go for anything ever again