Fork me on GitHub
#beginners
<
2018-08-02
>
porkostomus01:08:48

my CLI tools is giving me the following error: "Error building classpath. Map literal must contain an even number of forms". I can still use leiningen and boot without a problem, but it's bothering me that I can't figure this out.

porkostomus01:08:10

that is, when trying to start a REPL.

jonahbenton02:08:18

i believe i've seen that when there's a syntactic problem with the deps.edn

dpsutton02:08:27

@porkostomus it sounds like your deps.edn file may be malformed. neither boot nor lein will look at this file

dpsutton02:08:22

and make sure each key has one value and there aren't any extra forms in there

porkostomus02:08:55

that's what I thought, but it doesn't seem to be the issue

porkostomus02:08:54

ah, I fixed it. didn't check the one in the .clojure folder

seancorfield02:08:29

@porkostomus If you want lein and boot to also use the deps.edn files for dependencies, there are plugins/tasks for that.

porkostomus02:08:40

oh that's cool

kouvas10:08:18

Hi guys, is there a way to define a specific jdk for my project to use instead of the default one(the one in my PATH) on linux?

kouvas10:08:26

and is there a way to check that?

orestis11:08:55

Setting JAVA_HOME I think? /cc @kouvas

gklijs11:08:22

Setting JAVA_HOME works, you can use 'java -version' to check

kouvas12:08:00

JAVA_HOME worked fine but it affects all projects, i guess i will do a small script to change every time i want to

manutter5113:08:00

@seancorfield @alexmiller Spam alert [EDIT: message was deleted, thanks Sean!]

alexmiller13:08:30

For the record, I’m not a mod here and have no power to do anything

manutter5113:08:16

Oops, sorry!

seancorfield16:08:35

Message deleted. The offender will have their account deactivated shortly. Let me know if that spam needs to be deleted from other channels.

👍 4
JanisOlex13:08:57

hey what is idiomatic general approach to clojure program - topdown or bottom up?

JanisOlex13:08:14

or doesn't matter?

jonahbenton13:08:25

bottom up, function by function through the repl

JanisOlex13:08:58

but what if I don't know what are those "bottom"... Ok context - want to try make game on clojure (clojure version of board game) just to learn... I can come up pretty easy with general structure of board, players... and it seems for me that it would be natural top-down programming, filling up gaps... like start with setup_game, setup_players, make_move, calculate_points, repeat

jonahbenton13:08:45

that's fine, but using the repl allows you to do all of that incrementally

jonahbenton13:08:59

and test out the way you've modeled it as you go

JanisOlex13:08:02

of course... REPL is best invention in the world (well after computers themsleves)

JanisOlex13:08:16

ok thanks... so I guess, all the programming will go in REPL... BTW is there some "automated" REPL tools, I mean, I do lot of repling... at some point my REPL gives what I want... at that point I would like to take 'snapshot' and wrap it into repeatable test and extract and put it aside, so I have repeatable milestone of my work?

jonahbenton13:08:17

if by automated you mean a smalltalk-like snapshot the vm and return later- that's not a typical process. it's more common that as you work in the repl, you add bits that work to your code namespaces, and then "reload" to start from the new basis of the code in your namespaces. adding library dependencies also typically requires a restart-repl, though there are ways to do it dynamically in repl.

jonahbenton13:08:16

there are workflows like this around the component, system and boot tools that are specific to those tools

JanisOlex13:08:36

hmmm can you elaborate more... how is typical (ok yours?) work... you write your imagined function(s) in the namespace, load it in REPL and try to test it, or you first figure out function in REPL and then add it to namespace and relaod it?

mg13:08:19

I typically do a lot of my work in a dev namespace

mg13:08:38

then when it gets to a point that I like it, I move that code to a real namespace

jonahbenton13:08:43

i personally follow a variation on the stuart's reloaded workflow: http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded

JanisOlex13:08:11

ok, thanks... will read it... p.s. using Cursive for development

jonahbenton13:08:52

write functions directly in the repl in the user namespace, exercise them with static data in vars in user, then add them to a real namespace and (reload)

jonahbenton13:08:59

if i'm working on something that opens sockets or has lifecycle, i'm usually using stuart's Component library, so will stop the components, reload, then start them up again, and continue

jonahbenton13:08:40

Cursive is great, but I haven't used it for a long time

seancorfield16:08:12

The best way to work with the REPL is to use an editor that can evaluate forms into a running REPL. So you don't type into the REPL -- you type into a file and use a hot key to evaluate each form. That way everything from your REPL session is already preserved, and it's easy to copy'n'paste pieces into test files or source files as needed.

👍 8
upgradingdave18:08:01

Hey all, is it possible to rework add-node in this example so that it doesn’t stack overflow? https://gist.github.com/upgradingdave/68bf9554a92ae520a626190f7d69c9b6

noisesmith18:08:19

the idiomatic thing in clojure would be to use a vector instead of a linked list if you want to add at the end- I don't know what the constraints of your exercise are though

upgradingdave18:08:19

thanks, @noisesmith yes, this is just for fun. If I were doing it in real world, I’d use a vector, but I was just curious if there is a way to implement this as shown … curious if there’s a technique for recursive patterns like this?

noisesmith18:08:01

you'd need some way to lift the remaining work to be done out of the call stack and into an object in the heap. If you can create a loop/recur that tracks the work done so far and lets you replace the self call to a recur, it would eliminate the stack usage

noisesmith18:08:06

the result will not be pretty

noisesmith18:08:57

likely you'd end up with one of the loop bindings being a hash or vector or something holding the partial results waiting to be rolled up

upgradingdave18:08:20

makes sense, thanks much for the second pair of eyes

vuuvi19:08:27

I am having trouble getting a nested set of values out of a lazyseq and I can’t figure out why

vuuvi19:08:04

I’ve been using get select-keys etc and everything keeps returning either an empty list or nil

mg19:08:18

get and select-keys don’t work on a lazy seq

mg19:08:22

they work on maps

vuuvi19:08:29

is there any way to debug why my attempts to get out of set of values aren’t returning?

vuuvi19:08:51

okay so how could I get the values from those keys in a lazy seq?

vuuvi19:08:59

I’ve been trying to do something like

mg19:08:05

what does your lazy seq look like?

vuuvi19:08:18

(map #(select-keys % [:access_groups]) projects)

vuuvi19:08:31

it’s like this

vuuvi19:08:41

({:access_groups [{:group_name "saas", :group_id 1}]})

vuuvi19:08:56

and I need to get :group_id out of that lazy seq

vuuvi19:08:22

but something like (map (select-keys access_groups [:group_id]))

vuuvi19:08:46

is returning nil

vuuvi19:08:09

even (map :group_id access_groups) isnt returning anything

lilactown19:08:30

what does (map :access_groups projects) give you? (fixed a typo)

vuuvi19:08:02

that returns ([{:group_name "saas", :group_id 1}])

vuuvi19:08:09

so then I could do another map right?

vuuvi19:08:24

I was using (map #(select-keys % [:access_groups]) projects) originally

mg19:08:36

probably want (mapcat :access_groups projects)

mg19:08:13

select-keys will give you the whole map back in that example

mg19:08:21

since :access_groups is the only key

reborg19:08:04

If the inner vector always contains a single map, you could use get-in (which supports both vectors (by index) and maps (by key):

(map 
  #(get-in % [:access_groups 0 :group_id]) 
  '({:access_groups [{:group_name "saas", :group_id 1}]}))
;; (1)

vuuvi19:08:26

does anyone have any advice on how to better reason about lazy seqs? I feel like I’m always battling laziness

lilactown19:08:55

is it lazyness or just the nested structure of what you’re trying to do?

mg19:08:57

nothing that you’ve talked about here has anything to do with laziness, it has to do with the fact that it’s a collection

vuuvi19:08:08

okay I see

vuuvi19:08:24

I guess it’s just traversing the nested structure then?

vuuvi19:08:14

thanks for the advice guys

noisesmith20:08:42

@alexkeyes it might not be the solution here, but often in Clojure the simplest thing is to construct the data structure that makes your task easiest. There are so many good data functions, that is often the least effort way to get your result

vuuvi20:08:51

makes a lot of sense. Unfortunately I can’t edit the way the data is structured, but the idea is certainly still valid

noisesmith20:08:23

right, but clojure has good functions for getting the shape of data you need out of some input

noisesmith20:08:05

in other words, simplify the function that checks / verifies / calculates, and add a data transformation before it; this usually ends up being better code in clojure

pepas21:08:36

How can I make clojure return early from a function? e.g. (if (= i 0) (return ""))

thiru21:08:05

would love it if this was possible..

pepas21:08:55

so, what are you supposed to do then?

noisesmith21:08:36

have a branch that exits with that value (if, cond, whatever)

pepas21:08:45

So, I also tried (if (= i 0) "") but the function just continues past that point

pepas21:08:52

(instead of returning "")

noisesmith21:08:08

right, because you are not exiting with that value

noisesmith21:08:17

a function has an implicit do

noisesmith21:08:22

(do "" 1) returns 1

lilactown21:08:39

@jasonpepas what does your function look like?

noisesmith21:08:44

you need (if (= i 0) "" (do ... all your other code goes here ...))

👍 4
pepas21:08:00

So, this started by joking around with some coworkers at lunch about interview puzzles, and the "roman numerals" problem was mentioned, which I had never encountered before

pepas21:08:15

So I wrote this in python, and then started trying to figure it out in clojure

pepas21:08:06

(broken) clojure implementation looks like this so far:

lilactown21:08:53

gotcha. so in clojure, the last expression in a defn is what the function will return

seancorfield21:08:14

(and you don't want to be doing def inside defn @jasonpepas)

lilactown21:08:30

so we need to nest these branches

pepas21:08:32

ah. how do I name a temporary result?

lilactown21:08:58

you’ll want to use let. def always defines a global variable in the namespace, it’s not scoped

lilactown21:08:19

so what we really want is something like:

(if (= i 0)
  ""
  (if (= table [])
    ""
    (...

seancorfield21:08:27

let would allow you to destructure the first pair out of the table easily too:

(let [[[symbl value] & more] table] ...)
where more is essentially (rest table)

noisesmith21:08:40

also, cond handles that nicely

pepas21:08:45

oh, so actually I tried using let, but when I used let for table, it said something along the lines of

Exception in thread "main" clojure.lang.ExceptionInfo: Call to clojure.core/let did not conform to spec:
In: [0] val: table fails spec: :clojure.core.specs.alpha/bindings at: [:args :bindings] predicate: vector?

noisesmith21:08:59

(cond (= i 0) "" (= table []) "" ...)

lilactown21:08:10

that’s because let has slightly different syntax than def - it allows you to bind multiple values at once

pepas21:08:22

ahh, I misunderstood how if works

seancorfield21:08:33

(cond (zero? i) ""
      (empty? table) ""
      ...)
🙂

lilactown21:08:50

ya’ll are jumping far ahead 😉

pepas22:08:23

ok, I remember cond from scheme. I tried that as well but must have messed something else up.

seancorfield22:08:32

There's no harm in laying down a trail from "here" to "there" 🙂

lilactown22:08:52

yeah. because of the nested branches, if starts to become pretty unwieldy in this program

lilactown22:08:36

cond is nice because you can put all of your branches at the top level, like you did in python using if statements and early returns

pepas22:08:57

ok, now I remember the problem. how do you make cond's else execute more than one statement?

pepas22:08:24

i.e. the ... in the above examples

noisesmith22:08:41

I even includuded the ... inside do :D

lilactown22:08:03

you probably don’t want to execute things in a do though

pepas22:08:05

ahhhh, ok.

pepas22:08:15

I should have said "I'm trying to do it you would just tell me how!" 😜

lilactown22:08:20

unless you’re printing stuff to the screen

seancorfield22:08:10

By the way, this is a good illustration of the big shift in mindset between, say, Python (imperative, procedural, statement-based) and Clojure (functional, expression-based).

seancorfield22:08:33

if is an expression in Clojure (not a statement).

pepas22:08:59

yeah, actually the first implementation of the python solution was iterative, then I realized that translating it would probably be easier if it were recursive

seancorfield22:08:36

loop is also an expression in Clojure (not a statement) which causes a lot of confusion when getting started... along with for also being an expression (and not really anything like a for loop in other languages).

pepas22:08:45

thanks so much for the help guys!

👍 4