Posts tagged Programming Languages

things I already dislike about Python

:: Programming Languages, Teaching

I’m just getting started, but already Python is looking like a terrible teaching language, relative to Racket.

  • Testing framework involves hideous boilerplate.
  • Testing framework has standard problems with floating-point numbers.
  • Scoping was clearly designed by someone who’d never taken (or failed to pay attention in) a programming languages course.
  • The vile ‘return’ appears everywhere.

programs are not sequences of instructions

:: Programming Languages

Oh mainstream programmers, how I hate thee. Let me count the ways.

The following is a question taken from the “AP Computer Science Principles Course and Exam Description,” from the College Board:

  1. A programmer completes the user manual for a video game she has developed and realizes she has reversed the roles of goats and sheep throughout the text. Consider the programmer’s goal of changing all occurences of “goats” to “sheep” and all occurrences of “sheep” to “goats.” The programmer will use the fact that the word “foxes” does not appear anywhere in the original text.

Which of the following algorithms can be used to accomplish the programmer’s goal?

a)

  • First, change all occurrences of “goats” to “sheep.”
  • Then, charge all occurrences of “sheep” to “goats.”

b)

  • First, change all occurrences of “goats” to “sheep.”
  • Then, charge all occurrences of “sheep” to “goats.”
  • Last, charge all occurrences of “foxes” to “sheep.”

c)

  • First, change all occurrences of “goats” to “foxes.”
  • Then, charge all occurrences of “sheep” to “goats.”
  • Last, charge all occurrences of “foxes” to “sheep.”

d)

  • First, change all occurrences of “goats” to “foxes.”
  • Then, charge all occurrences of “foxes” to “sheep.”
  • Last, charge all occurrences of “sheep” to “goats.”

This question makes me angry. Why? Because the obvious lesson from this example is that YOU SHOULD NOT BE MODELING COMPUTATION AS A SEQUENCE OF MUTATIONS.

In other words, the correct answer is this:

e) For each word in the document, replace it using this function: * if the word is “goats”, return “sheep” * if the word is “sheep”, return “goats” * otherwise, return the word unchanged.

Voila. Problem solved. No resorting to nasty swap ideas.

At a lower level, I think the core problem may be the inability of the English language to handle this kind of abstraction. Notice that in my proposed solution (e), I tread dangerously close to mathematical notation; it’s not really purely English any more.

Racket Sucks, Don’t Try It

:: Racket, Programming Languages

I’ve done a lot of programming in Racket. A lot. And people often ask me: “What do you think of Racket? Should I try it?”

The answer is simple: No. No, you should not.

You’re the kind of person who would do very badly with Racket. Here’s why:

  • All those parentheses! Good Lord, the language is swimming in parentheses. It’s not uncommon for a line to end with ten or twelve parentheses.

  • Almost no mutation! Idiomatic Racket code doesn’t set the values of variables in loops, and it doesn’t set the values of result variables in if branches, and you can’t declare variables without giving them values, and Racket programmers hardly ever use classes with mutable fields. There’s no return at all. It’s totally not like Java or C. It’s very strange and unsettling.

  • Library support. Yes, there are lots of libraries available for Racket, but there are many more in, say, Python. I think there are currently fifty-five thousand packages available for Python.

  • Racket is an experimental language: when the Racket team decides that the language should change, it does. Typed Racket is evolving rapidly, and even core Racket is getting fixes and new functionality every day.

  • Racket is not a popular language. You’re not going to be able to search for code snippets on line with anything like the success rate that you’d have for JavaScript or Python or Java.

  • Racket will ruin you for life as a Java developer. You will be agonizingly aware of how much boilerplate you’re cranking out, and after every hour of shoveling Java, you will sneak off to the bathroom and write a tiny beautiful macro that no one will ever be allowed to see or use.

If none of these succeed in scaring you off, well, then, go ahead and give it a try. Just remember: I warned you.

Break it! Confrontational thinking in computer science

:: Programming Languages, Teaching

So here I am grading another exam. This exam question asks students to imagine what would happen to an interpreter if environments were treated like stores. Then, it asks them to construct a program that would illustrate the difference.

They fail, completely.

(Okay, not completely.)

By and large, it’s pretty easy to characterize the basic failing: these students are unwilling to break the rules.

my root password? oh sure, here it is.

:: Mac, Security, Programming Languages

So, I just bought a copy of Screenflow, an apparently reputable piece of screencasting software for the mac. They sent me a license code. Now it’s time to enter it. Here’s the window:

Hmm… says here all I need to do is… enter my admin password, and then the license code.

Wait… what?

Why in heaven’s name would Screenflow need to know my admin password here?

My guess is that it’s because it wants to put something in the keychain for me. That’s not a very comforting thought; it also means that it could delete things from my keychain, copy the whole thing, etc. etc.

This is a totally unacceptable piece of UI. I think it’s probably both Apple’s and Telestream’s fault. Really, though, I just paid $100 and now I have to decide whether to try to get my money back, or just take the chance that Telestream isn’t evil.

Is teaching programming like teaching math?

:: Programming, Programming Languages, Teaching

One of my children is in third grade. As part of a “back-to-school” night this year, I sat in a very small chair while a teacher explained to me the “Math Practices” identified as part of the new Common Core standards for math teaching.

Perhaps the small chair simply made me more receptive, taking me back to third grade myself, but as she ran down the list, I found myself thinking: “gosh, these are exactly the same skills that I want to impart to beginning programmers!”

Here’s the list of Math Practices, a.k.a. “Standards for Mathematical Practice”:

  1. Make sense of problems and persevere in solving them.
  2. Reason abstractly and quantitatively.
  3. Construct viable arguments and critique the reasoning of others.
  4. Model with Mathematics.
  5. Use appropriate tools strategically.
  6. Attend to precision.
  7. Look for and make use of structure.
  8. Look for and express regularity in repeated reasoning.

Holy Moley! Those are incredibly relevant in teaching programming. Furthermore, they sound like they were written by someone intimately familiar with the How To Design Programs or Bootstrap curricula. Indeed, in the remainder of my analysis, I’ll be referring specifically to the steps 1–4 of the design recipe proposed by HtDP (as, e.g., “step 2 of DR”).

Let’s take those apart, one by one:

DWIM vs. Small Semantic Core

:: R, Programming Languages

So, I’d like to do some statistical analysis. I hear that R is really good at this. Let’s download it and take a look.

(Ten minutes later)

AAAHHH! MY EYES! THEY’RE BLEEDING!

What about Matlab? It’s the same story.1 As a programming languages person, these languages make me … well, angry.

Why?

Well, after thinking about this for a while, it seems to me that what I hate most about these languages is their complete lack of a small semantic core.

Take a language like Racket, JavaScript, Java, or C— these languages don’t have a heck of a lot in common, but they share

is this all just library design? Most of the things I really hate can easily be constructed in any dynamic library through a suitable application of

Terrible Library Design (tm)

… except that when it applies to things like vector dereference, it feels like fairly ‘core’ syntax.

Example time! First, R does this crazy thing in distinguishing logical from numeric vectors.

> a
[1] "a" "b" "c" "d"
> a[c(2,4,3)]
[1] "b" "d" "c"
> a[c(FALSE,TRUE)]
[1] "b" "d"

In the first of these two array derefs, we’re using the indices from the vector to decide what elements of a to take. In the second case, though, the index expression is a ‘logical vector’ and is therefore tiled to the length of the original one, and used to decide whether to take the corresponding element.

If you imagine this as part of a language semantics, you’d see this horrible side-condition attached to these rules, where array deref’ing works in totally different ways depending on the kind of argument it gets.

To say nothing of the silent tiling, which seems like an open invitation to horrible bugs.

But wait, we can compound this problem with some nasty coercion:

> a[c(4,c(FALSE,TRUE,TRUE))]
[1] "d" "a" "a"

What on earth is going on here? First of all, vectors get silently flattened, so that c(3,c(4,5)) is the same as c(3,4,5) — ugh — but then, the logical values are coerced into numeric ones, so the index vector that’s produced is actually c(4,0,1,1), which is then used to index the vector a. But why are there only three values? Oh, well, there’s no index 0, so let’s just skip that one, shall we?

Honestly, I guess the real problem is in thinking of something like R as a programming language; it’s not. It’s a statistical analysis tool, with a rich text-based interface. After all, would I get upset if Photoshop used ‘b’ for blur and ‘s’ for sharpen and I couldn’t nest them the way that I wanted, using parentheses? Probably not.

And finally: apologies for everything I’ve written. I’ve used R for about fifteen minutes, and this piece is really just me blowing off a small amount of steam. Not well written, not well thought-out. Meh.

  1. Actually, maybe not; I spoke with a friend yesterday, and I get the impression that Matlab may not be as horrible as R, here. 

Macros: more important in low-level languages?

:: Macros, Programming Languages

Macros are a wonderful thing. A hygienic macro system puts language extensions within the reach of non-compiler-hackers.

However, to date, most modern, hygienic macro systems are associated with languages like Scheme and Racket that are quite high-level. My claim is that macros are probably much more useful in low-level languages. Here’s why:

Too Elegant For September

:: Programming Languages, Teaching

Being on sabbatical has given me a bit of experience with other systems and languages. Also, my kids are now old enough to “mess around” with programming. Learning from both of these, I’d like to hazard a bit of HtDP heresy: students should learn for i = 1 to 10 before they learn

(define (sum lon)
  (cond [(empty? lon) 0]
        [else (+ (first lon) (sum (rest lon)))]))

To many of you, this may seem obvious. I’m not writing to you. Or maybe you folks can just read along and nod sagely.

HtDP takes this small and very lovely thing—recursive traversals over inductively defined data—and shows how it covers a huge piece of real estate. Really, if students could just understand how to write this class of programs effectively, they would have a vastly easier time with much of the rest of their programming careers, to say nothing of the remainder of their undergraduate tenure. Throw a few twists in there—a bit of mutation for efficiency, some memoization, some dynamic programming—and you’re pretty much done with the programming part of your first four years.

The sad thing is that many, many students make it through an entire four-year curriculum without ever really figuring out how to write a simple recursive traversal of an inductively defined data structure. This makes professors sad.

Among the Very Simple applications of this nice idea is that of “indexes.” That is, the natural numbers can be regarded as an inductively defined set, where a natural number is either 0 or the successor of a natural number. This allows you to regard any kind of indexing loop as simply a special case of … a recursive traversal of an inductively defined data structure.

So here’s the problem: in September, you face a bunch of bright-eyed, enthusiastic, deeply forgiving first-year college students. And you give them the recursive traversal of the inductively defined data structure. A very small number of them get it, and they’re off to the races. The rest of them struggle, and struggle, and finally get their teammates to help them write the code, and really wish they’d taken some other class.

NB: the rest of this makes less sense… even to me. Not finished.

However, another big part of the problem is … well, monads are like burritos.

Let me take a step back.

The notion of repeated action is a visceral and easily-understood one. Here’s what I mean. “A human can multiply a pair of 32-bit integers in about a minute. A computer can multiply 32-bit integers at a rate of several billion per second, or about a hundred billion times as fast as a person.” That’s an easily-understood claim: we understand what it means to the same thing a whole bunch of times really fast.

So, when I write

for i=[1..100] multiply_two_numbers();

It’s pretty easy to understand that I’m doing something one hundred times.

Embedding Rust in Racket

:: Rust, Racket, Programming Languages

Is this post a thinly disguised ripoff of Brian Anderson’s post about embedding Rust in Ruby? Why yes. Yes it is.

Okay, let me start with a little background. Rust is a magnificent language that comes from Mozilla; it’s targeted at programmers who want

  • high and predictable performance,
  • control over memory layout,
  • good support for concurrency, and
  • safety.

I think the Mozilla Research homepage is probably the best place to start learning about Rust.

To be honest, though, I’m probably flattering myself if I think that this blog post is being read by anyone who doesn’t already know lots about Rust.

One of the key requirements of a language like Rust is that it be embeddable; that is, it should be possible to call Rust code from another language just as it’s possible to call C code from another language.

This is now possible.

To illustrate this, Brian Anderson posted a lovely example of embedding Rust in Ruby. But of course, embedding Rust in Ruby is pretty much exactly the same as embedding Rust in any other language.

Say, for instance, Racket.

So, without further ado, here’s the setup. You just happen to have a small web app written in Racket that performs a Gaussian Blur. You decide to optimize the performance by porting your code to Rust. Then you want to plug your Rust code into your Racket application. Done! Here’s the github repo that contains all of the code.

Let’s see that again in slow motion.

First, here’s the gaussian blur function, written in Racket. We’re going to stick with a grayscale image. It works fine in color, but the code is just that much harder to read.

;; the gaussian filter used in the racket blur.
;; boosted center value by 1/1000 to make sure that whites stay white.
(define filter '[[0.011 0.084 0.011]
                 [0.084 0.620 0.084]
                 [0.011 0.084 0.011]])

;; racket-blur: blur the image using the gaussian filter
;; number number list-of-bytes -> vector-of-bytes
(define (racket-blur width height data)
  (define data-vec (list->vector data))
  ;; ij->offset : compute the offset of the pixel data within the buffer
  (define (ij->offset i j)
    (+ i (* j width)))
  (define bytes-len (* width height))
  (define new-bytes (make-vector bytes-len 0))
  (define filter-x (length (car filter)))
  (define filter-y (length filter))
  (define offset-x (/ (sub1 filter-x) 2))
  (define offset-y (/ (sub1 filter-y) 2))
  ;; compute the filtered byte array
  (for* ([x width]
         [y height])
    (define new-val
      (for*/fold ([sum 0.0])
        ([dx filter-x]
         [dy filter-y])
        (define sample-x (modulo (+ dx (- x offset-x)) width))
        (define sample-y (modulo (+ dy (- y offset-y)) height))
        (define sample-value (vector-ref data-vec (ij->offset sample-x sample-y)))
        (define weight (list-ref (list-ref filter dy) dx))
        (+ sum (* weight sample-value))))
    (vector-set! new-bytes (ij->offset x y) new-val))
  (vector->list new-bytes))

Suppose we want to rewrite that in Rust. Here’s what it might look like:

fn blur_rust(width: uint, height: uint, data: &[u8]) -> ~[u8] {

    let filter = [[0.011, 0.084, 0.011],
                  [0.084, 0.620, 0.084],
                  [0.011, 0.084, 0.011]];

    let mut newdata = ~[];

    for uint::range(0, height) |y| {
        for uint::range(0, width) |x| {
            let mut new_value = 0.0;
            for uint::range(0, filter.len()) |yy| {
                for uint::range(0, filter.len()) |xx| {
                    let x_sample = x - (filter.len() - 1) / 2 + xx;
                    let y_sample = y - (filter.len() - 1) / 2 + yy;
                    let sample_value = data[width * (y_sample % height) + (x_sample % width)];
                    let sample_value = sample_value as float;
                    let weight = filter[yy][xx];
                    new_value += sample_value * weight;
                }
            }
            newdata.push(new_value as u8);
        }
    }

    return newdata;
}

Pretty similar. Of course, it uses curly braces, so it runs about three times faster…

So: what kind of glue code is necessary to link the Rust code to the Racket code? Not a lot. On the Rust side, we need to create a pointer to the C data, then copy the result back into the source buffer when we’re done with the blur:

#[no_mangle]
pub extern fn blur(width: c_uint, height: c_uint, data: *mut u8) {
    let width = width as uint;
    let height = height as uint;

    unsafe {
        do vec::raw::mut_buf_as_slice(data, width * height) |data| {
            let out_data = blur_rust(width, height, data);
            vec::raw::copy_memory(data, out_data, width * height);
        }
    }
}

On the Racket side, it’s just a question of making an ffi call, which is super-concise:

;; link to the rust library:
(define rust-lib (ffi-lib (build-path here "libblur-68a2c114141ca-0.0")))
(define rust-blur-fun (get-ffi-obj "blur" rust-lib (_fun _uint _uint _cvector -> _void)))

(define (rust-blur width height data)
  (define cvec (list->cvector data _byte))
  (rust-blur-fun width height cvec)
  (cvector->list cvec))

And away you go!

I’ve got this code running live at FIXME. What’s that you say? You can’t seem to find FIXME?