Question

I'm trying to build a website with Luminus in order to learn a bit of Clojure. I've had years of imperative experience but only now getting into functional programming. Right now, I'm trying to process a signed_request object from Facebook.

According to the site I have to:

  1. Split a string on a period (".") and get a vector of 2 strings.
  2. Take the first of those strings, decode it w/ base64 and compare with a secret.
  3. Take the second of those strings, decode it w/ base64 and decode again with JSON.

This is really straight-forward if I was doing it in an imperative language, but I am clueless when it comes to a functional approach. Right now I've only gotten as far as finding out how to split the string into a vector of 2 strings:

(defn parse-request [signed_request]
  ((clojure.string/split signed_request #"\.")
    ))

(defn redirect-page [signed_request]
  (layout/render "redirect.html"
                 {:parsed_request parse-request(signed_request)}))

(defroutes home-routes
  (GET "/" [] (home-page))
  (POST "/redirect" [signed_request] (redirect-page signed_request)))

redirect-page is run when the server receives the POST request, and then it takes the signed_request and passes it into the parse-request function. What is the functional way to approach this?

Was it helpful?

Solution

I think the basic answer to your question is that functional programming is more about input and output (think of a mathematical function), whereas imperative programming tends to be more about side effects and mutable data. Instead of thinking, "what do I need to do?", think, "what kind of data structure is my goal, and how do I define it?" You can have side effects too (like printing), but generally you are writing pure functions that take arguments and return something.

Destructuring is an invaluable tool in Clojure, and it would come in handy here. You want to split a string into two strings using clojure.string/split, and then do something with one of the strings and something else with the other. You could use a let binding to assign names to each string, like this:

(let [[str1 str2] (clojure.string/split signed-request #"\.")]
  (do-stuff-with-str1-and-str2))

I'm not too familiar with this specific problem, but based on the 3 steps you listed, it sounds like you will be getting 2 results, one from each string. So, maybe you should focus on writing a function that returns a vector containing the 2 results, like this:

(defn process-signed-request [signed-request]
  (let [[str1 str2] (clojure.string/split signed-request #"\.")]
    [(compare-fn (decode-with-base-64 str1) secret)
     (decode-with-json (decode-with-base-64 str2))]))

Note that the above is partially pseudocode -- you will need to replace compare-fn, decode-with-base-64, decode-with-json, and secret with the actual code representing these things -- that, or you can leave it as-is and just implement the functions so that compare-fn, decode-with-base-64 and decode-with-json refer to actual functions that you write. This is a common approach in functional programming -- write a short higher-level function that defines the solution to the problem, and then go back and write the helper functions that it uses.

By the way, there are a couple of other ways you can write the (decode-with-json (decode-with-base-64 str2)) part:

  1. ((comp decode-with-json decode-with-base-64) str2)
  2. (-> str2 decode-with-base-64 decode-with-json)

I often find the second approach, using a threading macro (-> or ->>) helpful when I know the sequence of things that I need to do with an object, and I want the code to read intuitively. I find it easier to read, like "take str2, decode it with base 64, and then decode it again with JSON."

Also, this is just a nitpicky thing, but pay attention to the order of parentheses when you're coding in Clojure. The way you have your code now, the parentheses should look like this:

(defn parse-request [signed_request]
  (clojure.string/split signed_request #"\."))

(defn redirect-page [signed_request]
  (layout/render "redirect.html"
                 {:parsed_request (parse-request signed_request)}))

If you have a lot of imperative experience, you're probably so ingrained in the fn(x) syntax that you accidentally type that instead of the (fn x) Lisp syntax that Clojure uses.

And while I'm nitpicking, it's idiomatic in Clojure to use hyphens instead of underscores for symbol naming. So, I would rename signed_request and :parsed_request to signed-request and :parsed-request.

Hope that helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top