Fork me on GitHub
#beginners
<
2020-05-27
>
hiredman00:05:58

it should look something like

(fn [f] (fn [req] (update-in (f req) [:cookies] conj "whatever")))

Mario C.00:05:22

(defn- wrap-jwt-payload
  [request]
  (let [jwt     (jwt/get-token request)
        payload (jwt/decode-token jwt)]
    (prn jwt)
    (-> (update-in request [:params] merge payload)
        (update-in [:cookies] merge (->> (jwt/create-token payload (jwt/token-lifetime))
                                         (jwt/jwt-cookie))))))

Mario C.00:05:07

The prn would print out the old jwt. But a new jwt cookie was never set, as in the old one was never overwritten by the new one

hiredman00:05:30

I am shocked that it printed anything

hiredman00:05:04

because that is just wrong, the middleware wrapping functions are all functions that return a function

hiredman00:05:24

they are functions from handlers to handlers, not functions from requests to responses

Mario C.00:05:18

oh yea I see how thats confusing.. Its not an actual middleware function

Mario C.00:05:37

Its returning a modified request which gets passed to a handler function

seancorfield00:05:55

It's confusing to call it wrap-... then...

Mario C.00:05:12

Yea thats true, ill probably change the name

hiredman00:05:58

Cookies needs to be set in the response, and that function does it's update in on the passed in request

hiredman00:05:27

So that is also potentially confusing

seancorfield00:05:15

It would be better as middleware since it could decode the JWT on the way in, add the payload to the request, call the handler, then add an updated JWT to the response before returning that (updated response).

Mario C.00:05:53

So thats the reason why I added it in the middleware but I need to figure out why its not working

seancorfield01:05:25

@mario.cordova.862 Middleware should have this general pattern:

(defn wrap-something
  [handler]
  (fn [req]
    (let [resp (handler (enhance req))]
      (enhance resp))))

seancorfield01:05:20

In your case, you'd want to decode the JWT in that let and enhance would include that update-in that merged in the payload to the request :params.

seancorfield01:05:48

And then the enhance on the response would merge the updated JWT into the :cookies.

seancorfield01:05:55

Does that make sense?

dpsutton01:05:59

(And note that those are two different enhance functions)

2️⃣ 4
Mario C.01:05:17

@seancorfield yes it makes sense. @smith.adriane gave an example earlier which pretty much mirrors yours. And that’s what I did to get it working. Thank you guys, I appreciate the help :)

4
hindol08:05:21

:jvm-opts not supported at the top level of deps.edn?

delaguardo09:05:48

no, only :deps , :paths , :aliases and provider-specific keys (as :mvn/repos) supported as a top-level keys in deps.edn

hindol09:05:46

Thank you for confirming.

Endre Bakken Stovner12:05:45

I am writing a workflow management system, something like GNU make. I have jobs in one of four states: done, running, ready, or not ready (i.e., dependencies missing). Each category has one set each, containing job ids. I guess each set should be in the same atomic map, to ensure atomic updates.

{:done #{1 2}
 :running #{3 6}
 :ready #{4 5 7}
:not-ready #{8 9 10}
When jobs finish, I need to move them from running to done. I also need to recompute the ready set, to see if any not ready jobs are now ready. Multiple jobs might be running and finishing simultaneously. What would be a Clojure-y way of doing this? I guess when jobs finish, they can call a function that 1) moves them from running into done 2) recomputes the ready set Is any chain in my thinking bad? There might be a better way to do this than an atom of sets which the finished jobs try to update themselves. It is the final part which feels like it is complecting stuff; the jobs adding themselves to the done set. Also, when the ready set is updated, new jobs should be dispatched.

Ben Sless13:05:28

Why do jobs need state? Introspection / monitoring? You have a graph of dependencies. You can "compile" the graph and make sure there are no circular or missing deps, then separately build an execution model for a graph. Once you linearize the graph via topological sort you just need a task model and pass it to any executor. Is there even a need to track the state of every node in the graph?

Scott Starkey13:05:44

Hi @UT770EY2K. I’m watching this thread, because I’m a beginner and doing exactly this! I have a dice game I’m working on, and the dice can be in various states: behind a shield, in play, or at rest. The way I handled it was to have a key map, almost exactly like you have above. Then when I move the used dice with assoc-in and update-in to :at-rest after they are used, and back to :behind-shield once they are rested. I’m not sure if this is Clojure-y or not, but it makes sense to me! 👼

Endre Bakken Stovner06:05:35

@UK0810AQ2 Yes: monitoring (dashboard), reprioritization of jobs whilst running, and probably more neat stuff I have not thought of yet.

Endre Bakken Stovner06:05:26

@UH0FTE610 Cool, but it seems like a case of newbs leading the newb XD. There might be a better way we do not know about.

Endre Bakken Stovner06:05:53

Was my question unclear/too long? Are there design patterns for this somewhere? Or was my naive suggestion actually ok and that was the reason for few replies XD

Endre Bakken Stovner06:05:56

Asked a new, simplified and more general q right now :)

Cas Shun14:05:12

Is there a function that gives me the position of regex matches? I need to split a string on regex matches and know which strings matched or not (I could use clojure.string/split, then re-run the regex on each item, but that seems inefficient?)

delaguardo14:05:42

you can use re-matcher with a bit of interop

Cas Shun14:05:35

cool idea, I'll try that thank you

delaguardo15:05:15

(let [m (re-matcher #"\d+" "qwe12345rty678ui")]
  (loop [acc [] match? (.find m)]
    (if match?
      (let [mr (.toMatchResult m)]
        (recur (conj acc {:pos [(.start mr)
                                (dec (.end mr))]
                          :res (.group mr)})
               (.find m)))
      acc)))
something like this should work

Michael Stokley14:05:00

the documentation of clojure.spec.alpha/fdef says that :args and :ret are require for :fn. is that still true?

Note that :fn specs require the presence of :args and :ret specs to
conform values, and so :fn specs will be ignored if :args or :ret
are missing.

Aleed16:05:19

say I’m creating a dynamic macro and also want to pass it a dynamic macro symbol

`(defmacro foo []
   `(~~sym ~@args))
when the macro’s symbol was coming from another package, ~~sym that worked fine. but within the same namespace (I was providing default value) I had to use ~'~sym to get it working
`(defmacro foo []
   `(~'~sym ~@args))
I imagine adding the quote tells it not to evaluate the macro, but why is the extra quote only necessary within same namespace? Is it some special case in how macro expansion is handled?

Aleed16:05:42

Also, does anyone have a better mental model for complex quoting and unquoting like ~'~ , I at first had tried '~~ thinking I wanted to first fully retrieve the symbol and then quote it, not too sure of how to think of former one.

phronmophobic16:05:08

the backtick ` operator will replace symbols with their fully qualified versions:

user> `foo
user/foo

phronmophobic16:05:46

if the symbol can’t be resolved, then it will just use the current namespace

Aleed16:05:26

the symbol I was passing was already backticked and resolved

phronmophobic16:05:03

ymmv, but in general, I probably would avoid writing macros that write macros directly

phronmophobic16:05:45

depending on the use case, there’s probably an easier way

phronmophobic16:05:01

also, ~foo gets expanded to (clojure.core/unquote foo) :

> (pr-str (read-string "~+"))
"(clojure.core/unquote +)"
I imagine that might be where your original error was originating

Aleed16:05:17

hmm i don’t think there is, the use-case is making it easier to wrap entire react libraries i.e.

(intern-comps `react-native '[View Text Input])
it needs to be a dynamic macro so that cljs arguments are pre-compiled to js ones

hiredman17:05:53

doubling up syntax quote like that is likely not required, and can be confusing

hiredman17:05:52

if you already inside a syntax quote, you can usually use a plain quote instead of adding another layer of syntax quoting

Aleed17:05:49

ah interesting, so this?

`(defmacro foo []
   '('~sym ~@args))

hiredman17:05:56

user=> (eval ``(~@[1 2]))
(1 2)
user=> (eval `'(~@[1 2]))
(1 2)
user=>

Aleed17:05:42

thanks, that’s a great tip, the extra backticks get confusing

hiredman17:05:16

dunno, I haven't read that much of what you are doing, but with no context my guess would that isn't correct

phronmophobic17:05:25

I know doing macrology with cljs can sometimes require some dark magic, but I’m still not sure why a macro that makes a macro would be necessary. i’m generally curious since it’s not totally implausible and sounds interesting.

hiredman17:05:54

usually macros that write macros can be turned into macros that expand into calls to other macros

Aleed17:05:00

i can post a link in a bit

😁 4
phronmophobic17:05:00

obviously, I understand it may be a complete waste of time for you to explain it to me

hiredman17:05:37

macros are not composable, so long term it is better to crack open a macro then to build on one. I don't write any cljs, but my general impression is it has a real problem with an abundance of macros

phronmophobic17:05:11

my reasoning for macros writing macros being unnecessary is that I can’t think of a reason why code produced via “macros -> macros -> code” couldnt’ be produced with simply “macros -> code”

phronmophobic17:05:09

cljs can complicate issues compared to clj since macros are only available at compile and manipulation of namepaces/vars programmatically is limited. if you’re trying to target bootstrapped cljs, then things get even more complicated

Aleed17:05:40

@smith.adriane yea it’s for cljs so I’m sure that complicates it. here’s the repo (still very much WIP): https://github.com/alidlo/rewrap if there’s a better way of getting to “macros -> code” I’d love to know

👀 4
hiredman17:05:25

you are mixing compile time and runtime state pretty liberally

hiredman17:05:03

at compile time/macro expand time the `rr you pass into interop/intern-comps is just a symbol

Aleed17:05:10

yes, i do fear that since i’m still new with macros I might be doing that. but I’m not sure i understand the specific issue you mentioned

hiredman17:05:18

and you are adding extra quoting to it

Aleed17:05:23

in the code itself i resolve the symbol

phronmophobic17:05:26

do you have an example of how you use the interned variables?

hiredman17:05:40

you can't pass non-literal arguments to macros

Aleed17:05:23

I quote the symbol b/c linter gives “unresolved symbol errors”

Aleed17:05:38

so i manually remove quote inside macro, and resolve symbol

hiredman17:05:56

maybe bin the linter instead

Aleed17:05:14

i use vscode with clj-kondo 😅

hiredman17:05:09

so for a macro (and this is extra true for cljs) if have (def x 1) and (some-macro x) the macro never gets 1

hiredman17:05:15

when you say (def x <any-expression>) you are defining x to be the result of evaluating the expression

hiredman17:05:27

and the result of evaluating the expression doesn't exist until runtime

Aleed17:05:32

@smith.adriane yes but example isn’t public rn, I can add one to repo later. but basically you’d use them like regular react components

hiredman17:05:21

which in the case of clojurescript, compile time is on the jvm and runtime is on a js vm, so they don't really share anything (they are kind of mixed in clojure)

phronmophobic17:05:56

so the name is interned via createElement ?

Aleed17:05:58

yeah, sorry, so in this case I don’t resolve the symbol I just add code for retrieving its component object

hiredman17:05:18

but it doesn't exist at macro expand time

Aleed17:05:22

so `rr is wrapped in this

(defn js-module* "Macro helper for getting js module, throws error if module does not exist."
       [sym k]
       `(if (goog.object/get ~sym ~k)
          (goog.object/get ~sym ~k)
          (throw (ex-info "Interned component not found" {:sym ~sym :key ~k}))))

Aleed17:05:22

thanks @borkdude will take a look. though tbh I didn’t mind manually adding the ' to the symbol

hiredman17:05:32

but that was the wrong thing to do

hiredman17:05:50

you should not being adding quoting to symbols passed to macros

borkdude17:05:17

@alidcastano hiredman is right. if you quote the symbol, the macro will see (quote a) instead of a.

hiredman17:05:37

which is why I rather flippantly suggested binning the linter

sova-soars-the-sora17:05:42

My clojurescript atom has a vector of words ["one" "word" "two" "blurred"] and i'm wondering why str/join is not making them into one string like I want.

Alex Porter17:05:10

You have to deref your atom like this:

(def my-atom (atom ["one" "word" "two" "blurred"]))
(clojure.string/join " " @my-atom)

borkdude17:05:43

i.e. a list instead of a symbol

hiredman17:05:05

because you aren't derefing the atom

Aleed17:05:22

@borkdude @hiredman I see, yeah I was incorrectly working around that, i.e. (if (and (seq? x) (= (first x) 'quote)) (second x) x)) , rather than knowing I had to configure the linter. thanks for clarifying that

hiredman17:05:08

I have started using clj-kondo a little at work, and I haven't found it to be a radical departure from other linters in that it is 80% keeping stuff tidy and 20% complaining about things that are correct the way they are

hiredman17:05:47

and that 20% is rather maddening and causes people to do things like add quoting or just declare the names of locally bound symbols in anaphoric macros

borkdude17:05:36

that's why the config exists. all the clojure code bases I've been working on have a fairly minimal config to get that 20% down to 0%

👍 4
sova-soars-the-sora17:05:46

thanks hiredman, I am derefing it with @ though. output: ["はくじょ" "が" "きく"] what i doin: (.log js/console (str/join @stale-stack-jp))

hiredman17:05:08

I just strongly suggest you never abandon your own judgment for the linters

borkdude17:05:25

yeah, agree. don't follow tools blindly

hiredman17:05:01

sova your atom contains an a vector of a vector of strings, not a vector of strings

sova-soars-the-sora17:05:13

Ah. Thanks. How can I ensure I'm not appending new vectors?

hiredman17:05:25

just don't do it

phronmophobic17:05:15

@alidcastano, going back to your question. I think you want something like:

(defmacro intern-comps [sym tags]
  `(do
     ~@(for [tag tags]
         `(defn ~tag [args#]
            (~'.apply  react/createElement  nil (~'clj->js (into [(~'js-module* ~sym ~tag)] args#)))))))
which will produce something like:
> (macroexpand-1 '(intern-comps  rn [View]))
(do
 (clojure.core/defn
  View
  [args__106650__auto__]
  (.apply
   react/createElement
   nil
   (clj->js
    (clojure.core/into
     [(js-module* rn View)]
     args__106650__auto__)))))

Aleed17:05:47

but the generated components won’t pre-compile their props/args, right? i was creating a macro for each one because their factory/compiler function is a macro

Aleed17:05:31

so for example, it could be used with Helix library $ macro, which pre-compiles components props

phronmophobic18:05:02

your code only had createElement as compiler, which isn’t a macro

Aleed18:05:25

yea just figured it’d be good to provide a default

phronmophobic18:05:37

but the intern-comps macro should still produce a function

phronmophobic18:05:48

I’m not familiar with helix

Aleed18:05:11

the example shows usage

(interop/intern-comps `rr
                       [View Text]
                       {:compiler 'helix.core/$})

Aleed18:05:31

i don’t think it can produce a function because it’s wrapping a compiler macro

Aleed18:05:00

to be clear compile-element was the compiler

Aleed18:05:05

and createElement was its generated code

Aleed18:05:18

it looks trivial in code because it’s just a default value

phronmophobic18:05:01

ok, sounds reasonable. I’m not familiar enough with the cljs compiler to know if dynamically generated macros would cause issues, but I don’t think it should.

phronmophobic18:05:37

but fwiw, I would probably do something like:

(defmacro $ [sym & args]
  `(helix.core/$ ~(js-module* ~(namespace sym) ~(name sym)) ~@args))
;; use directly
($ rn/View {:style {}} "child1" "child2")

phronmophobic18:05:01

thanks for taking the time to explain it to me. it is an interesting use case

Aleed18:05:48

yea this is definitely a convenience macro for nicer syntax i.e. (c/view (c/text "Hello")) versus ($ rn/View ($ rn/Text "Hello") , don’t want to see the $ symbols when rendering core components and of course, thanks for taking a time to look at code and always offering advice/suggestions

👍 4
sova-soars-the-sora17:05:39

I was using conj, moving to concat seems to have done the trick

Daniel Östling18:05:29

Hello, I've done something stupid and I can't figure it out 🙂

Daniel Östling18:05:07

I get a strange ns scoping error, like this:

Daniel Östling18:05:25

java.lang.RuntimeException: Unable to resolve symbol: parse-line in this context
clojure.lang.Compiler$CompilerException: Syntax error compiling at (mvp_test/core.clj:7:3).
    data: {#object[clojure.lang.Keyword 0x3bcc8f13 ":clojure.error/phase"] #object[clojure.lang.Keyword 0x6f112f70 ":compile-syntax-check"], #object[clojure.lang.Keyword 0x342723a3 ":clojure.error/line"] 7, #object[clojure.lang.Keyword 0xf3876ef ":clojure.error/column"] 3, #object[clojure.lang.Keyword 0x6b751cb1 ":clojure.error/source"] "mvp_test/core.clj"}
             clojure.lang.ExceptionInfo: Syntax error compiling at (mvp_test/core.clj:7:3).

Daniel Östling18:05:08

Code is:

(ns mvp-test.core
  (:gen-class)
  (:require [instaparse.core :as insta]))

(defn do-parse
  [line]
  (parse-line line))

(def parse-line
  (insta/parser
   "logline = #'.*'"))

(defn -main
  [& args]
  (when (empty? args)
    (do
      (println "Need line to parse")
      (System/exit 1)))
  (let [line (first args)]
    (do-parse line)))

hiredman18:05:31

compilation is single pass form at a time

Daniel Östling18:05:31

And build.boot is

hiredman18:05:03

you can't use a var before it is created (a var is what def creates)

Daniel Östling18:05:10

Hum, so order of defn matters, which is why it worked in Cider.

hiredman18:05:44

yes, if you run things in a repl in a different order than in a file you will get different results

Daniel Östling18:05:45

I think I have muscle memory to compile whole file in emacs/cider.

Daniel Östling18:05:26

Well, I did say I did something stupid 🙂

hiredman18:05:31

the other reason compiling the whole file likely worked is because dumping code into a repl over and over is not "clean"

hiredman18:05:42

state carries over, including previous definitions

Daniel Östling18:05:53

Yes, like common lisp/slime.

Daniel Östling18:05:12

Well okay, that's a lesson then. Thank you 🙂

Daniel Östling18:05:34

Also, what style guide are people using or is pretty mixed?

sova-soars-the-sora19:05:54

What's the easiest way to convert something like [:S [:ga-jar hakujo] [:verb iku]] to a map with the same keys?

dpsutton19:05:35

there are three top level forms in there. what would the output be in your mind?

sova-soars-the-sora19:05:56

good point, I would like to discard the :S and keep

{:ga-jar "hakujo :verb "iku"}

noisesmith19:05:24

(into {} (rest c))

noisesmith19:05:43

that only works with vectors though, not lists

MatthewLisp21:05:33

Hello everyone any easy way to get a random item from a hash-set ?

MatthewLisp21:05:12

(first (random-sample 0.5 #{"a" "b" "c" "d" "e"}))

MatthewLisp21:05:23

but there is a chance that this returns nothing at all ?

noisesmith21:05:38

I would use (rand-nth (vec s)) *edit - changed from seq to vec because the time-complexity of rand-nth is tied to the time-complexity of the collection

noisesmith21:05:48

I wonder if there's a cheaper way to get an ILookup sequential from a set...

MatthewLisp21:05:17

seems way way better than what i came up with

noisesmith21:05:52

in spec, if you use a set as a generator, it will pick a random item, might be worth looking up what it does

MatthewLisp21:05:53

(when-not (first (random-sample 0.5 #{"a" "b" "c" "d" "e"})) "c")

MatthewLisp21:05:16

ohhh completely forgot the spec way of doing it

noisesmith21:05:46

oh yeah you could just use spec and gen directly if you already use those :D

MatthewLisp21:05:51

i already used this in my project, but i forgot

sova-soars-the-sora22:05:12

I'd like to recursively smash something like [:ga-jar [:no-jar hakujo] [:no-jar gakkou] sensei] to become

[:ga-jar "sensei of the gakkou of the hakujo"] 
but I'm not really sure how to consume subvecs this way.

noisesmith22:05:51

@sova a description of the rule might be more helpful than a single example

noisesmith22:05:23

depending on the rules, you could use reduce on the vector, or (rseq v) to build the string

sova-soars-the-sora22:05:59

Cool! Yeah, basically I want to analyze a vector and if it has any collection of subvecs labeled :no-jar I want to read from the end and join them all with "of the" in reverse order.

noisesmith22:05:15

(->> '[:ga-jar [:no-jar hakujo] [:no-jar gakkou] sensei]
     (rseq)
     (keep (fn [t]
             (when (and (coll? t)
                        (= (first t) :no-jar))
               (second t))))
     (reduce (fn [sb e]
           (.append sb " of the ")
           (.append sb e))
         (StringBuilder.))
     (str))
" of the gakkou of the hakujo" 

noisesmith22:05:42

it's slightly easier to read if you use str instead of StringBuilder / .append

sova-soars-the-sora22:05:42

You're an amazing wizard

noisesmith23:05:14

not really - the building blocks in clojure are powerful, it just takes some practice to learn them

noisesmith23:05:06

and in well written code, the blocks and what they each do should be clear

sova-soars-the-sora23:05:13

It's really cool, I don't get how it works exactly. When the key is :no-jar, keep the val... and it appends them in reverse order?

noisesmith23:05:33

rseq is the efficient way to reverse a vector

sova-soars-the-sora23:05:33

oh (rseq) does that I presume. Thread-thru..

noisesmith23:05:56

keep is just like map, but it throws away nils

noisesmith23:05:05

so it's like map + filter in one

sova-soars-the-sora23:05:08

Cool cool. Hmm. This is very good. I have other blocks that may or may not need this logic, I have to figure out how to work this into my rule set

noisesmith23:05:11

and the reduce call uses the StringBuilder to construct the result as it sees the inputs

noisesmith23:05:07

sometimes it helps to write them separately and then make the parts that look similar identical, until you find things that can just be merged / reused

raspasov23:05:54

@noisesmith Reading the Java docs, StringBuilder can be faster, but from a simplicity standpoint and if performance is not critical, maybe we can just concat strings instead? (str str-1 str-2) etc

noisesmith23:05:42

@raspasov yeah, that's true, using str does lead to slightly simpler code in the reduce

noisesmith23:05:35

that part becomes

(reduce (fn [s e]
          (str s " of the " e))
        "")
then you don't need the final str call

👍 4
raspasov23:05:41

Yea, either way not a huge difference 🙂 just more of a “pure” Clojure approach

sova-soars-the-sora23:05:43

Still need to keep the very last element of the vector

noisesmith23:05:16

yeah - I literally followed the behavior you described rather than guessing rules to construct your example result

sova-soars-the-sora23:05:17

makes sense to add a new conditional to (when ) ?

noisesmith23:05:56

you might need some sort of polymorphism around the handling of elements - where you need different combining rules / different extraction rules for each one

noisesmith23:05:25

so instead of using keep before reduce, you'd use a cond or multimethod inside the reducing function to handle each case

noisesmith23:05:42

and you might also need to keep track of the previous rule / previous token for some rules...

sova-soars-the-sora23:05:04

I have some simple rules figured out, but a couple of them can be recursive and it's not clear how to do vector surgery quite yet, but what you have said so far helps quite a bit

noisesmith23:05:11

one pattern if you always need to see the previous token: (partition 2 1 (cons ::empty coll))

noisesmith23:05:31

where ::empty could be nil, or any other preferred indicator that there's no "previous" item for that input

sova-soars-the-sora23:05:04

neat pattern, not sure what it means

noisesmith23:05:04

user=> (partition 2 1 (cons ::empty '[:ga-jar [:no-jar hakujo] [:no-jar gakkou] sensei]))
((:user/empty :ga-jar) (:ga-jar [:no-jar hakujo]) ([:no-jar hakujo] [:no-jar gakkou]) ([:no-jar gakkou] sensei))
you end up with a collection of pairs, each one is the previous item, followed by current

sova-soars-the-sora23:05:32

ohh neat. partition size 2 step 1

noisesmith23:05:09

right, and using cons to prefix, to signify that the first item doesn't have a previous item

noisesmith23:05:54

if you need 4 tokens at a time to know the true context, instead of 2, you could use (partition 4 1 (concat [::empty ::empty ::empty] coll)) - the rule being that for partition of size N you need N-1 empty elements to accurately represent the state

raspasov23:05:10

@sova I would say, if an algorithm is becoming too complex, it might be worth re-examining the shape of the data structure you’re dealing with and if there’s a way to structure your data to make your life easier in the first place

raspasov23:05:15

There was a quote by someone, not sure who, but basically it said something like “figure out your data structure and then the algorithm is trivial”

sova-soars-the-sora23:05:22

that's a good point, I can render it to a nested map structure, but I feel like it's more or less the same acrobatics. Maybe not.

raspasov23:05:39

Yea… nested map can probably help… when I start reaching for things like “previous/next” data via (partition … etc) or some sort of reduce, it’s a sign that there’s too much acrobatics 🙂

sova-soars-the-sora23:05:58

Hmm. I wonder if the output of instaparse can be flattened rather than nested. If it can be flattened with a rule it would save me a lot of brain.

sova-soars-the-sora23:05:18

Right on, yeah it's easier when the drawers are labeled

raspasov23:05:23

What does that output look like?

sova-soars-the-sora23:05:20

The current output is a nested vector, [:S [:ni-jar gakkou] [:verb iku]] the map output is quite verbose, but it has everything associated perfectly {:content ({:tag :ga-jar, :content (hakujo)} {:tag :wo-jar, :content (kasa)} {:tag :verb, :content (ageru)})} Not sure why it decided to put parens around all the terms...

sova-soars-the-sora23:05:36

(those are not identical datas)

raspasov23:05:18

parens is probably because it’s a sequence, not a vector

sova-soars-the-sora23:05:21

So it's passable to use the vectors for simple stuff, but then I wanted to handle some nested types, which just get smooshed with string concatenation really,

noisesmith23:05:22

I'm going to politely disagree with @raspasov here. If you want to visit each element of a sequential input in order to build a result, reduce is precisely what you want. If the combining rule is effected by the previous item, you either want to use pairs to track each item's previous, or hold that as part of the accumulated state. Neither of these are signs of problems.

raspasov23:05:39

@noisesmith fair 🙂 I won’t disagree; it was more of a general observation; I don’t have a full grasp of the data shape/problem we’re dealing with here

sova-soars-the-sora23:05:15

Here's a concrete example

sova-soars-the-sora23:05:47

input: [:S [:ga-jar [:no-jar gakkou] [:no-jar sensei] hanashi] [:verb sabishii]] desired result: [:S [:ga-jar "hanashi of the sensei of the gakkou"] [:verb "sabishii"]]

raspasov23:05:52

@sova can you paste an example of where we won’t be doing the transformation? Or we always have the :no-jar tag?

sova-soars-the-sora23:05:38

no transform required: [:S [:ga-jar hakujo] [:ni-jar sensei] [:wo-jar question] [:verb kiku]]

raspasov23:05:30

;transform
[:S 
 [:ga-jar [:no-jar gakkou] [:no-jar sensei] hanashi]
 [:verb sabishii]]
;no transform
[:S 
 [:ga-jar hakujo] 
 [:ni-jar sensei] 
 [:wo-jar question] 
 [:verb kiku]]

raspasov23:05:37

Is that correct?

raspasov23:05:47

(split by line so it’s easier to look at)

sova-soars-the-sora23:05:07

Essentially any vector that has more than two elements [:key and val] needs to be smashed to have just key and val.

sova-soars-the-sora23:05:00

It's a fun puzzle. Recursive ish

raspasov23:05:25

And they can be arbitrarily nested ?

raspasov23:05:33

Or that’s the deepest they go?

sova-soars-the-sora23:05:21

that is actually the deepest they go.

sova-soars-the-sora23:05:35

there could be many [:no-jar vecs] but all that depth