Fork me on GitHub
#beginners
<
2020-01-28
>
bocaj00:01:35

I’m working with 150MB file, so I used powershell to pipe the first 10 lines to a test file, and this changed formats. I’ll report if I can explain what’s going on their. Got the wiki articles up now, thanks for your thoughtful response!

andy.fingerhut00:01:00

Yeah, not surprising if inserting another tool in the middle, especially one that does its own independent guessing/interpretation of input file format, and choice of encoding when writing an output file, causes different results than when the intermediate step is removed.

bocaj00:01:29

Right…didn’t realize head would be unsafe in powershell

andy.fingerhut00:01:29

You can wish/hope that such intermediate tools preserve the text encoding in all cases, but I do not think that is the case, and some are even designed to help you convert between text encodings.

bocaj00:01:34

:encoding "UTF-16" would be the solution

dpsutton00:01:33

Isn’t that the default encoding on .net?

bocaj00:01:17

Yeah, I just didn’t realize powershell changed encoding while reading some lines and piping out. Hind-sight I guess I could have assumed this. Otherwise how would powershell know what a line is.

dpsutton00:01:02

Ha and it makes sense seeing all the spaces between letters too. Hindsight

bv01:01:50

@seancorfield Could not find artifact org.ode4j:core-cpp:jar:pom:0.4.0 in central () ; sadly, this is what drove me to extension in the first place, and so i'll let the thread drop.

fabrao02:01:44

Hello all, how do I interop with this?

glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
			if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
				glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
		});

noisesmith02:01:42

you need to figure out what class those static methods are on first

fabrao02:01:25

I saw something by using reify but no interface to implement. Is it not like anon function:

noisesmith02:01:55

(Foo/glfwSetKeyCallback window (reify SomeClass (someMethod [window key scancode action mods] (if ... (Bar/glfwSetWIndowSHouldClose window true)))))

noisesmith02:01:28

it's an anonymous inner class, the method sig is used to figure out what it implements

fabrao02:01:33

I tried

(fn [window key scancode action mods]
                                    (when (and (= action GLFW/GLFW_RELEASE) (= key GLFW/GLFW_KEY_ESCAPE))
                                      (.glfwSetWindowShouldClose window true)))
but no way

noisesmith02:01:57

a function won't work, you need to implement a specific class or interface

fabrao02:01:14

@FunctionalInterface
public interface GLFWKeyCallbackI
extends CallbackI.V

noisesmith02:01:20

java lambdas are a lot like clojure functions, but they were implemented after clojure, so clojure's functions are not compatible

fabrao02:01:27

void (*) (
     GLFWwindow *window,
     int key,
     int scancode,
     int action,
     int mods
 )

noisesmith02:01:35

that looks right

fabrao02:01:54

right for what?

noisesmith02:01:54

you need to reify that, and put the code in its method

noisesmith02:01:14

the GLFWKeyCallbackI interface

fabrao02:01:36

what´s the method´s name?

noisesmith02:01:33

but really you need to find the doc or implementation for that class (if you use an ide it will look that stuff up, and most java ecosystem assume you are using a tool that does that stuff for you since it's so tedious)

ghadi02:01:09

you got multiple levels of things going on here a C library wrapped by some Java interfaces/classes,

ghadi02:01:25

then wanting to reify those interfaces from Clojure

ghadi02:01:04

start here:

ghadi02:01:18

figure out what the 2 arguments to glfwSetKeyCallback are

ghadi02:01:27

first one is "window", whatever that is

ghadi02:01:00

second one is a Java lambda, which is essentially a syntax sugar for an anonymous implementation of some interface

ghadi02:01:21

if you look at the javadoc of glfwSetKeyCallback, the second argument will be some interface

ghadi02:01:36

there will only be one possible method on that interface

ghadi02:01:01

(Java lambdas are known as "SAM"s - single abstract method)

noisesmith02:01:04

oh yeah I got lambda / Function conflated, and the method you want really isn't apply

ghadi02:01:32

yeah Function doesn't have anything to do with this, except that Function is one of many SAM interfaces

fabrao02:01:33

Can't define method not in interfaces: apply` 

ghadi02:01:53

please don't paste error messages without pasting the full text of what you tried

noisesmith02:01:03

yeah - you really need to find the classes these static methods are on, and their docs

ghadi02:01:26

s/static/instance/

ghadi02:01:50

never mind you were talking about GLFW/glfwSetKeyCallback

noisesmith02:01:18

I think @fabrao was confirming what you said already

fabrao02:01:31

(GLFW/glfwSetKeyCallback window (reify GLFWKeyCallbackI
                                    (apply [this window key scancode action mods]
                                      (when (and (= action GLFW/GLFW_RELEASE) (= key GLFW/GLFW_KEY_ESCAPE))
                                        (.glfwSetWindowShouldClose window true)))))

fabrao02:01:54

Caused by java.lang.IllegalArgumentException
   Can't define method not in interfaces: apply

ghadi02:01:08

thanks -- now look up GLFWKeyCallbackI

ghadi02:01:33

it is an interface with a single method

jakubl02:01:11

quick question why does the libspec in require need to be quoted (require '[compojure.core :as c]) -> ok (require [compojure.core :as c]) -> syntax error

noisesmith02:01:39

@jakub696 require isn't a macro, and those things don't exist until you load them, so quoting allows you to use a data structure of symbols to ask for them

jakubl02:01:22

hm but it's just a vector - why does it get evaled - and why is the same syntax fine when used in ns?

noisesmith02:01:44

everything in clojure is evaluated, unless a macro or quote prevents evaluation

noisesmith02:01:00

ns is a macro, and knows the args to require need to be quoted, so does that for you

jakubl02:01:28

ah interesting

jakubl02:01:48

thanks will ponder this - but yes forgot things get evaled - always

noisesmith02:01:08

some things evaluate to themselves - :a, "foo", 1

noisesmith02:01:27

and [], {}, #{}

jakubl02:01:51

so really [1 2 3] is just syntax sugar for (vector 1 2 3)

noisesmith02:01:00

but symbols and lists cause further simplifications - symbols are "looked up" to find a substitute value with that name, and lists execute their first item with the rest as args

jakubl02:01:42

ok thanks this helps a bunch

noisesmith02:01:52

so the result of evaluating (vector 1 2 3) is to resolve vector, finding a function in clojure.core, and then calling it with the other args

noisesmith02:01:07

this results in the same thing you'd get with the literal input of [1 2 3]

noisesmith02:01:10

glad I could help

jakubl02:01:29

got it and since in my case the symbol compojure.core did not exist - exception was thrown

noisesmith02:01:03

it's actually refreshing how simple and consistent this is - almost always if you see symbols you know they get looked up from innermost context outward, and if you see parens you know something is executing

📝 4
noisesmith02:01:16

right, exactly

👍 4
ghadi02:01:12

if so, it will be (reify GLFWKeyCallbackI (invoke [this window key scancode action mods]....))

fabrao02:01:42

many thanks

ghadi02:01:50

the formula to pass Java lambdas:

ghadi02:01:07

find the interface that the lambda is sugar for

ghadi02:01:17

(this will be in the java apidocs)

ghadi02:01:38

look up that interface's only abstract method

ghadi02:01:44

reify that interface and fill in the method

fabrao02:01:05

I know that reify that interface and fill in the method

fabrao02:01:30

but about function interface not

Oz09:01:59

I wonder what I'm doing wrong about using the REPL

yuhan09:01:04

The double-colon syntax is used for namespace aliased keywords, so the error is telling you that there's no subs alias in the current namespace

💡 4
Oz09:01:23

thank you!

yuhan09:01:55

(keyword ":subs" tag) constructs an "invalid" keyword, or at least one that can't be entered as a literal in the way you expect

Oz10:01:49

It's really confusing, because when I pass the literal ::subs/tag1 it works. the result of (keyword ":subs" "tag1") is ::subs/tag1

Oz10:01:20

But like you said, using the function inside reg-sub doesn't work

yuhan10:01:18

I assume you required crystal-web-client.subs :as subs ?

Oz10:01:41

so maybe (keyword "crystal-web-client.subs" tag) would work?

yuhan10:01:55

yes, that's because the :: notation expands to the full ns name at read time

yuhan10:01:01

note that the keyword and symbol functions don't validate their arguments, so you can make all sorts of illegal values without it throwing an error

Oz10:01:28

Thank you, now it makes sense And that sounds like a helpful tip

yuhan10:01:31

(keyword "many words with spaces" "and (delimiters??? [}")
;; => :many words with spaces/and (delimiters??? [}

Aviv Kotek12:01:45

What's the idiomatic way of using constants? I have seen the use of CONST: (def ^:const url "http://...", as ex-java this would suffice, is there any alternatives?

Mattias13:01:07

Forgive me now, but... aren’t all defs pretty much constant? As another ex Java dude I haven’t worried too much 🙂

kelveden13:01:07

Yeah, I'd say that's a good default assumption in Clojure: all vars are immutable. I've never used ^:const myself mainly for that reason in fact but I do seem to recall reading somewhere that there was some oddness to it too - it doesn't necessarily behave as you'd expect.

kelveden13:01:50

Just had a quick experiment to remind myself: it doesn't stop you redefining the var further down but any references to the var in functions do not change as a result.

kelveden13:01:06

So for example:

(def ^:const x 1)
(defn prn-x [] (prn x))
(def ^:const x 2)
(prn x)
=> 2
(prn-x)
=> 1

Aviv Kotek13:01:36

yep, that seems better, so this would be the 'right' way

kelveden13:01:12

I think the trick is to stop thinking in java terms of "constants" and "variables" and embrace the idea that everything is immutable. Generally in Clojure a var that represents something that is expected to change is wrapped in an atom (or ref potentially).

👍 8
Aviv Kotek13:01:28

And if it's reused by many functions in the program?

Aviv Kotek13:01:09

I won't need any 'state' mechanism here (atom, ref, etc)

kelveden13:01:17

Correct. Atoms, refs etc should be considered if the value is going to change. This discussion explains them very well: https://stackoverflow.com/questions/9132346/clojure-differences-between-ref-var-agent-atom-with-examples

noisesmith17:01:03

another thing to consider is that you can replace a def with a let, or a function arg, and that often leads to cleaner, more functional code

noisesmith17:01:34

there's a truism in the compiler design world that every global is an invisible arg to every function in the codebase

👍 4
Aviv Kotek13:01:12

@mattias504 replied on the thread

bronsa13:01:18

^:const is a hint to allow the compiler to inline certain constant literals, namely strings and numbers

👍 4
bronsa13:01:36

it should not be used liberally and doesn't mean the same thing as e.g. const in C++

dharrigan14:01:01

I used to use ^:const but I stopped given the reasons above - vals are immutable, so it's implicit

dharrigan14:01:41

question is, what's the favourite - to uppercase the def val, or to keep it lowercase? 🙂

Alex Miller (Clojure team)15:01:15

I just don't use def at all, then you don't have to decide :)

Alex Miller (Clojure team)15:01:31

(not really, but minimizing use is imo a good idea)

Alex Miller (Clojure team)15:01:44

and I think lower is fine, what is this, Java?

4
😁 4
😂 8
😛 4
bronsa15:01:05

just wrap your whole namespace in a let and you'll never need a def

😄 4
aw_yeah 4
dpsutton15:01:32

that's the javascript solution IIFE!

dharrigan15:01:50

I've started to favour keeping the defs lowercase (I don't use that many @alexmiller honest!) as well, as above, it's implicit and seems more clojure-ish!

jakubl15:01:41

curious - beginner myself i would be inclined to do this in my code (def tax_rate 0.13) - is that wrong?

Alex Miller (Clojure team)15:01:12

(and this really is the case where inlining via ^:const makes sense)

Alex Miller (Clojure team)15:01:20

it's really stateful or side-effecting defs that are problematic

Mattias15:01:00

What’s keeps that def from being optimized/inlined/otherwise tricked with (without const)?

Alex Miller (Clojure team)15:01:46

vars are stateful and require dereferencing (they can change)

dharrigan15:01:47

One of the things I had to understand is that when playing with the repl, you can define a var, i.e., (def foo "hello") then eval it foo, then change it (def foo "goodbye"), eval it and it'll print out goodbye - from my understanding is we all talk about Clojure having immutable values, but those values can change if confined to the same thread, thus foo can be redefined since it's in the same thread. Have I got that hopelessly wrong?

Alex Miller (Clojure team)15:01:21

immutable values are good

Alex Miller (Clojure team)15:01:55

vars created with def however are stateful identities that refer to an immutable value

Alex Miller (Clojure team)15:01:09

and can be changed to refer to a different immutable value

Alex Miller (Clojure team)15:01:49

and this is not confined to a single thread; vars are global and can be redefined from any thread (as can all stateful values, if you have a reference to them).

dharrigan16:01:22

so, is the var acting like a pointer to an immutable value, and that var pointer can change to another immutable value?

dharrigan16:01:27

tbh, it's one of the first questions that I'm asked when I demostrate clojure at the repl, and I haven't given a good answer yet (as I'm still very much a newbie), when I def a var, then show how I can redef the var and the person asks "I thought Clojure values were immutable, how come the var has changed?"

chepprey16:01:38

In Java, you can demonstrate mutability by having a reference to an object. Date d = new Date(); and you can then d.mutateTheValue(...) and, the critical thing here is, without changing the instance of the object that d references, you have mutated the value it's referencing - the instance of the Date was changed. That can't happen in Clojure.

dharrigan16:01:40

That's a good thing to show to people. I'll add that to my demonstration :)

Alex Miller (Clojure team)16:01:54

the var is a box - it holds an immutable value

Alex Miller (Clojure team)16:01:06

but you can put a different immutable value in the box

Alex Miller (Clojure team)16:01:33

(def a 1) means "declare a, in this namespace, to refer to a var, that holds the value 1"

Alex Miller (Clojure team)16:01:28

the key thing is that we have separated the identity (a), from the state (the var), from the value (1)

Hagenek16:01:23

Hi guys. I am trying to slurp some json data, and I get the following error: No such namespace: http: Any idea why? Also, is there a specific way to “connect” to an API, or do you just slurp URLS and convert them from JSON -> CLJ?

Andrew16:01:48

@georghagen sounds like you've missed the string delimiters around your URL

Andrew16:01:41

(slurp )
Syntax error compiling at (REPL:1:1).
No such namespace: http:
should be
(slurp "")

chepprey16:01:53

@dharrigan here's a nice little demo to show Java-minded folks, an example showing how even aggregates/collections/"objects", in Clojure, are immutable (and how this same exercise would end up with a different result in a similar Java scenario):

user=> (def v1 {:type :famous-person :fname "Luke" :lname "Wilson"})
#'user/v1
user=> (def v2 v1)
#'user/v2
user=> v1
{:type :famous-person, :fname "Luke", :lname "Wilson"}
user=> v2
{:type :famous-person, :fname "Luke", :lname "Wilson"}
user=> (assoc v1 :lname "Skywalker")
{:type :famous-person, :fname "Luke", :lname "Skywalker"}
user=> v1
{:type :famous-person, :fname "Luke", :lname "Wilson"}
user=> (def v1 (assoc v1 :lname "Skywalker"))
#'user/v1
user=> v1
{:type :famous-person, :fname "Luke", :lname "Skywalker"}
user=> v2
{:type :famous-person, :fname "Luke", :lname "Wilson"}
user=> 

4
Hagenek16:01:55

Screenshot 2020-01-28 17.19.30

Hagenek16:01:06

nvm you were correct. I had done it somewhere else in the editor

👍 4
Andrew16:01:21

@georghagen regarding connecting to an API, I use clj-http (https://github.com/dakrone/clj-http). When Cheshire (https://github.com/dakrone/cheshire) is also on the classpath you can get it to convert JSON responses into Clojure maps, like so (clj-http.client/get "http://...." {:as :json}) (https://github.com/dakrone/clj-http#output-coercion)

Hagenek16:01:33

Thank you so much. Now I just have to figure out what to call, and then how to work with data given to present it nicely!

mloughlin17:01:21

clj-kondo issues me a warning about my horrible fn:

(defn- safe-read-int [^DataInputStream dis]
  (try
    (.readInt dis)
    (catch .EOFException e nil)))
I'm calling (when-let [n (safe-read-int dis)] ... ) Can anyone help me write this in a better way?

noisesmith18:01:36

(catch .EOFException _) would do the same thing (_ is idiomatic for a value that's ignored, and an empty body always returns nil)

noisesmith18:01:59

the problem about returning nil (which you mention elsewhere) would come down to the consumer of this code needing a nil check now - you could take an extra "default" arg and return that on eof (a summing function could pass 0, a product function 1, etc.)

noisesmith18:01:18

or maybe an EOF is a bigger problem, and that NPE is helpful :D

mloughlin18:01:38

in this case I'm expecting an EOFException eventually (this might be incorrect usage of DataInputStream, I'm not a Java person). I'm essentially replacing readLine in the clojure.core line-seq source with (.readInt dis)

noisesmith18:01:48

in that case another option is (try [(.readInt dis)] (catch EOFException _)) and then concat at the top level - the nil safely disappears

noisesmith18:01:02

(due to coercion by concat of nil to empty)

mloughlin18:01:41

interesting, I'll see if it fits my use. thanks!

noisesmith19:01:52

this demonstrates the principle

user=> (mapcat #(try [(Long/parseLong %)] (catch Exception _)) ["1" "2" "3" "elephant" "4"])
(1 2 3 4)

noisesmith19:01:00

it might be nice to wrap the idiom in a macro - perhaps

(cmd)user=> (defmacro successes [body coll] `(mapcat (fn [x#] (try [(~@body x#)] (catch Exception _#))) ~coll))
#'user/successes
(ins)user=> (successes (Long/parseLong) ["a" "1" "2" "b" "3"])
(1 2 3)

borkdude17:01:04

@michael.e.loughlin what warning do you get?

mloughlin17:01:29

it's about the unused symbol e

delaguardo17:01:51

replace it with _e

👍 4
mloughlin17:01:07

but it drew my attention to the function, and it made me wonder if returning nil from a catch is Not Good

mloughlin17:01:28

in %IMPERATIVE_LANG% that's a no-no 🙂

borkdude17:01:54

@michael.e.loughlin as always, "it depends"

mloughlin17:01:55

I won't dwell on it then! Thanks @delaguardo and @borkdude

telekid17:01:33

Why does (set! **print-length** 10) work, but this raises:

(def ^:dynamic d)
(set! d 1)
;; Can't change/establish root binding of: d with set

hiredman17:01:43

because the repl establishes a root binding for a number of vars like print-length

telekid17:01:23

cool cool, thank you! I suspected that might be the answer.

Hagenek17:01:24

I am trying to extract the value associated with a keyword in a file of .clj, but it only returns to me the key, not the value.

(def return-station-id #(get-in % [:station] :station_id))
(map return-station-id si)

Example of data in the .clj: {:stations [{:station_id "1755", :name "Aker Brygge",

seancorfield17:01:59

@georghagen Your data structure has :stations (plural) but your code has :station singular. So it doesn't find a match and returns the "not found" value -- :station_id -- the third argument to get-in

Hagenek17:01:38

thanks. Now I just get: clojure-noob.core> (map return-station-id si) (nil nil nil nil nil nil nil nil nil nil nil nil)

seancorfield17:01:27

No, (map :station_id (:stations si))

seancorfield18:01:07

If si is {:stations [{:station_id "1755", ...} {...} {...}]}

seancorfield18:01:33

(:stations si) would be the vector of station information and you map over that to extract the :station_id

Hagenek18:01:35

si is the file containing the information about the stations yes.

Hagenek18:01:29

(map :station_id (:stations si)) returns an empty list

seancorfield18:01:08

user=> (def si {:stations [{:station_id "1755", :name "Aker Brygge", :other "data"} {:station_id "1234", :name "Station 2"}]})
#'user/si
user=> (map :station_id (:stations si))
("1755" "1234")
user=> 

seancorfield18:01:19

So it depends what exactly is in your si

seancorfield18:01:46

If you're getting an empty list, si is not the data you showed but something else.

Hagenek18:01:30

{:stations [{:station_id “1755”, :name “Aker Brygge”… } {station_id “1023 :name “Tollerud…}]

Hagenek18:01:34

This is the structure

Hagenek18:01:35

The value tied to :station is a vector of maps

seancorfield18:01:41

If si is that data structure then (:stations si) is the vector, and (map :station_id (:stations si)) will give you ("1755" "1023" ...)

Hagenek18:01:38

Im going to try restarting the repl and the editor

Hagenek18:01:17

Ok, so when I define the contents of the file to a value sykkelinfo in the repl, and then call (map :station_id (:stations sykkelinfo)), it works. But it does not work on the file. But when I (slurp si) it gives the contents of the file. Strange.

Hagenek18:01:11

Maybe it is accessed as a string

Hagenek18:01:55

nah that is just the slurp command, when I open the file in emacs its formatted correctly as a map with a key which has a vector of maps as a value

seancorfield18:01:10

If you just slurp the file, you get a string, not a data structure. You need to call clojure.edn/read-string on that to produce a data structure. Sorry, I assumed you were already doing that.

seancorfield18:01:23

add [clojure.edn :as edn] to your code's :require and then you can use (edn/read-string (slurp the-file)) to get a data structure back

seancorfield18:01:51

(you should always require namespaces before use, even if they happen to be auto-loaded)

Hagenek18:01:55

Ah ok cool

Hagenek18:01:37

(def si-data-structure(edn/read-string (slurp si))) then (map :station_id (:stations si-data-structure)) And it worked! Thank you so much.

4
seancorfield17:01:16

I think you want (map :station_id (:stations si)) ?

jakuzure20:01:18

hey, how would you recommend limiting api calls? the limit is 5 per minute/500 day, but not sure how to do it? write the result of every call to a db with timestamp and check when the last call was?

kenj20:01:12

At work, we use Redis to store state per session to make sure a quota is not exceeded. Redis makes a lot more sense than a DB I think.

jakuzure21:01:37

Will have a look, thanks

seancorfield21:01:07

Redis docs actually have an example of writing a rate limiter, based on IP address I think.

seancorfield21:01:17

But also watch https://www.youtube.com/watch?v=m64SWl9bfvk as a caution about rate limiting.

😀 4
Ian Fernandez22:01:21

how I can do this

(defn -main [& _] 
  (loop [line (read-line)]
    (when-not (nil? (parser line))

      ....

      (recur (read-line)))))

Ian Fernandez22:01:44

to read a line and in every interation add a line to a vector?

Ian Fernandez23:01:05

(defn -main [& _] (loop [line (read-line) p (conj [] line)] (when-not (nil? (parser line)) (println (conj p line)) (recur (read-line) (conj p line)))))

noisesmith23:01:11

@d.ian.b (loop [lines []] (when-not .... ... (recur (conj lines (read-line))))

Ian Fernandez23:01:54

I want to send to bash send-> 1 return-> [1] send-> 2 return-> [1 2]

noisesmith23:01:46

this loop adds an element to lines each time it recurs

noisesmith23:01:57

it gets each new line via read-line

noisesmith23:01:57

(ins)user=> (defn -main [] (loop [lines []] (println 'lines lines) (print "input> ") (flush) (let [l (read-line)] (if (seq l) (recur (conj lines l)) lines))))
#'user/-main
(cmd)user=> (-main)
lines []
(ins)input> a
lines [a]
(ins)input> b
lines [a b]
(ins)input> c
lines [a b c]
(ins)input> 
["a" "b" "c"]
*replaced example so it has a prompt

noisesmith23:01:18

on the last line it's hard to see, but I just hit return without typing, so it returned the vector of lines

Ian Fernandez23:01:00

the problem is, I have a reduce function and every input on bash I want to reduce a state again

Ian Fernandez23:01:38

and send to terminal after each interation

noisesmith23:01:44

OK - at each step of that loop you have the full vector of inputs so far, so youu can reduce over it or whatever

noisesmith23:01:01

and it currently prints them all

Ian Fernandez23:01:31

I made-it here

(defn -main []
  (loop [m {::st  {}
            ::txs []}]
    (let [l (read-line)]
      (when (string? l)
        (let [result (m->output m)]
          result
          (recur (transact m (parser l))))))))

Ian Fernandez23:01:46

but somehow it prints the last state on the app

Ian Fernandez23:01:50

not the current

noisesmith23:01:49

that line result is a no op, it literally does nothing at all

noisesmith23:01:46

what do you expect that line to do?

Ian Fernandez23:01:58

m->output is the function that prints

Ian Fernandez23:01:31

I want to print the current state off the app

noisesmith23:01:33

OK, what I'm saying is you have (let [v (f)] v y) - the part where v is before y does nothing

noisesmith23:01:46

it's the same as (let [v (f)] y)

noisesmith23:01:01

which is the same as (do (f) y)

noisesmith23:01:08

since v is never used

noisesmith23:01:18

and when has an implicit do, so you can replaace that when block with:

(when (string? l)
  (m->output m)
  (recur (transact m (parser l))))

Ian Fernandez23:01:15

it's printing the last m, not after the recur

noisesmith23:01:16

and I strongly suspect you actually want

(m->output m)
(when (string? l)
  (recur (transact m (parser l))))

noisesmith23:01:35

I'm showing you how to simplify the code so the error is easier to see

Ian Fernandez00:01:02

on the first iter, its printing {}

Ian Fernandez00:01:00

but understood

noisesmith23:01:58

there's no need to have read-line twice, unless there's a special case where the first line is used even if it's not parsed

noisesmith23:01:18

also, consider that when-not makes the whole block return nil instead of a vector as soon as a line isn't accepted

noisesmith23:01:36

(this might be OK, maybe the vector is only used inside the loop)

noisesmith23:01:12

also, idiomatically (when-not (nil? ...) ...) is just (when ... ...) - nil is falsey

noisesmith23:01:53

(unless parser returning false is still acceptable, but I'd still avoid double negation (when (some? ...) ...) )

Alex Miller (Clojure team)23:01:37

psst when-some is a thing

💯 4
Alex Miller (Clojure team)23:01:18

(and it's a homonym for winsome, which I really don't think is well enough appreciated)

🤯 4
pizzaspin 4
noisesmith23:01:47

I would have suggested when-some but the origiinal did not bind the return value of the parser

Ian Fernandez23:01:31

I made-it here

(defn -main []
  (loop [m {::st  {}
            ::txs []}]
    (let [l (read-line)]
      (when (string? l)
        (let [result (m->output m)]
          result
          (recur (transact m (parser l))))))))