Reputation: 1028
I am fairly new to functional programming. My previous programming experience is mostly in Java.
When designing functions in a functional language, is it better to pass primitives or collections? Due to my lack of knowledge in functional languages, I feel that I cannot see the impact by selecting one or the other.
In my opinion passing a collection results in cleaner and more intuituve code (e.g. passing a map named person instead of name, surname etc.). On the other hand, that creates dependencies of the collection structure. Maybe it just depends on the amount of information you have to use from that map. Unsure...
I realise that this is an open discussion, but my primary concern is if there is a defacto way to design functions.
update1
Lets suppose we have the person map like I said before and we want to str its contents. I guess we would do something like:
;
(def person {:name "John" :surname "Doe"})
(defn str-person
"Give the full name of a person"
[person]
(apply str (interpose ", " (map #(get-in person [%]) [:surname :name]))))
; => Doe, John
; primitive alt
(defn str-person
"Give the full name of a person"
[name surname]
(str surname ", " name))
; => Doe, John
Now, I believe that the first solution is closer to what FP is about and it can be extended etc., but on the other hand, it seems a bit of an overkill.
Upvotes: 3
Views: 117
Reputation: 9454
In Clojure, since your domain is modeled in datastructures like hash-maps, your work is mostly expressed by mapping functions and transformations over those datastructures.
So I find that the high domain-level expression of my application is often defined in terms of:
(map process-thing things)
For instance, here's the guts of our awesome tweet-processor app we've deployed as a service:
;; Get tweets from twitter
(def tweets [{:username "max"
:text "I'm tweeting."}
{:username "kate"
:text "I ate breakfast."}])
(defn capitalize-tweet [{:keys [text] :as tweet}]
(let [processed-text (clojure.string/upper-case text)]
(assoc tweet :text processed-text)))
(map capitalize-tweet tweets)
;=> ({:text "I'M TWEETING.", :username "max"}
; {:text "I ATE BREAKFAST.", :username "kate"})
If I need more processing done or if the scope of my tweet-processor expands, I find that the easiest way to reason about my application is to introduce more process-tweet
functions and chain them together into a workflow:
(map (comp do-something-else do-something capitalize-tweet) tweets)
My three processing functions accept one tweet and may destructure it in different ways, and they may collaborate with smaller functions that Do One Thing like clojure.string/upper-case
, but those are implementation details.
Smaller functions and library invocations were stitched together to serve the broader objective of sending tweets down a pipeline, and those little functions compose the higher-level tweet processing functions that I've used to express that pipeline.
This is generally how I like to approach problems with Clojure and it makes it easy to organize my code. These little functions that operate on primitives compose into functionA, and these other little functions into functionB where functionA and B operate on the core datastructures of my program.
For another example, check out Ring (https://github.com/ring-clojure/ring/wiki/Concepts). A request hash-map comes in, gets transformed through the handlers that define your app (and middleware functions that transform handlers!), and a response hash-map emerges.
Upvotes: 2
Reputation: 5372
Just from a practicality perspective, I prefer to pass collections and maps. You can put together a pretty complex data structure in a map and pass it around as a single argument. It's easier than passing around the bits of data as individual arguments. This doesn't apply to just functional languages -- the same reasoning could be applied to procedural or object-oriented languages as well.
Upvotes: 2
Reputation: 4748
it depends on the use-case.
I dont think FP "prefers" one or the other, as long as you are passing immutable structures, otherwise you are risking losing referential-transparency, which FP is pretty much built on.
Upvotes: 0