Fork me on GitHub
#beginners
<
2018-12-31
>
nikola12:12:23

I have a function that updates multiple values in a vector of maps:

(defn guard-awakes
  [guard
   asleep-minute
   slept-minutes
   guard-map]
  (update-in (update-in guard-map [guard :asleep] (fnil #(+ % slept-minutes) 0))
             [guard :minutes] (fnil #(concat % (range asleep-minute (+ asleep-minute slept-minutes))) ())))

nikola12:12:16

It also creates an entry if the key doesn't exist. Is there a better way to update twice instead of this nested call to update-in?

nikola12:12:05

This is an example data {{99 {:minutes (list 11 12 13), :asleep 3}} {10 {:minutes (), :asleep 8}}}

jakuzure13:12:17

hey, I'm having some troubles with debugging clojurescript. is the preferred way using figwheel and a repl or just figwheel? I can't seem to be able to connect my emacs to a cljs repl via cider, so I'm just using figwheel for now

enforser13:12:36

@nikola.kasev you could make that more readable with a threading macro (`->`).

(-> guard-map
    (update-in [guard :asleep] (fnil + 0) slept-minutes)
    (update-in [guard :minutes] (fnil concat '()) (range asleep-minute (+ asleep-minute slept-minutes))))
If you want to update twice in the same path then you could do something like:
(update guard-map
        guard
        #(-> %
             (update :asleep (fnil + 0) slept-minutes)
             (update :minutes (fnil concat '()) (range asleep-minute (+ asleep-minute slept-minutes)))))

Ali Ebadian14:12:06

how do you guys debug and find out where in the chain of functions an error i occuring?

enforser14:12:44

I had this talk sent to me when I first started working in Clojure, and I think that it has been the most useful piece of knowledge in my arsenal. https://www.youtube.com/watch?v=FihU5JxmnBg&amp;t=1213s Aside from that, if I'm trying to find the source of a problem in a large chain of functions, I try to use a binary search method. i.e. add print statements in the code to try and determine where the error is occurring, but in an intelligent way. Debugging will go faster if you can eliminate large chunks of code at a time. For example, if you have 100 functions and no idea/intuition on where the error may be, then start by trying to verify if the result at the 50th function is right or wrong. This allows you to eliminate half of the functions which you were previously considering. Then repeat that until the error is found! One of the very nice aspects of having pure functions is the ability to easily verify if they are working. They are simple to test, and you can generally debug them in the REPL

drewverlee00:01:06

I dont have much to add to what enforser is saying. when there is complex logic i usually use a threading macro

-> start
     map a
     filter b
     reduce c
Now if something is off, i just walk down the macro and comment things and explore the output.

drewverlee00:01:42

if that thread is part of a larger function i pull it out in my editor and do it from there

drewverlee00:01:33

there are several tools that can produce input data and show data along the way

drewverlee00:01:42

the most powerful of the generators is specs

drewverlee00:01:22

but a more light weight approach (usually enough) is to just capture some actual data that was fed to that function

drewverlee00:01:37

IMO the trick is to not have to debug. Must of what needs to be debugged is usually a product of approaching the problem in the wrong way in the first place.

jaihindhreddy14:12:48

^^ No need to use fnil on concat. It's nil punned away. (concat nil [1] nil [2]) returns (1 2)

jaihindhreddy14:12:08

@nikola.kasev It seems like :asleep is just the count of :minutes and :minutes seems to track the minutes which the guard slept in a particular hour. You can calculate :asleep when you want by calling count on the value of :minutes. Also, a set makes more sense for :minutes.

enforser14:12:40

@shin I would suggest watching https://www.youtube.com/watch?v=KZjFVdU8VLI to see a clojurescript workflow with figwheel. Cider should be capable of connecting to a cljs repl, but you may need to add some additional middleware. I would try #emacs or #figwheel for help getting the cljs repl setup

jakuzure14:12:57

@trailcapital thank you! I'll check the video out first

enforser14:12:13

Just remembered about this one as well: https://www.youtube.com/watch?v=j-kj2qwJa_E&amp;t=2012s It goes a little more in depth, if you feel like the previously linked one was a little lacking.

nikola15:12:40

@trailcapital thanks, your suggestion is much more readable than mine

dpsutton15:12:51

Check out process-sleeps&wakes

nikola15:12:23

@dpsutton haha, so you also did AoC 🙂

dpsutton15:12:59

Some of it. These ended up taking more time and feeling more like work this year

nikola15:12:57

It's been a very rewarding experience for me, I'm learning Clojure by solving the problems

nikola15:12:12

with much help from the community of course

dpsutton15:12:22

Yes it is a fun experience to do it with several people and discuss and see other approaches to the same problem

nikola15:12:21

How does it compare: the code one writes for AoC and in a real-world Clojure job?

nikola15:12:38

More specifically: would it be easy to work on a real-world Clojure project if I've done most of AoC?

dpsutton16:12:42

I don't think real world is ever as clean as fun problems like aoc. There's always more error states and dirty data and special cases. But being fluent in the language is always beneficial

Kelly Innes16:12:20

However I am also still just learning so I don't know how idiomatic that is

Kelly Innes16:12:11

One thing that seems real-world-useful about AoC is that it really makes you read and interpret requirements really carefully -- missing a single word somewhere seemed like it would cause me to go down a wrong path in implementing the solution.

mfikes16:12:56

@kinnes FWIW, I've tried to write my AoC solutions in an idiomatic fashion, optimizing for clarity / simplicity and not so much for performance. Here are mine if interested https://github.com/mfikes/advent-of-code

Kelly Innes16:12:55

Awesome! Will take a look at those.

Lucas Barbosa17:12:13

Is it worth creating protocols/records when creating "model" objects in an application? For instance, if I have a blog web app, and I want to have a map that represents a blog post: is there any advantage to create a record representing posts instead of simply creating the functions that create and manipulate a hash-map that represents a post?

Lucas Barbosa17:12:02

It feels like the record mechanisms reduce the boilerplate, but is there any other advantage?

seancorfield17:12:44

@lvbarbosa Start with regular maps. Switch up to protocols / records if you need them.

lilactown17:12:30

I often think about using protocols as a way of allowing external parties to extend my abstractions. if I control all the code, I rarely feel the need to use such things

SoV418:12:19

i was thinking about writing my own symbol generator, i mainly need unique IDs for storing things and retrieval. integers makes the most sense (especially for map lookups) .. are string lookups in a map about the same speed as a int? I can generate symbols like 12345 or nf_12345 and (gensym "nf_") makes that easy (the latter)... any opinions?

hiredman18:12:30

I would maybe back up a few steps

hiredman18:12:06

like, do actually need two types of ids?

hiredman18:12:09

(symbols + whatever key you choose for the maps), like if you were to use numbers to key the map, why not just numbers as the key and not bother with symbols at all?

hiredman18:12:21

key performance wise, I suspect ints would be faster, followed by keywords, then symbols, but the difference may not be that much. you may want to look at https://github.com/clojure/data.int-map

SoV419:12:28

@hiredman [thoughts] i suspect ints would be much faster for a lookup, but in a map of n size, i don't think string indexing would take much longer... i could use ints to store things and retrieve them, is there an easy way for me to generate unique ints ? i was thinking about just starting with some number and doing (inc x) to get ids but maybe there is a better way to generate a unique id integer. my guess is that in gensym there must be some sort of hash against the time that is monotonically increasing, but i'm not sure. so that might be one way. or just increment a digit and keep track of it, that is also an easy fix.

SoV419:12:36

data.int-map looks like overkill at the moment when i'm simply trying to put wheels on my sk8board. but i shall keep it in mind! and make a note of it in my progress.md

hiredman19:12:27

if data.int-map is overkill then just use java.util.UUID/randomUUID for keys

SoV421:12:18

UUIDs are so laborious for a URL though

SoV421:12:35

i'm thinking ~7-15 digits

hiredman22:12:09

use a different encoding

quadron23:12:55

when would I need a -> macro instead of a simple function that feeds its first argument to the reverse composition of the rest of its args which are all functions?

quadron23:12:18

(feed x f g h)

Kelly Innes23:12:44

I think it's an ergonomic convenience?

hiredman23:12:48

the -> macro manipulates syntax, which is very different from what a function would do, but the result is often the same

quadron23:12:21

the problem is, I often find the -> macro less readable, so it's not ergonomic at least to me

hiredman23:12:38

but for example: (-> 10 (Long/toString 16))

hiredman23:12:11

(Long/toString 16) is not a function value you can pass as an argument and apply

hiredman23:12:32

in general, when you learn new things you should expect them to be new

quadron23:12:46

(feed 10 #(Long/toString % 16))

hiredman23:12:06

right, and that is exactly the difference

quadron23:12:42

if i happen to want to add a function that takes its argument second i would need to go over and change -> to as->

quadron23:12:52

this happens very often