Fork me on GitHub
#beginners
<
2016-02-23
>
drewverlee01:02:12

Oddly, i feel this might be the best place to ask this question. Whats a good resource (book, blog, course, etc...) for learning java (v8 is perfered)? I picked out "Java for the impatient" and "java a beginners guide". I'm not enjoying the later as much as i would have hoped.Though im only on the first chapter.

jack_liang02:02:23

Hi there, i was wondering if anybody can tell me about return statements in clojure? As in me creating a function that returns something. I googled it but it didnt really turn up anything except for a person saying there is no return statement in clojure.

jack_liang02:02:34

is that true?

donaldball02:02:53

As a lisp, clojure doesn’t have statements, only expressions

jack_liang02:02:54

would i be able to get a function to return a list?

jack_liang02:02:09

ive done java but im new to clojure and lisp

donaldball02:02:44

Yes, clojure fns return values, including lists. You might want to skim through an introductory guide, e.g. http://www.braveclojure.com/introduction/

jack_liang02:02:11

ive read that, but im looking for the way to return the list, may i ask whats the keyword for returning values? like if i wanted to return "List X" in java it be " return X", whats the clojure equivalent of "return" ?

clumsyjedi02:02:32

jack_liang: there is no keyword for returning values. Clojure has “implicit return values"

clumsyjedi02:02:52

an example, to return the list [1 2 3] from a function

clumsyjedi02:02:06

(fn [] [1 2 3])

clumsyjedi02:02:19

or to return the string “hello” from a function

clumsyjedi02:02:26

(fn [] “hello”)

clumsyjedi02:02:52

as long as your return value is the last expression in your funtion, it will be returned implicitly, does that make sense?

jack_liang02:02:49

so lets say i have (fn [] (println "Jeff") (def ret ("jeff")))

jack_liang02:02:59

does that return the value "jeff" ?

clumsyjedi02:02:38

(fn [] 
  ;; do anything with Jeff you want here
  “Jeff”)

clumsyjedi02:02:12

do you have a clojure repl available that you can play around with? That will help a lot I thiink

jack_liang02:02:52

im more comfortable editing a clj file and just running it

jonahbenton02:02:53

hey @jack_liang it's tricky to come from java- from that perspective, clojure does a lot of stuff implicitly, whereas in java it's all done explicitly. return statements are one example. if statements are another- the else part of an if in clojure is implicit. one has to retrain one's eyes to read what are referred to as "forms" into the syntax that you're familiar with

jonahbenton02:02:14

anyway- in your example, the second "jeff"- in ("jeff")- will cause clojure's compiler to complain, because it will want to treat the string "jeff" as a function. but strings aren't functions in clojure.

jonahbenton02:02:54

if you change it to (fn [] (println "Jeff") (def ret "jeff"))

jack_liang02:02:20

so will the following work? (fn [] (println "Jeff") (def ret "jeff") ret)

jonahbenton03:02:16

well- what you get from that is a function- which, if executed, will print "Jeff" (to System.out in java terms) and return "jeff" to whatever calls the function

jack_liang03:02:46

so i can just leave the variable at the end of the function and if looking for return value, that variable will be the value

jonahbenton03:02:56

well, variables are another thing that work differently in clojure than java. the (def ret "jeff") creates a kind of global variable called "ret"

jonahbenton03:02:34

it's better to create local variables, using the let statement

jonahbenton03:02:47

(let [ret "jeff"] ret)

jack_liang03:02:11

so essentially (fn [] (println "Jeff") (let [ret "jeff"] ret) ret)

jonahbenton03:02:39

get rid of the second "ret"

jack_liang03:02:08

(fn [] (println "Jeff") (let [ret "jeff"] ret) )

jonahbenton03:02:09

the lifetime of the "ret" in the (let ) block is only to the closing paren of the let

jack_liang03:02:49

so if i left that second let, i suppose compiler complains that it doesnt know what the hell the second ret is?

jonahbenton03:02:40

correct- if you left in that second "ret", the compiler would complain. that symbol went out of scope at the close of its defining let

jack_liang03:02:07

so for local instances, do i have to nest lets for each local instance?

jonahbenton03:02:27

no, you can include multiple "bindings" within a single let

jonahbenton03:02:44

(let [a "jack" b "jeff" c "jonah"] b)

jonahbenton03:02:52

will return "jeff"

jonahbenton03:02:10

but within that closing paren, variables a, b and c have values

jonahbenton03:02:31

a has value "jack"

jonahbenton03:02:35

b has value "jeff"

jonahbenton03:02:38

c has value "jonah"

jack_liang03:02:54

thank you , you are very helpful

jack_liang03:02:58

i think i got the gist of it

jonahbenton03:02:37

sure thing. it can be tricky to retrain the eyes to read things in the clojure/lisp manner, but once you get there it's pretty cool

jack_liang05:02:13

i mean "def c" to "println"

seancorfield05:02:07

def creates a top-level "global" binding.

seancorfield05:02:50

Remember there really are no "variables" in Clojure -- you don't "assign" to vars, like you would in other languages.

jack_liang05:02:43

may i ask how you would go about this? what im trying to do is traverse a list of contraction words and replace the existing contraction words in the string with the base words, like replacing can't with cannot

seancorfield05:02:05

The closest construct to what I think you're trying to do is (loop [bindings... ] (if (end-condition) result-expr (recur updated arguments)))

seancorfield05:02:57

Since you want to work on a sequence of words and just replace each word with either its expanded version or itself, you probably want map...

seancorfield05:02:28

In general, in Clojure, you work on whole collections rather than "iterating" / "looping" like you would in imperative languages.

jack_liang05:02:11

how would you work on a whole collection ? when i hear a phrase like that, i think looping, I come from a background of java

seancorfield05:02:24

map / filter / reduce.

seancorfield05:02:25

I think for your problem you'd want a hash map from contracted words to their expanded form {"can't" "cannot", "won't" "will not", ...} and then you'd map over the collection like this: (map #(if-let [expanded (contractions %)] expanded %) word-list)

seancorfield05:02:59

where contractions is that hash map with all the contractions and their expanded forms

seancorfield06:02:22

You generally use map when the result collection has the same number of elements as the input collection. You use reduce when you're going to a different type of result -- a different collection type or a "reduced" collection (such as aggregating / counting values). And filter when you just want to remove some elements that match some condition.

seancorfield06:02:44

Does that help?

seancorfield06:02:00

Coming from Java to Clojure can be a big, confusing change. Pretty much everything you're used to in Java is... different in Clojure. No variables, no mutable data (well, mostly no mutable data), so no side effects (for the most part) and no loops in the Java sense...

jack_liang06:02:55

are the # and % actually part of the syntax in your snippet?

jack_liang06:02:32

sorry, yea they are

seancorfield06:02:34

#( ... % ... ) is an anonymous function.

seancorfield06:02:11

You could say (fn [word] (if-let [expanded (contractions word)] expanded word))

seancorfield06:02:04

Would this look simpler to you: (fn [word] (get contractions word word)) ?

seancorfield06:02:23

get takes a collection and a key, and a default value...

seancorfield06:02:47

I wasn't sure whether the explicit if would be easier to follow...

jack_liang06:02:03

oh i understood, took me a min cuz of the if-let but i understood

jack_liang06:02:19

i noticed you used "word-list", if i had a sentence, wouldn't i have to tokenize it into a list to use the map implementation you mentioned?

seancorfield06:02:04

(str/split sentence #" ") or something more sophisticated...

jack_liang06:02:33

so yes id have to have it in list form somehow before i can use implementation

jack_liang06:02:11

i do have a tokenizing method so that wont be too hard

seancorfield06:02:22

Ah, so you were replacing substrings in place? Hmm, well, then you could reduce over the contractions and do it that way...

jack_liang06:02:58

Originally i have "I can't walk" and after replacing, i was hoping to have "I cannot walk"

seancorfield06:02:13

(reduce (fn [s w] (str/replace s w (contractions w))) sentence (keys contractions))

seancorfield06:02:05

(keys contractions) gives you the sequence of contracted words -- the keys -- of the contractions hash map

seancorfield06:02:07

So that expression starts with the sentence as the initial value and a collection of contracted words, and "reduces" that by replacing each contracted word by the expanded equivalent...

seancorfield06:02:25

(sorry for the edits, I'm doing this in my head, rather than a REPL like I should be!)

jack_liang06:02:39

its alright, im just happy somebody responds at 1:23am

jack_liang06:02:04

for the record, I dont use repl whe i probably should, i edit the clj file and run via cmd

seancorfield06:02:41

Experimenting in the REPL is a great way to learn how various Clojure functions work.

jack_liang06:02:25

i never really got the gist of repl, i mean...entering code 1 line at a time? Gets messy

seancorfield06:02:36

Clojure isn't line-based tho', it's form-based. It's designed to be compiled and executed one form at a time.

jack_liang06:02:35

i might have been going about it wrong then, ill give it a shot, but nothin beats an old fashion editor! at least in my opinion

seancorfield06:02:07

@jack_liang: The best Clojure experience is an editor with a built-in REPL where you can write code and evaluate it with just a key stroke... Since you're coming from Java, are you used to Eclipse or IntelliJ?

jack_liang06:02:34

ive used both

jack_liang06:02:50

but for some reason, intellij wont print so i opted for notepad++ and cmd

seancorfield06:02:09

For Eclipse, there's Counter Clock Wise, and for IntelliJ, there's Cursive.

jack_liang06:02:09

i feel like it wont print for a very stupid reason but i cant put my finger on it

seancorfield06:02:03

Based on the code you showed earlier, I suspect you're running foul of a couple of things: 1. (println "whatever") evaluates to nil (even tho' it prints the string, it's result is nil); 2. if you have a lazy sequence and don't consume the values, you won't get the side effects of any functions in those expressions either.

jack_liang06:02:44

im sorry, consume the values?

jack_liang06:02:54

you mean using the values?

seancorfield06:02:36

For example (let [s (map println [1 2 3 4 5])] "Hi!") -- evaluates to "Hi!" but prints nothing because s is not "consumed" so map's value is never evaluated.

jack_liang06:02:37

sorry, maybe its time to go to bed for me. Thank you for educating me in the mysterious and somewhat confusing ways of clojure.

seancorfield06:02:38

You're welcome! Folks here are all very helpful around the clock!

adamkowalski07:02:39

Are transducers always evaluated eagerly? Or can I create a lazy sequence with them?

adamkowalski07:02:18

lets say for some reason I would like a function that gives me all of the even squares of the natural numbers

adamkowalski07:02:37

(def even-squares (into [] (comp (filter even?) (map #(* % %))) (iterate inc 1)))

adamkowalski07:02:12

if I then do (take 10 even-squares) it will hang as it seems to try to create all of the even squares before taking the first ten

adamkowalski07:02:26

(def even-squares [n] (into [] (comp (filter even?) (map #(* % %)) (take n)) (iterate inc 1)))

adamkowalski07:02:52

that seems to fix the issue, but then I do not have a way to take about the whole sequence, but rather some sub sequence of the whole

seancorfield08:02:15

into [] is an eager reduction.

adamkowalski08:02:39

so if I changed into [] with sequence would that change things?

seancorfield08:02:43

Try it and see.

seancorfield08:02:19

According to its docstring, sequence will not force a lazy seq and it returns a lazy seq.

adamkowalski08:02:55

yeah it seems to work! thank you

seancorfield08:02:01

(after midnight here and I've been traveling all day so I'm out)

adamkowalski08:02:49

How would you accurately time something like that?

adamkowalski08:02:10

if I would like to benchmark the time it takes to generate the sequence

adamkowalski08:02:32

(defn even-squares [n] (take n (sequence (comp (filter even?) (map #(* % %))) (iterate inc 1)))) (defn even-squares-2 [n] (take n ((comp (partial filter even?) (partial map #(* % %))) (iterate inc 1)))) (do (time (even-squares 1000000)) (println "even-squares")) (do (time (even-squares-2 1000000)) (println "even-squares-2"))

adamkowalski08:02:35

I tried doing that

adamkowalski08:02:44

but I am getting garbage times that are impossible

adamkowalski08:02:56

Elapsed time: 0.059917 for even-squares

adamkowalski08:02:08

0.226008ms for even-squares-2

rauh08:02:19

@adamkowalski: You need to realize the lazy seq. (`dorun` e.g)

adamkowalski08:02:48

thank you simple_smile now I am getting 282ms for the transducer version and 588ms for compose and partial version

adamkowalski08:02:01

can you still use things like pmap with transducers

olegakbarov10:02:49

totally newbie quiestion: how can i perform basic javascript:

let arr = [{title: “Tiltle”, value: 33}. {title: “Another”, value: 11} … ]
arr.map(i => { 
  return { 
    title: i.title.toUppercase(), 
    value: i.value++
})

olegakbarov10:02:44

i try to do same in clojure, but not sure i’m doing it right

olegakbarov10:02:16

this must be very easy, but i’m kinda stuck

olegakbarov10:02:27

(into [] (map #({:title (:title upper-case)
                  :id (inc id)} 
      %) arr))))

olegakbarov10:02:32

this obviously fails

doddenino10:02:14

Mmm maybe (into [] (map (fn [{:keys [title value]}] {:title (clojure.string/upper-case title) :value (inc value)}) arr))

doddenino10:02:29

but there are probably shorter ways 😄

doddenino10:02:36

@olegakbarov:

olegakbarov10:02:53

yeah, i’m looking into this, thanks!

grounded_sage10:02:54

Could I ask for some quick help from anyone hopefully this is question is not that Garden specific. I am trying to use pseudo elements. When I do this (defpseudoelement after) and run this (css [:body after {:background 'blue}]) in the repl I get back this "body, ::after {\n background: blue;\n}". Not sure how to stop the comma from after body.

doddenino11:02:21

@olegakbarov: maybe this is a little more readable (into [] (map #(-> % (update :title clojure.string/upper-case) (update :value inc)) arr))

olegakbarov11:02:33

yea, this looks nice i try this, but it throws in repl: (map #({:title (-> % :title .toUpperCase) :value (-> % inc}) arr)

doddenino11:02:32

I think it's because you can't just put a map in first position in the form, it has to be a function

doddenino11:02:59

so maybe something like #(into {} [[:title ...] [:value ...]])

olegakbarov11:02:32

this last notation, i don’t quite get it

doddenino11:02:51

the dots are just because I'm lazy

doddenino11:02:41

#(into {} [[:title (-> % :title .toUpperCase] [:value (-> % :value inc]])

doddenino11:02:25

I'm not entirely sure you can put js methods in a threading macro like this, but I've never tried

doddenino11:02:33

missing a couple of ) there 😞

mostr11:02:19

or like this, but not as short as the ones above

(->> arr
     (map (fn [e] ((comp
                     #(update-in % [:title] clojure.string/upper-case)
                     #(update-in % [:value] inc)) e))))

mostr11:02:44

also definitely not that readable too simple_smile

mostr11:02:57

but hey, it works troll

olegakbarov11:02:10

i’m really surpirised, that it takes quite an effort to perfrom such a trivial operation

mostr11:02:21

update-in is quite neat

mostr11:02:32

but unfortunately doesn’t support multiple keys/changes

mostr11:02:58

it’d be straightforward if it would

olegakbarov11:02:03

yeah this do works! 😃

olegakbarov11:02:58

i use this pattern all of the time in JS/React, need to get to the bottom of this in Clojure

olegakbarov11:02:40

update-in is cool and works (thanks again) but there have to be an easier way

mostr11:02:42

(map #(into {} [[:title (-> (clojure.string/upper-case (% :title)))] [:value (-> (inc (% :value)))]]) arr)

doddenino11:02:59

I think the one I put earlier with update is the shortest and easiest

mostr11:02:14

yeah, it looks so

mostr11:02:56

but anyway, did some brain gymnastic playing with this simple_smile

doddenino11:02:00

Ahah yeah sure, I'm doing the same. Still pretty new to clojure!

slipset12:02:17

I might just have missed something, but

slipset12:02:42

I think some of what @stuartsierra says in http://stuartsierra.com/2015/06/10/clojure-donts-heisenparameter is that you should strive to write functions which operate on one thing.

slipset12:02:16

So for the problem above, I’d write a function like (defn frobnicate [{:keys [title value]}] {:title (clojure.string/upper-case title) :value (inc value)})

slipset12:02:25

and then just

slipset12:02:32

(map frobnicate arr)

slipset12:02:48

which may or may not be more or less performant than the other solutions, but I’d leave that for the day when it becomes a proven problem.

olegakbarov12:02:46

i’ve just came up to similar approach!

olegakbarov12:02:13

moved function that processes item in map to separate function

olegakbarov12:02:56

(defn process [item] {:title (-> item :title .toUpperCase) …})
(into [] (map process arr))

slipset12:02:52

even cooler:

slipset12:02:07

(defn uc-title [m] (update-in m [:title] clojure.string/upper-case))

slipset12:02:16

(defn inc-val [m] (update-in m [:value] inc))

slipset12:02:37

(def frobnicate (comp inc-val uc-title))

slipset12:02:03

(map frobnicate arr)

slipset12:02:37

If you really need things to be in a vector at the end, you can use mapv

olegakbarov12:02:58

one caveat — i want to drop some existing values

olegakbarov12:02:32

so i have raw database data — with timestamps and stuff, and i want just to pull some values and format them

olegakbarov12:02:43

(thanks for mapv, i’ll take a look)

slipset12:02:56

select-keys is your friend

olegakbarov12:02:26

let’s see 😊

doddenino13:02:26

@slipset: Is there any reason to use update-in over update?

slipset13:02:41

not really

slipset13:02:48

just my bad

doddenino13:02:55

Oh I see, I thought that maybe it was "safer" or more performant simple_smile

mostr13:02:50

update-in allows for updating in nested structures, while update doesn’t

mostr13:02:59

but it doesn’t really matter here

moizsj13:02:52

Hey everyone, been working with Clojure past few months (coming from a Java background). One of the few things that I dont comprehend yet is how do you really design large, complex multi layered systems in Clojure? Have you come across a good guide or book that deals with this subject. Thanks for your advice.

mostr13:02:19

@moizsj: there is quite neat talk from Clojure/West on how Walmart uses Clojure https://www.youtube.com/watch?v=av9Xi6CNqq4

mostr13:02:56

neither a book, nor definitive guide but imho worth watching

slipset13:02:38

a propos select-keys, I find I agree to the comment on the bottom of this page https://clojuredocs.org/clojure.core/select-keys

slipset13:02:19

which is why I’d really like to see flip in clojure https://www.haskell.org/hoogle/?hoogle=flip

urbanslug14:02:23

Hello, how can I test the equality of regexes since simple patterns such as this fail: (is (= (re-pattern "ab\\?c") (re-pattern "ab\\?c")))

jonahbenton14:02:16

hey @urbanslug: (str ...) on the results of re-pattern should return the original string used to create the pattern object. e.g. (= (str (re-pattern ...) (str (re-pattern ...)))

curtis.summers14:02:22

@urbanslug: @jonahbenton is right. str effectively calls .toString on the java.util.regex.Pattern that re-pattern returns. So, I guess the other question is why is the hash code of a java.util.regex.Pattern different for the same pattern?

ghadi15:02:36

sigh, value equality.

urbanslug16:02:03

Is there a way to specify you want to install phantomjs in your project.clj? It doesn't seem to be where lein gets its deps.

adamkowalski22:02:45

is it a good idea to use :pre and :post conditions as a type checking mechanism? or is this going to cause performance problems?

adamkowalski22:02:58

like if I have a function which expects a vector should I have a (defn my-function [my-vector] {:pre [(vector? my-vector)]} (do-something-with my-vector))

jswart22:02:04

@adamkowalski: : you can do that or if you need more complex checking most people use Prismatic’s Schema library. You can decide when to use schema and take the performance hit.

adamkowalski22:02:10

interesting, and what about core.typed?

jswart22:02:26

its younger and picking up steam

adamkowalski22:02:42

as far as I know that only works with clojure but not clojurescript right? but pre/post and schema works with both?

jswart22:02:49

most in the community seem to be okay with schema as a sort of contracts rather than types setup

jswart22:02:27

not entirely sure of the second question

adamkowalski22:02:33

ok well i’m just glad to know it seems like there are a few options to validate your code, because sometimes purely dynamic languages seem to be a little scary to me

jswart22:02:09

Yeah that is fair. I tend to find that when I am working toward a solution I don’t want anything to get in they way. Then when I know exactly what code should do I “lock it down” with schema checks if necessary.

adamkowalski22:02:09

yeah that makes sense, but I think that generally what ends up happening without a type system is that you end up relying more on TDD to compensate

adamkowalski22:02:45

not saying that is good or bad, but I think that in the long run having a way to make certain assertions about what your code can and cannot do can actually save you time in the long term

jswart22:02:07

Yeah, to each their own. Dynamic vs. Static has been debated endlessly. In the end most of my issues don’t have anything to do the code but rather the complex interactions of a large systems. I do enjoy some of the benefits of typed code but in general find that I am much more productive in a dynamic environment.

adamkowalski22:02:40

I definitely see the appeal of dynamic languages and they are certainly starting to grow on me, especially with features like transducers which I don’t know how you could express in a type system

adamkowalski22:02:27

and with things like property based testing it makes it much simpler to check the things that could actually go wrong with your program in addition to unit testing

adamkowalski22:02:05

but when I was learning Haskell, one thing I loved is how I could look at the type signature of a function and immediately know exactly what I could put into a function, and what I could expect to get out

adamkowalski22:02:49

with clojure, it is generally up to the discretion of the author to have valuable doc strings, and well named functions/parameters that state the purpose of the function

adamkowalski22:02:33

which is why I would like to try out either pre/post or core.typed or schema for validation, but I dont know if this will be removed in production so I do not pay a performance penalty or if these things stay and will always be ran.

jswart22:02:00

Yeah, unfortunately still no silver bullets.