Skip to content

Instantly share code, notes, and snippets.

Eric Normand ericnormand

Block or report user

Report or block ericnormand

Hide content and notifications from this user.

Learn more about blocking users

Contact Support about this user’s behavior.

Learn more about reporting abuse

Report abuse
View GitHub Profile
View 00 runp!.md

parallel execution

If you need to run an action on all elements of a collection, you can use run!.

(run! println (range 100))

This will run all of the printlns in sequence. But there's no built-in function to run all of those functions in parallel. That's your task this week.

View 00 delete from a vector.md

delete from a vector

Clojure's vectors let you efficiently get a value given an integer index. Often, I want to remove a value from a vector at a certain index. This doesn't exist in the core library, so I wind up writing it each time. The question is how to do it in an efficient way?

Here's the task: write a function delete-at that takes a vector and an index and returns a new vector with the item at the index removed. The new vector should be one shorter than the argument.

(defn delete-at [v i]
  ;; your code here
  )
View 00 exponential backoff.md

exponential backoff

Our computers have real limits. Limited memory. Limited threads. Limited computations per second. Etc. We often treat our systems like they have unlimited resources, which is fine, until they get overloaded with work.

Two weeks ago, we wrote a higher order function to retry a function three times if it failed. However, if a function fails because the system is overloaded, trying again immediately is not going to help anything. In fact, it could make it worse.

A better response would be to wait a time before trying again. If it still fails, double the time and try again. Then double again, etc, until a limit is reached. This is known as exponential backoff.

Your job this week is to implement exponential backoff. Extra credit if you can tell it how long to wait before ultimately giving up.

View 00 rate limiting.md

rate limiting

The reality of the distributed, service-oriented world we live in is that we have to contend with the consequences of not only our requests, but the requests of all of the other clients of that service. For instance, many services have maximum request rates to protect the availability of a service from overambitious clients. An API might say "make no more than 10 requests per second".

How do you enforce that? My server has 24 cores, and I run as many threads. How do those threads coordinate so they don't go over the limit? It gets more complicated when you scale to multiple machines. Let's keep it simple and just talk about the shared-memory case.

There's a nice algorithm for implementing rate limiting called token bucket. If you can't make more than 10 requests per second, you make a rule: no one can make a request unless they have a token. Now dole out the tokens at no more than 10 requests per second. It's a great way to centralize the cont

View 00 retry three times.md

retry three times

One of the beautiful things about functional programming is higher-order functions. We write functions that operate on functions. This lets us pass around code to run later, bundled up as a function.

In last week's challenge, we saw how we could wrap a function in another to make it idempotent. This week, we will make a function that retries another function three times, or until it succeeds.

But first, what does it mean to fail? On the JVM, failure is commonly represented with a thrown exception. So, your task is to write a function that will call its argument. If it throws an exception, it tries again, up to three times total.

View 00 idempotence.md

make an action idempotent

Clojure.core has a function called memoize. You probably already know what it does, but just to be on the same page, memoize takes a function and returns a new function. The new function is just like the argument you pass it, but it has a superpower. It's now cached. It will remember any arguments you pass it, and the return value it found, and give it to you without doing all the work. Of course, it has to do the work the first time, but after that, it's all cached.

You can do this same thing, but instead of giving a function a superpower of being cached, you can make it idempotent. Let's say I had a function that connected to an SMTP server to send an email. I don't want to send the email twice, but I want to be able to retry in case it didn't succeed. So I make the action idempotent so the duplicates don't matter. And what does it mean to be the "same email"? There needs to be a notion of identity.

Write a function called idempotent-by that takes a function and returns

View clojure-devs-salary-buckets-500000.csv
salary-bucket count percent
0 8 1.13
1000 4 0.56
3000 2 0.28
4000 1 0.14
5000 1 0.14
6000 1 0.14
7000 2 0.28
8000 4 0.56
9000 2 0.28
View 00 ajax state.md

ajax state problem

AJAX calls in ClojureScript go through a lifecycle. They are placed, they're in flight, then they either succeed or fail. We often want to show this state to the user. For instance, when the ajax request is in flight, we want to show a loading spinner.

Create a representation of the state of a value that can be fetched via ajax. I suggest using the variant entity or the variant tuple. It may help to draw out the sequence diagram of an ajax request to capture all the states it may be in. Be sure to define the operations on this representation which transition it from one state to the next.

If you want some inspiration, Kris Jenkins has a neat post about modeling remote data. I've also written up two ways of approaching ajax state [here](https://purelyfunctional.tv/guide/opt

View 00 Permutations of a sequence.md

permutations of a sequence

The permutations of a sequence are all of the sequences with the same elements but in different orders. Here are some examples.

The permutations of [1, 2] are [1, 2] and [2, 1].

The permutations of [1, 2, 3] are [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], and [3, 2, 1].

The number of permutations grows really fast with the size of the list.

View 00 symbolic differentiation pt2.md

symbolic differentiation, pt 2

Now that we've got a basic symbolic differentiator (and the numeric one), we could simplify the expressions. SICP gives some basic simplifications.

  1. (+ 0 x) => x or additive identity
  2. (* 1 x) => x or multiplicative identity
  3. (+ 1 2) => 3 or constant simplification
  4. (* 3 2) => 6 or constant simplification

Here's the relevant link into SICP, for reference.

You can’t perform that action at this time.