1. 13

  2. 15

    Note that this is from 2010 and a lot has changed since then in Racket-land. I think both Racket and Clojure are fine languages, but Racket has been my daily driver this past year and I prefer it for various reasons.

    Unfortunately, Racket provides no built-in lazy list/stream, so you’d need to realize the entire list.

    It certainly does now:

    There’s even a whole language dedicated to this that interoperates with normal racket code!

    But even if that’s what you’d want to do, Racket doesn’t provide a built-in function to give you back the list of keys, values or pairs in a hash table.

    It does provide all three:

    Instead, you’re encouraged to iterate through the pairs using an idiosyncratic version of its for construct, using a specific deconstructing pattern match style to capture the sequence of key/value pairs that is used nowhere else in Racket. (Speaking of for loops, why on earth did they decide to make the parallel for loop the common behavior, and require a longer name (for*) for the more useful nested loop version?) Put simply, using hash tables in Racket is frequently awkward and filled with idiosyncracies that are hard to remember.

    This is more a matter of taste and it seems the author is bothered by Racket’s use of values. The sequence implementation for hash tables returns key value pairs as values and all values can be destructured within the for form so that seems quite natural to me.

    There are downloadable libraries that offer an assortment of other data structures, but since these libraries are made by a variety of individuals, and ported from a variety of other Scheme implementations, the interfaces for interacting with those data structures are even more inconsistent than the built-ins, which are already far from ideal.

    All of the libraries I’ve worked with in the past year have followed more or less the same conventions.

    1. 10

      There are a lot of good points here, but the comparison is very one-sided. It’s true that using freeform hash tables is awkward in racket, but that is largely because the language encourages structuring your data in such a way that the compiler can catch mistakes at compile time. Typoes in field names, etc, are much easier to catch in racket while clojure will happily return bogus nil values silently. The fact that nil simply does not exist means that a whole category of errors (in my experience possibly the #1 most common category) is impossible to make.

      There is a trade-off as it makes certain patterns more awkward (you can’t do update-in without lenses, for instance) but often it’s a good trade-off, and it’s not helpful to compare the two without mentioning the upsides.

      (Also clojure doesn’t have pattern matching built in which is a huge WTF)

      1. 1

        There are always trade offs with every approach. It’s worth noting that Spec provides a tool to deal with this problem in Clojure, and you could use contracts the same way in Racket as well. On the balance, I very much prefer the low friction approach that Clojure encourages.

      2. 7

        I would love a clojure like with tail calls and native compilation. Love clojure, hate the jvm baggage. Alas, the perfect programming language doesn’t exist.

        1. 5

          That’s really the only uneppealing part of the language I can think of. It’s ironic that CLR could do real tail calls for a long time. But even then, it’s still the lisp I find most appealing as an MLer.

          1. 2

            Tail calls are actually coming to the JVM in the near future, it’s mentioned in this talk. And you can do native compilation using GraalVM. Here’s an example of a command line util that compiles via Graal, and here’s a small service example that uses Postgres.

          2. 2

            It’s amazing to me how much has changed since 2010. I’ve never used most of the things he mentions as problems in Clojure. They still exist, but are not nearly as important when just Getting Stuff Done.

            1. 1

              I would like to mention Rackjure here. It ports plenty of Clojure’s syntactic conveniences to Racket.

              ;; (dict-ref my-dict key)
              (my-dict key)
              (key my-dict)
              ;; (dict-set my-dict key val)
              (my-dict key val)
              ;; (dict-ref (dict-ref (dict-ref dict 'a) 'b) 'c)
              (~> dict 'a 'b 'c)
              ;; '((key . "value") (key1 . ((key . "value") (key1 . "value1"))))
              {'key "value"
               'key1 {'key "value"
                      'key1 "value1"}}