This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-26
Channels
- # babashka (7)
- # beginners (85)
- # calva (39)
- # cider (3)
- # clara (1)
- # clj-kondo (10)
- # clojure (194)
- # clojure-europe (36)
- # clojure-madison (2)
- # clojure-nl (13)
- # clojure-spec (11)
- # clojure-uk (2)
- # clojurescript (17)
- # community-development (5)
- # component (9)
- # conjure (4)
- # core-async (3)
- # cursive (32)
- # data-science (26)
- # datomic (31)
- # graalvm (22)
- # holy-lambda (31)
- # honeysql (7)
- # introduce-yourself (1)
- # jobs (9)
- # jobs-rus (1)
- # lsp (3)
- # malli (9)
- # off-topic (54)
- # pathom (27)
- # pedestal (6)
- # portal (1)
- # re-frame (4)
- # releases (1)
- # remote-jobs (1)
- # sci (3)
- # shadow-cljs (4)
- # spacemacs (13)
- # vim (14)
- # xtdb (3)
Can somebody explain what this code is doing? Kind of confused.
(def fib-seq-iterate (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
(fib-seq-iterate (fn [x] x) 0)
(fib-seq-iterate (fn [x] x) 0)
throws an exception when I try it, but fib-seq-iterate
generates a sequence of fibonacci numbers:
fib> (take 10 fib-seq-iterate)
(0 1 1 2 3 5 8 13 21 34)
fib-seq-iterate
has at least 3 parts:
1. iterate
2. the function passed to iterate, (fn [[a b]] [b (+ a b)])
3. (map first ..)
Do any of those pieces makes sense? Is there a particular part that is more confusing?
nah I get the take 10 thing, just was confused with somebody passing it a function
I'm on the Exercises in Ch4 of the Brave and True book, and I'm stuck on the first problem:
1. turn result of the glitter filter into a list of names
Here's the original from the chapter:
(defn glitter-filter
[minimum-glitter records]
(filter #(>= (:glitter-index %) minimum-glitter) records))
Here's my modified version for the exercise:
(defn glitter-filter-names
[minimum-glitter records]
(filter #(>= (:glitter-index %) minimum-glitter) :name))
The REPL didn't pick up anything wrong. But when I run it on the sample output function from the book...
(glitter-filter-names 3 (mapify (parse (slurp filename)))) => ; Syntax error compiling at (src/fwpd/core.clj:56:25).; Unable to resolve symbol: mapify in this context
How do I output just the :name from each record? I didn't see anything in this or the previous chapter that explicitly shows me.
(map :name (filter ... ))
Ah, I understand now, thanks @corasaurus-hex.
I didn't need to create a new function. I just needed to do something else to the original results:
(map :name (glitter-filter 3 (mapify (parse (slurp filename))))) => ("Edward Cullen" "Jacob Black" "Carlisle Cullen")
I've been meaning to ask these questions as a perpetual beginner: 1. What are the biggest mistakes novices make when first trying to learn Clojure? 2. Between my day job, other commitments, and the need to stay healthy, I admit I can't devote all of my non-work hours to learning Clojure. What's the minimum amount of time I need to spend each day to learning Clojure to at least improve by 1% each day? 3. What are the best uses of that time to do that? (I'm using "Clojure for the Brave and True" now) 4. Conversely, what are the biggest misuses of that time? 5. What setbacks can beginners anticipate to encounter early on? And what are the practical ways to overcome them?
1. Not working in the REPL, not referring to cheatsheet, not using apropos and find doc, using a debugger instead of an inspector 2. More is better. See if you can find excuses to use it for scripts at work (also see babashka) 3. I like 4clojure and the koans. Also, the official docs are great 4. Depends on what you find effective 5. Imperative thinking, code organization, finding resources and putting libraries together correctly
As a perpetual learner myself, Iām interested about the question about debugger vs. inspector, as I often find myself wishing I had Ciderās debugger at hand. Why would you say the use of an inspector ought to be favoured and if possible do you know if good resources or discussion about the topic exist.
It's not that I don't use CIDER's debugger as a last resort, but my general methodology for "what's going on/what went wrong" is smaller functions and sticking taps in things. Especially when you get to an asynchronous context
By taps do you mean deffing/printing or something else? EDIT: Seems the clojure website aggregates many resources on the subject https://clojure.org/guides/repl/enhancing_your_repl_workflow. Iāve also heard about https://github.com/djblue/portal , which seems to provide ātappingā.
a learning path you find fun is the right one. I like puzzles, so http://adventofcode.com for me.
@U02CC5T8LLU taps, e.g. tap>
is the solution to print or inline def
s. They send everything to all sinks. Powerful stuff
Another question: How do I break a programming problem down, especially if I know very little? How do I read it? What keywords do I need to look out for? How do I not get lost/get frustrated/feel stupid before even typing defn
?
I write a test use pedestal . the response contains Chinese . run the test and return garbage characters in vscode output.calva-repl . I was add charset set utf-8
(defn hello3
[request]
{:status 200
:headers {"Content-Type" "application/json;charset=UTF-8"}
:body {:a "ęµčÆ"}})
(deftest hello-test
(testing "hello test"
(is (= "{:a \"ęµčÆ\"}"
(:body (response-for service :get "/hello3"))) )))
Besides the encoding issue it seems that your test treats the body hash-map as a string, but your response returns it as an actual hash-map. Or am I mistaken?
The problem seems to be that the endpoint returns something else than utf-8. When it looks right in Curl it will look right in Calva, I think.
Also, if the issue was with Calva, the "expected" data should also have encoding issues
I use clojure -M:run the result is ok. In edn config file
:run {:main-opts ["-m" "app.main"]
:jvm-opts ["-Dfile.encoding=UTF-8"]
but use repl run http service get garbage charactersNot sure if that means you've solved it, @U025AG2H55F, but in case not. I think adding a :dev alias something like this might solve it:
:dev {:jvm-opts ["-Dfile.encoding=UTF-8"]}
Then select this alias when you start the REPL.yes. you're right! @U0ETXRFEW
I'm trying to get rid of repeated function parameters across related functions. In OOP this is straight-forward: set those parameters using the constructor/initializer, and have the fuctions that depend on them call them on the class instance. Can someone point me to how to deal with this in FP (in this case Clojure)? (See thread for code example)
In Python this would look something like:
class Reader:
def __init__(self, exts, ctx_fn):
self.exts = exts
self.ctx_fn = ctx_fn
def read_file(self, path):
... # uses exts and ctx_fn
def read_directory(self, path):
... # also uses exts and ctx_fn
In Clojure, I have
(defn read-file [path exts ctx-fn] ...)
(defn read-directory [path exts ctx-fn] ...)
The amount of shared parameters could be even bigger perhaps, but even in this example it feels cumbersome to have to passs these around explicitly. Can someone help me undo my OOP thinking? And/or explain how to deal with this?
I think you can do that with https://clojure.org/reference/protocols and https://clojure.org/reference/datatypes:
(defprotocol AbstractReader
(read-file [this path])
(read-directory [this path]))
(defrecord Reader [exts ctx-fn]
AbstractReader
(read-file [{:keys [exts ctx-fn]} path]
(prn exts ctx-fn path))
(read-directory [{:keys [exts ctx-fn]} path]
(prn exts ctx-fn path)))
(let [my-reader (->Reader 5 5)]
(read-file my-reader "path")
(read-directory my-reader "path"))
That looks like a very good fit indeed š. I haven't yet looked into protocols and records, but certainly will. I am still interested to know though: before protocols were added (and in FP languages that lack them), do you know how people would deal with these pattens?
As seamlessly fitting your suggestion is, my guts tell me there's chance to learn more about the FP way of thinking here
Some alternative approaches:
You can also just keep the raw defn
approach.
If you translate your (python?) example directly, then you can close over the exts
and ctx-fn
:
(defn reader-for [exts ctx-fn]
{:read-file (fn [path]
;; exts and ctx-fn
)
:read-dir (fn [path]
;; exts and ctx-fn
)})
But you can also just put them into a map and pass that map as the first argument to your functions. From your example it seems like they belong together in some way, a map is just the right thing to convey that.
If youāre doing it this way then you can decide to use multimethods for example:
(defmulti reader
(fn [path]
(.isFile (io/file path))))
(defmethod reader true
[args]
(println "is a file"))
(defmethod reader false
[args]
(println "is a dir"))
In any case: whenever you want to translate āthisā or āselfā to Clojure, you can think of it as āthe first argument to my functions is a map with all the stuff in itā
Thanks for those suggestions. They look like really valuable alternatives!
Could you use a function with multiple arities? Like this:
(defn word-repeater
([word] (word-repeater word 5))
([word times] (repeat times (str word))))
(word-repeater "hello" 3)
;; => ("hello" "hello" "hello")
(word-repeater "hello")
;; => ("hello" "hello" "hello" "hello" "hello")
Or by wrapping in a higher-order function:
(defn word-repeater [times]
#(repeat times (str %)))
((word-repeater 3) "hello")
;; => ("hello" "hello" "hello")
((word-repeater 5) "hello")
;; => ("hello" "hello" "hello" "hello" "hello")
Never mind, after re-reading your question, I realized that my suggestions are irrelevant to your problem. I didnāt think well and answered the wrong questions. š
No problem, still interesting stuff that relates in a way :thumbsup:, thanks
be careful using records to translate OO, this can lead to bad (unidiomatic, hard to maintain / read / reason about) clojure code. the normal solution to your initial example is a hash map:
{:path ...
:exts [... ... ...]
:ctx-fn ...}
note that even the python example doesn't "get rid" of the repeated params per se, it just bundles them into one object, which is what the hash map does too
Yeah I am trying to be careful with "translations" from OO patterns, so I'm happy with your suggestion. Thanks!
I humbly suggest using threads to organize conversations so that notifications become more useful and the channel becomes easier to follow. In the #calva channel, we have converged upon the social convention of using the thread emoji (š§µ) as a kind reminder.
Is there an easier way to replace an item in a list, I want to replace a single item based on a criterium. Currenlty I map the whole list. (Another option would be to make a vector from the list, find the index and then update the vector, bus this feels too complex). I currenlty have:
(defn update-list [l new-item]
(map (fn [x] (if (= (:id x) (:id new-item))
new-item
x))
l))
If you access your list like this often then a map would be more ergonomic and faster (for sufficiently large collections and regular access) where you map from :id
to items in your collection (donāt worry about :id
being both the key and a value inside your map, thatās just indexing).
But if youāre using a vec or list then mapping over it is exactly what you would do to update a specific item. In other words: Your code is fine.
Do you have an example of a list and an item? What do they look like? (and how would you expect the function to work)
Judging from update-list, l
is a list (collection, to be more accurate) of maps and each map might contain an :id
key? :thinking_face:
Your code seems to break down for items that aren't maps (or don't contain :id
):
(update-list '(:a :b :c :d) :e)
;; => (:e :e :e :e)
(update-list '({:a 1 :b 2} {:c 3 :d 4} {:e 5 :f 6} {:g 7 :h 8}) {:i 9})
;; => ({:i 9} {:i 9} {:i 9} {:i 9})
For this case it is indeed a list of maps, the predicate function here is mostly for demonstration purposes. As the order in the list is important, I did not want to use a map keyed to ids. (and then have to do extra sorting).
.No, an ordered list of maps. The key also needs to be part of the map
Alright... Assuming this is an accurate description of the problem:
"Given a list of things l
, a 2-arity predicate function f
and a thing x
, how can I replace all items in l
that satisfy f
with x
?"
and trying to keep things as generic as possible, you can separate the predicate function (e.g. (defn pred-fn [old new] (> (:id old) (:id new)))
) from the mapping function: (defn update-list [l f x] (map (fn [i] (if (f i x) x i)) l))
Glued together:
user> (update-list '({:id 1} {:id 2} {:id 3} {:id 15}) pred-fn {:id 7})
;; => ({:id 1} {:id 2} {:id 3} {:id 7})
;; or
user> (update-list '({:id 1} {:id 2} {:id 3} {:id 15}) (fn [old new] (> (:id old 0) (:id new 0))) {:no-id 7})
;; => ({:no-id 7} {:no-id 7} {:no-id 7} {:no-id 7})
But that's a pretty hand-wavy suggestion. I'm pretty sure I don't understand the problem š
If that isn't an accurate description of the problem, can you try to formulate it in a similar way?Using lazy operations on a sequence to do a single update is a bad idea. I don't know what your actual domain needs are, but using a list for data that you look up by key is a sign you need a different data design.
what @U3JH98J4R suggested could be an intermediate representation, especially if you find yourself doing multiple updates by id, doing that transform once, then doing the updates, then translating back to the original structure is often a good idea.
I would like to require/import a library only when a feature flag is present. Whatās the best way to do this and is it an anti-pattern?
It is possible, for example clj-http library conditionally requires the cheshire library. But if the library is pure and doesn't change any external state when required in 90% cases it doesn't make much sense imho
Iāll take a look thanks! My use case is building packages for different builds, where test-builds use some libraries which should not be included in the production build.
https://github.com/dakrone/clj-http/blob/3.x/src/clj_http/client.clj#L21 Here you can find an example
Looks like this does the trick. Thanks again
If you need a library only for tests then i would make separate build processes. You can separate the code that depends on a lib from your core application by splitting source paths.
I will keep this in mind when starting afresh with a project in the future. Do you have in mind some projects or libraries with the structure you described for āgetting inspiredā?
Nothing that i can share unfortunately.
Hey friends. I'm taking a peek at the docs at the aero (https://github.com/juxt/aero#use-functions-to-wrap-access-to-your-configuration) and see this snippet:
(ns myproj.config
(:require [aero.core :as aero]))
(defn config [profile]
(aero/read-config "dev/config.edn" {:profile profile}))
(defn webserver-port [config]
(get-in config [:webserver :port]))
Is the (get-in config [:webserver :port])
mentioned last valid? since config
is a function I expect this to not work. Am I missing something here ?Or I have it all wrong?
So this shold be called with something like (webserver-port (config :profile))
?
but won't that mean that the (aero/read-config "dev/config.edn" {:profile profile})
will be executed every time a config option is read? isn't this bad for performance reasons ?
Or I should save somewhere the (config :profile)
and then pass this everytime ?
Yes, I believe this is what they recommend. They're simply suggesting to wrap getters as functions around your config.
You'd probably have something like:
(defn get-current-profile
[]
(keyword
(or (System/getProperty "APP_PROFILE")
(System/getenv "APP_PROFILE")
"dev"))
(defn config [profile]
(aero/read-config "resource/config.edn" {:profile profile}))
(defn webserver-port [config]
(get-in config [:webserver :port]))
(def config (delay (config (get-current-profile)))
;; somewhere else where you use it
(webserver-port @config)
Thank you very much!
Is there any difference between (into [] (filter ...
or (vec (filter ...
? I know they produce the same result in the end, which is returning a vector, but is there any difference in performance or something like this?
there can be, but I wouldn't worry about it unless performance tuning something in a hot loop
it depends a bit what the source is, and whether you want to also apply transducers (which you can with into but not with vec), etc
ah ok, thatās good to know about transducers, in my current case it wonāt matter then, thanks!
I consider them both fast :)