Skip to main content
added 104 characters in body
Source Link
NiklasJ
  • 675
  • 4
  • 6
(def transformations { :encrypt (fn [data] ... ) :compress (fn [data] ... ) :debug-print (fn [data] ...) }) ;<--- here to add as option (defn do-processing [options data] (let [transformations-to-run (map transformations options) selected-transformations (apply comp transformations-to-run)] (selected-transformations data))) (do-processing [:encrypt :compress :debug-print]) ;<-- here to use it (do-processing [:compress :debug-print]) ;or like this (do-processing [:encrypt]) ;or like this 
(def transformations { :encrypt (fn [data] ... ) :compress (fn [data] ... ) :debug-print (fn [data] ...) }) ;<--- here to add as option (defn do-processing [options data] (let [transformations-to-run (map transformations options) selected-transformations (apply comp transformations-to-run)] (selected-transformations data))) (do-processing [:encrypt :compress :debug-print]) ;<-- here to use it 
(def transformations { :encrypt (fn [data] ... ) :compress (fn [data] ... ) :debug-print (fn [data] ...) }) ;<--- here to add as option (defn do-processing [options data] (let [transformations-to-run (map transformations options) selected-transformations (apply comp transformations-to-run)] (selected-transformations data))) (do-processing [:encrypt :compress :debug-print]) ;<-- here to use it (do-processing [:compress :debug-print]) ;or like this (do-processing [:encrypt]) ;or like this 
rewrote to clarify
Source Link
NiklasJ
  • 675
  • 4
  • 6

In languagesI think "design patterns" are unneccesarily geared towards "oo patterns" and completely avoiding much simpler ideas. What we're talking about here is a (simple) data pipeline.

I would try to do it in clojure. Any other language where functions really are first class, such-class is probably ok as clojurewell. Maybe I could a C# example later on, you might write something likebut it's not as nice. My way of solving this would be the following steps with some explanations for non-clojurians:

1. Represent a set of transformations.

(applydef comptransformations functions{ :encrypt (fn [data] ... )   :compress (fn [data] ... )}) 

To createThis is a newmap, i.e. a lookup table/dictionary/whatever, from keywords to functions. Another example (keywords to strings):

(def employees { :A1 "Alice" :X9 "Bob"}) (employees :A1) ; => "Alice" (:A1 employees) ; => "Alice" 

So, writing (transformations :encrypt) or (:encrypt transformations) would return the encrypt function that. ((fn [data] ... ) is the combinedjust a lambda function of encrypt and compress.)

The step before this to actually make the choice would then be any normal sort of filtering. Say2. Get the options as a sequence of keywords:

(defn do-processing [options data] ;function definition ...) (do-processing [:encrypt :compress] data) ;call to function 

3. Filter all transformations using the supplied options.

(let [ transformations-to-run (map transformations options)] ... ) 

Example:

(map employees [:A1]) ; => ["Alice"] (map employees [:A1 :X9]) ; => ["Alice", "Bob"] 

4. Combine functions into one:

(apply comp transformations-to-run) 

Example:

(comp f g h) ;=> f(g(h())) (apply comp [f g h]) ;=> f(g(h())) 

5. And then together:

(def funcstransformations { :encrypt  (fn [data] ... )   :compress (fn [data] ... )}) ... (defn do-processing [options data] (let [funcs[transformations-to-run-here (map optionstransformations funcsoptions)   selected-transformations (apply comp transformations-to-run)]    ;filters(selected-transformations outdata))) (do-processing the[:encrypt only:compress]) 

The ONLY changes if we want to add a new function, say "debug-print", is the following:

(def relevanttransformations functions{ :encrypt (fn [data] ... )  combined-in-one-func  :compress (applyfn comp[data] funcs-to-run-here... )]   ;create single function     :debug-print (combinedfn [data] ...) }) ;<-in-one-func data)))here to add as option (defn do-processing [options data]  (let [transformations-to-run (map transformations options)  selected-transformations (apply comp transformations-to-run)]    ;apply it (selected-transformations data))) 

For the non-clojure viewer: funcs is defined as a lookup map. :encrypt is a keyword, here you can just view it as a enum, and is the key. The value is the function.

The only thing needed now would be to call it with the options

  (do-processing [:encrypt :compress]compress data:debug-print]) ;<-- here to use it 

And to extend it, you would add rows in the funcs map.

In languages where functions really are first class, such as clojure, you might write something like:

(apply comp functions) 

To create a new function that is the combined function of encrypt and compress.

The step before this to actually make the choice would then be any normal sort of filtering. Say:

(def funcs { :encrypt (fn [data] ... ) :compress (fn [data] ... )}) ... (defn do-processing [options data] (let [funcs-to-run-here (map options funcs) ;filters out the only relevant functions combined-in-one-func (apply comp funcs-to-run-here)] ;create single function    (combined-in-one-func data))) ;apply it 

For the non-clojure viewer: funcs is defined as a lookup map. :encrypt is a keyword, here you can just view it as a enum, and is the key. The value is the function.

The only thing needed now would be to call it with the options

(do-processing [:encrypt :compress] data) 

And to extend it, you would add rows in the funcs map.

I think "design patterns" are unneccesarily geared towards "oo patterns" and completely avoiding much simpler ideas. What we're talking about here is a (simple) data pipeline.

I would try to do it in clojure. Any other language where functions are first-class is probably ok as well. Maybe I could a C# example later on, but it's not as nice. My way of solving this would be the following steps with some explanations for non-clojurians:

1. Represent a set of transformations.

(def transformations { :encrypt (fn [data] ... )   :compress (fn [data] ... )}) 

This is a map, i.e. a lookup table/dictionary/whatever, from keywords to functions. Another example (keywords to strings):

(def employees { :A1 "Alice" :X9 "Bob"}) (employees :A1) ; => "Alice" (:A1 employees) ; => "Alice" 

So, writing (transformations :encrypt) or (:encrypt transformations) would return the encrypt function. ((fn [data] ... ) is just a lambda function.)

2. Get the options as a sequence of keywords:

(defn do-processing [options data] ;function definition ...) (do-processing [:encrypt :compress] data) ;call to function 

3. Filter all transformations using the supplied options.

(let [ transformations-to-run (map transformations options)] ... ) 

Example:

(map employees [:A1]) ; => ["Alice"] (map employees [:A1 :X9]) ; => ["Alice", "Bob"] 

4. Combine functions into one:

(apply comp transformations-to-run) 

Example:

(comp f g h) ;=> f(g(h())) (apply comp [f g h]) ;=> f(g(h())) 

5. And then together:

(def transformations { :encrypt  (fn [data] ... )   :compress (fn [data] ... )}) (defn do-processing [options data] (let [transformations-to-run (map transformations options)   selected-transformations (apply comp transformations-to-run)]    (selected-transformations data))) (do-processing [:encrypt :compress]) 

The ONLY changes if we want to add a new function, say "debug-print", is the following:

(def transformations { :encrypt (fn [data] ... )    :compress (fn [data] ... )     :debug-print (fn [data] ...) }) ;<--- here to add as option (defn do-processing [options data]  (let [transformations-to-run (map transformations options)  selected-transformations (apply comp transformations-to-run)]     (selected-transformations data)))   (do-processing [:encrypt :compress :debug-print]) ;<-- here to use it 
Source Link
NiklasJ
  • 675
  • 4
  • 6

In languages where functions really are first class, such as clojure, you might write something like:

(apply comp functions) 

To create a new function that is the combined function of encrypt and compress.

The step before this to actually make the choice would then be any normal sort of filtering. Say:

(def funcs { :encrypt (fn [data] ... ) :compress (fn [data] ... )}) ... (defn do-processing [options data] (let [funcs-to-run-here (map options funcs) ;filters out the only relevant functions combined-in-one-func (apply comp funcs-to-run-here)] ;create single function (combined-in-one-func data))) ;apply it 

For the non-clojure viewer: funcs is defined as a lookup map. :encrypt is a keyword, here you can just view it as a enum, and is the key. The value is the function.

The only thing needed now would be to call it with the options

(do-processing [:encrypt :compress] data) 

And to extend it, you would add rows in the funcs map.