Fork me on GitHub
#beginners
<
2018-09-18
>
scott.archer00:09:48

I want to start a new clojure project to mainly for learning purposes, but even deciding how to do that is a bit overwhelming. Should I use leiningen, boot, or deps.edn? Is there a wrong answer?

andy.fingerhut00:09:57

If you are learning on a beginning project, I suspect picking any of those 3 will work out reasonably well for you. If and when you hit some point of the project where one of those is clearly better for your purposes than another, you will probably be better positioned at that time to see why switching might be to your advantage.

andy.fingerhut00:09:58

All of them can get you to the point of starting a REPL and setting up your environment (e.g. Java class path) to be able to load your code and that of libraries you may want to use.

andy.fingerhut00:09:44

I haven't used boot yet, but I believe the places where it vs. Leiningen will become most evident is when you have specific needs for building an application from multiple kinds of source files, e.g. not only Clojure/ClojureScript, but also Java, JavaScript, CSS, etc. If you expect to have needs like that on your beginning project, then you might want to get more people's advice than mine on your choice.

scott.archer00:09:22

Thank you for the information. I'm going to try lein for now. I've used it before and I don't think I'll need a complicated build process.

jesse.wertheim01:09:27

@scott.archer tldr is that lein will get you more functionality out of the box and lets you leverage way more templates built for lein, etc. boot is considerably easier to extend and customize since your boot tasks are just straight clojure code, but you generally gotta know more about internals and there aren't plugins (or, in boot's case, pre-made tasks) for as much. Which I like because it simplifies the dev deps, but it does mean more initial work putting a project together

iagwanderson03:09:58

how can I get a full qualified function name and call it. For example, something similar to the importlib from python?

seancorfield04:09:04

@iagwanderson You'll need to require the namespace it is in and then resolve the fully-qualified name to a var, then you can call it.

seancorfield04:09:57

For example

user=> (def f "clojure.set/union")
#'user/f
user=> (namespace (symbol f))
"clojure.set"
user=> (require (symbol (namespace (symbol f))))
nil
user=> (resolve (symbol f))
#'clojure.set/union
user=> (*1 #{1 2} #{2 3})
#{1 3 2}
user=>

iagwanderson04:09:50

yes, that’s exactly what I meant

jarvinenemil05:09:47

Are there any benefits of having one leiningen project for the UI and one for the API which the UI communicates with?

jarvinenemil05:09:12

It feels unnecessary to me, I do have it split today though.

jarvinenemil05:09:06

If I were to target several plattforms it would be nice though.

coinedtalk08:09:56

Just want to give out a shoutout to https://www.codingame.com/home since they support Clojure for their puzzles, coding battles, etc. Great learning resource, even for advanced coders. 🙂

denisgrebennicov08:09:35

hackerrank supports Clojure as well. For almost all of their problems. Not only from the FP domain

sb09:09:02

If i want to call from clojure side (clj) a JavaScript code directly or trigger clojurescript code (trigger eg. add-watch at clj side > cljs trigger) then possible without websocket and rest api?

sb09:09:53

I can write fully eg atoms at clj side, call functions. I would like to solve the clj>cljs way like a websocket. I saw intern function not implemented at cljs side, any idea how to connect? So change clj atom then send to cljs part? Now, i set intervals, but not so good idea.

sb09:09:47

If somebody have any idea, i will be happy.

borkdude09:09:02

@sb I don’t fully understand what you want, but if you want websockets, sente is a nice library to work with

sb09:09:14

@borkdude i used sente, rest api. I solved from cljs side with macros like core.async the Vars (atoms) at clj side. I can read, write. Run clj commands from cljs part. One thing missing, if eg at clj side a websocket channel eg Slack.. when change the data I cant send to cljs just with sente.

sb09:09:48

No direct connection because in macros at cljs side i cant use the intern function

sb09:09:59

So i cant create a hook

borkdude09:09:32

why are you using intern?

sb09:09:47

Namespace hook

sb09:09:14

I want connect actual cljs ns to clj ns

sb09:09:54

Like cljc, with data storage

sb09:09:59

Maybe that is impossible, just i prefer use data communication without extra layer eg rest api or socket

denisgrebennicov10:09:07

is it common sense to write pre/postconditions in your function definitions? They throw exception on false, right?

borkdude10:09:52

@denisgrebennicov many functions throw exceptions anyway, e.g. if you call subs with out of bounds index. in that case a pre might give you some more information of what might have gone wrong anyway

denisgrebennicov10:09:30

So they do throw exceptions or it is up to you?

andrea.imparato10:09:52

do you write these pre/postconditions with spec or what?

denisgrebennicov10:09:06

You can, but AFAIK it's other func

borkdude10:09:07

> If any of the conditions evaluate to false and assert is true, an assertion failure exception is thrown. https://clojure.org/reference/special_forms#toc10

denisgrebennicov10:09:28

this is more or less the question, since AFAIK spec is kinda replacing pre-/postconditioning, right?

borkdude10:09:29

so you can turn it off

borkdude10:09:57

depends. you can even call spec from pre or post, it’s up to you

denisgrebennicov10:09:49

but is it idiomatic. AFAIUnderstood spec is pushed and is the "new" way to go instead of precondition tests Dunno if you can use spec for postconditioning though

borkdude10:09:46

spec doesn’t verify the :ret value unless you use clojure.spec.test. some libraries offer a workaround and do turn it on. but you can also hook it up yourself using :post

borkdude10:09:56

I usually write an fdef, but that’s because pre and post are not in my system, maybe I should use them more

denisgrebennicov10:09:07

this is a nice feature, IMO, but question is if this is just-nice-to-have-nobody-does-it, or nice-to-have-you-should-use-it

borkdude10:09:28

there is no idiomatic answer to this

borkdude10:09:01

I’ve never heard someone refuse a PR because he/she didn’t write enough pre/post/specs.

borkdude10:09:13

there are multiple ways to do it. I could have used pre now I think of a case I handled lately in a macro where I asserted that the input should meet some form, but I just used assert .

borkdude11:09:40

(require '[clojure.walk :refer [macroexpand-all]])
  (macroexpand-all
   '(defn foo [x]
      {:pre [(pos? x)]}
      x))
=>
(def
 foo
 (fn*
  ([x]
   (if
    (pos? x)
    nil
    (do
     (throw
      (new
       java.lang.AssertionError
       (clojure.core/str "Assert failed: " (clojure.core/pr-str '(pos? x)))))))
   x)))

mario.cordova.86217:09:16

Does the REPL have a mind of its own?

mario.cordova.86217:09:40

If I do this (apply hash-map (:test "example"))

borkdude17:09:10

what’s the result of (:test "example") ?

mario.cordova.86217:09:23

But that is not the case when my app is being run

dadair17:09:05

dev> (:test "example")
nil

hiredman17:09:10

because you have something more like '(:test "example")

borkdude17:09:37

there’s a difference between (:test "example"), '(:test "example") and (list :test "example")

hiredman17:09:08

(:test "example") as a literal in code means 'invoke :test as a function with the argument "example"'

mario.cordova.86217:09:26

Ahh such a rookie mistake

mario.cordova.86217:09:42

How does Clojure know when its dealing with a list or dealing with a function call?

borkdude17:09:49

that’s the difference between an unquoted or quoted list. the reader interprets an unquoted list as a function call

mario.cordova.86217:09:04

Suppose I have this (defn funca [name & args] (apply funcb name args))

mario.cordova.86217:09:27

(defn funcb [name & args] (apply hash-map args))

mario.cordova.86217:09:59

If I println args

mario.cordova.86217:09:13

Its just a list. No quote or unquote.

borkdude17:09:46

this is only important at read time

trailcapital17:09:51

internally the object is stored as a list - it's just when you define it in the code that the quote means it is a literal object- not an s-expression

trailcapital17:09:33

https://www.braveclojure.com/read-and-eval/ This chapter may be useful for understanding the reader! It goes fairly in depth on how expressions are evaluated

hiredman17:09:10

I would find a tutorial somewhere for writing a lisp. it will go really fast in clojure, and once you've done it, it makes this kind of thing very apparent and easy to understand

deanjohnson0320:09:46

Hey everyone, the main man himself Rich Hickey makes a lot of jokes about monads in his presentations. I am struggling to understand what they are, could anyone explain or link me to a resource where I could learn?

hiredman20:09:39

a monad is 1. a thing in math 2. a pattern that emerges in a lot of functional programs that has some similarities to #1 so the same name is used for both

noisesmith21:09:04

The source of the monad jokes is that they are famously unintuitive and amateur attempts to explain them tend to make people even more confused. They aren't a concept you need for clojure. That said, category theory involves some nice patterns for thinking about computation if you want to learn some math that would inform application design.

andy.fingerhut21:09:13

Google for "burrito". You'll find 100 tutorials on monads 🙂

noisesmith21:09:22

and 10,000 bad jokes about tutorials about monads

seancorfield21:09:34

(aside from the joke and the various attributions of it, it actually explains the math)

andy.fingerhut21:09:50

One of my favorite jokes (no idea who said it) on monads is paraphrased "When you learn enough to understand monads, you lose the ability to explain them to people who do not yet understand them."

seancorfield21:09:12

I think gigasquid did quite a good job in one of her talks (at either a Clojure conference or Strange Loop)... let me see if I can find that...

ericcervin21:09:13

Gottfried Leibniz or GTFO

seancorfield21:09:28

@deanjohnson03 I hope some of that helps! 🙂

mario.cordova.86221:09:12

When you build JS from CLJS using lein cljsbuild once

mario.cordova.86221:09:21

And then run lein ring uberjar

mario.cordova.86221:09:37

Is the JS separate entity or embedded within the .JAR?

noisesmith21:09:15

The goal of an uberjar is to create a single deployment artifact. You can open the uberjar in your editor or with a file manager - it's a special zip file.

noisesmith21:09:26

the js will also be created outside the uberjar before packaging it

lockdown-21:09:02

@noisesmith you still need an external sh script for jvm options etcc no?

noisesmith21:09:28

right - that's outside the scope of uberjar

lockdown-21:09:33

or can that be specified insided the jar somehow?

noisesmith21:09:37

there is a trick for turning the jar into a #!/bin/sh script that runs java but usually you don't need that

noisesmith21:09:55

I usually don't think of the runner script as part of the deploy, but rather part of the infrastructure deployed to - but I guess not everybody does it that way.

lockdown-21:09:08

I would think too, makes more sense, since you also need to setup the app as a service, etc.. I guess that is why some convert the app to a distro pkg

lockdown-21:09:32

maybe this is one of docker advantages

dpsutton21:09:53

what's your lazy-seq question/doubt?

dpsutton21:09:04

oh. it went away

lockdown-21:09:30

@dpsutton heh, I'm rethinking it

mario.cordova.86221:09:23

After opening the uberjar I notice that my site.js file isn't under the public folder anymore.

mario.cordova.86221:09:52

After removing :clean-targets ^{:protect false} ["resources/public/js/site.js" :target-path]

mario.cordova.86221:09:06

It now builds it and its in the proper place

mario.cordova.86221:09:27

What gives? I thought :clean-targets only affects the outcome of a lein clean

noisesmith21:09:11

I think uberjar implicitly cleans

noisesmith21:09:35

one way to do this is to make the optimized cljsbuild a prep-task for uberjar

noisesmith21:09:01

because usually you want a different kind of cljs output for prod vs. dev

mario.cordova.86221:09:27

Any links that explains the optimized cljsbuild as a prep-task for uberjar?

noisesmith21:09:48

:uberjar {:prep-tasks [["cljsbuild" "once" "opt"]]} or something like that

mario.cordova.86221:09:38

Thank you @noisesmith

deanjohnson0321:09:01

@seancorfield @hiredman @noisesmith @andy.fingerhut thank you all for your responses, I will check out all the links and info you gave me 🙂

noisesmith21:09:01

@mario.cordova.862 you might need to manually add "clean" or any other prep tasks since that gets overwritten rather than merged (because order is important)

lockdown-21:09:29

is deploying from github a bad idea? wink it is

dazld06:09:12

not sure what you mean there, but literally thousands of companies do this every day, even from public repos.

dazld06:09:35

so, no, deploying via CD setup on your repo isn’t a bad idea at all 🙂

noisesmith21:09:34

so ["clean" "javac" ["cljsbuild" ...]]

mario.cordova.86222:09:53

@noisesmith Would adding :disable-implicit-clean true to my project.clj achieve the same effect?

mario.cordova.86222:09:18

Because I am running a lein clean before running lein ring uberjar

noisesmith22:09:03

the difference would be no longer needing to run the cljsbuild yourself (or the clean)