Fork me on GitHub
#beginners
<
2020-08-29
>
joel38004:08:38

(match-list [:one :a] [ :one _ ] "one" [:one :a] "suba" [:one :b] "subb") ; => ("one" "suba") I have above functionality working EXCEPT the underscore _, I'm using <nil> instead, how can I change my function so that the underscore means "anything matches". I take it I need to use a macro. BTW, if this function exists already, let me know, I couldn't find equivalent.

dpsutton04:08:20

oh i see. i think i saw something like this in metabase's codebase

joel38005:08:59

yeah, the description of that sounds like what i want, although didn't really "get" the examples. core.match is also very close, but I want to match multiple, I didn't see how I could use that one.

dpsutton06:08:31

there are tests that show how to use it. it seems like it descends into forms to perform matching at different levels which i didn't expect: https://github.com/metabase/metabase/blob/master/backend/mbql/test/metabase/mbql/util_test.clj#L31-L56

mail98516:08:03

Is the use of :use within the ns macro considered bad form? Clj-kondo gives me the warning use :require with alias or :refer when I write this:

(ns foo.bar.navigation
  (:use (foo.bar.differentns))

dpsutton16:08:07

yes it is. prefer (:require [foo.bar :refer [differents]) or perhaps even better (require [foo.bar :as bar]) and then use as bar/differents. I often use the namespace alias as a way to add more information to call sites. So if an alias is users then the var doesn't need to duplicate users in it. For instance, users/fetch instead of users/fetch-users or something similar

mail98516:08:29

So what I'm wanting to do is pull in all the public namespace members from the other namespace into the namespace I'm working in. Normally I would just use (require [full.ns.path :as whatever]) but in this case I do actually have legitimate reasons for wanting to be able to reference all the functions from the first namespace without using an alias

mail98516:08:51

My understanding is that in Clojure you can use :refer :all inside your ns form, but this is a Clojurescript project, and apparently that https://stackoverflow.com/questions/24463469/is-it-possible-to-use-refer-all-in-a-clojurescript-require

dpsutton16:08:54

i see. you want to expose other vars in a common namespace. Honestly I prefer a straightforward (def thing other-place/thing). Make your api explicit in that case

stopachka16:08:32

Noob question: Say I wanted to make a function, that "creates accumulators"

(def f (make-accum 1))
(f 2) ;; 3
(f 2) ;; 5
Here's my implementation:
(defn make-accum [n]
  (let [acc (atom n)]
    (fn [to-add]
      (swap! acc (partial + to-add)))))
Would this be the idiomatic clojure way? Or is there something else you'd do?

dpsutton16:08:11

if there's state i'd want a good reason to not just pass the atom around. make it clear that you are updating a mutable container rather than behind a function. this reads like scheme code but not particularly like Clojure code.

dpsutton16:08:46

also, you don't need to partial + there. (swap! acc + to-add) achieves the same result

stopachka17:08:14

Awesome! Thanks for the thoughts @dpsutton -- makes sense! And indeed re: scheme -- am going through sicp atm xD

dpsutton17:08:44

yeah. scheme is "pure" and you make your mutable containers through closures and setting. clojure has really nice ways to do this and you should use them. caveat is if you are learning. there is always benefit to trying different strategies and making your own things to understand tradeoffs made by others.

stopachka17:08:55

Also, one other noob question: Was thinking one way I could model this, is to do something like generator with an infinite loop:

(defn make-accum-loop [n]
  (loop [acc n]
    (recur ??))
  (fn [to-add]
    ;; somehow get the loop above to move forward a step, and get the latest value
    ))
Do you think something like this could be possible? One idea for doing this, is by using core-async (could have a in-ch and out-ch that keeps the loop going, and passes around the answer. But am not sure if there's a different way

dpsutton17:08:35

i'm not following what the function should do. can you explain in words what you want to accomplish?

stopachka17:08:49

Sure thing! I want to create a function, which creates accumulators. It returns a function, that given a number, will increment the value, and remember it for a successive call:

(def f (make-accum 1))
(f 2) ;; 3
(f 2) ;; 5
One way I can do this, is by using an atom:
(defn make-accum [n]
  (let [acc (atom n)]
    (fn [to-add]
      (swap! acc (partial + to-add)))))
But I was wondering if there was another way to do this: For example, would it be possible to keep a kind of "generator" This generator would run in an infinit loop, but be paused, waiting for f to be called When f is called, the generator runs through one loop. This loop would increment the value with whatever f provided

dpsutton17:08:41

if you squint, that's what an atom is. it is state that you can pass in a function to modify it. (swap! accumulator inc). The state is at rest until a function is passed in and it runs.

dpsutton17:08:23

But i'm trying to figure out what you want. Do you want a list of successive values? You don't necessarily need an atom here. There are ways to build up sequences of successive states that might be useful to you

stopachka17:08:23

Wow, great point!

stopachka17:08:53

re: what I want -- I would just want the "next" value Something like initial value: 1 (f 1) // returns 2 (f 3) // returns 5 (f 5) // returns 10 --- If using core.async, I guess I could implement it like this:

(defn make-accum-loop [n]
  (let [in-ch (channel)
        out-ch (channel)]
    (go-loop [acc n]
             (let [to-add (<! in-ch)
                   new-v (+ acc to-add)]
               (>! out-ch new-v)
               (recur new-v)))
    (fn [to-add]
      (>!! in-ch to-add)
      (<!! out-ch))))
(haven't actually used core-async deeply yet ^ above is kind of pseudocode)

stopachka17:08:19

Are there other ways I could get successive values?

dpsutton17:08:31

(reductions + 0 [1 1 3 5]) will return a list of successive states

dpsutton17:08:43

(0 1 2 5 10) which are all your values

stopachka17:08:53

Ah, what a beautiful function!

stopachka17:08:04

I guess one problem with that though, is that I have to know my values ahead of time. (In the accum, case, I am providing values with a function at a later, asynchronous point) I guess the main way to model this is with atoms / refs etc?

dpsutton18:08:21

I’m still not sure I’ve seen anything other than changing mutable state so I’m not sure how to help more

stopachka18:08:53

You've helped quite a bit, thanks @dpsutton -- appreciate the insight about how atom, when squinted is in essence similar to the generator idea

stopachka18:08:32

(to clarify a bit more with what my intention was, I was trying to replicate something like this: https://github.com/islomar/seven-concurrency-models-in-seven-weeks/blob/master/Actors/counter/counter.ex#L22 -- here the mutation is "encapsulated in a kind of infinite loop, that is paused until called" -- From the research I think in clj the best way to do this is indeed either atoms, or if we really really wanted we could do something like the core-async approach

stopachka19:08:48

Ah! Okay one other way I can write this, which ~kind of matches the loop idea in example above:

(defn make-accum [n]
  (fn [to-add]
    (let [new-v (+ n to-add)]
      [new-v (make-accum new-v)])))

(def f (make-accum 2))
(print (first (f 2))) ;; 4
(def f (second (f 2))
(print (first (f 2)) ;; 6
(Carry on! All of above pretty pointless, as atom is indeed best, but wanted to share along the learning : }

ben.sless06:08:46

yay, state monad 🙂

stopachka21:08:30

huh, very cool! Thanks for the gist, will explore it more!

hobosarefriends20:08:21

Not pointless, since I had fun reading it. 😄