Fork me on GitHub
#beginners
<
2018-03-10
>
mfikes02:03:52

@kara I wonder if (or (not-empty a) b) matches the semantics you need

Kara02:03:13

I mean... I think something like (if (not-empty a) a b) would probably do it? but I guess I was just wondering if there was something "fancier"... maybe that is the best solution tho?

Kara02:03:05

This ties into my enviornment problem from #cljsrn ...essentially if I have local path set I want to use that... otherwise I want to use the library default

mfikes02:03:09

@kara (not-empty a) returns a if it is not empty, so (or (not-empty a) b) is the same as your if construct

Kara02:03:35

ooh... see that's the fanciness I meant πŸ˜‰

yogidevbear08:03:59

Morning :) I'm trying to find a way to get a collection of grouped subsets from a sequence. For example, if I have (range 16) to represent a 4x4 grid:

(0   1   2   3
  4   5   6   7
  8   9  10 11
12 13 14 15)
I'd like to group this into 4 subsets (mini 2x2 grids) of:
(0 1 4 5)
(2 3 6 7)
(8 9 12 13)
(10 11 14 15)

pooboy10:03:56

Suppose i.have a basket: (def basket '() ) I want to add cornflakes to it : (conj bakset "cornflakes")

pooboy10:03:18

But my basket remains empty - Immutable data structures .

pooboy10:03:53

So how I add items to my basket ? Should I make a new basket everytime ,I want to add a item ?

matan11:03:12

I seem to spend 50% of the time spent on recatoring my clojure libraries, on figuring out the shapes of function arguments

matan11:03:29

In shapes I mean, is it a string or a vector, is it a map with this structure or that

matan11:03:41

In short, all the things that Java/Scala would save you from

matan11:03:49

Questions: πŸ™‚

magra11:03:55

@yogidevbear maybe partitionis what you are looking for.

matan11:03:00

1. What would you suggest as a workflow or tooling, to reduce this inefficient programming experience? 2. Would you say clojure and myself are not a fit, if I don't recall by heart the shapes?

magra11:03:11

@matan If I create a function that takes a list of strings I write it (def myfun [los] (do-something...)). I know los is short for list of strings.

matan11:03:07

@magra yeah, it gets more complicated when the data types are more composite though

matan11:03:33

I guess that's why spec creeped in into the language, because it's impossible to track your shapes/types when revisit a library for refactor

matan11:03:38

and then it all gets more cumbersome than writing typed code in a typed language, in a way

magra11:03:25

@suryapjr jep. Most of the time I store all state in a single atom. The basket is then in the atom. In the atom you swap! the basket for a new one.

pooboy12:03:00

Can anyone give a simple example of an atom

pooboy12:03:12

Im having trouble getting it

joelsanchez12:03:36

following your example, suppose you have a basket: (def basket (atom [])) you want to add cornflakes to it: (swap! basket conj "cornflakes") now the atom contains the basket with the cornflakes: @basket

joelsanchez12:03:43

and @magra beat me to it πŸ™‚

magra12:03:52

@joelsanchez I like your vector better though πŸ˜„

pooboy13:03:10

So an atom is mutable

magra13:03:28

In a controlled sort of way, yes.

magra13:03:01

Or think of it as a place where you lock away your mutable state.

magra13:03:55

In the example above you can add the cornflakes to the basket without knowing what is already in the basket, or whether another thread just added something to the basket or removed something etc.

magra13:03:47

You don't have to care, you will not collide with the other thread.

pooboy14:03:16

Is there a way to know the available functions for a data structure ?.. Like for example ..I have a list (def a '() ) Now I want to know the available functions for the list like conj .. can clojure show it in the REPL? Like we have methods? In ruby

yogidevbear14:03:35

Thanks Markus. Not sure if that will suit though. The dimensions might be variable (e.g. 6x6 grid or 8x8 grid etc)

yogidevbear14:03:37

I was looking into partition, but gets a bit complicated, esp. with a larger grid

pooboy14:03:09

@magra cool stuff ! Thanks !

noisesmith14:03:37

@yogidevbear partition is actually simple and it describes exactly the task you are attempting

noisesmith14:03:56

if you don't know the total collection is an exact multiple of your input, use partition-all instead

magra14:03:09

@suryapjr You can treat a list as a collection or as a seq (or a stack). So what you find under seq aplies as well.

noisesmith14:03:18

you don't need to worry about the skip arg since you don't want to repeat or skip items

pooboy14:03:43

@magra grasping collections finally!

pooboy14:03:59

@magra ..atoms are still a bit blurry :)

noisesmith14:03:35

and @suryapjr @magra an atom in a def works but typically isn't the best option, for functional code the ideal is to take a basket as an argument to your function, return a new basket from the function, then use that return value from the caller

pooboy14:03:07

Ummmm.. @noisesmith..a simple example would be very helpful !

noisesmith14:03:35

(defn purchase [basket item] (conj basket item)) (defn serve-customer [init-basket events] (reduce (fn [basket e] (cond (buy? ev) (purchase basket ev) ...)) init-basket events))

noisesmith14:03:37

eventually you'd likely track more than a basket (which is why I defined purchase rather than just use conj) -you'd likely use an account which includes a basket in a hash-map along with other customer data

noisesmith14:03:26

@suryapjr the problem you have otherwise is you would need a new def for each customer

noisesmith14:03:41

so now what do you do, make a new namespace on each customer signup? it doesn't really work out

pooboy14:03:26

Yea.....true

noisesmith14:03:15

so in a web app for example, you have a function that gets all your customer data from the db (or cache), performs the operations with that data, and returns the data to put in the db (and also indicate api calls to make, etc.)

noisesmith14:03:26

it's all done with functions - some input, some other output

noisesmith14:03:35

no need to mutate data, just make sure you use return values

pooboy14:03:15

cool..will keep that in mind! Thank you !

Russ Olsen14:03:05

@suryapjr An atom is just a thread safe container for some other value. The idea is that you (mostly) march from one value in an atom to the next by applying a series of functions to it. Behind the scenes if different threads try to update the atom at the same time, one wins and the other gets to rerun its function with the new value.

pooboy14:03:18

@russ767 cool !! Crystal clear! Gracias !

pooboy15:03:37

Guys..when we do Java or scala ,we call the respective compilers and they generate a class bytecode which is interpreted by the JVM. ..what happens in the case of Clojure ?

pooboy15:03:57

Clojure is also JIT compiled ...but we don't see a .class bytecode ..

pooboy15:03:17

Noob question Just Curious

noisesmith15:03:00

clojure's byte code is generated as each form is evaluated, and it's in memory

noisesmith15:03:14

optionally, you can create class files on disk, via aot and gen-class

noisesmith15:03:52

for example, every fn or defn in your code becomes a new class

noisesmith15:03:15

it has methods like .invoke and .applyTo that you can use to run the function

pooboy15:03:31

Ohhh...cool..

pooboy15:03:36

But it's still fast

pooboy15:03:52

If compared to other interpreted languages

noisesmith15:03:58

well, it's not fast if you create and eval new fn or defn forms inside your code after startup

noisesmith15:03:01

and startup is slow

pooboy15:03:09

Yea ..the startup

noisesmith15:03:10

it's not interpreted, it's compiled

noisesmith15:03:16

that startup time is compilation

noisesmith15:03:26

it's just compiled into memory instead of disk

pooboy15:03:40

I have seen people complaining about the startup..

pooboy15:03:08

Especially lein

pooboy15:03:23

Clojure REPL IS pretty ok in terms of startup

noisesmith15:03:51

but fundamentally there's some design decisions in the language (and to a lesser degree the vm) that mean start up time isn't prioritized - if startup is a primary concern there's languages that are much better about that

pooboy15:03:12

To be frank..I have never experienced a language as simple as clojure...I'm a novice in the field of programming..but clojure is the epitome of simplicity..I can say ..very beautiful..simpler than even python

noisesmith15:03:09

I agree - clojure is definitely optimized for simplicity in its design, and has fewer special cases and gotchas than most languages

pooboy15:03:06

Takes a while to get your head around the ()..but then there's no other syntax for u :)

noisesmith15:03:24

it could be even simpler if we were OK with worse performance or less libraries available (see scheme)

noisesmith15:03:38

but clojure strikes a nice balance of useful simplicity

pooboy15:03:52

Yesss...very good ...

pooboy15:03:13

Top notch build system - leiningen ..central repo for libraries - Clojar

noisesmith15:03:17

also it could start up a lot faster if we were OK with slower performance too

pooboy15:03:37

And the wonderful community !

joelsanchez15:03:45

the rest of the lisps are unusable for anything serious in a reasonable timeframe (personal opinion)

pooboy15:03:13

Haven't tried any other lisp ...

pooboy15:03:21

Heard about picolisp

noisesmith15:03:02

@joelsanchez guile ties into a gnu/linux ecosystem nicely, but that's not the ecosystem I need to target for a web app

pooboy15:03:29

So..the bytecode that clojure generates is the same bytecode that Java generates ?

noisesmith15:03:56

well - clojure has it's own byte code emitter and doesn't do some java stuff, but yes - the jvm just runs the bytecode

pooboy15:03:09

Coool !!! Awesome !

pooboy15:03:20

Whenever I create a new lein app ..I see a (gen : ) ;; something like that ..what's that

noisesmith15:03:48

gen-class tells clojure to write a class file to disk, so that the code can be called directly by java

pooboy15:03:53

Will a GUI app in clojure give decent performance ?

pooboy15:03:27

Not as fast a binary ..not as slow as ruby :)

noisesmith15:03:32

without extraordinary effort, you'll have about 1/10th the speed of java - which is close enough in a desktop app most of the time to not notice the difference

pooboy15:03:44

I want to make clojure my go-to language

pooboy15:03:08

The last time I felt so productive and happy was when I did ruby

noisesmith15:03:17

@suryapjr to date all of my big clojure jobs have been attempts to rewrite ruby to be faster

pooboy15:03:48

Hehehe...that's great !!..and clojure must have nailed it

mbjarland15:03:02

Somehow suspect the answer will either be "don't do it" or "macros", but I'm pretty new to clojure so I'll see where this lands : )

noisesmith15:03:46

@mbjarland same answer I gave to pooboy - make functions that take ant as an argument, and return a new ant

noisesmith15:03:25

if you have to wrap a stateful object, you can hide the side effects inside the ant objects operations (we don't do typical OO most of the time, but it's useful for managing boundaries when they get tricky)

pooboy15:03:40

What is seq ..

pooboy15:03:52

It's similar to lists ,right ?

noisesmith15:03:03

there's a seq function, and there's a ISeq abstraction

Russ Olsen15:03:12

You can think of a seq as a very abstract list

noisesmith15:03:13

you call seq to get an ISeq instance from a collection of some sort

noisesmith15:03:21

and yes, a seq acts like a list

noisesmith15:03:12

+user=> (seq "hello")
(\h \e \l \l \o)

noisesmith15:03:31

functions like map, filter, reduce all turn arguments into ISeq via seq if needed

noisesmith15:03:51

+user=> (map int "hello")
(104 101 108 108 111)

justinlee15:03:01

@mbjarland @noisesmith since ant is fairly declarative, I was wondering if maybe what you do is have jar and zipgroupfileset and other similar functions evaluate to data that describes the actions to take and then have ant interpret that data

Russ Olsen15:03:25

A seq is something you can get the first item off of and also get all but the first and you can add something to the front. (This is more or less what implementing ISeq means).

noisesmith15:03:37

@lee.justin.m sure - but somewhere you still want some context data representation passed from one to the next

mbjarland15:03:56

@lee.justin.m that is exactly my criteria in the above

pooboy15:03:22

@russ767 Crystal clear..

justinlee15:03:25

I just wasn’t sure if you really need context passing

noisesmith15:03:35

@mbjarland I think the key concept here is any global can be replaced by an object taken as an argument by each function, and returned by it also

justinlee15:03:35

maybe you do

mbjarland15:03:50

@noisesmith that is more or less what I was thinking but I would have liked not to litter the dsl with a bunch of ant as first argument

noisesmith15:03:52

you need data somewhere

pooboy15:03:59

So it's list,map,sets and seq

noisesmith15:03:23

@mbjarland taking your state object as an arg is actually the simplest thing - every other option

noisesmith15:03:30

every other option is more complex

noisesmith15:03:38

it might be terser, never simpler

mbjarland16:03:09

@noisesmith yes, agreed, I'm just wondering if you could opt to not show the noise to the user by using macros or something

noisesmith16:03:19

you can use functions that take a state as a first arg to implement every other option (global mutables, dynamic vars, db storage, whatever) - nothing else is flexible in that way

noisesmith16:03:41

@mbjarland worry about noise later - first implement the reliable simple thing (args) - make a syntax once it works

mbjarland16:03:03

well I have the syntax more or less working aside form this little "detail"

noisesmith16:03:11

then you started wrong

justinlee16:03:39

basically what i was thinking is maybe everything could evaluate to i/o primitives like fileset and what have you, and then ant would be a big interpreter that would go make it happen. i’m just not sure how much data gets passed from one primitive to the next.

noisesmith16:03:02

sorry, that's a bit strongly worded - I've made a lot of design mistakes that were expensive, and the solution was "take immutable data as an arg, return immutable data" -the longer you wait to implement that the more painful the translation is

mbjarland16:03:31

@noisesmith I totally hear you, should be noted this is an explorative task for me, I am not pushing for any solution, rather trying to learn the pros and cons of different ways of doing things in clojure. Your answer is exactly why I'm asking the question here

noisesmith16:03:01

for example I used core.cache in the app I work on

noisesmith16:03:13

people complain about how it takes and returns immutable data, and doesn't cache store anything

noisesmith16:03:32

that was a huge benefit, because I was able to replace putting data in an atom with putting data in a document store

noisesmith16:03:44

while leaving all the core.cache logic, since the storage wasn't baked in

noisesmith16:03:42

and it just worked on the first go - it's the only time I've implemented a cache of any sort that didn't have long term bugs show up

mbjarland16:03:09

@noisesmith so assuming that we pass in immutable data and return immutable data, would you also opt for being explicit in this case? I.e. force the user to see all those ant arguments or would it be ok to make ant a macro (if this is even possible) and make the first argument ant optional...or would that push us over onto the dark side again?

noisesmith16:03:51

you can easily make a system that uses a namespace level atom on top of pure functions - it can be a separate namespace (I actually recommend that)

noisesmith16:03:10

then, that new namespace allows using all the pure code, while always operating on a given atom

noisesmith16:03:28

that way, the user doesn't need to bother with that arg, beyond telling the system which atom to use

mbjarland16:03:44

@noisesmith that was kind of where I was going...but that is still global state and could get tricky in multi threaded situations...which is how I ended up here : )

noisesmith16:03:04

well that's what atoms are - they protect data when modified by multiple threads

mbjarland16:03:09

@noisesmith ah...ok I think I'm starting to catch on

noisesmith16:03:16

just make sure you never @ and followed by swap! in the same block of code

noisesmith16:03:06

no, atoms are never per thread

noisesmith16:03:21

swap! is a mechanism that ensures that modifications are not racing

noisesmith16:03:54

the value of an atom is shared by all threads, and updates are retried on conflict

mbjarland16:03:03

yeah, knew that...was thinking more "flows of work", the user of the api would still have the option to give different invocations different atoms if the intent is to separate them

mbjarland16:03:21

bad use of language in the above there

noisesmith16:03:10

right, the first thing to avoid is forcing the user into a singleton

mbjarland16:03:11

so perhaps there would be an inplicit atom and when needed the user could provide their own

noisesmith16:03:26

but I'd also avoid forcing the user to use an atom - make sure all the logic works without the state mutation layer

noisesmith16:03:54

maybe a specific API with an implicit atom or user provided etc. - as long as one can opt out

noisesmith16:03:01

(that's my preference at least)

mbjarland16:03:57

well I came here for a preference...thank you, this is very helpful

noisesmith16:03:12

you mentioned macros before and I'd make a similar argument there - don't provide features via macro only, have a way to work with functions and an optional macro layer

noisesmith16:03:36

because nothing is flexible like taking and returning immutable collections is

mbjarland16:03:37

I would like to avoid macros in this exercise...I've come to understand that they have somewhat of a viral effect on code

mbjarland16:03:48

if I can get away without I would rather do that

mbjarland16:03:45

You mentioned namespaced atom in a different namespace, care to elaborate that a bit?

mbjarland16:03:09

you mean break out the "shorthand form" api into its own namespace?

noisesmith16:03:22

what I mean there is have a namespace that is sufficient to use your entire API, and then another that wraps it

noisesmith16:03:53

@mbjarland a big reason for this, if nothing else, is it simplifies testing (and writing tests that can be run in parallel threads)

noisesmith16:03:23

but to me "easy to test" is a good canary for good code

noisesmith16:03:45

hard to test code is the kind of code that usually goes wrong - not because of lack of tests, but for the same underlying reason that a test was hard to write

mbjarland16:03:25

agreed - that rhymes with my previous lives in other languages as well

mbjarland16:03:43

@noisesmith ok, I get the feeling this conversation just saved me a whole lot of head scratching and dead ends...thanks a ton. Might publish this as a clojars lib if and when I get it done...some io operations (pick out a bunch of patternmatched files from a zip file, copy them somewhere else etc) are still a tad verbose in clojure. This kind of dsl would make some system scripting tasks nice and concise

noisesmith16:03:54

@mbjarland even in C++ you see the most knowledgable people saying "your classes should act like an int" and by that they mean const (AKA immutable), and without surprising behaviors of any sort - exactly what we want in clojure :)

noisesmith16:03:42

I'd be interested to see what you come up with, and glad I could help - I should do a blog post on all this stuff

mbjarland16:03:22

I come from a world where I've written a crapload of systems automation scripts in all kinds of languages...mostly groovy and bash (and bash really makes you want to jump off a cliff when complexity goes anywhere past copy file from a to b). I get the feeling that the clojure community is mostly sitting in containers and not really dealing with "list processes, filter list, copy files from a to b, deploy files to prod server" kind of scenarios

noisesmith16:03:15

@mbjarland something I played with recently that might inform what you are trying is scsh -it's a scheme DSL for shell scripting

noisesmith16:03:13

anyway, scsh is quite complex and does a lot of cool stuff, there's like a couple of nice ideas you can steal from it

noisesmith16:03:36

this is the repo I was using to run those repl examples https://github.com/ChaosEternal/guile-scsh

noisesmith16:03:13

usage has much better examples than my little gists https://github.com/ChaosEternal/guile-scsh/blob/master/USAGE ChaosEternal/guile-scsh Resurrection of guile-scsh on guile-2.0

pooboy16:03:37

Something wrong with my syntax : (defn makelist [ a] ( let [ mylist '()]) (conj mylist "ok"))

noisesmith16:03:18

the closing paren on let needs to wrap around the code using the binding created by let

noisesmith16:03:44

@suryapjr a core concept in clojure is that things are visible in predictable ways - if you create a value (other than def at the top level), everything that uses it has to be inside the same form that created the value, or has to receive the value as an argument

mbjarland16:03:19

@noisesmith last quesion...I was planning on all the nested functions returning pure datastructures, essentially maps. Do you see a reason for them to return the passed in "collector" (or ant or whatever) instance instead?

noisesmith16:03:20

to be inside the let form, you need to be between the parens surrounding let

pooboy16:03:06

@noisesmith (let [ a "poo"])

noisesmith16:03:15

@mbjarland on an abstract level, whose to say an ant isn't a hash-map? realistically, start with hash-maps, they are simple and you don't need the extra features of a custom class, later if you need it you can use a record or deftype

mbjarland16:03:12

@noisesmith ok, I will noodle on this. Probably need to settle the concepts a bit and things will land

noisesmith16:03:13

the nice thing about a defrecord is that you can do anything to it that you can do with a map, so switching later is simple

mbjarland16:03:52

@noisesmith and thanks for the scsh links, will take a look and see what I can learn

mbjarland16:03:02

@noisesmith ha, I guess I keep adding to the list of questions. Just figured I would compare to clojure.data.xml which has a very similar pattern for building up data:

(element :foo {:foo-attr "foo value"}
     (element :bar {:bar-attr "bar value"}
       (element :baz {} "The baz value")))
the problem I have with this is that as far as I can understand this becomes a "pure data" dsl, i.e. without passing in a "collector" of some sort, it becomes difficult to inject logic and conditionals in the middle of a tree of element expressions like the above. You can, but then you need to re-structure your entire tree to support the conditional...at least this is how it seems to me. I guess for xml this is a decent compromise for terseness

mbjarland16:03:40

essentially you get the same issue I had in my original code

noisesmith16:03:08

it's very doable to work with and edit such a tree with zippers

noisesmith16:03:44

there's also things like clojure.walk/post-walk and even update-in depending on the transformations you need

noisesmith16:03:10

but yes, deeply nested data can get tricky in clojure's immutable data structures

noisesmith16:03:44

as a worst case you can transform to adjacency-list, carry out operations on that representation, and translate back (I wonder if anyone has a lib for this...)

mbjarland16:03:12

ah...I think the coin just dropped : ) @noisesmith ok thanks for all the help, I'll go back to the shed and see what comes out

pooboy17:03:37

How is atom thread-safe ?

noisesmith17:03:44

the swap! function detects concurrent modifications, and commits the first modification and retries the rest

noisesmith17:03:30

this totally breaks if people use reset! or if they use @ before swap! to drive their logic, but if you use swap! as intended you can ensure you don't have races

noisesmith17:03:59

the key here is that swap! takes a function, which takes the value in the atom, and returns the new value for the atom

noisesmith17:03:18

so even if two modifications try to happen, the second one can retry with the updated value from the first

pooboy17:03:39

Ohhh..kk..nice

pooboy17:03:44

Kindly look at this atom : (def myatom (atom 3)) (swap! myatom 4)

pooboy17:03:59

Can't swap myatom to 4

pooboy17:03:37

But I can (reset! myatom 4)

noisesmith17:03:09

swap! requires a function, 4 isn't a function and reset! is not safe

noisesmith17:03:28

reset! is safe in that you won't get broken state, but it isn't safe from data races in your logic

noisesmith17:03:08

@suryapjr what about (swap! myatom inc) or (swap! myatom - 2)

noisesmith17:03:22

that's the kind of thing where each one can retry, and the math will work out

noisesmith17:03:51

(if you mix in (swap! myatom * 3) you still get issues because reordering changes the meaning of course...)

pooboy17:03:48

Ummm..deep..

pooboy17:03:00

But since - is a function

pooboy17:03:40

We should have done : (swap! (- 2 myatom)) ?

pooboy17:03:57

The operator comes first,right ?

noisesmith17:03:28

that's not the syntax of swap

pooboy17:03:35

I'm sorry I'm not getting it quickly..I'm just trying to get a firm grip

noisesmith17:03:00

sure - (swap! some-atom function ...args)

noisesmith17:03:43

so to subtract it from 2, you would need (swap! my-atom #(- 2 %)) or to subtract 2 from it, (swap! my-atom - 2)

pooboy17:03:10

Awesome !!

pooboy17:03:34

So swap! Is actually mutating the value or am I getting a copy of the updated value

noisesmith17:03:58

it mutates the atom, by replacing the single object inside with a new object

pooboy17:03:35

Atlast a mutable thing in clojure :)

noisesmith17:03:04

we very rarely use atoms

noisesmith17:03:19

and even in the few cases they get used we often find ourselves rewriting to remove them

pooboy17:03:34

Ohhhh..why..pure functions?

noisesmith17:03:12

right - if you just want mutable values to work with there's other languages that can do that in a straightforward way - a lot of the benefit of clojure relies on the fact that we are using immutable values everywhere

noisesmith17:03:25

even atoms break if you put anything mutable inside

pooboy17:03:08

To program with immutability is something I want to learn

pooboy17:03:02

And to say that a function returns a value :

(defn hi []
       
       1 )

pooboy17:03:20

Above, the hi function returned 1 , right ?

noisesmith17:03:29

right, it will return 1 if you call it

pooboy17:03:47

No return statement..

pooboy17:03:49

What is the reduce function used for

noisesmith17:03:44

reduce is for repeatedly calling a function on a series of inputs, and using each result on the next call

noisesmith17:03:57

there's another function, reductions that returns all the values along the way

noisesmith17:03:36

+user=> (reductions + 0 [1 2 3 4 5])
(0 1 3 6 10 15)
+user=> (reduce + 0 [1 2 3 4 5])
15

noisesmith17:03:55

so you can probably look at that and see how it calculated each of those values

pooboy17:03:22

Yea..thanks !! This is cool

noisesmith17:03:54

+user=> (reductions conj () [1 2 3 4 5])
(() (1) (2 1) (3 2 1) (4 3 2 1) (5 4 3 2 1))

noisesmith17:03:28

+user=> (reductions conj #{} [1 2 3 4 5])
(#{} #{1} #{1 2} #{1 3 2} #{1 4 3 2} #{1 4 3 2 5})
+user=> (reductions conj [] [1 2 3 4 5])
([] [1] [1 2] [1 2 3] [1 2 3 4] [1 2 3 4 5])

pooboy17:03:33

It's running conj to every element

noisesmith17:03:47

on each element, using the previous result as the first arg

pooboy17:03:31

Yessssss....should have taken 5 x more lines of code to do this in other languages

noisesmith17:03:57

often a for in another language translates directly to a reduce in clojure

pooboy17:03:44

Umm..for iterating ..yes

pooboy17:03:17

I have noticed how clojure has acquired a niche loyal following ..

pooboy17:03:29

While the masses are after Java and scala

pooboy17:03:48

And droid programmers are jumping into kotlin

pooboy17:03:09

What could be the simplest example of a macro

noisesmith18:03:37

(defmacro simple [x] `x)

noisesmith18:03:49

it doesn't do anything interesting, but it's quite simple :P

pooboy18:03:21

So what does a macro do

pooboy18:03:36

I read that it had powerful features to clojure

noisesmith18:03:23

fundamentally it takes a form, returns a new form, and the form it returns gets compiled

noisesmith18:03:11

+user=> (defmacro simple [] (list '+ 1 1))
#'user/simple
+user=> (simple)
2

noisesmith18:03:27

it made a list with the symbol plus, the compiler compiled and ran that list

noisesmith18:03:44

so you can use clojure's built in functions to modify code before it compiles

noisesmith18:03:56

(where in most languages all you can do is manipulate strings before compiling)

pooboy18:03:39

This is so good

noisesmith18:03:33

+user=> (defmacro make-next-n [s n] `(def ~s ~(inc n)))
#'user/make-next-n
+user=> (make-next-n two 1)
#'user/two
+user=> two
2

noisesmith18:03:55

the ~ inside means "expand this before compiling"

noisesmith18:03:46

it's good to learn how macros work, but also one should be very careful implementing them - it's easy to make bad libraries that are hard to use with poorly designed macros

noisesmith18:03:09

but a lot of our favorite clojure features aren't in the compiler, they are macros

lispyclouds18:03:24

A nice example of macros that i found is the infix one. A good demo of clojure’s ability to change its own behaviour.

(defmacro infix [exp] (list (second exp) (first exp) (last exp)))
The a call like (infix (1 + 2)) produces 3 like a usual infix evaluation in other languages.

lispyclouds18:03:18

Run macroexpand on the expression to see what actually is evaluated. (macroexpand '(infix (1 + 2))) gives (+ 1 2)

Russ Olsen19:03:49

The other thing to keep in mind is that while syntax quoting is very useful inside of macros, you don't need a macro to use -- or more likely -- play with it. So if you evaluate this: (def x 44) `(println "the number is" ~x)

Russ Olsen19:03:54

(clojure.core/println "the number is" 44)

noisesmith19:03:00

there's some nice trick with that

Russ Olsen19:03:05

Or (def a [1 2 3]) `(println ~a) `(println [100 100 ~a]) `(clojure.core/println [100 100 1 2 3])

noisesmith19:03:26

+user=> `#(+ % %2)
(fn* [p1__184__186__auto__ p2__185__187__auto__] (clojure.core/+ p1__184__186__auto__ p2__185__187__auto__))

Russ Olsen19:03:03

Ops that last expression should have been `(println [100 100 ~@a])

Russ Olsen19:03:33

Macros + syntax quoting means never having to wish that you could change the compiler source code. Well maybe never is a little too strong...

noisesmith19:03:57

I think it means the main reason to edit the compiler is to make it emit better byte code

noisesmith19:03:10

I'm probably forgetting other cases though

bringe20:03:29

I have some confusion about the difference in what's happening with output when evaluating code in my editor (using an nrepl addon, evaluate selected code), vs running the code in the terminal repl (lein repl, the same one my IDE is connected to and uses to evaluate selected code). If I run the below code by typing it in my code file, selecting it, and evaluating it, in my "Output" window it waits three seconds, then prints 0, 1, 2, nil all at once, like it printed to an output buffer and waited for the loop to finish before showing me the contents of said buffer. If I run the below code in the terminal repl, it prints 0, waits a second, 1, waits a second, etc. Can anyone explain what is happening here?

(dotimes [i 3]
  (Thread/sleep 1000)
  (println "==>" i))

noisesmith20:03:07

the terminal output is correct and I'd consider the editor integrated behavior a bug

noisesmith20:03:19

nrepl must be waiting to collect all output of the form before displaying

justinlee20:03:53

i’d bet a nickel the editor is blocking until the form evaluates and then goes and retrieves the output

noisesmith20:03:57

this has to do with nrepl's protocol, where it reifies inputs and output behavior

noisesmith20:03:23

it's nrepl, there's no explicit "retrieval" - but that's basically what nrepl does

noisesmith20:03:39

perhaps someday nrepl will implement streaming or partial output of forms?

dpsutton20:03:33

i think nrepl does? here's the nrepl traffic in CIDER:

(<--
  id         "679"
  session    "87958be7-a29e-48de-9b5c-702418748369"
  time-stamp "2018-03-10 14:20:17.914045319"
  out        "2
"
)
(<--
  id         "679"
  session    "87958be7-a29e-48de-9b5c-702418748369"
  time-stamp "2018-03-10 14:20:17.929469596"
  ns         "com.breezeehr.docker.zookeeper"
  value      "nil"
)

dpsutton20:03:15

note the out vs value payload keys

justinlee20:03:14

@dpsutton how did you spy on the nrepl like that? is that a feature of cider? the reason i ask is because i would love to screw around with protorepl to see if i can maybe implement more features for cljs

dpsutton20:03:42

i'm in CIDER. nrepl-toggle-message-logging

dpsutton20:03:54

and that will create a messages buffer with the nrepl traffic for you

justinlee20:03:06

shoot. i was hoping there was a way to spy on the otherside

justinlee20:03:21

that’s probably still helpful though

dpsutton20:03:57

as far as i know it should be pretty faithful to what's going back and forth. obviously annotated with some extra stuff like time stamp

justinlee20:03:21

well i was hoping to see what protorepl is doing and i don’t think it has any convenient logging features like that

dpsutton20:03:02

@brandon.ringe my only guess is that you're building stuff up in a buffer to output when it's "done". you could try to poke it a bit and println a really big string and see if it will flush before it is totally done

justinlee20:03:19

@brandon.ringe does the behavior change if you cut-n-paste the code into your editors repl instead of selecting it and evaluating it?

dpsutton20:03:16

(dotimes [i 3]
  (Thread/sleep 1000)
  (println (clojure.string/join (take 500 (repeat "a")))))
something like that? see if you can fill up its buffer and make it dump it

bringe20:03:02

@lee.justin.m Yes, the behavior changes. Select->evaluate waits for all output from the form to finish and prints all output at once. Copying and pasting into the terminal repl prints each value after each Thread/sleep.

justinlee20:03:39

so i’m distinguishing between a terminal repl and your editor’s interactive repl

bringe20:03:06

Yes I believe so. I'm very fuzzy on the real difference besides the way I use them.

bringe20:03:14

They both use nrepl

noisesmith20:03:02

ahh right, I stand corrected, it's definitely a client side bug and not nrepl

dpsutton20:03:02

can you add an explicit call to (flush) at the end of your do times block? is it possible that could work?

justinlee20:03:48

sorry i’m not being super clear. there three potential ways to evaluate the code (1) select and use some key combination to evaluate the code, (2) cut-n-paste into a repl running in your editor, (3) cut-n-paste into a repl running in a terminal. anyway it doesn’t matter. i was just thinking that method (1) is probably doing some thing different from (2) and (3)

bringe20:03:08

@dpsutton I ran your code and it has the same behavior as before. I tried the flush too, it still waits in the integrated repl.

dpsutton20:03:31

see if this produces any output for you

(.start (Thread. (fn []
                   (dotimes [i 3]
                     (Thread/sleep 1000)
                     (println i)))))

dpsutton20:03:46

it immediately returns nil and only after prints. i wonder if after something is "done" if it can still watch for output

noisesmith20:03:09

the problem with code like that in nrepl is that Thread doesn't inherit a re-bound *out*

dpsutton20:03:56

i think CIDER is a little greedy in taking over *out* and can cause problems which is why this "works" in CIDER

bringe20:03:41

Yeah that code just prints nil, nothing else

bringe20:03:07

In the integrated. Terminal prints nil and each value 1 second apart like before

bringe20:03:01

I'm using VS Code with an extension called Clojure which is what gives me the integrated nrepl. Idk if the editor or extension have anything to do with it.

dpsutton20:03:45

@noisesmith that works at lein repl. did you expect it to not?

dpsutton20:03:11

i'm not terribly clear about jvm and *out*

noisesmith20:03:29

@dpsutton try it with the server run :headless and the client in a new terminal

dpsutton20:03:12

i'm not actually sure how to connect like that

noisesmith20:03:23

lien repl :connect <port>

noisesmith20:03:57

heres's a zoom in on my tmux:

+justin@HOST:~$ lein repl :connect 59109                                         
Connecting to nREPL at 127.0.0.1:59109
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.8.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_05-b13
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e


user=> (.start (Thread. (fn [] (println "hi"))))
nil
user=> 
───────────────────────────────────────────────────────────────────────────────────────
gpg-agent[28197]: a gpg-agent is already running - not starting a new one
+justin@HOST:~$ lein repl :headless
nREPL server started on port 59109 on host 127.0.0.1 - 
hi

bringe20:03:26

My question originally stemmed from me trying to connect to a websocket feed. My on-receive function that handles received messages from the feed uses println to display the message. The same thing happens with that. When I connect to the websocket server using the integrated eval, I don't see the received messages in output. When I connect to it in the terminal repl, the received messages are immediately printed in my terminal and continue to print when received.

noisesmith20:03:10

sounds like someone is getting mucked up in their binding of *out*

noisesmith20:03:22

if you use a logging library, you can ensure that all its usage goes to the right place

noisesmith20:03:58

or you can make sure to always start clojure from a terminal, so it's more likely you at least see the misdirected messages, you can always :connect to it from elsewhere

bringe20:03:23

I'll have to look into logging and read up on output more. Thanks for the help.

geeky_vin22:03:20

Hi There, has anyone developed an app with pedestal?

geeky_vin22:03:37

I am trying to figure out how to start a pedestal server in dev mode…

geeky_vin22:03:56

so that I can make-changes and not have to reload the server everytime

alexk22:03:41

I used a filesystem watcher to trigger reloads…really not ideal so I hope you find something else useful

geeky_vin22:03:58

hopefully… did you run your watcher in repl?

geeky_vin22:03:13

do you have a sample code for that somewhr in github?

alexk02:03:46

The ns-tracker library does most of the work

bringe23:03:21

Is there a way I can make my lein repl automatically reload my namespace when changes are made to the file? In my repl, I enter (load "my_folder/core") and then (use 'my-folder.core :reload-all). I call a function, see output, change the function to output something else, save the file, call the function in the repl and see the old output.

bringe23:03:49

Shouldn't :reload and :reload-all automatically reload on file save?

mfikes23:03:06

@brandon.ringe The :reload and :reload-all options only take effect when you do use and require (they don't involve filesystem watchers)

bringe23:03:29

Ahh thanks

mfikes23:03:41

@brandon.ringe Having said that, it is certainly possible to build such functionality. For example, ClojureScript has built-in watch capability https://github.com/clojure/clojurescript/blob/56c774be6227d51f4aa3a52571cb2640c7325db7/src/main/clojure/cljs/closure.clj#L2933 and here is an example of using it to call load-file when files change http://blog.fikesfarm.com/posts/2015-05-30-poor-mans-figwheel-for-ambly.html

mfikes23:03:47

Maybe someone has already built this and made it available as a library