Hacker News new | past | comments | ask | show | jobs | submit login
Why Go? Use Racket (cxwangyi.wordpress.com)
61 points by leanthonyrn on March 12, 2015 | hide | past | favorite | 57 comments



I'm sorry you lost me after your outrageous claim that one must learn everything about the standard types of Go before one can program effectively.

You should know about how your types are passed around if you are using any language.

The example given was arrays in Go. Arrays are passed by value, but slices (pointers to arrays) are passed by reference. This is very simply described in the Effective Go reading. It's not a hard concept. I don't like you writing off Go with that example. It sounds like you made an emotional decision against the language and are grasping for logical reasons to support your already made decision.

How many people programming with Go pass arrays around? It's an easy lesson to learn.


There are many valid criticisms of go, but the one presented in this article make no sense. Or maybe only make sense if you're a huge dynamic languages fan and can't stomach static typing. Personally, as someone who used to be a huge fan of dynamic languages like Python, I've sobered up as I've grown older and had to fight with large python and JavaScript code bases. A compiler that is fast with clear error messages is tremendously valuable.


If using dynamic typing like Python's is being drunk, then at least Go is like being tipsy - not quite sober, but quite functioning.


Only if you make a habit of using reflection (you shouldn't). Otherwise it's designated driver all the way.


> Only if you make a habit of using reflection Or type switches. Or nearly anything involving interface{}.


Tipsy is probably the most dangerous mode. People feel like they can probably still drive when they're just tipsy.


No. Drunk driving is much worse. (Yes, people do that -- I mean they actually understand that they are "drunk" and not just "tipsy"... and unfortunately still drive :/.)


Ah, yeah, I wouldn't want to claim that tipsy driving is worse than flat out drunk driving. I only wanted to suggest that there's higher net risk when it's less dangerous but is perceived to be not dangerous at all.


> I'm sorry you lost me after your outrageous claim that one must learn everything about the standard types of Go before one can program effectively.

I can't help but be amused to see the complaint that Go's type system is, of all things, too complex.

At the time I first picked up Go, Racket was my favorite language of choice[0]. One of the biggest things I noticed about Go was how little I needed to think about types and implementation. Part of this was simply having a static typechecker[1], but part of it is how the rest of the tooling is designed as well.

> How many people programming with Go pass arrays around?

Most people using Go shouldn't be using arrays at all, since they really don't fit most use cases (fixed size and size is part of the type).

[0] For the record, I still like Racket, but I've found I'm undeniably far more productive in Go.

[1] Type Racket exists, but at least at the time, most libraries were untyped, which meant I had to write typed wrappers for everything.


Yes, thank you. This was an instant head-scratcher:

"However, the type system of Go is so complex"

The type system is anything but complex, even in the non-system complaints expressed.


Just a nit, everything is passed by value in Go including slices. Here is an example highlighting why it may be important to keep this in mind: https://play.golang.org/p/VOUJI88PNj .

Regardless of this, I learned Go during the last Stripe CTF and placed top 5 in one of the rounds with it sooo... I don't believe learning to be effective with it takes all that long. It was quite enjoyable to learn as well.


Not just a nit. You are right. I wrote my comment in haste. Had to take someone to the airport.

I think the essence of my point still stands in that the example the author provides is very shallow and hardly something that stops you from writing Go or even writing any kind of protocol in Go.


I think slices are actually passed by value.

From "Effective Go":

"We must return the slice afterwards because, although Append can modify the elements of slice, the slice itself (the run-time data structure holding the pointer, length, and capacity) is passed by value."


You are absolutely correct. To borrow form another's comment, https://play.golang.org/p/VOUJI88PNj

The slice, however, is a small structure to pass to a function in that you are not performing a copy of each individual element in the slice.


No. Because static typing.

However much Go's type system might lack some means of expression, the type system is there, and it prevents many bugs that can easily crop up while using a dynamic language.

I'd rather say 'use OCaml' or 'use F#' or even 'use Haskell'.


Or Clojure or Erlang. Funny enough out of "functional" languages those have probably just as much (or even more) been used to develop distributed, fault tolerant systems than F# or Haskell. Just saying.

BTW both Clojure and Erlang allow a form a gradual typing.

From personal experience. I've used both static (C#, C++, Java) and dynamic typing (Python, Erlang), and have to say, the prevents-many-bugs excuse as the main problem with dynamic types just wasn't true.

Maybe my code is more complicated and dealing with distributed state, consistency, and protocols but what what kills everything is usually logic errors, network failures, files not being there, misconfiguration, segfaults, user-after-free, and the most evil ones, shared global states in a concurrent shared-heap environment.

Things like "oh I expected an int but got a string" are pretty trivial and easy discovered.

Moreover, in a recent service I implemented. I did in Python and I believe the faster development time and less code to write in general, gave me time to write better integration tests that exercises and covered the code a lot better. I would have had to write the tests anyway, even if this was written in Haskell.

Perhaps static typing helps a lot more in large code bases. Think of a large game, or a CAD application or some huge C++ back-end. But, if you buying into the whole micro-services fad, well, you shouldn't have large code bases if you can help it.

Another big motivation for types, which I think most people have in mind when they pick a strongly typed language is performance. Usually static typing and a very large performance increase would go hand in hand. Now with the effort put into v8 and PyPy we see that gap getting smaller.


> Or Clojure or Erlang. Funny enough out of "functional" languages those have probably just as much (or even more) been used to develop distributed, fault tolerant systems than F# or Haskell. Just saying.

(I generally agree with the gist of your comment, I think. This just stuck out.)

I don't believe Clojure belongs in that class (as "proven" for writing reliable software), but whatever.

If you want truly reliable software and have the budget, then you just need to throw process at the problem -- it doesn't matter much which language you use. Several (if not all) the Mars landers were programmed in C -- with very few critical flaws experienced/discovered[2]. The Ericsson switches that achieved previously-unheard-of reliability were programmed in Erlang, but they used process and a huge number of engineers to achieve that. (Plus they're mostly stateless and so can just reset if they do it fast enough and don't lose important state when doing so.)

For large-scale software which has to evolve fast, I belive strong type systems do win out. It's not so much that they prevent bugs which could be caught by test suites, but having a strong type system means that you can be certain that there are a lot of tests that you actually will never have to write or, more importantly, rewrite as the system evolves.

There have been actual studies which may be of interest[1], but even if I'm on the "winning" side, I don't think I would put too much stock in the methodology/analysis of this particular study. (E.g. concluding that JavaScript suffers from few concurrency bugs relative to other languages is kind of being oblivious of the fact that JS is single-threaded (semantically) and that all the other languages in the comparison permit "real" concurrency/parallellism, and that it should thus perhaps be excluded from the category or at least treated separately.)

[1] http://macbeth.cs.ucdavis.edu/lang_study.pdf

[2] https://www.usenix.org/conference/hotdep12/workshop-program/...

(Sorry, references out of order because edited-post-facto)


> If you want truly reliable software and have the budget, then you just need to throw process at the problem -- it doesn't matter much which language you use.

I also generally agree with your comment, but I have to take issue with this statement. If this were true, Ericsson wouldn’t have had to abandon the AXE-N project.

Quoting from Joe Armstrong’s PhD thesis[1]:

“1995: The AXE-N project was a project to build a “next generation switch” to replace the Ericsson AXE-10. This extremely large project ran from 1987-95. After the AXE-N project collapsed a decision was made to “restart” the project using Erlang. This project eventually resulted in the development of the AXD301 switch.”

“During the period 1996–1997 a three-person group (myself, Magnus Fröberg and Martin Björklund) redesigned and implemented the OTP core libraries.”

“1998: Ericsson delivered the first AXD301. The AXD301 is the subject of one of our case studies in Chapter 8. At the time of writing (2003) the AXD301 has over 1.7 million lines of Erlang code which probably makes it the largest system ever to be written in a functional style of programming.”

[1]: http://www.erlang.org/download/armstrong_thesis_2003.pdf


Thanks for digging out the history. I don't think we can conclude from your excerpts that the deciding factor was Erlang. However, I accept that my statement that it was "just engineers and process" may have been exaggerated or mistaken.


The deciding factor, as always, is the ability to manage complexity. There are many ways to improve this ability, and process is certainly one of them, however, the choice of programming language is, to my mind, the most important one.

XenSource/Citrix is another great example[1]:

“In 2002, members of Cambridge University released the open source Xen project to provide free high-performance virtualization technology for the masses.”

“In 2004, the team founded the company XenSource and began developing the enterprise-level distributed management software that would make virtualization easy and form the backbone of their products in the years to come. A team of thirty dedicated programmers in Palo Alto California began developing the software in C, Python and Ruby.”

“Two years later, the US team had succeeded in burning tens of millions of dollars funding but had failed to produce any product. So XenSource, under new management, took the bold step of replacing their large team of US developers with a team of only four British OCaml developers. Within months, all of the Python and Ruby was replaced, both reliability and performance were dramatically improved and the company shipped their first product. One year later, XenSource sold to Citrix for $500M.”

[1]: http://ocamlnews.blogspot.co.uk/2008/11/stunning-slides-abou...


Indeed. I must admit that my pro-FP inner geek giggled just a little bit at this bit of "money talks".

I think it's imperative to question the "reliability and performance were dramatically improved" kinds of statements with quantitative research. It's just that this kind of research is insanely hard.

(Disclosure: I'm definitely on the FP team, but I want do be able to argue objectively and quantitatively for the benefits of my approach.)


I fully agree. Have you seen the 1994 Hudak study[1]?

“A simplified version of real-world problem was chosen by the Naval Surface Warfare Center (NSWC). This problem, a geometric region server (geo-server) is one component of a much larger system, NSWC’s AEGIS Weapons System (AWS), which NSWC is in the process of redesigning.”

“The participants, each considered an expert programmer in one of the programming languages being tested, was asked to write a fully functional prototype of the geo-server, while keeping track of software development metrics such as development time and lines of code and documentation.”

“The results indicate that the Haskell prototype took significantly less time to develop and was considerably more concise and easier to understand than the corresponding prototypes written in several different imperative languages, including Ada and C++.”

[1]: http://www.cs.yale.edu/publications/techreports/tr1049.pdf


Yes, indeed, I've read it. Unfortunately, I think we should regard the early studies (such as Hudak's) as basically preliminary and mostly not-well-founded. I know that's horribly conservative, but y'know...


In the study you linked to Clojure code is basically as bug free as Haskell and Scala code and Erlang code isn't far behind.


As I recall they conclude that the general stats show that "static-functional" is more bug-free than "dynamic-functional" which is more bug-free than "static-procedural/dynamic-procedural", which is more bug-free than...

As I said, I'm not sure I buy into the whole design/classification/statistical scheme of the study myself, so YMMV.


Of course everything is a tradeoff; using a static type system is not free. If anything, types also serve as always-in-sync documentation.

Gradual typing that Erlang and Clojure (both excellent languages) introduce is also a tradeoff: you usually want strict static checks on the outer interfaces of your modules, and/or along the most critical chains of computation. So you build it on top of your dynamic language; I've seen such things done in Python, too.


Or 'use Typed Racket'.


Unfortunately, the article in question did not mention words 'typed' or 'static' at all.

I didn't know about Typed Racket; I suppose it might be a kind of gradual / optional typing somehow simila to what Clojure has.


Clojure's gradual typing system is heavily inspired by Typed Racket, which preceded it.

https://twitter.com/ambrosebs/status/262387983339098113

For more information, see Ambrose Bonnaire-Sergeant' dissertation:

A Practical Optional Type System for Clojure

https://cloud.github.com/downloads/frenchy64/papers/ambrose-...


The OP's complaint about Go's type system seems to mainly be that memory allocation is explicit.

Go is always "pass by value", and it's true that I've watched developers struggle to grasp the full ramifications of what that means regarding memory usage. But not understanding the subtlety of memory allocation usually means your programs will simply run slower and consume more memory. It does not usually mean that your programs will run incorrectly.

Given that the OP mentions that they find the slower, more expensive execution of racket programs to be an acceptable trade-off, I'm surprised that they have a problem with this.

Personally, I prefer running in a language that allows me to reason about the memory allocation explicitly. I'd only prefer to give that up in favor of a declarative language that could compute the optimal execution for me. Unfortunately that language is a bit of a unicorn for general purpose programming.


In Go you can pass a pointer, and a pointer to a pointer. I understand both cases are usually called "pass-by-reference". If you call "pass-by-value" to passing a pointer, then the notion of "pass-by-value" and "pass-by-ref" are not very useful.


If what you are passing is a pointer, then the value of the pointer (an integer) is copied. So pointers are passed by value.

"Pass by reference" means that the runtime will convert what looks like an instruction to copy a value (such as a data structure) into an instruction to copy only the address. Go has no such concept. You cannot make a new value that is a "reference" to another value. If you make a new variable that takes a value, that variable allocates a new block of memory the size of that value. Assigning a value to the variable (or passing a value to a function) makes a copy of the value into the new memory address. You can make a "reference" only by explicitly making a pointer and copying the address of another object.

"Pass by reference" languages treat everything as a pointer by default, forcing you to make a copy operation when you do not want this behavior. This may seem like an equivalent programming model, and possibly simpler because this pointer business if confusing at first. However, basic concepts such as a call stack or an array of values can no longer be expressed easily, because with "pass by reference" languages you have removed the concept of a value from the language. So pass-by-reference languages can lead to confusion in large programs, even if they seem simpler at first.

Basically, go is "pass by value" because the language forces you to always be explicit about when you are copying an entire value, and when you are only copying a pointer.


Of the complaints you can make about Go's type system (and there are many), being too complex is not one of them.


In the comments he says he changed his mind about Go, and the necessity of the efficiency gain over Racket. So there's some confusion apparently. https://cxwangyi.wordpress.com/2012/07/22/why-go-use-racket/...


It's a chicken and egg problem, but the real reason for me to use Go vs Racket is that learning the former will further my career significantly, while latter won't at all. Sad, but true.


It is incredibly sad and reflective of a broken industry.

One of the major advantages of Go is that it is easy to learn for existing programmers as there are very few different abstractions/features.

Yet plenty of companies are now requiring prior experience in Go, erasing most of the benefits of using Go.


...use your words... not your down votes


It depends. Learning Racket and the design methodology taught by Felleisen, et al. could help you become a better programmer in general, and thus significantly further your career.

How to Design Programs, Second Edition

http://www.ccs.neu.edu/home/matthias/HtDP2e/


Sure, I agree. But the impact is much less direct. Most interviews go like this: "Have you worked with Angular?". And you just don't really need to be a very good programmer to have a well-paying job.


I'd certainly be much more likely to hire someone who had worked through How To Design Programs


Author thinks Go's select statement has any resemblance to with epoll and kqueue. Didn't read further.


That's too bad, because you missed the brilliant ending to the post, in which the author answers the question posed in the title:

> P.S. As noted in [link], Racket programs build and run much slower than Go programs.


I like crosscompilation and being able to deploy a stand alone executable without the whole sdk needed.Does racket do that?



I may have missed in those docs any mention of cross-compilation. The first result for 'cross compile racket' is [1]. Of course that's from 2013, so maybe it changed since then.

[1] https://groups.google.com/forum/m/#!topic/racket-users/LLB6o...


You're right – I keyed in on "stand-alone executable" and not the "crosscompilation" part of the question.


The blog post is a couple of years old, so it's probably worth noting that PLaneT[1], mentioned by the author, has been superseded by a new Packages[2] system. PLaneT is still operating, however, and libraries hosted there can be installed with raco[3].

[1] http://planet.racket-lang.org/

[2] http://pkgs.racket-lang.org/

[3] http://docs.racket-lang.org/raco/index.html?q=


I'll come right out and say that I despise Go. It's a terrible language that ignores decades of language research and brings nothing new to the field, but has become popular due to Google hype.

That said, Go -> Racket is a pretty big jump. Even though I think Go is terrible, I can at least take the time to see the problems it's solving and offer comparable languages. I suggest Rust as a Go replacement.

Don't get me wrong, Racket is pretty good. It's not the Lisp I would pick (I really like Gambit Scheme) but it's at least a Lisp. But a high-level language like Racket isn't really comparable to Go. They're just in different spaces.


Rust's and Go's strengths are very dissimilar, there's no reason to consider one as the replacement for the other without considering a dozen other worthy languages in between.


Sure, if you make vague enough statements, they're hard to disprove, which might lead you to believe that what you said is correct even though you haven't made a provable or disprovable claim.

There are plenty of problems where one might consider using Go or Rust. The same cannot be said of Go and Racket, or Rust and Racket. I wouldn't consider using Racket for a situation where I need high performance or low memory usage--the numbers just don't work. Likewise I wouldn't look at Go or Rust for building a data-presentation type webapp.


I'm inclined to disagree about the distinction between pointers and values being explicit, and how that is bad - I think it might be the right decision in an imperative language that tries to be performant in a straightforward way (as opposed to language that tries to be performant by relying on compiler optimizations - that might work for certain languages). It allows you to be aware and more careful about indirection and memory layout, which obviously can buy you a lot with regards to performance.

It certainly seems like the more lower-hanging fruit than what the usual advice for getting a fast programming language is - make memory management explicit/manual (though it seems that if you have pervasive manual memory management, you end up with a clear and explicit distinction between values and pointers to values anyway).


shouldn't be: Why Go? (Use (Racket))


That looks like you want to use the result of Racket, rather than use Racket itself.


Racket's website (http://racket-lang.org/) has an "explain" button by the code snippet. Is the existence of such a button perhaps a statement about the quality of the syntax?


Did you even read what pops up when you hit that explain button?

  Sending email

  #lang racket ; Sending email from racket
  (require net/sendmail)
  (sleep (* (- (* 60 4) 15) 60)) ; 4h - 15m
  (send-mail-message
   (getenv "EMAIL") "Parking meter alert!"
   (list (getenv "EMAIL")) null null
   '("Time to go out and move your car."))

> "Racket comes with plenty of libraries."

> "To run the example, install Racket, start DrRacket, paste the example program into the top area in DrRacket, and click the Run button. Alternatively, save the program to a file and run racket on the file."

That section of the landing page is meant for people who have not been exposed to Racket before. It is meant to sell those people on the expressiveness/power of the language. The explain button is meant to explain exactly what that code snippet is meant to be demonstrating. Very few of those seem to touch on syntax at all.

It seems insane to me that somebody could spin user-consciousness on a language's website as as indicative of a low-quality language.


No, it's a statement that the developers of Racket care about explaining how things work.


Perhaps because they need to? Or do you find Racket as clear as other languages? Even other Lispy ones?

Without trying to be insulting, I find Racket aesthetically unappealing.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: