In Clojure, the language itself is modifiable
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
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…
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
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-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:
(re-find re s)to check if a string matches a regular expression
(clojure.string/replace s match replacement)to do string replacements
We will show what to do in order to be able to write that:
(re s)to check if a string matches a regular expression
(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