Hello, I'm Artyom.
You can write me at yom@artyom.me,
by XMPP (yom@artyom.me),
Skype (mayangreen),
or in IRC (indiagreen at freenode).
This site was last updated on December 11, 2014
under intriguing and mysterious circumstances.
If you're unsure whether I'm alive (or at least have internet), see my
Last.FM.
Here's a
random TVTrope,
xkcd comic, and
kitten
for you. Meow.

Learning Racket #1: Introduction

Alternative title: A Haskell Programmer Tries to Learn Racket (a Language from the Lisp/Scheme Family), Documenting This New Experience with Quite Unusual Honesty and Diligence, All the While Secretly Plotting to Steal the Good Bits Which Haskell Doesn't Currently Have.

Y'know, I always wanted to learn myself some Lisp for greater good and what-not. I've heard nice things about Racket (don't ask when or where, I don't remember), so it's going to be the first Lisp I learn.

Random facts about myself:

I spent a year learning Pascal and Delphi (yeah, TButton1), then ditched it for Haskell, which is to Pascal as Bender's shiny metal ass is to my dull meaty butt.

95% of what I know about Lisp is actually about configuring Emacs.

I use Arch Linux, have long hair, love parentheses and hate nondescriptive titles.

Racket is about as old as I am.

I'm learning it because learning things is fun, not because I want to add it to my resume (which I don't have anyway).

Finally, I'm tired of “how I spent one day learning [something] and found that it sucks horribly” posts, so let me state in advance that when something doesn't work as expected or sets my laptop on fire, I might react with “this is unfortunate” but nothing beyond that. As long as Node.js exists in this world, I can't truly hate anything else.

(Hm. I know I shouldn't give in to hatred... But whenever I think about a language originally conceived to animate jumping monkeys on web pages now being used to power web-servers, my only reaction is “I don't want to live on this planet anymore”. Oh, and don't tell me “it doesn't matter what the original purpose was” – the problem with Javascript (and web in general) is that it's a hack on a hack and supported by a hack, like PHP (which is a fractal of bad design), except that PHP doesn't try to be fucking everywhere and Javascript does.)

Day 1

Installation

There's a racket package in Arch's extra repository. 50 MB, quite small for a modern language full of features and batteries included. Right?

$ yaourt -S racket

One minute later, Racket is installed. And it takes only 350 MB on my laptop, vs. 700 of GHC.

Do I need an IDE? Is there an Emacs-mode for Racket? What do I do now? Aha, there's a new program on my computer – “DrRacket”. It's probably what I want.

DrRacket

Fucking Retina-schmetina (just kidding, I love Retina) – font is so small I can barely read. I'm almost sure it can be fixed, tho. Edit → Preferences → Font size = 21. Hm, it hasn't done anything to button labels, but at least font is readable.

That's all I can say about DrRacket... for now.

Looking for a tutorial

Welcome to DrRacket, version 6.0 [3m].Language: No language chosen; memory limit: 128 MB.DrRacket cannot process programs until you choose a programming language.Either select the “Choose Language...” item in the “Language” menu, or get guidance.

Yes, I need guidance!

Using How to Design Programs?Start with Beginning Student.

Fine, beginning student it shall be... No wait, it appears to be a language chooser, not a built-in tutorial. Whatever, built-in tutorials are for suckers anyway. I'll open The Racket Guide in browser and start reading.

The Racket Guide: 1.1. Interacting with Racket

You type a Racket expression, hit the Return key, and the answer is printed. In the terminology of Racket, this kind of calculator is called a read-eval-print loop or REPL.

I love REPLs! And if it's a calculator, surely I can add 2 and 2 with it. If I recall correctly...

>+22
#<procedure:+>
22

Not enough parentheses, I guess. Okay, second try.

> (+22)
4

Now that's better. Does it support power operator? What about bignums?

I wonder if Tab can help me. Nope. But there's Ctrl-/, let's type pow and press it.

Hang for a minute (on a new MacBook Pro!), then a list pops up (pow isn't on it). Later Ctrl-/ works flawlessly. It's probably been downloading autocompletion data or generating it or sending all my data to NSA or something.

So, substring really does need a genuine string. Or am I mistaken about the quote?

> (length '(0123))
4

Fine, whatever. I'll learn list slicing later.

What I think so far

Racket's default IDE is better than GHCi and probably on par with Emacs (you almost certainly can configure Emacs to be better than anything, but it's not trivial and people don't bother, while DrRacket provides autocompletion and documentation out of the box).

Racket's default help (installed on computer) is also better than Haddocks. I mean, it includes instant search for functions! (You can try it here.) There are lots of tutorials too, which means I can install racket on my laptop, turn internet connection off and have a few hours of studying without wasting time on Hacker News.

Off-topic: generally, it's very important that people have goodies out of the box. Programmers are lazy, and many of them won't lift a finger to make their own lives better (unless they realise it's a real problem and make a conscious attempt to improve the situation. For instance, I had to beemind fixing small problems with my laptop – otherwise I'm pretty sure I'd still live without working hibernation and sound controls.)

TRG: 1.2. Definitions and Interactions

If calling (extract "the boy") is part of the main action of your program, that would go in the definitions area, too. But if it was just an example expression that you were using to explore extract, then you’d more likely leave the definitions area as above, click Run, and then evaluate (extract "the boy") in the REPL.

I've just realised that I missed something very important. Very, very important.

What's the shortcut for Run?

Aha, Ctrl-R. While I'm at it, killing the program is Ctrl-K. These two are the most important shortcuts if you want to experiment but aren't good enough (yet) to avoid freezing the interpreter every ten minutes while you Just Wanted to Calculate Factorial of Billion, What's Wrong with That. Yes, I'm a bignum junkie.

Back to definitions. Extracting the boy is boring; as I am a Haskell programmer, my first definition must be a factorial.

(define(factorial n)
(if

Er, how do I if?

What if I select it, right-click...

Search in Help Desk for "if"

Yay, Help Desk!

(if test-expr then-expr else-expr)

Evaluates test-expr. If it produces any value other than #f, then then-expr is evaluated, and its results are the result for the if form. Otherwise, else-expr is evaluated, and its results are the result for the if form. The then-expr and else-expr are in tail position with respect to the if form.

Fi-ine, guessing mode on. It returns bool so it probably ends with ?; can it be something like eq?.

(Wow, when I point on an identifier, lines appear and show me where this identifier occurs and where it's imported from! (Note to self: I should find somewhere a huge program, open it in DrRacket and point at racket in #lang racket.))

(Wow #2: a small box in the upper right corner shows me type of whatever is under cursor! That's awesome and also much faster than looking up types in ghc-mod for Emacs.)

(No, wait, drawback: it doesn't show any information for user-defined functions. Pfft.)

Reading the documentation for eq? now. Apparently, there are lots of different comparisons and what I actually want is equal? (or =).

Seven minutes later and factorial of 100000 is computed and printed. (Note to self: Ctrl-K doesn't work if a menu is open... and when GUI doesn't respond, menus can't be closed.)

I'll try to define Quicksort when I know a bit more about lists.

What I think so far

Uniform syntax is harder to read. There should be more syntax highlighting (or different fonts – what about define in small caps, for instance?).

Four different equality functions isn't a good sign. There's = for comparing numbers, eq? for testing whether two objects are the same object (in Haskell such stuff is well-hidden, and rightly so), eqv? which is the same as eq? except for numbers and characters, and equal? which works for most things. Oh, and boolean=? specifically for booleans. And string=? for strings. And char=? for characters. C'mon, I understand why all these things exist, but why expose them?

TRG: 1.3. Creating Executables

A hello world program written in Haskell takes 760 kB; I wonder how big is Racket's hello world going to be, considering that I'll be sure to pack the entire RTS into it.

#lang racket
(print "Hello, world!")

Now Ctrl-S and then Racket → Create Executable. First let's try “stand-alone”.

TRG: 1.4. A Note to Readers with Lisp/Scheme Experience

I.e. not to me.

The module system is designed to avoid these problems, so start with #lang, and you’ll be happier with Racket in the long run.

Hm, is it like starting with module Main where in Haskell? Okay, okay, I solemnly swear to never start a Racket file with anything but #lang, unless, of course, some new circumstances arise blah blah blah earthquakes blah blah blah too lazy to type #lang blah blah blah.

TRG: 2.1. Simple Values

Whoa, finished the first chapter!

Numbers are written in the usual way, including fractions and imaginary numbers

Within nobake, there are no parentheses around string-append flavor "jello", so they are three separate expressions instead of one function-call expression. The expressions string-append and flavor are evaluated, but the results are never used. Instead, the result of the function is just the result of the final expression, "jello".

I bet I would've made this mistake eventually if not for this warning (and I'm not sure I won't make it anyway).

The use of square brackets for cond clauses is a convention. In Racket, parentheses and square brackets are actually interchangeable, as long as ( is matched with ) and [ is matched with ]. Using square brackets in a few key places makes Racket code even more readable.

It's a really neat idea. I like Racket more and more.

> (twice (lambda (s) (string-append s "!"))
"hello")
"hello!!"

Aha, lambdas! My little evil functional heart is beating merrily inside my chest. Let's see if I can write function composition at this point without cheating.

(Meanwhile: I ran into this bug, which caused me to restart DrRacket.)

(define(. f g)
(lambda (x) (f (g x))))

Module Language: invalid module text
read: illegal use of `.'

Fine, I don't remember what characters are allowed in identifiers. What about <>?

(define(<> f g)
(lambda (x) (f (g x))))

> ((<> (lambda (x) (+ x 1))
(lambda (x) (* x 2)))
7)
15

Clumsy lambdas. Can I use λ instead? I can. Cool.

Hm, given that λ is just a Greek letter and not part of syntax (like in Haskell), there's probably some sneaky define somewhere which equates λ and lambda. Can I define my own alias for lambda?

This is not fai— no, wait, lambda is probably not a function at all but some macro-schmacro, and there's a list of lambda-aliases somewhere, and a parser, and what-not, and even if it's possible to define my own alias for lambda, it's Black Magic and definit— who am I kidding? I won't be able to go to sleep until I define l to be lambda and I know it.

What I think so far

There should be an alternative to let with argument order reversed. I'm very used to treating local definitions as clarifying footnotes for “big picture” code, and don't want to give up this habit.

I like the flexibility given by many let-forms (there are let, let*, letrec, and let-values + let*-values + letrec-values for binding multiple outputs), but I feel that they all should've been just one let with optional modifiers.

Lambdas are clumsy even with λ instead of lambda. I would've preferred something like (λx. (* x 2)) or even (* _ 2), and I'd be quite disappointed if there isn't a macro for that.

Time to sleep

Plans for tomorrow:

honor Haskell by writing a Quicksort

honor Tony Hoare by writing a true Quicksort (with mutable array)

finish ch. 2 and ch. 3

Day 2

TRG: 2.3. Lists, Iteration and Recursion

The list function takes any number of values and returns a list containing the values

Such a useful function! Tho I guess the same could be said about Haskell's $ (which applies a function to an argument) and id (which returns its argument)... Okay, I'll see if there are any non-obvious usecases for list later.

Interlude: list functions

I made myself a reference table for list functions:

Haskell

Racket

notes

null

null? or empty?

not to be confused with null/empty

map, zipWithN

map

Haskell's map is just zipWith1, after all

length

length

length . filter

count

filter

filter

filter . not

filter-not

also, negate can be used to inverse a predicate

lookup

assoc

foldr

foldr

foldl

foldl

all

andmap

polyvariadic

any

ormap

polyvariadic

head

car or first

(!! 1) ... (!! 9)

second ... tenth

aka cadr, caddr, cadddr and caddddr

!!

list-ref

clumsy name hints that it isn't needed very often

tail

cdr or rest

:

cons

since lists are tuples, it's also ,

last

last

not in racket/base

reverse

reverse

intersperse, intercalate

add-between

permutations

permutations

++

append

concat

append*

deep version of concat is called flatten

sum

apply +

product

apply *

maximum

apply max

minimum

apply min

maximumOn

argmax

minimumOn

argmin

replicate

make-list

take

take

there's also take-right

drop

drop or list-tail

takeWhile

takef

dropWhile

dropf

splitAt

split-at

span

splitf-at

elem

member

that's where everything-but-#f-is-true proves useful

find

memf

partition

partition

nub[By,On]

remove-duplicates

controlled with optional arguments

delete

remove

\\

remove*

removes all occurences, not only the first ones

sort[By,On]

sort

mapM_

for-each

list ranges

range

random shuffle

shuffle

Update: originally I had here apply and and apply or for Haskell's and and or, but they don't actually work due to and and or being macros and not functions.

Lists are pairs

hints that “type safety” is emulated by pre- and post-conditions (or contracts), and various list?, boolean?, number?, etc.

Update: I'm wrong about type safety here; Racket is safe in the sense that it won't let you silently coerce two values of different types into participating in all sorts of abominable things (like taking a float and making an int out of it without changing inner representation). However, if I want to find out that not-a-list (say, '(1 . (2 . 3))) has been passed to reverseearlier than when reverse sees 3 and becomes upset that it's neither a pair nor an empty list, I still need to use explicit checks or contracts.

< is not overloaded

There's such thing as apply

Basically it's the ultimate version of uncurry: it takes a function with any number of arguments and a list and feeds elements of list to this function. (Have I already made clear that lists don't have to be homogeneous in Racket? Well, now I have.)

Back to TRG

It turns out that if you write

(define(my-map f lst)
(for/list ([i lst])
(f i)))

then the for/list form in the function is expanded to essentially the same code as the iter local definition and use. The difference is merely syntactic convenience.

Hey, you haven't explained for/list yet!..

Ah, it's just a list comprehension, like in Haskell. Let's generate some Pythagorean triples.

Clumsy! On the other hand, a) there's probably a macro for Haskell-style comprehensions, b) that's the price of uniform and predictable syntax, and c) I don't use list comprehensions that often anyway. (By the by, I love how Racket's interpreter works with multi-line expressions – Enter for newline, Ctrl-Enter to evaluate, indentation is automatic.)

A fun fact

I forgot to mention that a lot of functions in Racket are polyvariadic (just like in Wolfram Mathematica). Behold:

Apparently – for reasons completely unclear to me – time is a special form and not a procedure, which means I can't use it as a parameter to for-each (or I can, but I don't yet know how). This is weird.

Turns out that while evaluating the factorial is pretty fast, printing it is terribly slow: (factorial 20000) takes 57 ms to calculate and 8 seconds to print. Moreover, it's not even converting the number to string that is slow; I used format "~a" to explicity convert it to string, and printing just the evaluated string was still awfully slow. Even compiling it into executable hasn't made it any faster.

Back to TRG

Suppose, for example, that you want to remove consecutive duplicates from a list. While such a function can be written as a loop that remembers the previous element for each iteration, a Racket programmer would more likely just write the following:

I mean, this is not built-in functionality and it has more features than Haskell's pattern-matching. Okay, Racket, you're forgiven for your weird time and multiple return values and slow printing and, above all, name which makes it hard to search for tutorials without also hitting upon sites selling tennis apparel.

What I think so far

Multiple return values are a cool idea, but there doesn't seem to be good support for it in standard library. Using let-values every time I need second-returned-value is both verbose and doesn't allow for compositional style (i.e. it doesn't let me say (compose 2nd-value time-apply)).

Update: performance sucks much less when racket executable is used to execute the program. For instance, printing becomes almost instant, and factorials are calculated twice as fast as compared to using GHC's runghc. However, compiling with raco exe doesn't cause any further optimizations.

Support for functional paradigm is somewhere in the middle. Everything needed is there, but it's not very convenient to use. (I expect to stumble upon goodies/fp or haskell-racket module one day, but for now I won't be looking for it – I need to understand Racket's main paradigm before allowing myself to consciously deviate from it.)

Time to sleep

Given that reading every chapter of TRG raises lots of questions and provokes endless tinkering with not-quite-related concepts, I'll be happy if I manage to read as much as chapter 2.4.

Day 3

TRG: 2.4. Pairs, Lists, and Racket Syntax

The cons function actually accepts any two values, not just a list for the second argument. When the second argument is not empty and not itself produced by cons, the result prints in a special way. The two values joined with cons are printed between parentheses, but with a dot (i.e., a period surrounded by whitespace) in between:

> (cons12)
'(1 . 2)
> (cons"banana""split")
'("banana" . "split")

I.e. Racket doesn't distinguish between lists and tuples where the second part is a list. Tsk, tsk.

The name rest also makes less sense for non-list pairs; the more traditional names for first and rest are car and cdr, respectively. (Granted, the traditional names are also nonsense. Just remember that “a” comes before “d”, and cdr is pronounced “could-er.”)

Granted, the traditional names in Haskell are also not that great (fst and snd), but they're still better than car and could-er... er, I mean cdr.

You are perhaps most likely to encounter a non-list pair when making a mistake, such as accidentally reversing the arguments to cons:

> (cons (list23) 1)
'((23) . 1)
> (cons1 (list23))
'(123)

Er, what? Are pairs used so rarely that if I ever encounter one, the most likely thing is that I made a mistake?

Non-list pairs are used intentionally, sometimes.

Ah, sometimes.

The only thing more confusing to new Racketeers than non-list pairs is the printing convention for pairs where the second element is a pair, but is not a list:

> (cons0 (cons12))
'(01 . 2)

In general, the rule for printing a pair is as follows: use the dot notation unless the dot is immediately followed by an open parenthesis. In that case, remove the dot, the open parenthesis, and the matching close parenthesis. Thus, '(0 . (1 . 2)) becomes '(0 1 . 2), and '(1 . (2 . (3 . ()))) becomes '(1 2 3).

It's not that great – actually, it's pretty stupid – but I don't know what a better design decision would be, so I guess it could be treated as a necessary evil. Maybe Racket programmers really don't use pairs which aren't lists any often, if they are willing to tolerate quirks like this one.

By the way, why is . not prefix? Discussion on c2 wiki concedes that “it's an accident of history, as with most notations”. And it would have to be a special case for parser no matter whether prefix or infix, so there's nothing gained.

...the quote form lets you write a list as an expression in essentially the same way that the list prints:

It stumbled me for a while, before I remembered that rest returns the rest of the list, and not simply its second element.

Hm. Would quote try to simplify (list 1 2 3) into '(1 2 3)?

> '(list123)
'(list123)
> (first '(list123))
'list

Nope.

A value that prints like a quoted identifier is a symbol. In the same way that parenthesized output should not be confused with expressions, a printed symbol should not be confused with an identifier. In particular, the symbol (quote map) has nothing to do with the map identifier or the predefined function that is bound to map, except that the symbol and the identifier happen to be made up of the same letters.

I haven't really expected this (but I should've – I already knew that Lisp had something called “atoms” and could guess it was about quoted identifiers).

Amazing variety, isn't it. Googling “racket vertical bars” reveals that this is merely a form of syntax for symbols containing spaces or special characters.

(Meanwhile: I'm tempted to start speculating about symbols, based on what I heard, but I'll try to refrain from doing so for now.)

...Now this is a real hack:

Normally, . is allowed by the reader only with a parenthesized sequence, and only before the last element of the sequence. However, a pair of .s can also appear around a single element in a parenthesized sequence, as long as the element is not first or last. Such a pair triggers a reader conversion that moves the element between .s to the front of the list. The conversion enables a kind of general infix notation:

> (1 . < . 2)
#t> '(1 . < . 2)
'(<12)

This two-dot convention is non-traditional, and it has essentially nothing to do with the dot notation for non-list pairs. Racket programmers use the infix convention sparingly—mostly for asymmetric binary operators such as < and is-a?.

On one hand – cool, I can write infix-style if I want to (tho I bet There Is A Macro For This Somewhere – after all, writing math expressions in prefix notation must suck horribly).

On the other hand... It's not that convenient (dots, spaces, meh) and it definitely isn't justified enough to be included in language.

I did some googling and found a much nicer proposal for infix syntax – just wrap it into curly brackets and that's all. Unfortunately, vanilla Racket seems to be treating curly brackets just like square brackets – a substitute for parens and nothing more.

And to write like this, the only thing I need to do is to import one package.

It must be any language designer's ultimate dream.

(And this is probably Lisp's greatest weakness as well – with this level of possible diversity, everyone has to use the “common lowest denominator” simply because nobody can agree on what alternative syntax / library / etc. is better and should be used.)

Off-topic: it's not enough to give everyone opportunity to improve the language; you have to choose the winners and promote them heavily. The rules of free market don't work here; people won't use the best thing, they'll use the one which is available out of the box and which is used by their peers.

Array Quicksort

Arrays are considered an “advanced topic” in Haskell and it's nigh impossible to write an elegant piece of code dealing with mutable arrays in Haskell without spending indecent amount of time (at least, on your first try). At this moment I am clueless about how to write imperatively in Racket – which, of course, means that it would be even more fun.

Step two: make a helpful table. Since in Racket most functions operate on both mutable and immutable vectors, I'm going to use functions Data.Vector for comparison because they're shorter, unless the function in question is specific to mutable vectors.

What I think so far

This day consisted mostly of ranting and awing, and I haven't learned much new. However:

Even if Racket turns out to be not suitable for industry programming, I'm still going to use it as a vehicle for language experimentation. It may not be a language I will program in, but I can probably make out of it a language I love to program in.

The true power of symbols and S-exprs is yet to be unraveled.

I don't know how well imperative parts of Racket mix with functional parts. I've programmed in Pascal, and I've programmed in Haskell, and I'm used to both paradigms, but a mix of them is probably going to bring some surprises.

Time to sleep

Plans for tomorrow:

Read chapters 3.1 – 3.5.

Apparently, SPOJ doesn't support Racket. (Oh, but it does support Node.js.) Therefore, I must either find something which does support it, or solve Project Euler tasks instead (but I still will use console input/output).

This would be in my plans for the day after tomorrow. After all, following The Racket Guide gets boring, doesn't it?

The lambda shorthand that's available is the cut macro, which oddly is not very popular, even if I personally use it a lot. See here and count the number of <>s in here, for example.

Tomorrow!

“Why the heck can't < compare strings?” – Good question. In the meantime, use string<?.

Okay. In the similar venue, why does length only work on lists? There is a generic interface for various types of sequences in standard library, but as long as sequence-length is more keystrokes than vector-length, nobody is going to use it except when specifically designing a function to work on every type of sequences – and it doesn't happen often. In short, I need something like Snoyman's classy-prelude (see the original post), but for Racket.

“The true power of symbols and S-exprs is yet to be unraveled.” – Indeed. You are yearning for unquote and syntax-quote.

Looks like something between generics and Template Haskell. I'll save it for future as well.