Fork me on GitHub
#beginners
<
2017-12-13
>
ErhardtMundt00:12:58

I'm a bit lost with namespaced keywords

ErhardtMundt00:12:38

given I'm in a namespace foo.bar, ::a should evaluate to :foo.bar/a

ErhardtMundt00:12:09

what if I want to reference :foo.bar/a from foo.baz?

noisesmith01:12:20

then you use :foo.bar/a or you can alias foo.bar to bar and use ::bar/a

noisesmith01:12:55

alias is usually done via the :as key in require

ErhardtMundt01:12:51

what's the advantage of using ::bar/a instead of :foo.bar/a?

noisesmith01:12:06

Clojure 1.9.0-RC2
(ins)user=> (ns foo.bar)
nil
(ins)foo.bar=> ::a
:foo.bar/a
(ins)foo.bar=> (ns foo.baz (:require [foo.bar :as b]))
nil
(ins)foo.baz=> ::b/a
:foo.bar/a

noisesmith01:12:12

it’s more succinct

hawari03:12:32

Hi guys, How mostly do you guys do your function tests? If there are moving parts, for example calling another function which query a database, making the function "impure", do you guys have some kind of convention to "inject" the parts into the parent function? For now I'm doing something like:

(defn register
  ([user create-fn]
   (if-let [new-user (create-fn user)]
     {:type :created :message (dissoc new-user :password)}
     {:type :error :message {:error "failed to register user"}}))
  ([user]
   (register user u/create)))
Is there a way to do it better, especially if the function gets more moving parts in it (the case of: if condition is A, do A', if condition is B do B') ?

noisesmith17:12:10

this is a normal way to do things in clojure code - especially if create-fn uses an initialized resource, in which case it’s conventional to pass in something that is created on initialization and knows that resource, rather than storing the resource globally in indeterminate state

noisesmith17:12:22

if you need multiple operations on one logical entity, and also have it be something passed in, protocols match this pretty directly (and you can implement the protocol using reify or defrecord or deftype depending on your other needs)

noisesmith17:12:44

but most people would say that this isn’t #beginners level material

hawari03:12:27

Hi @noisesmith, sorry for the late reply. I've had some trouble trying to understand protocols usage in cases like this: 1. There's a namespace which cater solely for business logic and data transformation, at some point the functions in that namespace will need to call what I would call a repository function from another namespace to store the entity. 2. The logic namespace shouldn't care about the implementation of the storage itself, in most imperative language I would just use interface in the logic class, then inject a concrete class to it, if I want to change implementation later on I wouldn't have to change anything on the logic part, just have to inject something else. From what I understand, protocols rely on the type of the entity itself, which function implementation that will be invoked determined based on the type of the entity. How would it serve the above rules, especially the part of not having to change anything on the logic code?

noisesmith03:12:14

The logic should only calling the functions defined in the protocol

noisesmith03:12:39

It should not reference or access the actual type

noisesmith03:12:28

The thing that implements the protocol should be passed in as an argument

noisesmith03:12:52

Consider that you don't access methods that belong to clojure.lang.PersistentVector, instead you call a conj or get etc these use methods a vector implements from interfaces and protocols in clojure.core

hawari03:12:06

Alright, I'll give it a try. Thank you for the pointer @noisesmith 😁

noisesmith03:12:42

@hawari.rahman17 so (defn f [frobber] (foo.proto/frob frobber x)) then you can call it like (f (reify foo.proto.Frobber (frob [this x] ...)))

noisesmith03:12:26

And in ns foo.proto (defprotocol Frobber (frob [this x]))

noisesmith03:12:10

It usually only makes sense if you have at least two protocol methods you care about though (even if on two different protocols), otherwise why not just use a function

hawari04:12:55

Thanks @noisesmith, guess I'll need to understand about protocol first 😁

hawari03:12:51

So based on the function above, I inject a mocked function when testing, a function that immediately returns value just for the sake of testing the parent function.

nakiya03:12:57

So this maybe something that's asked quite a lot here, but I'll ask anyway. I find myself interested in clojure, I was interested in LISP some time back but never got the chance to use it. Now that I'm doing more web stuff (Doing django at my work atm), I started to read about clojurescript. It's been a couple of weeks and I see so many libraries and frameworks and comparisons between each other that I'm literally frozen by too many choices - it's debilitating. I only can do clojurescript in my spare time and that spare time is a bit hard to come by these days with all the other stuff in my life. To start with, I'm not a genius coder by any stretch of the imagination so I can't 'grok' a framework or a language in two afternoon. With all this, I go read abour reagent, re-frame, Fulcro, Keechma, Om next, etc. And there are so many blog posts detailing why one of these is better than the others. I started trying out re-frame once, tried Fulcro also. But for me this takes time. I am an intermediate level coder who'd like to just learn something useful and perhaps fun. I don't want to put a lot of effort into learning something (Doing a personal project with it say,) and learn later that that framework is abandoned. It seems there's so many stuff around at the moment that it's implausible to think that they all will survive for long. But if you look at other languages, they have either one or two well respected ways of doing something. So, what would be my safest bet? Any suggestions are welcome.

manutter5113:12:53

fwiw, I started out writing a simple Electron app using CLJS and reagent (because I couldn’t quite figure out the original Om). From there I moved up to re-frame, and found that it was easier to learn re-frame having first learned reagent, and also some things became easier in re-frame than they had been in reagent.

manutter5113:12:47

Then I did some (non-CLJS) tutorials on Falcor and Redux and found that these made it a lot easier to understand Om/Om Next.

manutter5113:12:13

So I’d say it’s not quite a case of “if I learn this one, my experience will be wasted.” There’s enough overlap between the React-based frameworks to make it worthwhile no matter which one you start with.

michaels14:12:15

I used “Clojure for the Brave and True” for my main intro - I think it’s free to read online still. Now I’m making my way through “modern-cljs” to learn more about clojurescript. Just going to go with whatever he’s recommending!

pithyless17:12:08

@duminda - highly opinionated rite of passage in CLJS: * reagent (learn and understand basics of react + cljs + hiccup) * tooling: lein+figwheel or boot+boot-reload; Just stick with one template that runs out of the box for you. * side-quest: have a look at garden * re-frame (learn one-way dataflow + how to structure apps) * rum (easily compare different ways of managing state at a component level; learn the pros/cons of each) * om/next (one approach to highly complex apps) * fulcro (one opinionated approach to build om/next apps) At this point, you have gained the knowledge you need to make your own decisions based on project needs and you’ll know what interests you and where to dig deeper.

nakiya02:12:13

Thanks guys for the detailed responses. So I will just start with reagent as recommended. Hopefully I can move on to other stuff after that.

jgh03:12:32

clojure’s fun, plus it’s JVM so useful beyond just hacking around 🙂

codonnell03:12:18

when I'm picking a tool, I usually go look at the options on https://www.clojure-toolbox.com/ and pick a project that seems well-established

jgh03:12:56

oh i see, i got confused by the post.

SoV404:12:23

@duminda having spent the last year playing with om.next and clojurescript, i'm currently evaluating rum and it's really good. i'm not sure what sort of web dev you're working on. my needs are: realtime server-> client and client -> server comms for which I use sente. beyond that, my current recommendation would be rum. i'm just getting started with datascript, which is like a clientside db that looks like datomic syntax. Check out this post and see if you're building something that would benefit from this model / approach. http://tonsky.me/blog/the-web-after-tomorrow/

SoV404:12:19

@hawari.rahman17 i'm curious about this too. making fake inputs to test database grabs seems like a decent way to get about it.

hawari04:12:00

Yup @sova, as of now it's decent enough to get by. But I still kinda have a feeling I'm missing out on something here. 😆

derpocious13:12:09

Hey all, I'm really confused about core.async

derpocious13:12:23

Can someone help me understand why this doesn't return "Hello World"?

akiroz13:12:09

@derpocious A few problems: On line 13, you're taken the "Hello Word" out of the channel and discarded it. On line 15 you returned an empty channel instead of "Hello World". go also returns a channel so you must take from the channel returned by the go block

akiroz13:12:03

Since Klipse runs on CLJS in the browser, you can't take from the channel synchronously. This will work in Klipse:

(def a-channel (chan 1))
(put! a-channel "Hello World")
(take! a-channel println)

derpocious13:12:13

But why is it (take! a-channel println) instead of (println (take! a-channel))

akiroz13:12:47

take! is async, it calls a function when it's able to take a value off the channel

akiroz13:12:31

it's not possible to wait for a value synchronously in JavaScript land 😉

akiroz13:12:57

in Java you can do this: (println (<!! a-channel))

derpocious14:12:45

Hmm I thought <! was also async take a value off of a channel

joost-diepenmaat15:12:37

<! is only available in a go block

dpsutton15:12:58

look at it's source 🙂 (source <!)

derpocious16:12:03

thanks. I need I need to require source. Does it work in cljs?

derpocious16:12:37

what's the best way to find the import for cljs?

derpocious16:12:29

For example, take! just causes my code to break and googling "cljs take!" doesn't seem to give me the answer

New To Clojure16:12:25

There's no take! function in http://cljs.github.io/api/ Looks like it's not implemented.

stvnmllr216:12:18

so trying to implement a nightly email job for a web app. would it be recommended to run it in a separate process from the web request it gets triggered by? if so, is there a simple way to do that. or need core-async

stvnmllr216:12:50

i think i read each method implements runnable, so probably not.

donaldball17:12:07

There are a lot of variables to consider when answering that question. Is your web service process currently a singleton? Likely to remain that way? Must your job be run by only one process? Do startup time or other operational considerations suggest adding other processes is a pita?

stvnmllr221:12:23

Yeah, there are. I'll probably have more than one running at some point so i can restart them without downtime. Only want one a night, as it sends daily tasks for each user. I can add other processes if needed. But yeah, started with lambda, but wasn't easy to get access to db and email service without another NAT router or something like that.

stvnmllr221:12:23

My current idea is to have lambda call the url (which is load balanced) so it runs once.

stvnmllr221:12:57

Oops. thanks @U04V4HWQ4. See above if you are still on today

donaldball21:12:41

lambda calling the url would be easy and effective if you’re satisfied with the security story. Alternately you could coordinate electing a leader, which would be nice potentially if your app could know when the job was finished successfully so you could potentially retry failures.

stvnmllr222:12:05

yeah, security is ok with https i think and a shared key. Hmm.. you mean knowing which web app would handle it? I'd know success by the result of the lambda function, but not sure what to do about it if it fails. Have to look more into lambda triggers i guess. Thanks for the input.

donaldball00:12:32

Assuming your request is processed synchronously and the response does not timeout 🙂

zignd18:12:02

is it possible to rollback a transaction created inside a dosync call on purpose and without having to throw an exception?

noisesmith18:12:12

shouldn’t anything that rolls back a transaction throw an exception to the caller? otherwise I’d expect strange and very hard to find bugs

zignd18:12:01

i'd like to rollback in the context of unit tests, but you have a point

zignd18:12:12

maybe i should stick to using bindings over those ref values

noisesmith18:12:20

if throwing an exception to the caller is OK, but you don’t want to create one artificially you could call (io!) maybe?

noisesmith18:12:45

or make the ref something that is passed in explicitly, so you can create fresh ones explicitly for each test

noisesmith18:12:14

that’s often a good pattern, making all mutable resources (refs would count as a mutable resource) be arguments rather than namespace level globals

noisesmith18:12:53

a compromise is to accept an explicit resource arg, and default to the global

noisesmith18:12:00

that way at least your tests can be sane

zignd18:12:35

i'm doing the opposite of that, i have them defined as namespace level globals, then on my unit tests i'm binding them to another value

noisesmith18:12:53

right, that’s the thing that causes problems and the thing I’m saying maybe not do

noisesmith18:12:39

in particular, since the use case for refs is for things where performance concerns mean you want to reduce contention and coordinate acting on N items - that’s often a case where you want a collection of refs and that gets inconvenient with globals

zignd18:12:42

i decided to do it like that because i'm using them in multimethods, it seemed to make more sense, but i'll see if i can work on a refactor

chris19:12:15

you never print it

chris19:12:39

replace (<! a-channel) with (println (<! a-channel)) if that's all you want

chris19:12:57

a-channel is a channel

chris19:12:13

which is why at the bottom it's saying ManyToManyChannel

vinnyataide19:12:22

hello, anyone knows why the clj executable doesn't return anything?

vinnyataide19:12:33

I've installed rlwrap already

vinnyataide19:12:44

I hoped in the tutorial to open the repl

vinnyataide19:12:03

I've used the bash installer

vinnyataide19:12:14

using linux mint latest

noisesmith19:12:20

so it just silently exits?

New To Clojure19:12:37

@vinnyataide You need to install Leiningen: https://leiningen.org/#install That's the easiest way, then you can run lein repl.

vinnyataide19:12:40

I think I found the problem

vinnyataide19:12:44

I have no java

vinnyataide19:12:49

It's a brand new

noisesmith19:12:57

@ghsgd2 that’s an alternative but the clj tool is totally different

vinnyataide19:12:59

It should check and warns doesnt it?

noisesmith19:12:09

@vinnyataide haha that would do it

vinnyataide19:12:27

So that remains as a future feature haha

noisesmith19:12:29

@vinnyataide yeah that would be worth a PR or jira ticket I bet

vinnyataide19:12:47

can I do it? or would I need to call someone?

noisesmith19:12:29

you can make a PR or jira ticket - anyone can make a jira tiket, you need to sign up an online contributor agreement or they won’t look at a patch though

vinnyataide19:12:58

will do! just a heads up a default-jre install solved it

urbis19:12:30

Hello everyone, recently I started with a book called web-development-clojure v2 The libraries it uses changed slightly, according to the book I need to connect to repl and run a query on the db That is what I have:

user=> (use 'guestbook.db.core)
nil
user=> (get-
get-in get-messages get-method
get-proxy-class get-thread-bindings get-validator
user=> (get-messages)

IllegalArgumentException db-spec [email protected] is missing a required parameter clojure.java.jdbc/get-connection (jdbc.clj:379)
My db.core:
(ns guestbook.db.core
  (:require
    [clj-time.jdbc]
    [conman.core :as conman]
    [mount.core :refer [defstate]]
    [guestbook.config :refer [env]]))

(defstate ^:dynamic *db*
           :start (conman/connect! {:jdbc-url (env :database-url)})
           :stop (conman/disconnect! *db*))

(conman/bind-connection *db* "sql/queries.sql")
My queries:
-- :name save-message!
-- :doc creates a new message
INSERT INTO guestbook
(name, message, timestamp)
VALUES (:name, :message, :timestamp)

-- :name get-messages
-- :doc selects all availible messages
SELECT * from guestbook
I tried to reinstall the whole project 3 times already, but could figure out the problem. Could smbd pls point me to the right direction?

seancorfield19:12:15

@janis.urbis Does the book tell you which versions of the libraries to use? (in your project.clj I expect)

seancorfield19:12:53

If you change the versions from what's in the book, you will run into incompatibilities between the libraries.

urbis19:12:27

@seancorfield no, it's just says use luminus

urbis19:12:09

so it's scaffolded project with only one change

urbis19:12:37

I changed migration to what book requires

CREATE TABLE guestbook
(id INTEGER PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(30),
message VARCHAR(200),
timestamp TIMESTAMP);

derpocious19:12:54

It is possible for me to return the value that was in the channel instead of just printing it?

seancorfield20:12:16

@janis.urbis How did the book tell you to create the project?

urbis20:12:38

lein new luminus guestbook +h2

seancorfield20:12:54

The author @yogthos is on this Slack but not in #beginners -- I'll invite him in

seancorfield20:12:38

@yogthos wondering if you can help @janis.urbis as he's trying to follow the guestbook example in your book?

yogthos20:12:21

@janis.urbis the error you’re seeing means that the database state hasn’t been started yet

yogthos20:12:11

mount uses namespace hierarchy to resolve the resource loading order, and if a namespace isn’t included anywhere, then the resources in that namespace won’t be loaded automatically

yogthos20:12:01

to load the resource by hand you just have to run (mount.core/start #'guestbook.db.core/*db*) in the REPL

admay20:12:05

@janis.urbis try running (mount/start #'database) in the repl and run the query again

admay20:12:19

neeeevermind… lol

admay20:12:04

Welp, my work is done here boys!

seancorfield20:12:41

(sometimes there's so much "magic" in these Leiningen templates that they're not the easiest way for beginners to learn Clojure!)

urbis20:12:07

@yogthos wow, thank you for the detailed explanation) it works now)

yogthos20:12:13

yeah there are definitely some gotchas

yogthos20:12:22

I do try to keep luminus as simple as practical

urbis20:12:53

@admay @seancorfield thank you for the help too 🙂