LuminousMonkey

What is a Monad like a writing desk?

I have seen this talk, however, given that someone I know was recently asked about monads in an interview, I thought it best to link to it here so I can find it later.

What is a Monad like a writing desk?

Even if you're not interested in monads, you should watch this talk, because it tells a story. Talks are not about dense information, but are about getting people interested in the subject.

What follows are my own notes, for no other reason, but for me to follow along and try to understand.

Monadic return operation:

(defn return [v] (fn [] v))

You define a function that takes a value and wraps that value in a function. When that function is executed, it returns the value.

((return "jelly")) ;=> "jelly"

This is not a monadic return, because the value is not wrapped up in a function:

(defn return [v] v)

(return "jelly") ;=> jelly

Monadic bind operation:

(defn bind [mv f] (f (mv)))

(defn with-toast [s]
  (return (str "toast & " s)))

(bind (return "jelly") with-toast)
;=> container/monadic value (Our value wrapped in a function)

((bind (return "jelly") with-toast))
;=> "toast & jelly"

Bind will take a monadic value, and a function, calling the monadic value so it will get the actual value, and pass that in as a parameter. The function passed into bind must be a monadic value (wrapped in a function.)

Ok, I follow this, but… why?

Can you write a function that makes you grow and returns a monadic value. Sure:

(return "me")

(defn grow [s]
  (return (str s (last s))))

(grow "me") ;=> monadic value

((grow "me")) ;=> "mee"

Using bind to grow:

(defn m-grow [mv]
  (bind mv grow))

((m-grow (return "me"))) ;=> "mee"

Ok, and you can chain them… but what is the purpose? What is the benefit? Handling of nil. With modifications to bind, you can have monadic operations occur with nil, that don't blow up. Instead it could pass though a call chain, and return nil wrapped in a monadic value.

Monads are like astronauts, monads are like burritos. Yeah, what the hell are they on about?

Updating of return and bind, so they can include state:

;; Will now return a monadic value that needs a parameter.
(defn return [v]
  (fn [s] [v s]))

;; Same here
(defn bind [mv f]
  (fn [s]
    (let [[v sn] (mv s)]
      ((f v) sn))))

There was a tea function, which I haven't written down, but it's used again without modification here.

(defn m-tea [mv name]
  (bind mv (fn [v]
             (return (str v " and " name)))))

((-> (return "me") (m-tea "you")) 10)
;=> ["me and you" 10]

So, we can track state.

(defn take-sugar [mv]
  (bind mv (fn [v]
             (fn [s] [v (sec s)]))))

Seems to be all about using closures for state and composing operations?

Identity monad: return, bind.

Maybe monad: The handling of nil.

(defn bind [mv f]
  (let [v (mv)]
    (if (nil? v)
      (return nil)
      (f v))))

State monad: Tea.

Things in common:

Left Unit - "return" acts as a neutral element of bind.

(bind (return v) f) ; same as (f v)

(defn return [v] (fn [] v))
(defn bind [mv f] (f (mv)))

(defn grow [s] (return (str s (str (last s)))))

((bind (return "me") "grow")) ;=> "mee"
((grow "me"))                 ;=> "mee"

Right Unit - "return" acts as a neutral element of bind.

(bind mv return) ; same as mv

(defn return [v] (fn [] v))
(defn bind [mv f] (f (mv)))

(defn grow [s] (return (str s (str (last s)))))

((bind (return "me") return)) ;=> "me"
((return "me"))               ;=> "me"

Associative - Binding two functions in succession is the same as binding one function that can be determined from them.

(bind (bind mv f) g) ;same as (bind mv (fn [x] (bind (f x) g)))

(defn return [v] (fn [] v))
(defn bind [mv f] (f (mv)))

(defn grow [s] (return (str s (str (last s)))))

((bind (bind (return "me") grow) grow)) ;=> "meee"
((bind (return "me")
       (fn [v] (bind (grow v) grow))))  ;=> "meee"

Monads are all about keeping inside functions pure and having messy stuff on the outside.

Trying Org Mode

Apparently the static site generator I use supports Org Mode. So, this is a test post.

(let [fish (+ 1 2 3)]
  (println fish))

#"Test"

(+ 1 2 3)

(defproject artemis "0.1.0-SNAPSHOT"
  :description "Artemis stores the data for DirectGPS and tracks the
  GPS units"
  :url "https://directcommunications.kilnhg.com/Code/Artemis"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [compojure "1.2.0"]
                 [cheshire "5.3.1"]
                 [ring/ring "1.3.1"]
                 [ring/ring-json "0.3.1"]
                 [http-kit "2.1.19"]
                 [org.clojure/data.json "0.2.5"]
                 [camel-snake-kebab "0.2.4"]
                 [prismatic/schema "0.3.0"]
                 [com.fasterxml.jackson.core/jackson-core "2.4.3"]
                 [org.clojure/tools.logging "0.3.1"]
                 [log4j "1.2.17" :exclusions [javax.mail/mail
                                              javax.jms/jms
                                              com.sun.jdmk/jmxtools
                                              com.sun.jmx/jmxri]]
                 [org.slf4j/slf4j-log4j12 "1.7.5"]
                 [clojurewerkz/propertied "1.2.0"]
                 [com.datomic/datomic-pro "0.9.4956" :exclusions
                  [org.slf4j/slf4j-nop org.slf4j/log4j-over-slf4j]]
                 [crypto-random "1.2.0"]
                 [clj-oauth-server "1.0.5"]]
  :plugins [[lein-ring "0.8.5"]]
  :ring {:init artemis.config/load-config
         :handler artemis.routes/app-routes}
  :profiles {:dev
             {:dependencies [[midje "1.6.3"]
                             [ring-mock "0.1.5"]
                             [org.clojure/test.check "0.5.9"]
                             [org.clojure/tools.namespace "0.2.7"]]
              :source-paths ["dev"]}}
  :repositories {"my.datomic.com" {:url "https://my.datomic.com/repo"
                                   :creds :gpg}})
1: (save-excursion
2:   (goto-char(point-min)))

In line 1 we remember the current position. Line 2 jumps to point-min.

This should have some math…

Foo bar \(f(x) = \frac{x^3}{n}\) chicken checken.

\begin{equation} x=\sqrt{b} \end{equation}

If \(a^2=b\) and \( b=2 \), then the solution must be either $$ a=+\sqrt{2} $$ or \[ a=-\sqrt{2} \].

3D Robots

There are some units, at Curtin University, where you have to do a project. This is not an unsual prospect, lecturers have been using students for such things all the time at pretty much any University. My team is no different, we have been tasked, essentially, to develop from scratch the Open Academic Robot Kit. Now, I've sounded a little cynical, and I probably am a little, but it's actually a pretty neat project. To be sour about it, makes you sound like the kind of person who would be sour against more money being given to hospitals.

The basic idea is allow people to create robots, robots that they can 3D print, and construct from a few parts. The type of people that are targeted for this, are people who wouldn't normally have the skills necessary in making these robots. Physical designs can be downloaded, modified, and constructed into whatever sort of robot that people would want to make. But the primary goal would be to allow, say, high school students to put these together and experiment. You know, get kids interested in science! I'm sounding a little cynical again, I'm not, I'm just trying my own poor brand of humour.

Physical designs are the easy part, our particular trick, is to come up with the software they can use for these robots. This is a big problem, not the technical issues, those are easy to tackle, no, the problem here is the abstraction. Computers are easy, they follow logical rules, and (if you have the right access to the source code, or other way of seeing what is going on, something you can eventually get right). But abstractions, they involve humans, human brains which are all different, and think about things in different ways. This is also a problem domain that none of our team have experience with, so we don't even know the right approach for an abstraction that will make sense.

So, future blog posts will probably be documenting my descent into madness, fair warning.

Should have used CVS

It seems that I lost all my old blog data, I thought I had saved the SQL database (I was using Wordpress previously). But I can't find it, whoops, that's a few years of blogging lost.

Maybe I'll check the Internet Wayback Machine, and recover anything that seems interesting from there.

Throwaway variables in C

I'm re-reading "Refactoring" by Martin Fowler, which in turn was the result of me re-reading Steve Yegge's blog post (unfortunately I can't remember the post). One of the common things that programmers do, that may be a sacrifice of readability for optimisation, are temporary variables. As it turns out, this is an optimisation that seems like it doesn't have to be done.

Unfortunately I don't have any large project that I can use as an example, so I've had to create my own contrived test.

#include <stdio.h>

int square(int);

int main()
{
  for (int i = 0; i < 5; i++)
    {
      printf("Result: %d\n", square(i));
    }

  printf("Called again: %d\n", square(3));
}

int square(int toBeSquared)
{
  return toBeSquared * toBeSquared;
}

This code doesn't have any temporary variables, because I wanted to see what the compiler would do without them. The usual case for a temporary variable is something like the following:

  int tempVariable = someFunction(x);

  for (int i = 0; i < tempVariable; i++)
    {
      printf("Blah: %d\n");
    }

Of course this is done so you're not calling someFunction on every test of the loop. But, at least with optimisations turned on, you don't have to worry.

Anyway, I compiled my contrived example with GCC:

gcc -fverbose-asm -std=c99 -O3 blah.c -S

Checking the resulting code, we have:

main:
.LFB3:
  .cfi_startproc
  pushq %rbx  #
  .cfi_def_cfa_offset 16
  .cfi_offset 3, -16
  xorl  %ebx, %ebx  # i
.L2:
  movl  %ebx, %esi  # i, D.2001
  xorl  %eax, %eax  #
  movl  $.LC0, %edi #,
  imull %ebx, %esi  # i, D.2001
  addl  $1, %ebx  #, i
  call  printf  #
  cmpl  $5, %ebx  #, i
  jne .L2 #,
  movl  $9, %esi  #,
  movl  $.LC1, %edi #,
  xorl  %eax, %eax  #
  call  printf  #
  xorl  %eax, %eax  #
  popq  %rbx  #
  .cfi_def_cfa_offset 8
  ret
  .cfi_endproc
.LFE3:
  .size main, .-main
  .section  .text.unlikely
.LCOLDE2:
  .section  .text.startup
.LHOTE2:
  .section  .text.unlikely
.LCOLDB3:
  .text
.LHOTB3:
  .p2align 4,,15
  .globl  square
  .type square, @function
square:
.LFB4:
  .cfi_startproc
  movl  %edi, %eax  # toBeSquared, D.2006
  imull %edi, %eax  # toBeSquared, D.2006
  ret
  .cfi_endproc

Well, we have a function that gets made inline inside the for loop, and looking at my call to the function outside the loop, we can see it doesn't even bother, it just loads the value 9.

Now, this is a very simple function, and I would have to do more tests, but it would appear that the performance of certain refactorings, in particular ones involving replacing math and Boolean operations should not be a concern.

Write it as clear as possible, even to the extent of using a function just so you can give Boolean conditions a better name, and don't bother to use temp variables to cache results to avoid what you might think will be a function call. The compiler probably does it anyway.