Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Why Go? Use Racket (cxwangyi.wordpress.com)
42 points by brudgers on Sept 18, 2013 | hide | past | favorite | 76 comments


> But it is inefficient to pass an array or struct; instead, we should pass pointers to these types.

I feel like the author hasn't used go much. You almost never work with arrays directly, you use slices, which CAN be efficiently passed around.

EDIT: Upon re-reading I think the author was making a point about the consistency of go, and how different types have different behavior with regards to pass-by-reference/copy. There's actually a pretty simple rule for it:

If the variable is a primitive, or defined by the user, it is passed by copy (ints, strings, structs, etc...). Everything else (compositional types like slices and maps, and others like channels) are passed by reference.

The outlier are raw arrays, but again you don't ever actually use those, so you can just ignore them.


So... you have the right idea, but your statement about passing is technically wrong.

Everything in Go is passed by copying its value.

However, slices, maps, channels, and interfaces all have small values, which are just a pointer and some other small data (length, type, etc), so passing them directly (rather than by pointer) is not a big deal.


As some one who is not familiar with Go, that sounds like it supports his statement that the type system is confusing.


I think it might be stretching it a bit to say that one lesser used exception makes the whole system copy/reference system confusing. There is also the upside that if you know what you are doing you can extract significant improvements in performance by being able to specify which passing method is used; a fair number of newer languages offer you no such option.


I do appreciate the additional optimization opportunities that derive from the fact that user defined types can be used as values and not just references like in Java. That's very important for memory usage.

But the complexity that comes with it is undeniable. It's not just one exception that is confusing. The whole business with nil interface values versus non-nil interface values that contain nil pointers is not exactly pretty. It is consistent though.


I don't dispute the usefulness of being able to control how parameters are passed, especially in a systems language. But it seems like the two ways of passing could be consistently marked by syntax. They are in C, C++, and Rust, right? In Rust, if you had some type 'channel', then:

fn spam(victim: channel)

takes the channel by value (impossible in Go, and probably nonsensical anyway), whereas:

fn spam(victim: @channel)

takes a pointer to the channel.

I think the str type is currently an exception to this in Rust.


> the type system of Go is so complex;

For someone coming from a Scala background, I feel this statement is absurd.

One thing we are all forgetting here is that anyone can prove language X is better than Y. But it's always wise to choose the best tool for the job (X is better than Z in doing what exactly?).

Languages and frameworks are a personal preference. You choose them based on their philosophises and if their philosophies resonate with yours.

After a certain point, language choices will no longer matter. Your end goal, should be to get to this point, instead of fighting over them. Just my 2 cents.


> But it's always wise to choose the best tool for the job.

I'm so tired of this meme. Yes, in some situations there is a "right tool". If you are working on embedded devices you probably should just use C. But in the wider world of programming there isn't a "right tool". In web development especially, there are many tools that can get you to the same place. And because of that it's perfectly reasonable to debate that one is better/worse than another.


I think you may have taken that statement out of context. The OP was talking about the complexity of the type implementation, not the theory behind it.

The specific example he used was how a programmer needs to know exactly how some different types are passed as arguments to know what the most efficient way is, since it's not entirely consistent.

EDIT: I agree with the rest of your comment, though. Languages are just tools. The whole, "Why are you using X? Y is better," thing is getting old.


Even though I partly agree with your, is language really just "tools"? I think there's more to it, it's a notation, it's a way to think about problems, etc.


As long as we treat languages as personal preferences, the programming community will remain deplorably divided. In order to join forces to work on demanding projects, I think we need to agree on a relatively small number of good-enough languages. To do this, we need objective criteria for choosing one language that is good for a particular class of software.


> As long as we treat languages as personal preferences, the programming community will remain deplorably divided.

Languages are personal preferences; they certainly have objective differences which have some effect on utility, but a substantial part of what makes a language the best for a particular programmer for a particular purpose is how well the the language matches the programmer's experience and patterns of thought for addressing the problem domain. These are inherently subjective factors.

> In order to join forces to work on demanding projects, I think we need to agree on a relatively small number of good-enough languages.

We may need to agree, for large projects that need to be integrated in such a way that you can't use a mix of languages for different components, on as few as one language for a particular project, but that doesn't constrain the number of languages we can have in the whole programming ecosystem (and, indeed, restricting the domain of available languages would restrict the development of new techniques and languages and the development of better languages for new use case.)

> To do this, we need objective criteria for choosing one language that is good for a particular class of software.

This requires, obviously, the ability to objectively categorize software into "classes" to apply the criteria. Since I think you are going to fall down hard on that, I'm not really holding my breath to seeing your vision realized, even if I thought it was a good thing.


I would content that for such a person (not that I am a stellar programmer or savvy on type theory) Typed Racket will make this man/woman/smizmar lose sanity.

That said, I think Racket is so-so cool.


Racket is not only a great language. It's also a great developing environment, a great community.

And unlike most other languages, it's out of box support for Windows is perfect.


To be fair, maybe this blog post is getting more attention than the author expected. Regardless, I don't think it makes a great case for "Why __? Use Racket!".

The case I'd make: The shiny new feature X in today's fashionable new lang Y? Racket probably does this, does it elegantly, and has done it for awhile.

CSP is just one example. It's great that Go is helping popularize it.

Also I'd headline it as, "Intrigued by __? Also try Racket!".

Racket draws on a lot of PL research (which is why it probably already has feature X) but is also very practical. It's easy to install and use on all platforms. There's also a wonderful community.

About the only thing it lacks is a BFD/cult leader phenom such as some companies and langs have. ;)


Yeah. And Racket suits programmers of different levels well.

For beginners, it's an easy to learn language with an easy to setup programming environment.

For advanced programmers, there're a lot of advanced features available as 6cxs2hd6 mentioned.


> the type system of Go is so complex;

Go's type system is not complex at all. What is complex about it? There is a limited set of types, and they can have methods. You can define your own types. What's so complex about that?

> programmers need to know all the details about the implementation of standard types, before they can use them correctly.

You should not have problem with knowing the details of the language or the third party libraries you are using if you want to be a proficient engineer builds stable, secure and easily-maintainable systems.


>You should not have problem with knowing the details of the language or the third party libraries you are using if you want to be a proficient engineer builds stable, secure and easily-maintainable systems.

What if you instead want/have to be a pragmatic programmer, you doesn't "engineer stable, secure and easily-maintainable systems" with all the time of the world available to him, but has to build something working adequate enough, as quick as possible, warts and all?

You know, like 90% of early startup code out there, including companies that sold for hundrends of millions.


You are welcome to write shit code in go too, nothing is stopping you.


Thanks, this thread needed a strawman.


You don't need to thank me, I didn't help you write your post. Did you mean to thank your caretaker?


> the third party libraries you are using

That's true only to a point. I'm not sure how deep the dependency tree of the two main Java projects I work on at the moment is, but I can guarantee that I don't know "all the details" of, well, most of it, despite considering myself a "proficient engineer".


Why do we keep playing this like a competition? I'm sure someone could put together an equally valid post about why Go is "better" than Racket. It's frustrating how so many people forget that we're all in this together, how every language has it's pros and cons, and how our job is to pick the best tool for the job.


Err, because "pick the best tool for the job" is precisely a competition that each of us has to judge?


These posts could help us in choosing the best tool for our jobs.


Best is vague and ambiguous, you might as well be trying to find out what the best instrument is for making music (or the best color for painting).


A composer doesn't necessarily sit down and decide to "make music". A composer often has some sort of goal in mind, and yes, they will indeed seek out the best instrument for the job. The criteria may very well even include such things as "how skilled am I on the instrument"; someone may just noodle around on a keyboard or a guitar for something that may end up on another instrument entirely, for reasons of familiarity, or because the instrument has useful characteristics. For instance, a piano is a very useful instrument to write for a symphony orchestra, because even though it may not sound like any instrument that will be in the final composition, it's pretty hard for one person to play two flutes and two trombones at the same time, whereas the piano makes it easy to play at least the same notes, so you can hear the harmony.

Similarly for painting; you're looking at a world of difference between watercolor and oil (just to pick two examples), and what you chose depends on the goals.

I'm not sure your metaphor was as useful as you'd like.


Those analogies don't work. Paintings use multiple colors, and many pieces of music use multiple instruments; but for most programs, it's far simpler to use just one language. And if one is going to develop a large program over the span of many years, it's good to think carefully about what language to use before diving in, as that decision is not easily changed.


Except the selection of "facts" presented by these kinds of posts are rarely amenable to independent analysis.


I think it sums it up at the very last sentence, almost as if it isn't really relevant :

"Racket programs build and run much slower than Go programs."

Edit: By this I meant to be agreeing with the above statement that someone will come along and write a post about why Go is better - it's already been done in this one post. Sorry for the misunderstanding..

Obviously, sometimes execution speed is important, and sometimes it isn't - it depends on what you are trying to do.


The author's postscript links to this follow-up blog post:

http://cxwangyi.wordpress.com/2012/07/29/chinese-whispers-in...

To me, the speed at which the code executes is a second derivative problem - this is to say that if a language gets version 0.1 deployed two months faster, then it is faster in a more important sense. A hammer and screws is more useful than a screwdriver and nails - intact it is more useful than a Phillips screwdriver and slotted screws.


If you see the links after that (and the comments) there were some issues with Racket's GC, and there were some fixes offered. YMMV.

That said, of course it isn't "really relevant". Or, at least, it's not all that matters, if Racket offers a superior experience otherwise AND its speed is adequate for the task.

Else we'd use C all the time, and nobody would use Python or whatever higher level language.


That's only an implementation detail that could one day be overcome. PyPy is an excellent example of why, "X language is slow," is a silly statement. The implementation of the language runtime can be made to be faster.

As well as speed being a secondary concern for all but a few application domains.


My problem with Racket is that it's startup time is slow. Because of that it competes with Clojure, and Clojure is frankly just better. I would probably use Racket if it started up instantly; could be great for cli programs. But Chicken has a decent ecosystem and is blazing fast, so I use that.


The author has a follow-up post http://cxwangyi.wordpress.com/2012/07/29/chinese-whispers-in...

Each thread reads from the channel to its left and writes the processed input into the channel to its right.

Except that the programs he posted are flowing the other way(start with rightmost, read final value from leftmost). The implementation of the author's wording needs a slight change.

Go implementation:

    package main

    import "fmt"

    func setup_channels(left chan int, num int) chan int {
        for i := 0; i < num; i++ {
            right := make(chan int)
            go func(left, right chan int) { right <- 1 + <-left }(left, right)
            left = right
        }
        return left
    }

    func main() {
        leftmost := make(chan int)
        rightmost := setup_channels(leftmost, 100000)
        leftmost <- 1
        fmt.Println(<-rightmost)
    }



Racket implementation:

    #lang racket

    (define (setup-channels left idx num)
    (if (>= idx num)
        left
        (let [(right (make-channel))]
        (thread (λ ()
                    (channel-put right (+ 1 (channel-get left)))))
        (setup-channels right (add1 idx) num))))

    (let* [(leftmost (make-channel))
        (rightmost (setup-channels leftmost 0 100000))]
    (channel-put leftmost 1)
    (channel-get rightmost))
I also don't understand why is he writing to the leftmost channel in a separate thread. I don't think that's needed.


I'm very ignorant on the difference between the functional languages, but could someone please explain, apart from the implementation which affect the performances, the actual difference between the languages? To me they all look like LISP.

I feel like a non-linux user categorizing Debian, Ubuntu, Mint, Red Hat, and so on altogether as 'Linux'; While he's not incorrect, there are subtle difference that he's not seeing. It feels rather frustrating. I'm using Racket(when I want to experiment in functional language) because that's what I'm used to, but it would be great to hear X and Y are different in ... than X is better than Y because...


Dunno, ML-based languages have a pretty different feel to Lisp. They're compiled languages, have type inference, heavily rely on pattern matching, type declarations. An example from OCaml's front page:

type tree = Leaf of int | Node of tree * tree

let rec exists_leaf test tree = match tree with | Leaf v -> test v | Node (left, right) -> exists_leaf test left || exists_leaf test right

let has_even_leaf tree = exists_leaf (fun n -> n mod 2 = 0) tree


Most LISP implementations also provide compilers. The difference is that ML variants are statically typed, while LISPs are generally dynamically typed.


> "generally dynamically typed"

I appreciate the "generally" word here. Racket and Chicken Scheme for instance, both provide statically type extensions.


But not, I assume, type inference? So you'll end up writing the types in the code all the time.


Yes... you can have type inference as well (if you wish[1][2]). I guess the philosophy is to use it if you need it but otherwise you might prefer to stick to the dynamic part.

----

[1] http://docs.racket-lang.org/ts-guide/quick.html

[2] http://docs.racket-lang.org/ts-guide/more.html#(part._.Type_...


But they are essentially garbage are they not? Adding java-style static typing is not helpful at all. The benefits of static typing are very much tied to the power of the type system in question.


Typed Racket definitely doesn't add a "Java-style static typing" system at all. Its type system is quite specifically designed to accommodate the kinds of programming idioms you find in Racket programs (via occurrence typing, function intersection types, and so on), and comes with local type inference.


I don't use racket obviously, but looking through the docs on typed racket it certainly looks like it adds java-style static typing. Having crappy partial type inference doesn't make the type system powerful or expressive, C# has that too and it certainly falls into the java-style type system camp.


> but looking through the docs on typed racket it certainly looks like it adds java-style static typing

It's unclear what you mean by "Java-style static typing" then, because the Java type system and Typed Racket's type system are completely different.

One actually has local type inference (which is not "crappy" type inference necessarily; HM-style type inference is notoriously brittle), the other doesn't. One has nominal class types, the other has no class types (yet). One has intersection types, occurrence typing, "true" union types, and so on, while the other has none of those. One has bounded polymorphism, the other has System F-like polymorphism. One of them has variable-arity polymorphism, while the other doesn't. The list goes on.

In fact, aside from being explicitly typed, there are few similarities.


>One actually has local type inference

Which is totally orthogonal to the power and expressiveness of the type system. I specifically gave an example that C# also has crappy limited type inference too, and is still java-style static typing.

>HM-style type inference is notoriously brittle

Yeah, obviously no languages could possibly exist that use it with no problems.

>One has nominal class types, the other has no class types (yet)

What does being OO have to do with anything? I am talking about the expressiveness of the type system. The amount of things you can express in the type system.

>One has intersection types, occurrence typing, "true" union types

So racket has a partial implementation of ADTs? And that's it? Ok, you win, racket has java + 0.1 level of static typing, hooray! The point is, using static typing in racket gets you virtually nothing, because it is such a primitive type system. Go use ML or haskell and then compare it to typed racket.


>"The benefits of static typing are very much tied to the power of the type system in question."

One of the main benefits of static typing is to detect errors at compile time which is a benefit of typed Scheme compilers too.

BTW, static typing goes way before Java :)


>One of the main benefits of static typing is to detect errors at compile time

Obviously. But a static type system that lacks power and expressiveness is very constraining and can detect very few classes of errors. Hence the example of java, a very inexpressive static type system.

>BTW, static typing goes way before Java :)

Again, obviously. Why are you saying random obvious things to me?


Because they seem to elude you, especially if you think Racket was given an "Java-style static typing".


Your comment is even dumber than I would expect from you. That is a very impressive feat. Non-sequiturs don't become relevant because you say "DURRRR".


If you do not comprehend the type system in your language, your program will be inefficient garbage if it runs at all. This is true in Racket as much as in Go.

And Racket's type system (being an expandable, routinely expanded language) is much, much larger.


How lightweight are Racket's threads? I'm curious if they're really lightweight in the same way that goroutines are. Go's documentation states: "It is practical to create hundreds of thousands of goroutines in the same address space."

Is the same true for Racket threads? Or are they closer to "lightweight" green threads in other languages. I'm genuinely curious. If they are really as lightweight as goroutines, that should be publicized more heavily. If they're not, then it's disingenuous to claim that Racket's CSP story is equivalent to Go's.


I would imagine that Go has the advantage of being significantly faster than Racket. Although if people still loves to do things in Ruby maybe performance is less than a concern in web development.


> Although if people still loves to do things in Ruby maybe performance is less than a concern in web development.

As a user of web sites (and a web developer), I find it disappointing and frustrating to use web sites designed by people who clearly have not made performance a priority. So just as a tangential point: if you are one of those web developers who loves doing things on slower platforms, believing that it's just fine to disregard performance, please think of your users! We are the ones who suffer! 250ms here, 500ms there, sometimes over 1,000ms before the server responds with the first byte! It adds up to an annoying user experience.

Among popular sites, those that are slow tend to really irk me, and I seek out alternatives. For example, there is this very popular source code social network site...


User-perceived response time has little to do with the choice of language on the server. Access to DBs, lack of proper caching strategies, CDNs, minification of CSS and Javascript play a much larger role in the big picture. Only when you've extracted all the juice from all those lower-hanging fruits must you start to blame the language or framework used to developer a website IMHO.


I'm not talking about a client-side performance issue. Those are tangential matters. We should all endeavor to comply with best practices for delivery of static assets to improve client-side performance including minification, strategic concatenation, use of content delivery networks, caching strategies, and so on.

Let's put that aside, however.

What I am talking about is a real request to a non-cachable dynamic page or service endpoint. It's the frustration of "Waiting for [servername]..." status bar messages. It's the frustration of looking at the Network panel in Chrome or Firebug and realizing that it's just spinning waiting for the first byte of the response. It might be a server-templated HTML page. It might be an API call fetching some JSON. But the sorry state even in 2013 is that a disturbingly large number of sites will spend hundreds of milliseconds, and in some cases full seconds to respond to dynamic requests that ultimately deliver quite trivial response payloads.

I for one want developers to stop sweeping these performance failures under the rug by deflecting server-side performance failure with client-side cover.


I believe, in most cases of those milliseconds amount to network and database/storage latency, not site code's performance.

So it's just insignificant (performance-wise) whenever you write site in carefully optimized C or just write some Ruby magic that performs, say, 100x slower. 1ns vs 100ns is insignificant when it's your database that responds in 300ms.


You couldn't be more wrong. PHP and Ruby page generation times can easily exceed 1000ms if the programmer isn't careful. Nanoseconds is just rubbish. Even the fastest template engines typically take on the order of a few ms.


TylerE is correct.

Each application is its own unique snowflake, admittedly, but I encourage web developers to profile their applications to understand where server time is actually consumed. Furthermore, I implore you to realize that time spent in the ORM is not the same as time waiting for the database. If your profiler conflates ORM machinations as database time, you are not seeing the correct data.

Modern databases are extremely efficient at fetching data from well-indexed tables. They are in many cases not the bottleneck although conventional wisdom would have you believe otherwise.

In our experience, slower platforms consume an impressively large amount of time doing things unrelated to the actual database queries. Under load, the database server can be nearly idle while the application server is falling over itself in its slow ORM, its template engine, or helper functions. Meanwhile, faster platforms mean faster frameworks, faster ORMs, and faster templates. These platforms can easily saturate the Gigabit Ethernet connection to their database server without the database server hiccuping.


The site's code absolutely is part of those milliseconds. Often times it can even be tbe bulk of them, particularly in cases of PHP noobs writing the code.


That discussion keeps popping up from time to time. I do prefer a better performance but most of the time that not even dependent on the language but on the developer. I've seen super fast websites done in PHP and super slow ones done in Java.


Sure. There are exceptions to every rule.

Web application performance will have its exceptional cases. But in general, I find Java web application when modestly-well architected, will respond in low tens of milliseconds--and in many cases single-digits--to the vast majority of their routes. A PHP developer would need to be nearly superhuman to accomplish the same level of performance.


If you are waiting 1000ms for the first byte, clearly there's something wrong and it is not the language the site is written in.


Perhaps the two go hand in hand. I say evidence suggests that is not only plausible but perhaps common.

Something is indeed wrong. We tolerate servers responding to trivial requests in hundreds of milliseconds (sometimes even greater than 1,000 milliseconds!)

Here is an off-the-cuff--not cherry picked--screen grab from Firebug [1] showing 342ms of waiting for the server to respond to a relatively trivial request of listing summary information about approximately 20 entities that are presumably stored as rows in a database table from a popular web site. This type of delay is my chief complaint about a site that I am otherwise very fond of. Until this first response is delivered, the user is just plain waiting. Static assets should also arrive quickly, but I am more forgiving of them arriving gradually to decorate the page.

Yes, it's certainly possible the database server is the origin of this pain. And web developers should profile their applications to gain insight. Unfortunately, as I mentioned above, many profilers incorrectly conflate ORM or platform database driver time with genuine database time. Given what we have seen from frameworks and platforms making database queries and processing responses through ORMs, I know that MySQL (and other data platforms) on decent hardware can respond to tens of thousands of such trivial queries per second. Meanwhile, some application stacks reach reduced responsiveness and worsened user experience well before the database's CPU and disk register more than a blip.

[1] http://imgur.com/OXyhFbO


You'd be wrong. Go IS faster, but not significantly so, and it's not a clear cut winner either.

http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te...


Well five out of ten Go is at least 2X faster (that's significant enough to me). In any case, probably the difference won't be noticeable since the bottleneck for most websites is the database side.


But compare either, with, say, Python or Ruby and the difference is more than lost in the noise.


I'm pretty sure at least one of those tests Racket does better at is essentially a GC benchmark. Go's GC is still fairly weak.


> But we are not allowed to convert the interface into that type. Instead, we can convert it into a pointer to that type.

I don't quite follow this "problem" - it sounds like a complaint about the lack of generics. I haven't been programming in Go for long, but I found the reflection system pretty easy to use and it basically allows you to do this.

Sure, it's not EXACTLY the same, but in what case do I actually need a float or integer instead of an interface method that acts on them in the way I expect?


I like racket and I've always wanted an excuse to use it, but the development experience with go is very, very nice. Nice enough that I'm starting to reach for go where I would reach for ruby most days. The racket experience seemed pretty great, but go has me hooked. Fast compile, fast tests, fast startup, binary deploys. The whole go experience seems fast, clean, and sane.

Is there a great reason to use racket instead of go?


Racket threads (which are green threads hosted on the same native thread in 1:n model) are not really equivalent to Goroutines (which are green threads hosted across a pool of native threads in an m:n model).

Racket has a richer set of concurrency/parallelism structures (threads, futures, places, engines) than Go provides (goroutines). This is both a source of power, and a source of cognitive overhead.


What?! If anything the type system is quite simple.

Probably only Oberon-7 strives to be simpler, from the current set of modern languages.


Correction: Oberon-07, which has so few users, I doubt it qualifies as valid option.


> Correction: Oberon-07...

Right, I always forget the right naming. From the Oberon family, I prefer Active Oberon anyway.

> .., which has so few users, I doubt it qualifies as valid option.

True, it is unfortunate that the whole Oberon ecosystem never left the academia, except for a single digit set of companies that managed to make some money out of it.

Still, it does count when discussing about language type systems.


Since it was one of the very few semi-modern systems programming languages other than C used to build an OS, I would love to see some form of Oberon gain more nerd traction.


I was really into it, back when System Oberon was being actively developed.

My experience with it, made me a supporter of GC enabled systems programming languages and OS made with them.

The other one I also played with was Modula-3.

Nowadays it seems even at ETHZ, Oberon does not get used that much, if at all and they switched their focus to research based on .NET.

> I would love to see some form of Oberon gain more nerd traction.

I doubt it will ever get one, due to its Pascal syntax heritage, which many don't like, specially the all caps for keywords.

Oh well, at least Go got some influence from it.




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

Search: