This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-10-09
Channels
- # announcements (23)
- # asami (25)
- # babashka (38)
- # beginners (53)
- # calva (17)
- # clara (5)
- # clj-commons (1)
- # clj-kondo (18)
- # clojure (11)
- # clojure-europe (17)
- # clojure-france (1)
- # clojure-germany (5)
- # clojure-nl (2)
- # clojure-sg (4)
- # conjure (3)
- # deps-new (6)
- # fulcro (16)
- # off-topic (46)
- # pedestal (11)
- # react (2)
- # reagent (5)
- # reclojure (7)
- # rewrite-clj (1)
- # sci (18)
- # shadow-cljs (75)
- # sql (3)
- # xtdb (12)
I think I’d done it something like this:
(defn ->note [line]
{:note/id (UUID/randomUUID)
:note/text line
:note/note-list {:note-list/id :singleton}})
(defn read-lines [file]
(-> file slurp (string/split-lines)))
(defn persist-notes! [env notes]
(run! (partial m/add-note! env) notes))
(defn do-it [env files]
(let [notes (->> files first :tmpfile read-lines (map ->note))]
(persist-notes! env notes)
{:note-list/notes (set/project notes [:note/id])}))
Not sure, but it kind’a feels right. Maybe it’s a sign that the m/add-note
should be a m/add-notes
. Not knowing how you persist, but basically going to the database is one of the more expensive things your app does. So limiting the amount of times you do that is a quick performance win. Also, you might want to consider sticking a transaction around this, and then suddenly it might be worth its own fn.
Depending on your taste, you might let persist-notes!
return the notes it persisted, but then, again, you might not 🙂
There's that old adage that a function should either create a side-effect or return a value, but not both. What do you think about that?
What is the difference between add-lib and pomegranate?
Hello friends, again me! I have a specific scenario here that I'm trying to understand what is better. This works, already - so it's more about what is more idiomatic.
I have a collection of waypoints, they are sequential. I want to get the distance across all of them. So I calc the distance between A - B, then between B and C. I have implemented a recursive version that works well with my two argument function for distance. But, I find that using partition
and map
and reduce
much more cleaner. However, this forces me to create a specific signature for a pair version, like below.
So, what is the more idiomatic way to solve this? Distances will only be calculated in pairs. I can see a reason for having a standalone function that deals with loose points.
Maybe here we can go with multi arity methods
?
;; two arg version
(defn distance-meters [a b]
(* 1000 (safe-haversine a b)))
;; single pair version
(defn distance-pairs [pair]
(distance-meters (first pair) (second pair)))
;; map reduce
(defn sum-distance-partition [waypoints]
(let [pairs (partition 2 1 waypoints)]
(reduce + (map h/distance-pairs pairs))))
;; recursive version
(defn sum-distance [waypoints]
(loop [waypoints waypoints
total 0]
(if (empty? waypoints)
total
(let [curr (first waypoints)
lead (second waypoints)
distance (h/distance-meters curr lead)]
(recur (rest waypoints)
(+ total distance))))))
Any thoughts?Just a follow up! Multi arity is freaking nice! not sure if this is good clojure, but I have this now:
(defn safe-haversine [a b]
(try
(haversine a b)
(catch AssertionError ex 0)))
(defn km->m [distance]
(* 1000 distance))
(defn distance-meters
([a b] (km->m (safe-haversine a b)))
([pair] (km->m (safe-haversine (first pair) (second pair)))))
Which I can now use with the sum-distance-partition
with ease.
While it was good to write the loop
version of sum-distance
(for learning purposes), I'm going with map/reduce 🙂you don’t want a function to handle different ways of passing in the parameters. A more extreme example would be accepting both a seq of maps or a map and then quasi dispatching on what you get so you can call reduce on the seq or whatever.
https://clojure.org/guides/learn/functions#_apply There is this in the docs, let me give it a go.
But I agree that the version with partition
would be more idiomatic.
However, I don’t think you need map
there.
@U01EFUL1A8M can you give me an example on this?
wait a second can’t you just skip the whole partition thing and reduce over the waypoints with distance-meters
?
as for the apply
version I think this would work:
(defn distance-pairs [pair]
(apply distance-meters pair))
Ahh, you mean still having a separate function. I was considering removing the multi arity signature and using apply on sum-distance with reduce, map and apply
consider this: is your algorithm still correct if you just reduce
with distance-meters
over waypoints
?
I think you are correct. Let me try.
But, how would reduce understand what is the last input given individually, in order to calculate the distance between points?
Imagine the following string of points
:
a --- b --- c
The distance between a and c is the sum of the distance between ab
and bc
.
When reduce gets first invoked, it will be fed a
, but only that. On the second iteration, b
, and only that.
:thinking_face:
👍 let me give that a go.
btw just to be sure: you do know that you don’t need to define all functions at the top level right?
You can pass an anonymous inline function fn
or use letfn
for functions that you only need in a specific context.
It’s a matter of style, if you like naming things and using defn
then that is competely fine. Just wanted to make sure 🙂
I know about anonymous functions, yes. But In this example I have two namespaces. I just bundled everything together to make it easier for readers here. In general, the functions defined, I believe could be reused. of couse, this is just a silly exercise, the goal is to learn, so thanks for checking 🙂
I started to learn implementing design pattern in clojure and I started with command
design pattern, Is this the way where we can write command design pattern in clojure ?
(defn execute [command args]
(command args))
(defn switch-on [val]
(println "Switching on for " val))
(defn switch-off [val]
(println "Switching off for " val))
(def light-map
{:livingRoomLight "Living Room Light"
:bedRoomLight "Bed Room Light"})
(def fan-map
{:livingRoomFan "Living Room Fan"
:bedRoomFan "Bed Room Fan"})
(defn call-function []
(execute switch-on (light-map :livingRoomLight ))
(execute switch-on (light-map :bedRoomLight ))
(execute switch-off (fan-map :livingRoomFan ))
(execute switch-off (fan-map :bedRoomFan )))
(call-function )
Just a pointer for further investigation, I’m not an authority on this subject, especially not in terms of design patterns / OO: In functional programming people don’t typically use this pattern or the name for this pattern, but there is a very similar thing: “effect” systems. I think they are essentially the same thing. Somewhat controversial, haven’t seen it used except in UI contexts where user actions are quite literally commands.
As an aside, it looks like you reimplemented double dispatch, i.e. multimethods. You might want to check them out
One approach:
(defmulti execute (fn [[cmd & args]] cmd))
(defmulti switch-off (fn [[location & args]] location))
(defmethod execute :switch-off [_ & args] (apply switch-off args))
(defmethod switch-off :living-room [& args] "Living Room Light")
Another:
(defmulti execute (fn [[cmd location & args]] [cmd location]))
(defmethod execute [:switch-off :living-room] [_ _ & args] "Living Room Light")
One could argue that a lot of the patterns in the GoF book is there to solve problems that are caused by short comings of the language used. You’d need the command pattern in a language where functions are not first class, in languages where they are first class, you just use functions.
To further belabour this point. In Clojure, functions are implemented by extending the abstract class https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/AFn.java, which has for all intents and purposes, an execute
method, called invoke
. So one could argue that a function in Clojure is an implementation of the Command pattern.
@U01J3DB39R6
For one, the classic OO design patterns that some of us learned a while ago, are in large parts obsolete. Even in say Java, because it, and other big OO languages, introduced closures, DTO’s records and so on. Many of these patterns were basically workarounds for not having first class data and function objects. To add, some of the patterns are controversial regardless.
Secondly Clojure is basically built on first class constructs like data and function literals, but also macros, ad-hoc dispatch, multiple dispatch and so on, that further obsolete some of the patterns. The standard library then gives you a ton of stuff on top of that so you generally don’t have to reach for pattern abstractions, but rather just use what is there.
That last part is important and might be the right way to go about this I think. Instead of looking for patterns, study and use what is already there: core especially, zippers, logic, match, async…
Then, then are “idioms”, which emerge from using and composing the above. They are often explicit! Read the doc strings of a function and it often becomes clear what the idiom is (Clojure is very opinionated). Sometimes they emerge from practice. For example wrapping a protocol implementation with a defn
or wrapping side effects with do
and so on.
The other thing that is helpful is to think in terms of functional algorithms and the underlying data structures you are using. What their characteristics are and when to use which one. How to them into efficient compositions and so on.
thanks for the write @U01EFUL1A8M 🙂 , yeah I have learned all of them, Now I thought let me do some exercise on design pattern so that, i get more confident 🙂
Ahh I see what you mean now 🙂 well in that case, what works for me: solve problems that you find interesting or build tools that you find useful!
Also recently there was an announcement of http://exercism.org having expanded their clojure exercises. I think most of these are algorithmic but there are also idioms in there. And typically you can find a lot of solutions that you can compare against, there is always one that’s very elegant.
In clojurescript, I'm trying to use https://github.com/isomorphic-git/isomorphic-git library
(ns app.main.vcs (:require ["isomorphic-git" :as git]))
Usually I get functions that I can use, but here when typing git
in repl gives
#js {:Errors #object[Object], :STAGE #object[STAGE], :TREE #object[TREE], :WORKDIR #object[WORKDIR], :add #object[add], :addNote #object[addNote], :addRemote #object[addRemote],...
These are all actually the functions defined in js source code eg https://github.com/isomorphic-git/isomorphic-git/blob/main/src/commands/WORKDIR.js so I'm confused why they show up as map kv pairs. How do I call these functions?
I tried (goog.object/get git "WORKDIR")
I got #object[WORKDIR]
and doing ((goog.object/get git "WORKDIR"))
gives #object[Object]
Tried ((js/Symbol "GitWalkSymbol") (w))
as well got
Execution error (TypeError) at (<cljs repl>:1).
Symbol(...).call is not a function