Fork me on GitHub
#beginners
<
2020-07-07
>
yh04:07:36

Hi everyone! I am using emacs cider for Clojurescript. After cider-jack-in-clojurescript, I cannot get any autocompletion for js/ (I would expect some candidates like js/Document). It could be due to this issue (https://github.com/rksm/clj-suitable/issues/15). Does anyone know how to get around this problem?

seancorfield04:07:39

@haoyuan80s Since that's a pretty specific question, you might get a more useful answer in the #cider and/or #shadow-cljs channels.

seancorfield05:07:31

(although, reading that issue, I suspect that rolling back to shadow-cljs 2.9.x would solve the problem @haoyuan80s?)

yh05:07:14

Awesome, thank you!

chucklehead05:07:19

is there a way to get clojure.data.xml/parse to ignore leading indentation/whitespace when the input is pretty-printed? I don't have control over the input, not all input is indented, and I get it as a byte[] from a queue initially so I was trying to avoid processing as text to trim prior to parsing if there's some way to get it done by the parser.

chucklehead05:07:30

Alternatively, I guess I'd like to filter all elements of the form "\n " in the :content of all tags in the parsed map. (various numbers of spaces and arbitrary levels of tag nesting)

Alex Miller (Clojure team)12:07:13

There are some parser options you can set on parse - I think of you set :coalescing to true maybe that does what you want?

chucklehead00:07:44

thanks, I tried :coalescing true but didn't seem to make a difference. currently using clojure.walk/postwalk to remove indentation strings from the parsed content, but not sure if there's a more idiomatic/performant way to go about it.

chucklehead00:07:25

current approach looks roughly like

(comment (->> (io/input-stream (rand-from-corpus))
              (xml/parse)
              (clojure.walk/postwalk #(cond (and (string? %) (re-matches #"^\n\s*$" %)) nil ; remove indentation
                                            (seq? %) (keep identity %) ; remove the nils created by removing indentation
                                            :else %))
              (xml/emit-str)))

teodorlu13:07:38

Is there a shorthand for negation? Now I just do (* -1 x)

teodorlu13:07:36

Thanks! Right, 1-arity minus negates the argument.

Frosku14:07:38

If I have 3 vecs -- x, y, z, what's the idiomatic way to create this new vec? [x1 y1 z1 x2 y2 z2 x3 y3 z3]

Frosku14:07:16

Is that interleave?

dpsutton14:07:52

absolutely. caveat read the docstring about their lengths

dpsutton14:07:17

ah, its not in the docstring. evaluate (interleave [:a :b] (1 2 3))

Michaël Salihi15:07:26

@dpsutton Little typo here, the missing quote I think (interleave [:a :b] '(1 2 3))

dpsutton15:07:44

yeah thanks

👍 3
Andrew Doolittle15:07:30

Why does this output nil?

(= nil (if (nil? 0)
          [:a :b :c]))
;; nil

noisesmith15:07:25

it doesn't it returns true

noisesmith15:07:36

and nil isn't equal to 0

dpsutton15:07:55

(= nil (if (nil? 0)
         [:a :b :c]))
true
yeah i get true

Andrew Doolittle15:07:25

hmm. my REPL outputs nil, which is why I'm confused

dpsutton15:07:56

what are you confused about?

dpsutton15:07:25

(nil? 0) is false so (if false [:a :b :c]) returns nil

dpsutton15:07:37

then (= nil nil) is true, which explains why you are getting true in your repl

dpsutton15:07:28

and i'm confused why you asked earlier "why does this output nil?"

dpsutton15:07:33

and then you say your repl says true

noisesmith15:07:22

I think it's trying to teach you that 0 isn't nil, but doing it in a very weird way

Chase15:07:23

That is telling you that the if expression will equal nil

Andrew Doolittle15:07:47

that was a typo, my REPL outputted nil

noisesmith15:07:58

it's odd to have = nil and nil? in the same code

Andrew Doolittle15:07:04

that link is the solution branch. I'm working on the fill-in-the-blank branch

Chase15:07:41

I've seen it in a lot of tutorials as just a way to show you what the concept they are teaching will result in.

Andrew Doolittle15:07:40

I played around and it wasn't clear to me until now that nil is the default output if the output for a false condition isn't explicitly stated. (if (boolean? "not a boolean") true) ;; nil (if (boolean? "not a boolean") true false) ;; false

noisesmith16:07:20

FWIW if treats nil the same as false, and that's what matters most of the time in application logic (very rarely does one explicitly need false)

👍 3
Andrew Doolittle16:07:39

Thanks for the help and clarification everyone.

noisesmith15:07:38

in many languages 0/nil/false are all the same value, in clojure they are three different values

papachan15:07:39

user=> (boolean 1)
true
user=> (boolean 0)
true

noisesmith15:07:22

@papachan haha, I responded to your snippet before you shared it :D

😄 3
noisesmith15:07:55

the boolean of all values that are not nil or false is true

noisesmith15:07:59

this isn't machine language

papachan15:07:16

yeah @noisesmith understand. i was expecting some logical response by casting to boolean 0 or "" But its right.

Aron15:07:18

what is the programmatic way to check if my dependencies have newer versions released?

seancorfield16:07:41

For Leiningen there's lein-ancient and for deps.edn there's Depot so you can look at the code for those.

seancorfield16:07:34

I think you could also just use tools.deps.alpha directly and use "RELEASE" as the version in the coordinate... I'm not sure what in that library exposes the actual version resolved tho'

Aron16:07:12

that's a long way to say there isn't one

Aron16:07:11

hah, but it's untrue, Depot is unmaintained but it links https://github.com/liquidz/antq

Aron16:07:14

thanks 🙂

seancorfield16:07:57

(I know he says that in the README but he's been updating the code quite a bit lately -- so that's a bit of a contradiction 🙂 )

Aron16:07:46

Well, let's not argue the semantics of when would we say unmaintained 🙂

Aron16:07:04

in any case, through your pointer, I think I found what I needed, thanks again!

seancorfield16:07:12

My problem with Depot (since 1.8.4) and with projects like antq is that they assume they can just read your project file and figure out everything from that alone -- which just isn't true.

Aron16:07:03

What do you mean? I just want to know if someone published a newer version of a dependency

seancorfield16:07:08

We had to stop using Depot because of that (and we wouldn't be able to use antq either) -- which is why I suggested using t.d.a. directly.

seancorfield16:07:30

Because deps.edn alone doesn't provide the full story, e.g., if you're using aliases to :override-deps from a user-level file (as most folks with monorepos do -- using CLJ_CONFIG to treat a master deps file in the repo as the "user-level" file to combine with the project deps file).

Aron16:07:24

I am coming from javascript. We had a package.json. If it wasn't in a package.json, it wasn't part of the project. I do not intend to change this practice transitioning to clojure

seancorfield16:07:35

You can ignore the user-level deps.edn file by specifying -Srepro -- and then your project-level file is the whole world.

Aron16:07:51

good to know, thanks

Aron16:07:06

btw and fyi 🙂 I am using your dot-clojure with tiny modifications as my $HOME/.clojure/deps.edn I think you had some tutorial with it on youtube, good stuff

Aron16:07:01

so it had the outdated alias for depot all this time, and I was wondering why my copy paste shows a linting error :D

seancorfield17:07:39

Ah, yeah. It uses 1.8.4 because that version actually relied on t.d.a. to process dependencies, just like clojure/`clj` would. In the 2.x version, it tries to just read the file (which doesn't work for us).

noisesmith15:07:19

the castings polymorphisms for nil are a bit wierd and seem arbitrary at first • nil is cast to empty list by cons and conj • nil is treated as a false value by if and all the macros built on it

👍 6
noisesmith15:07:46

I think the nil/false/0 vs. nil/falsey/() split is about Algol vs. LISP heritage, and most languages used today are descended from Algol

noisesmith15:07:33

and java shares bits of each side

noisesmith15:07:51

and clojure takes that and tweaks it some more to be lispier

Alex Miller (Clojure team)16:07:23

"cast" is the wrong word here. it is better to think about it as "how does operation X work polymorphically on nil"

👍 3
noisesmith16:07:52

thanks, that's a good point

Alex Miller (Clojure team)16:07:08

and "nil is treated as a false value by `if` and all the macros built on it" is really about the definition of logical truth in Clojure, not "treated as false"

Alex Miller (Clojure team)16:07:01

logical truth of an expression = false if value is nil or false, true in all other cases

noisesmith16:07:42

but even here you use "false" twice

noisesmith16:07:16

(with two senses, which I wasn't clear enough about distinguishing myself)

Alex Miller (Clojure team)16:07:32

boolean is the function that represents logical truth

Alex Miller (Clojure team)16:07:22

that is a coercion from <any> to true/false

noisesmith16:07:12

but even that doesn't reflect the behavior of if - they two diverge for instances of "false" that are not the static Boolean/FALSE (which of course doesn't matter as long as you never use the constructor for Boolean)

Alex Miller (Clojure team)16:07:32

well, I'm happy to ignore the case you should never use

Stuart17:07:45

hi, is there a way to get the index of an element when using reduce ? I have a vector [[:a 5][:b 6][:x 0][:label "foo"][:y :g][:label "bar"]] And I'm trying to get a map out of labels and locations that would have {"foo" 3 "bar" 5} that I can use as a lookup later. I have

(reduce (fn [a i] (if (= :label (first i))
                      (assoc a :i)
                      a)) foo)
I was hoping i could put something like (reduce (fn [a i ix] ... ) foo (range)))but no 😞

dpsutton17:07:57

(reduce (fn [a [i ix]] ...) init (map vector (range) coll))

hiredman17:07:26

(map-indexed vector coll)

dpsutton17:07:52

i do the map vector because i always forget which order the index and the item are in for map-indexed

Stuart17:07:41

brilliant! thank you 😄

noisesmith17:07:04

also, often the real solution is to rearrange your data or your algorithm so you don't have to switch between indexed and sequential representations/access

noisesmith17:07:03

another nice trick is to create a map from position of element to element: (zipmap (range) coll)

noisesmith17:07:20

user=> (pprint (zipmap (range) [:a :b :c :d :e :f :g :h :i :j :k :l]))
{0 :a,
 7 :h,
 1 :b,
 4 :e,
 6 :g,
 3 :d,
 2 :c,
 11 :l,
 9 :j,
 5 :f,
 10 :k,
 8 :i}
nil

👍 3
Stuart17:07:30

thanks for the help and tips guys 🙂

Stuart17:07:53

I presume you guys use the repl a lot. Do you run into issues where you end up losing code, or having to scroll way back up a lot, or are you continunally copying and pasting from the repl into your soure file as soon as its right? Or is their a workflow I'm missing here?

practicalli-johnny11:07:26

As has been mentioned already, using the editor is typically more productive. The most common editors seem to be http://practicalli.github.io/clojure/clojure-editors/

noisesmith17:07:43

I do most work in my editor, only working directly in the repl for raw experimentation (seeing how some API works etc.)

noisesmith17:07:53

some people swear by never typing directly into the repl

noisesmith17:07:23

you can copy into the repl from editor, and most editors have shortcuts for sending to a repl

noisesmith17:07:37

though I prefer integrations that have a true repl session showing all inputs and results

Stuart17:07:04

what editor do you use?

noisesmith17:07:13

that's a personal choice, but I use neovim with the neoterm plugin for sending code to an embedded terminal

noisesmith17:07:35

any decent code editor should have some feature

noisesmith17:07:02

emacs (CIDER) and Intellij (Cursive) are quite popular

seancorfield17:07:25

@qmstuart Try to develop a workflow where you never type into the REPL: use your editor, eval code into the REPL from there. That way you always have all your code saved for future use.

6
seancorfield17:07:43

Watch Stu Halloway's presentations "Running With Scissors" and "REPL-Driven Development".

seancorfield17:07:11

If you don't mind paying for an online course, look at Eric Normand's "REPL-Driven Development" on http://PurelyFunctional.tv

Stuart17:07:51

oooh, i've bought a couple of videos of http://purelyfunctional.tv, bought his DSLs one and one on re-frame. THey were both really good.

Stuart17:07:01

I'll have a look at his repl one

seancorfield17:07:02

The REPL one is excellent! I loved it!

Stuart17:07:16

do you know if he covers intelliJ use in it?

Stuart17:07:35

Im finding intellij mouse hover over a command and I get a pop up with the clojure docs to be EXTREMELY useful

seancorfield17:07:53

What he teaches is applicable to all editors. I don't remember whether/how much he covers IntelliJ.

dpsutton17:07:04

the documentation story in cursive is extremely nice. control-j and alt-space are just fantastic

seancorfield17:07:38

I use Atom with Chlorine and I get popups as I type that show vars and functions, with their arglists inline and their docs below the suggestions.

seancorfield17:07:08

I can also see docs inline in the code with a hot key if I want them onscreen to read while coding.

seancorfield17:07:52

Example: as I typed (ass I get possible completions and can move the cursor up/down to see arglists and docstrings inline.

seancorfield17:07:08

Arglists and docstrings show up as I move the cursor down the list:

Stuart17:07:54

I get similar, but also get what seems a lot of information that isnt' all that helpful :S

Stuart17:07:17

assert-valid-fdecl, dont think i can even call that

seancorfield17:07:48

I'm surprised it shows private functions. I shouldn't be surprised it shows all the Java stuff, being IntelliJ. You can call private functions, with #' in front of them, BTW.

vasergen19:07:49

Hi, I am learning clojure, coming from nodejs/typescript. And I have a question, what is the idiomatic way to implement code below in clojure?

type Balance = {
    amount: number;
};

interface IPayment {
    sendMoney(balance: Balance): Promise<Balance>;
}

class PaymentServiceA implements IPayment {
    async sendMoney(balance: Balance) {
         // do something
         return balance;
    }
}

class PaymentServiceB implements IPayment {
    async sendMoney(balance: Balance) {
        // do something
        return balance;
    }
}

hiredman19:07:36

It kind of depends, there isn't really a single thing promises map to, if you are using clojure and not clojurescript, you might even not do anything at all and just block a thread (you can have many threads for cheap)

hiredman19:07:14

As far as polymorphic dispatch, there many choices there as well, multimethods are a decent starting point

Johnny19:07:59

Although this looks like protocols would fit here as well

hiredman20:07:06

I'd suggest not using protocols

vasergen20:07:09

yes, I think my question was more into protocols, how to implement it properly, and make sure the correct data returned by functions sendMoney in my example above

hiredman20:07:48

as beginner, coming from a language like typescript you are likely to reach for defining new types too often, and protocols just sort of make that worse

vasergen20:07:00

I used to define interfaces and then use them instead of actual classes in usecases

hiredman20:07:05

you can use protocols without defining new types now, but for a long time that has not been the case, so most of the documentation etc. you see for protocols will show it used with defining types

vasergen20:07:17

and use invertion of control

Johnny20:07:34

Yeah, I mean protocols are still quite different from deftype and whatnot

hiredman20:07:23

so for example, if sendMoney is actually the same for all payment services, and all paymentservices are just a map with a balance key

vasergen20:07:28

I see as well spec, maybe can use it in additional to that

vasergen20:07:43

not sure how to combine all together in clojure way

hiredman20:07:49

then the polymorphism disapears and you just have a single function that takes any map with the right key

Johnny20:07:21

that's true. How you would design this heavily depends on what it's actually supposed to be

Johnny20:07:01

Optimally you wouldn't define any protocols and just model it with pure data, I agree

vasergen20:07:24

real example is way more complex, here just posted simple one, to have an idea.

Johnny20:07:57

in what ways do the implementations for sendMoney differ?

vasergen20:07:19

actually it would be different services

vasergen20:07:07

like Paypal and DeutscheBank for example

vasergen20:07:29

and there could be a lot

vasergen20:07:09

then using interface I want to be sure that other services implement it correctly

Johnny20:07:31

And the function results in some request over the network with authorisation and everything?

Johnny20:07:44

In that case I'd also recommend looking at multimethods first. Chances are this can be done nicely with a multimethod that takes a map representing the service and the money. Something like this:

(defmulti send-money (fn [service amount] (:type service))

(defmethod send-money :paypal ...)
(defmethod send-money :btc ...)
And then you would pass a map like this as the first arg:
{:type :paypal
 :auth {...}
 :whatever-else "is needed"}

vasergen20:07:20

is it make sense to use spec in addition to this?

Johnny20:07:40

Sure, you could make specs for the service map. I've personally never used spec but depending on the properties all services share and so on it may surely make sense.

noisesmith20:07:40

I'd be similarly cautious with spec as you would be with protocols and records - they aren't a type system and aren't really something to declare for every function like you would types, they work best as something to validate things that cross system boundaries

💯 3
vasergen20:07:21

I think in my case it would be just 1 spec and each implementation should follow it

telekid20:07:36

> @hiredman: I’d suggest not using protocols I’m curious, what is the motivation behind this recommendation?

hiredman21:07:19

because this is #beginners and the original asker mentioned coming from typescript, and refuges from typed langues tend to reach for types and type based polymorphism over using simple datastructures like maps, sets, and vectors

telekid21:07:20

that makes sense!

noisesmith20:07:33

@vasergen then your code isn't polymorphic

vasergen20:07:09

not sure what does it means. I just want to implement a special usecase and make sure that schema of input/output between different services the same

noisesmith20:07:23

so you want polymorphism between services

noisesmith20:07:33

and want a single spec across the implementations

vasergen20:07:32

guess so, like an interface in typescript, where I have defined input/output

noisesmith20:07:03

I guess I find this strange because one of my first design rules for crossing system boundaries is that I keep the crossing points as simple and narrow as possible. If I'm not crossing a boundary I usually don't need a spec. If I am crossing one I know the precise data that is needed / accepted and do conversions to match on each side.

noisesmith20:07:35

one thing that might be missing / implicit here is that there's a strong preference in clojure to use a small set of data types (mainly the ones that are built in) with a large number of functions on them

noisesmith20:07:44

it's alien when coming from a lang like ML or typescript or whatever, but there is a logic to it

vasergen20:07:40

yes - this is actually what I want to learn

noisesmith20:07:25

I think the best clojure book for this is Zach Tellman's Elements of Clojure https://leanpub.com/elementsofclojure

noisesmith20:07:44

but I think there are other resources from common lisp / scheme that others could recommend

vasergen20:07:18

thanks ,will take a look. I am reading living clojure right now

noisesmith20:07:41

eg. I think I picked up the "small number of data types with a large number of functions" idea from SICP, but it's been long enough that I'm not sure

vasergen20:07:54

I see this idea a lot in clojure community

Raymond Ko20:07:32

Is this a known ClojureScript limitation? Functions don't get spec validation when they are references at the same scope level of the namespace, or am I doing something wrong?

(defn f [x] (+ 1 x))
(spec/fdef f :args (spec/cat :x int?))
(def m {"foo" f})

;; properly fails spec validation
(f "bar")

;; does not fail spec validation
((get m "foo") "bar")

noisesmith21:07:38

I would have been surprised if that worked - spec attaches to the metadata, the function itself isn't used to store the metadata (at least on jvm clojure)

lunik121:07:22

Hello. I am trying to do something with spec but I am having toruble when it comes to generators. I have the function and spec

(defn lowest-val
  "Given two keys x and y, returns the key with the lowest associated value in
  m. Returns nil if the values are =."
  [m x y]
  (let [m (select-keys m [x y])]
    (case (compare (-> m first val) (-> m second val))
      -1 (-> m first key)
      0 nil
      1 (-> m second key))))
(s/fdef lowest-val
  :args (s/and (s/cat :m (s/map-of keyword? integer?) :x keyword? :y keyword?)
                #(contains? (:m %) (:x %))
                #(contains? (:m %) (:y %)))
        :ret any?)

(s/exercise-fn `lowest-val)
but when I call exercise-fun I get "Couldn't satisfy such-that predicate after 100 tries.", I assume because the generator does not try to generate keywords for x and y that are in m. Is there a way to do this with a custom generator? I couldn't figure out how to feed the incoming m to with-gen so it knows that keys to generate.

Alex Miller (Clojure team)21:07:42

you can do this with a custom generator using gen/fmap

Alex Miller (Clojure team)21:07:03

first generate a tuple of x, int, y, int, then fmap to make a vector containing a map of that stuff, plus x and y

lunik121:07:50

by wrapping the first s/and with s/with-gen it is possible to generate the whole argument list at once as a vector?

g21:07:45

i’ve got an httpkit server running with compojure, and one route leads to a websocket handler

g21:07:50

but i’m not sure how to connect to it with an aleph client

g21:07:16

so i have (routes (GET "/" [] ws-handler) (GET "/other" [] http-res))

g21:07:32

i can do http/get /other no problem, but connecting to / hangs

g21:07:03

(with http/websocket-client

hiredman21:07:39

you can't do websockets through compojure

g21:07:43

got it. could you suggest an alternative?

g21:07:54

(httpkit recommended compojure for this case 😞 )

hiredman21:07:26

oh, maybe they do something special to make it work

hiredman21:07:03

in general compojure's expectation is when it returns the handler is done running

hiredman21:07:21

your hanging might be because you are trying to keep running without letting the handler return

g21:07:40

yes i’d like to keep the connection open

hiredman21:07:43

which is not the same thing as not returning from the handler

Mario C.21:07:26

I got an API that uses timbre for logging. I log various things throughout the request life-cycle. There are some logs that don't include some useful bits of information that I would like to add to the logs. But I am not liking the idea of having to refactor code to pass through information that will ultimately be only be used for logging. Is it possible to set up the logger with a context on a per request basis? So that for each log during that request it logs some basic information associated with that request. There appears to be a function called with-context but not sure if thats exactly what I want

g21:07:50

@hiredman sorry could you elaborate?

hiredman22:07:32

you have to return from your wshandler, the return value is the result of the as-channel function

hiredman22:07:32

if ws-handler is something like (defn ws-handler [req] (while true (println "loop forever")) ...) then you would get a hang

g22:07:05

understood, thank you

ksd22:07:21

Any tips on REPL-driven development in VSCode, preferably using Boot? The appeal to me of Boot is that I can make single-file programs, without needing any directory structure and while specifying my dependencies in my program. I’ve only written very small Clojure programs so far. I’ve tried using Calva but haven’t figured out how to “jack in”. So for now my code and my REPL have not been connected. Conversely, is it possible to specify dependencies within my program and without putting my code in a src directory when using Clojure CLI?

pez22:07:18

@ruyvalle With Calva you can start your boot repl in some terminal (VS Code's terminal views work fine) and then use Connect instead of Jack in. If you use the project type Generic you should get away without needing any directory structure or anything like that.

pez22:07:28

(Also have a look at babashka if your Clojure coding is script like.)

ksd22:07:45

thanks, I’ll give that a shot

seancorfield23:07:12

@ruyvalle Yes, src is just a convention -- the default value of :paths is ["src"] but you can specify that to include other directories, including "." if you want.

seancorfield23:07:09

You can also just say clojure script.clj to run a Clojure script in the current directory:

(! 820)-> ls -l
total 8
-rw-r--r--  1 sean  staff  19 Jul  7 16:02 hello.clj
(! 821)-> cat hello.clj 
(println "Hello!")
(! 822)-> clojure hello.clj 
Hello!
(! 823)-> 

seancorfield23:07:16

If you're on Linux/macOS, you can also do this:

(! 826)-> cat bin/time.sh 
#!/usr/bin/env clojure -Sdeps {:deps,{clj-time,{:mvn/version,"0.14.2"}}}
 
(require '[clj-time.core :as t])
(println (str "Time is now " (t/now)))
(println (str "Java version is " (System/getProperty "java.version")))
(! 827)-> bin/time.sh 
Time is now 2020-07-07T23:03:46.105Z
Java version is 14

👏 6
ksd23:07:09

nice! I didn’t think of putting -Sdeps on the #! line

ksd23:07:00

I guess I was doing something wrong a few days ago, because I definitely tried clojure script.clj

ksd23:07:06

thank you!

lunik123:07:21

I have tried writing a custom generator for my earlier spec problem but I am running into the same problem - the generator still cannot meet the spec

(s/def ::key-int-map (s/and (s/map-of keyword? integer?)))

(defn lowest-val
  "Given two keys x and y, returns the key with the lowest associated value in
  m. Returns nil if the values are =."
  [m x y]
  (let [m (select-keys m [x y])]
    (case (compare (-> m first val) (-> m second val))
      -1 (-> m first key)
      0 nil
      1 (-> m second key))))
(s/fdef lowest-val
  :args (s/with-gen (s/and (s/cat :m (s/map-of keyword? integer?) :x keyword? :y keyword?)
                           #(contains? (:m %) (:x %))
                           #(contains? (:m %) (:y %)))
          (fn [] gen/fmap
            #(let [s (seq %)]
               [% (-> s rand-nth key) (-> s rand-nth key)])
            (s/gen ::key-int-map)))
  :ret any?)

lunik123:07:29

I assume I am doing something wrong when it comes to generating the whole argument list together, but I am not sure what 😕

noisesmith23:07:28

I suspect that code would be easier to work on if the (s/with-gen ...) were pulled into its own def or s/def, similarly with the fn that should return the generator

noisesmith23:07:59

and as I looked closer to enumerate the things that needed abstracting out, I see this: > (fn [] gen/fmap #(...) (s/gen ...))

noisesmith23:07:38

that takes no args, discards gen/fmap (doing nothing with it), discards an anonymous function (doing nothing with it) and finally returns the s/gen that didn' twork in the first place

noisesmith23:07:05

which is surely your bug

lunik123:07:24

yes I think that is it, thank you!

noisesmith23:07:57

but back to the original point, things like that are easier to see when you don't throw so much nesting into one form

noisesmith23:07:13

especially anonymous functions that aren't closing over locals, can be moved into proper functions

lunik123:07:49

yeah I have a bad habit of writing big forms and then only pulling them apart once they work

lunik123:07:45

that gen was in a seperate form but I couldn't quite get it wrapped in a function correctly

deactivateduser23:07:51

Strangely I tend to go the other way - I like to “name” every little discrete step in my logic (by putting it in its own fn), then afterwards I look for all the silly little “don’t add any value” fns and move them inline. 😉

deactivateduser23:07:48

But then I have a terrible memory, so have trouble holding large blocks of logic, and how the data changes “shape” through it, in my head.

lunik123:07:01

huh, I generally look for things I've used more than once in a function and tput them into lets, and then find repeated logic across functions and give them their own function

lunik123:07:13

+ split up forms that get a bit too nested

👍 3