Fork me on GitHub
#beginners
<
2018-12-30
>
Jessie Ross09:12:19

Hi everyone, is there a way to turn off CIDER's stacktrace popping up everytime there is an error?

Ali Ebadian12:12:15

Hi everyone. Here with another basic question. If I have a vector and I would like to do something to its last element n times, how would I do that?

Ali Ebadian12:12:21

at the moment I do this:

Ali Ebadian12:12:25

`( dotimes [x 3] (take-last 1 p)) `

madstap12:12:55

The last element three times: (repeat 3 (last v)) (if you know you have a vector you should use (pop v) instead of last because it's O(1) and last is O(n))

Ali Ebadian12:12:19

ok so options are repeat, for and dotimes?

jaihindhreddy12:12:20

Yeah, (let [x (pop v)] (dotimes [_ n] ...)) where ... is the work you wanna do n times to x

jaihindhreddy12:12:36

Assuming it's purely side effectful.

jaihindhreddy12:12:44

And you don't care about the return value

Ali Ebadian12:12:35

@jaihindh.reddy how would it differ if i wanted the return val?

jaihindhreddy12:12:15

I didn't phrase it quite right.

madstap12:12:39

Dotimes and doseq are for side effects and therefore return nil

madstap12:12:08

Generally you want to do either one or the other.

👍 4
jaihindhreddy12:12:18

If you want the return for each of the n times you do your work, you're better off doing (doall (map f (repeat n (pop v)))).

👍 4
jaihindhreddy13:12:03

Here all side effects execute right there and you get a sequence of the return values of f for each of the n times.

Ali Ebadian13:12:52

Ok in that case ive done everything wrong

Ali Ebadian13:12:06

whoever said clojure was easy?

Ali Ebadian13:12:39

So this function is suppose to take a vector and add its elements by eachother

Ali Ebadian13:12:28

am i doing that wrong?

Ali Ebadian13:12:51

(that is my first step to making a pascal function )

jaihindhreddy13:12:15

What you seem to want is a function that takes a row of the pascal's triangle and returns the next row

jaihindhreddy13:12:39

But your function is putting zeroes on the ends whereas what you want are ones.

jaihindhreddy13:12:13

This function does just that:

(defn next-row [row]
  (let [sums (mapv #(apply + %) (partition 2 1 row))]
    (concat [1] sums [1])))

jaihindhreddy13:12:57

And you can create a pascal triangle of size n by doing (take n (iterate next-row [1]))

Ali Ebadian13:12:15

oh fuck i have no idea what your code does 😅

jaihindhreddy13:12:07

Sorry about that, lemme break it down a bit. Lets say (def row [8 3 7 9 5]) (partition 2 1 row) transforms it to a seq of consecutive pairs, i.e, ((8 3) (3 7) (7 9) (9 5)) And (mapv #(apply + %) (partition 2 1 row)) just takes that and adds the pairs up i.e, [11 10 16 14] And finally we call that sums, and tack on a 1 on either ends

Ali Ebadian13:12:48

No its my bad for not quite knowing what I am doing

jaihindhreddy13:12:30

I'm a beginner too, and I'm sure there are more elegant ways of doing the same thing 😄

Ali Ebadian13:12:37

ok, so partition and mapv are the two fucntions I had not seen before

jaihindhreddy13:12:55

Check out all the arities of partition. Very handy

Ali Ebadian13:12:19

Maybe I jumped into doing thing too fast

jaihindhreddy17:12:27

PS: It got too long. Feel free to ignore 😅 Nah man. It just takes time. Keep at it. A couple of months ago I would have written the solution the same as you did. I noticed that people that come from imperative/OO paradigms initially use loop, recur, for and let a lot. Especially let because that's what's familiar. Then slowly, you start to use map, filter and reduce, and instead of creating locals everywhere, you start to compose transformations of data with ->, ->> and comp. After that point, the more stuff understand in clojure.core, the more expressive and elegant your code becomes. You don't find things like partition, iterate, reductions, interleave, interpose and frequencies in most languages' core libraries as generic sequence operations and so you're left to repeat the logic of these in terms of things like reduce, and various utility libraries emerge. Clojure really gives you a rich vocabulary to express your logic, and gets out of your way in that sense.

Ali Ebadian22:12:53

Thanks, that does sound quite encouraging.

Ali Ebadian23:12:56

I only learn by doing and it looks like there are no tutorials that take you from 0 to intermediate. So I am doing my own excersices

jaihindhreddy23:12:33

That's precisely what it does.

Ali Ebadian23:12:55

I couldnt figure out if they were in order

Ali Ebadian23:12:23

im reading through brave and true book

jaihindhreddy23:12:02

Do them in Elementary, Easy, Medium, Hard order. Ignore the suggestions it gives you after solving each problem. Also after you solve each problem, you can admire the elegance of others' weavings.

Ali Ebadian10:12:54

damn got stock on the 22nd

Ali Ebadian10:12:04

they are harder than I thought

Lucas Barbosa17:12:30

What libraries is the community using to test ring-based apps? I found Kerodon (https://github.com/xeqi/kerodon), but looks like it is not being updated very often (maybe it does not need to)

lilactown17:12:21

what exactly are you looking to test? like, if the HTTP endpoint returns the correct thing?

lilactown17:12:27

or interacting with the page in some way?

Lucas Barbosa17:12:48

For instance, filling forms, pressing buttons and checking results

lilactown17:12:57

I would look into a webdriver solution like selenium

lilactown17:12:18

there’s a clojure webdriver implementation here: https://github.com/igrishaev/etaoin

👍 4
Lucas Barbosa17:12:22

Thanks! I will take a look. Also, a more philosophical question about testing: doing this kind of html test feels like I am testing the same functionality twice. For instance, imagine a form to create a blog post. I can test two things: the html interaction through libraries like the ones we are talking about and the business logic for creating a blog post (validation, etc.)

Lucas Barbosa17:12:59

Is it ok to have tests like this? I mean, checking for validation errors both on HTML and from the business logic function?

lilactown17:12:32

TBH I think that end-to-end tests are far more useful than unit tests to start

lilactown17:12:07

the problem is that end-to-end tests like selenium / etaoin are often a PITA to setup. or at the least, unfamiliar to most devs

jaihindhreddy17:12:31

+1 on the webdrivers being a PITA.

lilactown18:12:14

as your app grows and you start to “pour concrete” on portions of your app, then unit tests can help prevent breaking previous assumptions

lilactown18:12:55

just my opinion 🙂

jaihindhreddy18:12:31

If your front-end architecture looks something like fulcro's, UI is decomplected from all the other stuff in the front-end, and you can test your front-end app without webdrivers. ^ Just something I heard other people talk about and do. I'm clueless 🙂

jaihindhreddy18:12:35

My jaw dropped to the floor when I saw this: https://www.youtube.com/watch?v=qijWBPYkRAQ

lilactown18:12:47

haven’t watched the talk, but if you’re talking about something akin to shallow-rendering (testing the hiccup or react tree is correct), you still are not quite testing it end-to-end

👍 4
absolutejam21:12:00

This may sound like a stupid question - especially for a functional language

absolutejam21:12:37

I'm finding myself splitting a lot of logic that was otherwise just part of a function (eg. iterating a list and formatting a string) into separate functions

absolutejam21:12:44

Is this something common?

absolutejam21:12:21

I know that each function should ideally be a small unit of work for DRY and testability, but in the likes of Python, I'd never normally split parts out so granularly

Kelly Innes21:12:09

I'm new-ish to Clojure but not to functional programming and I think the answer seems to be a qualified yes.

Kelly Innes21:12:23

Seems like it's frequently done in the form of inline lambdas in addition to splitting out named functions, though.

Kelly Innes21:12:54

I think the presence of map and other similar functions to work with collections kind of encourages it. I feel like I don't often see itertools map used in Python, but in Python I do use a lot of list comprehensions which are pretty Haskell-y/Clojure-y

jaihindhreddy21:12:05

@james662 can you give us an example where you feel there's unnecessary splitting going on?

jaihindhreddy21:12:17

If the smaller functions after the splitting still make sense in your domain, then it is good. On the other hand, if the newly split functions don't make sense anywhere except the original call-site, then that's not so good.

absolutejam22:12:36

Well, I'm not sure if it's because I'm still new to the lisp syntax and I don't want to add too much to one place or what

absolutejam22:12:56

I'm finding that where I may have had say 4 lines of logic to determine a variable, I'm resorting to a function

absolutejam22:12:18

I;m not entirely against that because it kinda makes sense, but I'm not sure if this is idiomatic or overkill

absolutejam22:12:06

I mean, I could nest those functions inside the format-bookmark function with letfn I guess

absolutejam22:12:15

the fmt-name-or-alias is pretty bad I realise, because returning "" is dirty, but I was just wanting to get something working while I'm still learning

jaihindhreddy23:12:39

Nothing dirty about returning a empty string. I would personally write it like this:

(defn fmt-bookmark
  "Formats a bookmark to a string"
  [{:keys [name alias tags]}]
  (let [fname (if alias
                (format "%s /%s/" name alias)
                name)
        ftags (when (not (empty? tags))
                (->> (map #(str "#" %) tags)
                     (clojure.string/join " ")
                     (format "|%s|")))]
    (str fname " " ftags)))
A couple of possibly tangential points: The name format-bookmark suggests it returns a formatted bookmark. So the concerns of printing the result and caring about the index can be moved upstream. As to whether fmt-name-or-alias should be factored out, it depends on whether it's useful elsewhere. I would move it out when needed.

absolutejam23:12:33

Sweet, makes sense. I keep looking for an excuse to use the threading macros because they seem so handy when I see them in example code

absolutejam23:12:00

Also, I didn't realise I could have login in a let statement's expressions

absolutejam23:12:03

which is obvious now

jaihindhreddy23:12:15

Also, because you aren't formatting the index here, you don't need format here. And str treats nil as an empty string, allowing us to use when which removes the "" from str-tags

absolutejam22:12:34

unless I can get format not to force nil into "nil"

enforser22:12:49

You could use str to turn nil into "". i.e. (format "%s" (str nil)) Your code looks fairly standard to me as far as splitting logic up into functions. One way to reduce the number of functions is to try and find generic logic and make one function that can handle multiple cases. In your case:

(defn fmt
  [name v fmt]
  (if (nil? v)
    (str name)
    (format fmt name v)))
could eliminate two of the helper functions

enforser22:12:14

another way to change the way nil is handled is with fnil. (fnil format nil nil ""), returns a function which acts the same as format but when the third arg is nil it will use "" instead

absolutejam23:12:04

That's good thinking, thanks for your help @trailcapital

absolutejam23:12:29

I'm not all too worried about the efficiency etc. at the moment, I'm just trying to convert my thinking to the functional paradigm