Fork me on GitHub
#beginners
<
2022-04-26
>
Jon Olick00:04:28

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)

phronmophobic00:04:25

(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)

phronmophobic00:04: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?

Jon Olick00:04:31

nah I get the take 10 thing, just was confused with somebody passing it a function

šŸ‘ 1
Jon Olick00:04:41

probably just a broken example then

Michael Agres00:04:16

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.

Cora (she/her)00:04:01

you'd need to call a map on the results of that filter

šŸ§µ 1
Cora (she/her)00:04:30

(map :name (filter ... ))

Michael Agres00:04:03

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")

Michael Agres00:04:32

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?

Ben Sless04:04:34

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

šŸ™Œ 1
šŸ‘ 1
ossosoi loso16:04:20

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.

Ben Sless16:04:19

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

ossosoi loso16:04:14

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ā€.

tschady16:04:23

a learning path you find fun is the right one. I like puzzles, so http://adventofcode.com for me.

Ben Sless17:04:25

@U02CC5T8LLU taps, e.g. tap> is the solution to print or inline defs. They send everything to all sinks. Powerful stuff

Ben Sless17:04:42

Portal is a sink for taps. There's also reveal

šŸ‘ 1
Michael Agres00:04:57

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?

quan xing09:04:13

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"))) )))

quan xing09:04:23

output.calva-repl

Bart Kleijngeld09:04:39

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?

pez09:04:29

Does it look OK if you use curl on the endpoint?

quan xing09:04:01

yes curl return garbage characters too

quan xing09:04:06

How can I set charset "Start project nREPL and connect Jack-in"

pez09:04:51

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.

Bart Kleijngeld09:04:45

Also, if the issue was with Calva, the "expected" data should also have encoding issues

quan xing10:04:21

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 characters

quan xing10:04:13

I add :jvm-opts ["-Dfile.encoding=UTF-8"]] to my edn file that is ok!

pez10:04:14

Not 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.

quan xing13:04:23

yes. you're right! @U0ETXRFEW

Bart Kleijngeld09:04:48

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)

Bart Kleijngeld09:04:56

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] ...)

Bart Kleijngeld09:04:49

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?

Martin PÅÆda09:04:00

(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"))

ā¤ļø 1
Bart Kleijngeld09:04:41

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?

Bart Kleijngeld09:04:32

As seamlessly fitting your suggestion is, my guts tell me there's chance to learn more about the FP way of thinking here

dgb2310:04:11

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"))

ā¤ļø 1
dgb2310:04:07

(untested code but you get the drift)

dgb2310:04:23

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ā€

Bart Kleijngeld10:04:33

Thanks for those suggestions. They look like really valuable alternatives!

leifericf11:04:57

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")

leifericf11:04:48

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. šŸ˜…

šŸ‘ 1
Bart Kleijngeld11:04:14

No problem, still interesting stuff that relates in a way :thumbsup:, thanks

šŸ‘ 1
noisesmith15:04:42

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 ...}

šŸ’” 1
noisesmith15:04:50

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

Bart Kleijngeld15:04:05

Yeah I am trying to be careful with "translations" from OO patterns, so I'm happy with your suggestion. Thanks!

šŸ‘ 1
leifericf10:04:44

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.

šŸ‘ 2
Arjen van Elteren13:04:48

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:

Arjen van Elteren13:04:41

(defn update-list [l new-item] (map (fn [x] (if (= (:id x) (:id new-item)) new-item x)) l))

genmeblog14:04:06

A map (a data structure) is what you probably need.

dgb2314:04:13

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.

pavlosmelissinos14:04:51

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})

Arjen van Elteren15:04:01

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).

emccue17:04:34

two data structures? ordering and map?

emccue18:04:13

{:order '(:a :b :c :d)
 :data {:a {}
        :b {}
        :c {}}}

Arjen van Elteren06:04:56

.No, an ordered list of maps. The key also needs to be part of the map

pavlosmelissinos07:04:35

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?

noisesmith15:04:18

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.

noisesmith15:04:54

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.

ossosoi loso15:04:38

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?

delaguardo16:04:19

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

ossosoi loso16:04:22

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.

ossosoi loso16:04:59

Looks like this does the trick. Thanks again

delaguardo16:04:56

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.

ossosoi loso16:04:34

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ā€?

delaguardo17:04:36

Nothing that i can share unfortunately.

Serafeim Papastefanos19:04:59

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 ?

ghadi19:04:43

arguments [config] shadow var definitions

Serafeim Papastefanos19:04:46

Or I have it all wrong?

Serafeim Papastefanos19:04:26

So this shold be called with something like (webserver-port (config :profile)) ?

Serafeim Papastefanos19:04:17

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 ?

Serafeim Papastefanos19:04:03

Or I should save somewhere the (config :profile) and then pass this everytime ?

didibus00:04:17

Yes, I believe this is what they recommend. They're simply suggesting to wrap getters as functions around your config.

didibus00:04:26

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)

Serafeim Papastefanos06:04:40

Thank you very much!

Vinicius Vieira Tozzi21:04:03

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?

Alex Miller (Clojure team)21:04:30

there can be, but I wouldn't worry about it unless performance tuning something in a hot loop

Alex Miller (Clojure team)21:04:57

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

Vinicius Vieira Tozzi21:04:58

ah ok, thatā€™s good to know about transducers, in my current case it wonā€™t matter then, thanks!

Alex Miller (Clojure team)21:04:47

I consider them both fast :)

didibus02:04:03

You can also use filterv instead, it'll return a vector directly

šŸ˜® 1