Fork me on GitHub
#beginners
<
2018-01-23
>
michael74000:01:55

i'm trying to model a singly linked list. I don't think hash maps will work because i want to allow same-valued nodes. what about a record?

michael74000:01:31

in python i'd use a class, and as far as i can tell the simplest "class"-like thing in clojure is a record

noisesmith01:01:39

list and lazy-seq are already singly linked lists, I don't know why you would use a hash-map, how is that useful when each node has exactly one successor?

noisesmith01:01:55

if you want to make a singly linked list in clojure and not use a list or lazy-seq (or something from java.util.Collection), the easiest thing is {:value foo :next bar} where bar is another hash with the same keys, or nil

michael74001:01:26

what would node.next look like if i used a list?

noisesmith01:01:05

in fact, there's a next method too

noisesmith01:01:17

user=> (.next (list 1 2 3))
(2 3)

noisesmith01:01:54

but it's better to use the next function, as it knows how to handle other types, and it's a first class value

michael74001:01:06

looks like that would work great

michael74001:01:31

but part of the exercise is deliberately using a more primitive data type, perhaps

noisesmith01:01:43

function instead of method

user=> (next (list 1 2 3))
(2 3)

michael74001:01:44

where you're handling the nodes and nothing else, if that makes sense

noisesmith01:01:03

sure - then you can use (:next l) if l is the hash map I initially proposed

michael74001:01:20

when would you use a record over a hash map?

noisesmith01:01:42

+user=> (defn construct [x y] {:val x :next y})
#'user/construct
+user=> (construct 1 (construct 2 (construct 3 nil)))
{:val 1, :next {:val 2, :next {:val 3, :next nil}}}
+user=> (def ls *1)
#'user/ls
+user=> (:next ls)
{:val 2, :next {:val 3, :next nil}}
+user=> (:next (:next ls))
{:val 3, :next nil}

noisesmith01:01:06

generally you would use a record if you need to implement a protocol or interface - if you have methods you need to overload

michael74001:01:25

ok, that makes sense.

michael74001:01:53

am i right in thinking the above example doesn't implement a protocol/interface that hash-map doesn't already have?

noisesmith01:01:09

right - it literally is a hash-map

noisesmith01:01:40

if you wanted it to print like a list, or be accepted by all the standard list functions, then you'd want to implement the Interfaces that define a clojure list

jstuartmilne01:01:46

Im trying to build a simple rest-api with Luminus. just playing. would like to expose the guestbook as a json api. been googling for a while now and i still cant find a simple example for returning basic json data like array or simple objects from a given db query

noisesmith01:01:52

@michael740 if you wanted to also make sure it wasn't a hash-map and couldn't be used that way, there's also deftype

michael74001:01:55

good to know.

noisesmith01:01:46

@jstuartmilne there's a wrap-json middleware that can take a request handler function and return a new one that makes json - then you just return your data structure

noisesmith01:01:26

@jstuartmilne any standard clojure binding for a database should hand you plain data structures as a result, then it's just a question of making the transforamtions (if any) needed to construct the data your client should expect

jstuartmilne01:01:57

thank u @noisesmith I kind of got the theory behind it, just couldnt mange to find an example.

jstuartmilne01:01:36

do you know of a working example to check it out.

noisesmith01:01:45

where f is a function that takes a request and returns some data

jstuartmilne01:01:23

interesting ill give that a shot

noisesmith01:01:50

np - I think the example in those docs should be enough to start with - though I understand if you haven't used ring much yet you'll want to play with the request and see what's in it

noisesmith01:01:08

the good news is it's a hash-map, you can print it, look at the keys, use standard clojure data functions on it

noisesmith01:01:34

ring is an exemplary clojure library in that it eliminates a lot of unneeded complexity to just let you use data

jstuartmilne01:01:10

yeah i was going through the Luminus docks. and the documentation specifies By default, muuntaja middleware library is used to infer the response type when a route returns a map containing the:body key:

jstuartmilne01:01:20

meaning it should infer

noisesmith01:01:48

interesting - what did you try putting under that key? and what would it use to infer by?

jstuartmilne01:01:56

or at least thats my understanding. Im surprised i could not find an example for a simple CRUD

jstuartmilne01:01:56

(GET "/message" []
     :return      []
     :summary     "x^y with header-parameters"
     (ok (db/get-messages)))

noisesmith01:01:59

OK - looking at the muuntaja repo, it looks like it adds the right headers if your :body contains a string that looks like json?

jstuartmilne01:01:20

so been trying different things

noisesmith01:01:10

oh, you have to explicitly call muuntaja's encode function if it is using that

jstuartmilne01:01:51

i think the middleware does that

noisesmith01:01:11

but what I don't understand is what tells it "make this json"

jstuartmilne01:01:25

yeah you are right

jstuartmilne01:01:34

im clearly missing soemthing hang on

noisesmith01:01:38

anyway, looks like there's docs at the muuntaja repo https://github.com/metosin/muuntaja/wiki/With-Ring (edited for better example)

jstuartmilne01:01:43

thank u. i still havent figured it out but im not completly lost now

jstuartmilne01:01:45

@noisesmith thank u got it

jstuartmilne01:01:58

was the simplest thing

jstuartmilne01:01:24

(GET "/message" []
         {:body {:data (db/get-messages)}})

michael74001:01:45

i'm trying to translate a simple tail call recursion into loop...recur

michael74001:01:48

(defn reverse_l
  [list_]
  (if-not (seq list_)
    list_
    (concat (reverse_l (rest list_))
            (list (first list_)))))

michael74001:01:05

am i correct in thinking my call to reverse_l is not in the tail position?

michael74001:01:09

and therefore i'd need to rework it into something fundamentally not recursive

lockdown-01:01:41

yes, is not in tail position

michael74001:01:13

yeah, it throws a compiler exception right away.

michael74001:01:29

or rather, this does:

(defn reverse-l
  [l]
  (loop [l l]
    (if-not (seq l) l
            (concat (recur (rest l))
                    (list (first l))))))

lockdown-01:01:39

it can be recursive, but there are many options for recursion because of the limits of the JVM

noisesmith01:01:57

in order to make reverse tail recursive, you need an extra argument representing the already-reversed

noisesmith01:01:18

and then you are putting data into that, instead of the stack

michael74001:01:21

(reverse-l (rest l))?

lockdown-01:01:25

the way you should reach first is to use the sequence abstraction

noisesmith01:01:45

@michael740 to make it tail recursive it needs at least two args

noisesmith01:01:04

so you end up with more convoluted code, and a helper function

michael74001:01:11

hmm. i thought loop was sort of the iterator?

noisesmith01:01:21

if you work out on paper what the function is doing, you'll find that you need two accumulators as it recurs - you can't represent everything in one argument

noisesmith01:01:39

loop is a target for recur - it's basically a goto label plus argument bindings

noisesmith01:01:46

you need two bindings in the loop

lockdown-01:01:11

if you want to tail call with recur, you can define a local fn that takes the extra arguments

noisesmith01:01:33

@lockdown- loop takes as many args as you like, there's no need for a local function

michael74001:01:29

oh. the extra arg you're talking about is the accumulation?

noisesmith01:01:55

the essential thing here is to recursively reverse an argument and not consume stack, you need to track two things - unprocessed items, and processed items

lockdown-01:01:02

@noisesmith yeah, I said that because he can still make his main function take just argument (more elegant I think in this case) and inner function that recurs use the other args

michael74001:01:21

ok, this gets me on the right track... i'll work it out 🙂

noisesmith01:01:35

@lockdown- right and what I'm saying is that loop takes as many args as you need, and that's the reason you use it instead of recur to the function itself (also a valid target for recur)

lockdown-02:01:01

oh yeah, it can be loop or a local function

lockdown-02:01:51

I would think the clojure way here would just be to use the sequence api abstraction

lockdown-02:01:20

unless its for learning purposes

noisesmith02:01:41

yes, we have to assume that what @michael740 is doing here is an exercise, the existing clojure.core/reverse does use the sequence abstraction (implicitly via reduce1)

noisesmith02:01:42

the code is quite elegant for those who aren't doing this exercise by the way, figuring out why it works can be enlightening (and I'll spoil it a bit by saying it does use a two argument function but ... not exactly the way I am suggesting at first glance)

michael74002:01:58

(defn reverse-l
  [l]
  (loop [to-reverse l
         reversed (list)]
    (if-not
        (seq to-reverse)
      reversed
      (recur (rest to-reverse)
             (concat
              (list (first to-reverse))
              reversed)))))

lockdown-02:01:07

@noisesmith good point

noisesmith02:01:30

@michael740 check this out

user=> (source reverse)
(defn reverse
  "Returns a seq of the items in coll in reverse order. Not lazy."
  {:added "1.0"
   :static true}
  [coll]
    (reduce1 conj () coll))

michael74002:01:04

how do i see the source code for reduce1?

noisesmith02:01:50

for now you can mostly ignore reduce1 and just look at reduce - it's defined in clojure's java code iirc

noisesmith02:01:36

oh - reduce isn't much helpful - it just invokes .reduce method or coll-reduce in clojure.core.protocols

noisesmith02:01:49

so yeah, both are in the java code - they are folds if that helps

michael74002:01:04

i have a little experience looking at folds in SICP. i remember they can often reverse collections

michael74002:01:20

maybe that's the intuition you were getting at?

noisesmith02:01:33

that's part of it yes - a reduce can be treated as a loop that always has 1 accumulator for an arbitrary value, and one loop binding that represents "the next item" - it returns the last value of that accumulator when it gets done with the last item

noisesmith02:01:20

@michael740 a very simple improvement you can use with your existing code is use (cons (first to-reverse) reversed) to replace your usage of concat

lockdown-02:01:45

just wanted to point out that there are many ways to recur in clojure because of the jvm can't optimize direct recursion, and choosing which one is the aproppiate one is not that easy at the beginning, there to many variables to have in mind

michael74002:01:54

@lockdown- can you say more?

lockdown-02:01:54

no, in private, I'm newbien and may misguide you.

michael74002:01:47

cons works in "reverse" order??

noisesmith02:01:49

equivalent is (conj reversed (first to-reverse))

noisesmith02:01:58

compared to conj, yes, it's opposite order

michael74002:01:17

user> (cons 1 (list 2 3))
(1 2 3)

michael74002:01:35

your suggestion works but i don't see how

michael74002:01:08

cons puts the first arg in the head position. i want (first to-reverse) last

noisesmith02:01:12

(cons x l) and (concat (list x) l) do the same thing - except cons does it more directly

noisesmith02:01:30

no - you want it at the current head, the same way you were putting it with concat

noisesmith02:01:06

if you add a println that shows each item you process and the current value of what you have processed so far, I think you'll see how it works

michael74002:01:56

oh. ok. i think i was still thinking about the simpler, recursively-defined function.

michael74002:01:34

where you do indeed want to return something like (cons (reversed (cdr l)) (car l))

noisesmith02:01:02

cons has args in the opposite order though

noisesmith02:01:23

the second arg to cons must be a collection or nil (treated as equivalent to ())

michael74002:01:32

yes, that's right.

lockdown-02:01:13

@noisesmith in the wild, is trampoline or leftfn used much?

noisesmith02:01:47

not as often as you might expect - they are pretty niche tools but very handy when you need them

noisesmith02:01:54

and often they work well together

noisesmith02:01:17

(since you want to be able to refer to other steps mutually in mutual recursion)

lockdown-02:01:03

@noisesmith by working together you mean using trampoline inside a letfn?

noisesmith02:01:57

I mean the should be able to return each other in order to trampoline (or use each others in partials as the case may be)

noisesmith02:01:20

this can also be done with declare, or by passing one function to the other, but it can simplify things to use letfn

lockdown-02:01:01

letfn alone consumes allocates stacks frames right?

noisesmith02:01:57

it creates functions, those functions use stack, yes

janis.urbis05:01:16

Hello, could smbd please explain why in one case

(take 5 (map prn (range 10)))
REPL prints 10 numbers & in other case
(transduce (comp (take 5)
                 (map prn))
           conj
           []
           (range 10))
it prints only 5 but we have take 5 in both examples?

scallions07:01:10

I believe the first example prints 10 because of chunking. The latter doesn't chunk.

michael74006:01:01

does prn cause side effects?

michael74006:01:40

both map and range are supposed to return lazy sequences

michael74006:01:52

seems like the short answer is something like, lazy-sequences and side effects don't mix?

janis.urbis06:01:28

@michael740 yes, to that post

janis.urbis06:01:04

@michael740 😅 Yes, I got that from the article. Currently trying to understand trasreducers better. Thank you for your response)

michael74006:01:16

i hope you get a more detailed explanation. i'd be curious to know, too

ikitommi06:01:35

For those who use compojure-api (directly or via Luminus), there is a namespace in the 2.0.0-version. Works like this:

(require '[ :as help])

(help/help)
;; all topics

(help/help :meta)
;; all registered handlers

(help/help :meta :path-params)
;; help on :path-params

ikitommi06:01:57

the last on prints (with nice colors ;)):

:path-params

path-params with letk. Schema is used for both coercion and api-docs.


(POST "/math/:x/:y" []
  :path-params [x :- s/Int, {y :- s/Int 1}]
  (ok {:total (+ x y)}))

timok10:01:26

hi there. how do I figure out how to integrate a flatpickr datetime-picker (https://clojars.org/cljsjs/flatpickr) in my reagent form? I don't get how to call the flatpickr. Any help much appreciated!

joshkh11:01:22

clojars question. 🙂 i have a client/server project that i'm deploying to clojars via lein deploy clojars. :prep-tasks takes care of the clojurescript compilation, css is processed, etc. but when i inspect the jar that comes down from clojars it only has some content in the /public dir. most importantly it's missing my compiled javascript. any thoughts?

joshkh12:01:31

hmm, fixed it by adding the uberjar profile to lein deploy

vincent.cantin13:01:11

I successfully wrote a type that prints out its content, based on bits I found in the source code of clojure.core. However, I don't fully understand the code I typed:

(deftype Node [graph id]
  IPrintWithWriter
  (-pr-writer [this writer opts] (-write writer (str id))))
What is that -write function, and does the - prefix have a special meaning?

bronsa13:01:16

@vincent.cantin that’s cljs not clj

vincent.cantin13:01:35

yes, you are right

bronsa13:01:46

- doesn’t have any special meaning, but it’s idiomatic to prefix a protocol method with - and then wrap it in a regular function

bronsa13:01:11

-write is another protocol method

sb14:01:36

Hello, maybe somebody created here osx app based on java. I need to use java to access native things. The jar run fully normal with ‘lein run’ but when I deployed with javapackager.. and start the application .. I got “application need to accept incoming network connections’. I read many java forum now.. somebody can help me, who solved this problem?

vincent.cantin14:01:49

If I understand correctly, your app is not using the network and you want to avoid this message. Isn't it?

vincent.cantin14:01:22

Maybe try to ask on StackOverflow.

sb16:01:59

Yes, that is the problem. I used my application as a local server and therefore I have incoming network connections.

sb16:01:25

I read about in java forums maybe that can solve with SocketLock or else.. but I don’t understand fully.

sb16:01:43

Yes, I will drop to Stackoverflow. thx! @vincent.cantin

sb17:01:09

I found out.. I signed the pkg.. but something not correct and in this case: “Such errors usually indicate broken application signature.”

sb17:01:19

I need to fix the signature

vincent.cantin14:01:27

Just accept in the security settings of OSX.

vincent.cantin14:01:47

That's an OSX issue, mainly.

sb14:01:02

I understand that. Just I would like to create an app for users.

sb14:01:17

Can I pre-setup something in the java or in the compiler?

josmith201616:01:13

Where is the best place to go to learn clojure? I’m coming from java and I’ve been struggling with learning clojure for the last 2 weeks. I’m finding the reason I’m struggling is because I don’t understand the language and some of it’s basics. It’s hard for me to understand a lot of the clojure docs as well. Are there any books, websites, articles, etc… you guys recommend to help me get up to speed?

anantpaatra18:01:16

Have you tried http://purelyfunctional.tv? Eric is great in breaking the basics into digestible pieces. After trying books and tutorials, I've found his videos to work best for me.

chris16:01:10

clojure for the brave and true

martinatice16:01:48

also I liked this just for puzzle practice http://clojurescriptkoans.com/

drewverlee16:01:37

Living Clojure or Clojure for the brave and true. Both have a different style. If your looking for a more nuts and bolts approach, Joy of Clojure.

dpsutton16:01:43

if you're willing to purchase pragprog has a book written written by alex miller and stu halloway. i've purchased it for myself and a coworker based on how much i enjoyed it. definitely worth the purchase price https://pragprog.com/book/shcloj3/programming-clojure-third-edition

wmichaelshirk16:01:25

I started with clojure for the brave and true. That fact that it was free online was great - and so I went and bought it!

wmichaelshirk16:01:22

Also, if you’ve done https://www.codewars.com/ , clojure is one of the languages. It can be interesting to solve a kata in, say, java, then try it again in clojure, then once you’ve solved it, compare your solution to others and say, “Oh, ok, I didn’t know that was a thing.”

josmith201616:01:16

Thanks a lot guys! I’ll try all these out

sundarj16:01:05

i also found reading through all the stuff on https://clojure.org great

sundarj16:01:15

good luck, and welcome! 🙂

sundarj16:01:07

oh! of course! there is this series too: https://www.youtube.com/watch?v=P76Vbsk_3J0, by rich hickey, the designer of clojure

ekhart16:01:25

there are also challanges at http://www.4clojure.com

matan18:01:26

Is a Java array the only option for O(1) indexed access?

noisesmith18:01:55

vectors are very close - O(log32(n)) iirc

noisesmith18:01:15

they have to get very large before the difference between that and O(1) is likely to matter

matan18:01:26

Yes, I know

matan18:01:41

But otherwise Java arrays. Thanks for confirming!

noisesmith18:01:45

if a vector isn't fast enough, us an array

noisesmith18:01:16

but do profile - it's very likely that indexed lookup is not your bottleneck

matan18:01:27

yes, of course

matan18:01:44

which tool do you profile with yourself, by the way?

matan18:01:59

free for open source right?

noisesmith18:01:14

you can get a free license for open source use yes

jeroen.leenarts21:01:46

Any thoughts or recommendations on an editor/IDE for Clojure? Using NightCode and Atom with some REPL support. But both are not really it. Emacs is just too intimidating… I do use VIM occasionally, but it seems a hassle to set up…

noisesmith22:01:07

the vim setup was much easier than cider for emacs was fwiw, it's just a language mode and then optionally a lispy editing mode (I like vim-sexp) and finally fireplace if you want repl integration - fireplace is trivial to set up

raheel18:01:57

I would highly recommend giving Spacemacs a try. I am a complete Emacs newbie, and after 4-5 months, I am the most productive I have ever been in any IDE/language setup

r.feijolo00:01:26

spacemacs is good but the repl kept getting in my way, I had a better time using vim + fireplace, the vim-sexp is cool and the vim-sexp-mappings-for-regular-people makes editing a breeze

r.feijolo00:01:44

spacemacs is indeed powerful but I kept getting the impression that some things were off the place

chris21:01:50

emacs or cursive

chris21:01:24

or just whatever editor and a repl

seancorfield21:01:25

@jeroen.leenarts Is there something specific you don't like about Atom/ProtoREPL? That's what I use (I switched from Emacs).

jeroen.leenarts22:01:21

It’s a hassle to setup… 🙂

alexchalk1722:01:09

@jeroen.leenarts if you want to avoid setup hassle, check out http://lighttable.com/

jeroen.leenarts22:01:14

Lighttable seems abandoned.

donmullen22:01:34

@jeroen845 Still a bit intimidating - but a lot of clojure dev are likely http://spacemacs.org/ - and very little additional setup needed from defaults.

sundarj22:01:25

@jeroen.leenarts there's also a clojure extension for Visual Studio Code (great editor, haven't tried the extension though)

seancorfield22:01:52

@jeroen.leenarts You only have to set it up once per machine and the "opinionated guide" is pretty straightforward 🙂

crashtown22:01:15

About a month ago I tried every option available. Vscode, atom, emacs and cursive. I’d suggest emacs with cider or cursive. Spacemacs is free and easy to install and require a little configuration. Cursive is free for studying :)

seancorfield22:01:40

(my usual suggestion is "whatever editor you're already using" with some sort of Clojure plugin to hook into a REPL)

jeroen.leenarts22:01:56

That last medium link seems to have worked for me… Trying atom with basic repl support now. Works much better then the “opiniated guide I used before”.

dadair23:01:47

I regularly try out a bunch of IDEs and I almost always go back to Spacemacs + CIDER. Although I don’t think Spacemacs is substantially greater than any other editor. I feel like they all have certain limitations that can be annoying for certain use-cases (e.g., lack of extract-function in cursive, lack of static analysis in spacemacs — but also some static analysis can be annoying when using certain macros; such as Clara Rules rules/queries)

jeroen.leenarts14:01:13

Really looks interesting… Emacs for my VIM fingers… Only problem is that their install from scratch is currently broken… Just my luck… 😂 https://github.com/syl20bnr/spacemacs/issues/10244

pablore14:01:13

They are migrating from the standard melpa package repository to their own “spacemelpa” repository.

jeroen.leenarts15:01:39

@ Any reference on that I can keep an eye on?

jeroen.leenarts10:01:41

They fixed things. 🙂 And this looks kinda good actually.