Fork me on GitHub
#beginners
<
2017-09-08
>
didibus06:09:58

Regression tests, would be hard to build any complex system without it

didibus06:09:11

docs won't do anything about that type of problem

didibus06:09:40

But in Clojure, I do not write tests to know code works, only tests to make sure public behaviour is never altered after release

didibus06:09:03

And fuzz/generative tests, as a form of deep bug finding exercise

didibus06:09:52

I've also found, component or mount, and the word state is confusing. State can be stored in a global Var/Atom/Ref or passed around from fn to fn. You only need component of mount if you're state needs to be initialized and cleaned up. So if a namespace needs a DB connection, expecting it to be connected already, with mount, you'd put a dependency on that "state", and mount will know to initialize it before giving it to you. But if by state, you mean just like running app state, like say I keep track of users created in a global Atom, the only need for mount/component, is to maybe clear the atom on reload, if I ever want too. But the value proposition is much less in this case.

petr.mensik10:09:27

Hello guys, is there a way how to reference other project files in project.clj? Say I have DB configuration in src/config/db_config.clj and I want to pass it to some lein plugin configuration. Thx for help 🙂

seancorfield17:09:37

@petr.mensik You can use ~ in project.clj to cause evaluation of forms, so you could read the db_config.clj file that way... but I'm not sure I'd recommend that. You could join #leiningen and ask for more approaches there. We switched to #boot and found that much easier to use as a build tool for anything even vaguely complicated.

hmaurer18:09:39

Trying boot at the moment and it’s “freaking awesome”

petr.mensik21:09:58

The point is, obviously, to follow DRY principle and don't repeat the same configuration on multiple places 😕 But I can ask there, thanks

seancorfield22:09:03

We have our config in EDN files. Our Boot script contains tasks that build Components and start them and then run functions. Those Components depend on our Configuration Component, which reads the EDN files. Our applications also use Components. So our config is DRY and both Boot and our application leverage (some of the same) Components, ensuring they are DRY too.

seancorfield22:09:25

(that's much harder to achieve with Leiningen and plugins, than with Boot and tasks)

hmaurer22:09:44

@U04V70XH6 on a bit of a sidenote, you mention #boot being much easier to use for “anything vaguely complicated”. I am new to Clojure and initially jumped on the Lein ship but recently switched to boot and I found it simpler to wrap my head around. Maybe I am missing something about lein and/or didn’t read the doc properly, but overall, even for simple projects and as a beginner, boot made more sense to me

seancorfield22:09:33

Yeah, I think I would probably recommend boot to everyone starting Clojure these days, with the caveat that nearly all the examples you find in books and online still use lein...

seancorfield22:09:50

...which means you kinda need to know how to convert project.clj to build.boot.

seancorfield22:09:09

Not that it's hard per se, but it's yet another hurdle to picking up Clojure.

hmaurer22:09:15

@U04V70XH6 Is it really a hurdle though? It seems to me that you can pick up boot very incrementally. The ability to declare dependencies at the top of a Clojure file seems brilliant for a beginner. You can incrementally teach the bascs of clojure, then tell them to add a few lines at the top of a file to pull a dependency, and later tell them to move that part to a build.boot and drop their code in a src/ folder

seancorfield22:09:14

True... and the other thing is that you can run a new app project's main without even needing build.boot:

lein new app foo
cd foo
boot -r src -r resources call -f foo.core/-main
=> Hello, World! and then to run the tests:
boot -r src -r resources -s test -d adzerk/boot-test test

seancorfield22:09:37

(and you don't even need -r resources for the simple case)

hmaurer22:09:49

@U04V70XH6 oh, that’s pretty neat 😮 One thing I found out about while learning boot (which might be doable with lein, I’ve no idea) was the ability to pull dependencies in the command line. e.g. boot -d some-dep:1.0.0 repl

hmaurer22:09:01

really useful too

hmaurer22:09:34

exactly what you did above actually

theeternalpulse19:09:35

Can I use functions created with defn as helper functions to craft forms for defmacros. Say I have a list of symbols that I want to have a specific set of symbols output will it work in a reduce function or does it have to be a defmacro helper.

noisesmith19:09:23

macros can call functions (or other functions) freely

noisesmith19:09:56

just remember that the macro doesn’t see the actual data passed in at runtime, it only sees the form from the source code

noisesmith19:09:09

as long as your function can do something useful with that, you are good to go

noisesmith19:09:01

also, it’s good to distinguish a macro calling a function from a macro emitting a call to a function

theeternalpulse19:09:12

right, that's still an exercise to wrap my head around.

theeternalpulse19:09:43

I've got most of it but the macro is getting large and hard to follow, so didn't know how I should split it up

noisesmith19:09:49

the thing that helped me most was focusing on the fact that a macro takes source code (in the form of symbols, lists etc, not a string) and returns the new source code to use in its place

theeternalpulse19:09:24

The end part I can wrap my head around, emit symbols and it evaluates after macroexpansion. Actually crafting it is the hard part for me

noisesmith19:09:59

as a beginner, one way to do that is often to make a function that just takes a form and returns a form (and break that down into more functions as needed) no macro definitions needed at any point there, and finally wrap a call to this function in a macro

bfabry19:09:05

breaking it up by writing functions that take code-data and return code-data to the macro is a good approach

theeternalpulse19:09:37

@noisesmith that is definitely what I should have done, working to piece the whole is already taxing.

bfabry19:09:50

helps with repl testing too as you don't need to macroexpand all the time

noisesmith19:09:51

switching to that approach is easy though

noisesmith19:09:24

you can pretty much just take your defmacro, change it to defn, give it a new name, and now pass it quoted args as needed

theeternalpulse19:09:44

Also how do I solve this issue. I'm appending to a form that needs to end up as

(do (ns some-ns) (print (escaped-form) ~(escaped-form))
so I first create a `[do] vector and am conj to that since it's append last. Then I convert it to a list and return that from a macro

noisesmith19:09:03

first off, there’s no vectors there

theeternalpulse19:09:25

that first one is after the conversion

noisesmith19:09:40

oh sorry, now I get it

noisesmith19:09:35

usually people use ~@ for that

theeternalpulse19:09:44

(defmacro repl-step-do
  [time & forms]
  (reduce
   (fn [print-exprs [title & rest-forms]]
     (let [namespace (.toLowerCase (clojure.string/replace title " " "_"))
           form-exprs (conj (for [expr rest-forms]
                              ('print expr))
                            'do)]
       (-> print-exprs
           (conj `(ns ~(symbol namespace)))
           (conj form-exprs))))
   `[do]
   forms))

theeternalpulse19:09:44

;;;; Example
#_(repl-step-do
   500
   '("Basic Operators"
     (+ 1 1)
     (+ 2 2))

   '("Some Other Stuff"
     (def a 12)
     (str "This number is a " a ".")))

;;;; Output
;;; * Basic Operators
;;;   (ns basic-operators)
;;;   (+ 1 1) => 2
;;;   (+ 2 2) => 4

;;; * "Some Other Stuff"
;;;   (ns some-other-stuf)
;;;   (def a 12) =>#'basic-operators/a
;;;   (str "This number is a " a ".") => "This number is a 12"

noisesmith19:09:24

so the ~@ version would be ````(do ~@(for [expr rest-forms] `(print ~expr)))```

theeternalpulse19:09:25

I'm basically just creating a "slideshow" for repl blocks to easily run through an example for reference

nimblerabit22:09:45

I'm struggling with something really simple, hopefully you guys don't mind being asked a dumb question. I need to generate a game board, let's say a chess board, so I'm trying to create nested vectors like this: [[1 2 3 4][5 6 7 8][9 10 11 12][13 14 15 16]] I can't figure out how to generate something like that in a functional way

dpsutton22:09:16

play with partition and range

seancorfield22:09:38

(yeah, what @dpsutton said ^^ -- beat me to it!)

dpsutton22:09:55

that should be enough to get you going. let us know if you run into anything or get stumped

dpsutton22:09:12

also be mindful of your types (vector versus lazy lists) if that's important for you

seancorfield22:09:42

Hint: you can get from a list of lists to a vector of vectors with (mapv vec ...)

nimblerabit22:09:51

That was super easy, thanks. Follow up question, say I didn't want those vectors to be contain numbers but something else (say a map at each location), still with the same structure of vectors though. Is range still the way to go? For example, this is what I have right now:

nimblerabit22:09:30

I don't need those id's in the map, that was just how I created the maps. I'm trying to create the 16 maps in that configuration and put some other stuff in there

nimblerabit22:09:42

That solves it, thanks

hmaurer22:09:43

@noisesmith how do you use clojurebot?

noisesmith22:09:46

you don’t need the into calls if lists are OK

noisesmith22:09:54

@hmaurer /clj (maybe buggy)

noisesmith22:09:01

it barfs on strings for example