神刀安全网

Extend IFn protocol like if you were part of clojure’s core team

In Clojure, the language itself is modifiable

In clojure , the developer and the author of the language have a very intimate relationship: almost every part of the language author is extensible and modifiable by the clojure developer.

It is well known that macros allow the developer to add new semantics to the language – as if they were an original part of the language.

But what about functions?

Functions as a concept is at the core of clojure (like for any other functional programming language). So it’s not obvious to imagine that any kind of extensibility will be provided also for the concept of function.

I felt completely enlightened when I discovered – after a couple of months of clojure programming – that:

In clojure, any type of the language could behave like a function

This article will guide you through the path to enlightenment…

Extend IFn protocol like if you were part of clojure’s core team

Keywords and maps as functions: how does it work?

You probably already know that keywords and maps could be used as functions.

Let’s see it in action withKLIPSE:

Now, let’s see what happens behind the scenes…

We will see that the mechanism that enables keywords and maps to behave like function is completely open and extensible.

Let’s have a look at an excerpt from clojurescript source code (something similar holds for clojure ) to understand this mechanism.

Don’t be afraid by the big pyramid…

(defprotocol IFn   "Protocol for adding the ability to invoke an object as a function.   For example, a vector can also be used to look up a value:   ([1 2 3 4] 1) => 2"   (-invoke     [this]     [this a]     [this a b]     [this a b c]     [this a b c d]     [this a b c d e]     [this a b c d e f]     [this a b c d e f g]     [this a b c d e f g h]     [this a b c d e f g h i]     [this a b c d e f g h i j]     [this a b c d e f g h i j k]     [this a b c d e f g h i j k l]     [this a b c d e f g h i j k l m]     [this a b c d e f g h i j k l m n]     [this a b c d e f g h i j k l m n o]     [this a b c d e f g h i j k l m n o p]     [this a b c d e f g h i j k l m n o p q]     [this a b c d e f g h i j k l m n o p q r]     [this a b c d e f g h i j k l m n o p q r s]     [this a b c d e f g h i j k l m n o p q r s t]     [this a b c d e f g h i j k l m n o p q r s t rest]))  (deftype Keyword [...]   ...     IFn     (-invoke [kw coll]       (get coll kw))     (-invoke [kw coll not-found]       (get coll kw not-found)) ... )  (deftype PersistentHashMap [...] ...   IFn   (-invoke [coll k]     (-lookup coll k))    (-invoke [coll k not-found]     (-lookup coll k not-found)) ... )  

What an amazing discovery:

clojure exposes its most basic element – the function – as a protocol!!!

Keywords and maps behave like functions simply because they implement the -invoke method of IFn protocol.

And you – as a clojure developer – are not limited to the types that implement the IFn protocol. The reason is that in clojure every protocol can be extended through extend-type and extend-protocol after the type is defined.

We will see now how simple it is to make strings behave like functions.

From this below, we are dealing with hacky stuff. The objective of this article is to illustrate advanced feature of the language and I definitely discourage you to deal with that hacky stuff on production code.

David Nolen stated it this way in a interesting discussion with me on clojurians Slack :

“Extending base types is really about convenience, expressivity – no way at the moment for it to be performant” – David Nolen.

Strings as functions: Home brew

Let’s see how to make strings behave like functions using extend-type and implementing -invoke in a similar way that it was done for keywords in clojurescript source code:

For some technical reason (see CLJS-1618 ), we have to wrap the string into str . But beside that, it is really simple.

Regular expressions as functions: Home brew

Now, let’s see how to do fancy stuff with regular expressions.

Instead of having to write this:

  1. (re-find re s) to check if a string matches a regular expression
  2. (clojure.string/replace s match replacement) to do string replacements

We will show what to do in order to be able to write that:

  1. (re s) to check if a string matches a regular expression
  2. (match replacement s) to do string replacements

Let’s see it in action withKLIPSE:

Clojure extensibility allowed us to create new type of functions with a few lines of (simple) code…

Don’t you feel enlightened?

Like I said above, dealing with base types is really dangerous. So, please be careful…

If you have any ideas about implementing IFn protocol for custom types, I’d really appreciate if you share it in the comments below.

Clojure & Clojurescript rock!

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Extend IFn protocol like if you were part of clojure’s core team

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮