Adding custom behavior to a Clojure sequence

Part of what is so powerful in Clojure is that all the basic data types implement the same sequence abstraction: clojure.lang.ISeq.

This means that functions like "first", "concat", "cons", "map", "rest", etc. work mainly on all of these data types.

My question is this: how can I add my own custom function to the mix and use it for all types that are distributed from ISeq?

The first attempt was to define my own protocol, then "(ext-type clojure.lang.ISeq ...", but this does not work (it compiles, but does not add behavior to the actual types). Another idea was to write a macro that explicitly expresses the β€œextension type” in all types of Clojure (PersistentHashMap, PersistentList, etc.), but this is similar to kludgey.

Is there an elegant / idiomatic way to do this? Perhaps there are many methods?

+6
source share
3 answers

What exactly are you trying to do?

If you are trying to add behavior to existing types: either write regular functions that handle seqs, or use multimethods or extend to do what you want.

In addition, it should be noted that most types of Clojure "sequence" (vectors, sets, maps) are not sequences in themselves (they do not implement clojure.lang.ISeq ), so you need to do more than just add to clojure.lang.ISeq if you want to support them.

+8
source

In an article by Stuart Sir, the title was published by IBM Developerworks, "Resolving an Expression Problem with Clojure 1.2 , which could provide information and answer your question.

It uses protocols to define a group of functions across several data types and uses extend to extend existing classes (data types) so that they can use these functions. This may not be exactly what you want, but it may be one way to solve your problem.

It also shows how you can define custom data types (using defrecord and deftype ) that implement existing protocols / interfaces.

+3
source

The best way to do this is to write your new functions using existing common Clojure functions that correctly handle different types of data.

Examples of such common functions:

  • into adds elements to a collection of any type
  • empty returns an empty collection of the same type as the parameter

Then you can write your own generic function that uses these, for example:

 (defn take-every-other [coll] (into (empty coll) (map first (partition 2 coll)))) (take-every-other [1 2 3 4 5 6]) => [1 3 5] (take-every-other {:a 1 :b 2 :c 3 :d 4}) => {:a 1, :c 3} 

If you still need a more general function, you can always dive into the Clojure source to see how these functions are written.

+3
source

Source: https://habr.com/ru/post/897441/


All Articles