Fork me on GitHub
#beginners
<
2019-07-30
>
bartuka02:07:12

is there a way to remove nil keys from a record and not transform it into a map?

bartuka02:07:55

I am using:

(apply dissoc                                                                                            
       record                                                                                                  
       (for [[k v] record :when (nil? v)] k))

bartuka02:07:06

but the end result are maps instead of records

seancorfield03:07:22

@iagwanderson You cannot remove any of the base keys from a record. Why do you want to do this?

seancorfield03:07:11

After all, (some-key record) is going to be nil if some-key is not present or if some-key refers to a nil value.

bartuka03:07:55

I modeled a xlsx ROW to be a record because I have several operations that depend on the type and I wanted to clean the row at some point

bartuka03:07:13

I probably have to do this before converting the original map to the record, though

seancorfield04:07:39

@iagwanderson Does the "type" depend on one field of the CSV? Maybe you want multimethods and a tagged hash map instead?

Crispin05:07:29

I lambdalove multimethods

jubalh06:07:59

Hi there. I follow the book Web Development with Clojure 2nd Edition. But run into several problems. If I run lein migratus create guestbook like the book describes I get: https://paste.opensuse.org/view/raw/31a637a5

jumar07:07:10

This looks as a problem with newer jdk versions and obsolete flatland/ordered dependency -> you should update the dependency

jumar07:07:00

BTW. There is 3rd Ed. of the book already available so I'd recommend that instead

jubalh09:07:18

There is? My company has a oreilly subscription but I only see the second adition

jubalh09:07:02

By updating you mean not using `{:user {:plugins [[luminus/lein-template "2.9.10.74"]]}} in the profile? But then it seems that migratus is not available as a subcommand

jubalh09:07:17

BTW This is the first time I use clojure 🙂 So I might not understand the ecosystem

jumar09:07:45

Check lein deps :tree and look for flatland/ordered

jumar09:07:25

Note the version used and add an explicit dependency in the project.clj to the latest version: https://clojars.org/org.flatland/ordered

jumar09:07:39

It might be different if that dependency is bring by a plugin, though

jumar09:07:09

This in particular is, I think, the issue you hit: https://github.com/clj-commons/ordered/issues/45

jubalh09:07:09

Interesting. It seems on http://learning.oreilly.com I can't see those beta books.

jubalh09:07:34

Okay I'll try what you wrote after lunch. Thanks!

jubalh06:07:31

This is when I have {:user {:plugins [[luminus/lein-template "2.9.10.74"]]}} in the lein profiles file. Without that migratus doesnt even seem to be a task

RickardK08:07:36

Is there a distinction in naming conventions between transformation functions that may fail (and return nil) and transforms that throws on failure (I want to use the former with the keep collection function and the latter with map)? In F# for instance the former would likely be called try<whatever> like tryParseInt and return an Option.

jakuzure09:07:13

Hi, I'm running into some problems trying out re-frame subscriptions. the commented line above works perfectly, but if I use the subscription version it doesn't work for some reason

jakuzure09:07:05

this is how I initialized it in the db

jakuzure09:07:06

only thing I could think of is maybe it has something to do with lazy eval? but I'm actually asking it to realize, so I'm quite lost

danielneal11:07:45

@shin can you post the definition of your :locales subscription

danielneal11:07:24

Everything looks fine - although you can generally only dereference subscriptions within a render function, which may be where you’re going wrong

danielneal11:07:15

that line (def lcl (re-frame/subscribe [:locales]) is not intended usage

danielneal11:07:53

would be more like

(defn some-component []
  (let [locales @(re-frame/subscribe [:locales])]
    ... do something with locales)))

🙂 4
jakuzure11:07:52

hmm, I'm using tempura (https://github.com/ptaoussanis/tempura) to play around with using different languages. you give it a list of locales and it will try to use them in the given order. I'm trying to subscribe to that, so I can change the order and thus language used or would you recommend any other way of doing that?

danielneal11:07:27

Hmm, various ways. The thing is once you have a subscription you can’t really go back to the land of normal function calls anymore. Dereferences are tracked and cause re-rerenders, and are cached, and when unmounted the cache is disposed, so there’s a fair amount of machinery involved.

danielneal11:07:40

This is why the partial is not working

danielneal11:07:05

One option would be to call tr with the locales each time you use it, seeing as you want the locales to be editable

danielneal11:07:22

(def tr (partial tp/tr {:dict my-dict}))

(defn some-component []
  (let [locales @(re-frame/subscribe [:locales])]
        text (tr locales [:text])]
    ... do something with text)))

danielneal11:07:05

There’s definitely other ways of arranging it though

jakuzure12:07:00

I'll try it out at home, thanks very much! But is the way to do this I thought about sane or is there a better way to do things like that?

snurppa05:07:37

We have put all stuff regarding trinto app-db and use subscriptions, something like this:

(def default-tconfig {:default-locale :en
                      :dict           {:en {:missing "*** Missing localization! ***"
                                            :message {:hello "Hi"}}
                                       :fr {:message {:hello "Bonjour"}}
                                       :fi {:message {:hello "Moro"}}}})

;; -- Subscriptions -----------------------------------------------------------

(rf/reg-sub
  :language
  (fn [db _]
    (:language db)))

(rf/reg-sub
  :tconfig
  (fn [db _]
    (-> db :tconfig)))

(defn tr-query [tconfig lang]
  (partial tempura/tr tconfig [lang]))

;; Returns new translations fn 'tr' after :language or :tconfig changed in app-db.
(rf/reg-sub
  :tr
  :<- [:tconfig]
  :<- [:language]
  (fn [[tconfig lang] _]
    (tr-query tconfig lang)))
and then we have event that fetches translations from backend and sets that dictionary into :tconfig in app-db. Then in view you can just @(rf/subscribe [:tr]) and you get function with correct language and dictionary. And if the language would be changed in the app-db, then all subscriptions get updated with new tr function.

danielneal12:07:02

With re-frame, once you have subscriptions, you can build on them with more subscriptions, but you can’t pass a subscription to a normal functions in the place of a value. You can run a normal function on the de-referenced value of a subscription, because you now have an actual value - like (tr opts @locales @text)

danielneal12:07:23

the thing to remember is that subscriptions are objects whose dereferences are tracked so that components that dereference them re-render when the value changes

sakalli12:07:23

what is a quick and simple way of testing if my function actually returns a lazy seq, instead of killing a restarting the repl each time it starts evaluating an infinite seq?

manutter5112:07:50

You could do something like this:

(defn lazy-seq? [coll]
  (= (class coll) clojure.lang.LazySeq))
=> #'user/lazy-seq?
(lazy-seq? (for [x (range 10)] x))
=> true

lispyclouds12:07:57

@sakalli Also if you wanna peek into the infinite seq, try https://github.com/borkdude/finitize

sakalli12:07:23

thanks both 🙏

mfikes12:07:09

@sakalli Another useful trick is to (set! *print-length* 100) in the REPL. This will prevent lockup if an infinite sequence is printed.

👍 16
clojure-berlin 4
eskemojoe00717:07:44

Where is the proper place to put a doc string within defmulti and defmethod

noisesmith18:07:24

after the name of the defmulti, and defmethod shouldn't require its own

noisesmith18:07:24

if you have (defmulti foo "get a foo from the arg" ...) (defmethod foo :bar ...) how would you even ask for the method's doc other than getting the one for the multi?

noisesmith18:07:22

a defmulti creates a var, those carry docstrings, a defmethod alters the multi, it doesn't have its own var to put doc on, or ask doc from

👍 4
gonza-lito18:07:28

hi all! mega beginner here, i'm doing this (https://practicalli.github.io/clojure-webapps) practicalli tutorial, and when I try to run the repl with lein repl I get this error, couldn't find any usefull info online

Caused by: java.lang.NoClassDefFoundError: IllegalName: todo_list.core/_dev_main.proxy$java.lang.Object$SignalHandler$d8c00ec7
        at java.lang.ClassLoader.preDefineClass(ClassLoader.java:654)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:761)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:642)

noisesmith18:07:18

is there a file src/todo_list/core.clj which contains something like (def -dev-main (proxy ....))

gonza-lito18:07:49

the project runs, but not the repl

gonza-lito18:07:24

(defn -dev-main
  "A very simple web server using Ring & Jetty that reloads code changes via the development profile of Leiningen"
  [port-number]
  (jetty/run-jetty (wrap-reload #'app)
     {:port (Integer. port-number)}))

noisesmith18:07:21

hmm - I'd try running lein clean then starting the repl again

SgtZdog18:07:46

when I use (require) in the repl, is the :all implied (Compared to doing (ns myns (:require randomlib :refer :all))?

noisesmith18:07:18

no options are implied

noisesmith18:07:59

:refer :all should only rarely be used (there's an annoying thing where all the lib README files seem to show it in their examples...)

gonza-lito18:07:45

no luck, I'm on WSL btw, could this have anything to do?

noisesmith18:07:53

it really could - if you are confident enough you could convert the project to not use the ring plugin, that's a likely source of hard to debug magic

gonza-lito18:07:20

cool! i'll try that

noisesmith18:07:04

also be sure your leiningen and clojure versions are the newest stable versions, and you might try downgrading your jvm if it's a fresh one

noisesmith18:07:27

(there's various stuff that goes weird with newer java versions and older clojure stuff, using a newer lein and clojure helps)

gonza-lito18:07:55

hmm, these are my version, I think on the clear for that

REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.6.0
OpenJDK 64-Bit Server VM 1.8.0_212-8u212-b03-0ubuntu1.18.04.1-b03

noisesmith18:07:35

yeah, that clojure is ancient

gonza-lito18:07:58

let me try taht

gonza-lito03:07:26

k, so late reply, but updating packages and switching to osx did the trick, it seems like wsl is not quite there yet, thx for your help!!

SgtZdog18:07:57

I'm in a use case where I actually need all the functions in the file.

noisesmith18:07:17

you can always call all the functions, the normal way to do this is via :as

SgtZdog18:07:19

(and there's no name conflict)

noisesmith18:07:36

OK - use :refer :all if it's appropriate, just know it's not considered normal

noisesmith18:07:54

the normal thing is (require '[clojure.set :as set]) then you can use (set/union ...) (set/intersection ...) etc.

SgtZdog18:07:14

well, :all isn't actually an option for (require) (unlike :require as part of a (ns) call).

noisesmith18:07:18

require as used alone is exactly the same function that ns calls

noisesmith18:07:41

and has the same options (once you sort out the quoting differences outside a macro)

SgtZdog18:07:55

oh weird. ok. I see that in the doc now. It threw an exception at me.

noisesmith18:07:48

(ins)user=> (require '[clojure.set :refer :all])
nil
(cmd)user=> (union #{:a} #{:a :b :c})
#{:c :b :a}

SgtZdog18:07:26

ty! I forgot/didn't know I needed the '[].

noisesmith18:07:48

it's the same as in the ns form in that respect

SgtZdog18:07:32

What does the ' do here? The code I'm working with doesn't have that in the (ns) declaration.

noisesmith18:07:59

that's what I meant by has the same options (once you sort out the quoting differences outside a macro)

noisesmith18:07:05

' means "don't evaluate this form"

noisesmith18:07:18

so symbols are used as is and are not resolved, and function calls are just lists

SgtZdog18:07:51

Ok, so is the ' optional (and included for best performance)?

noisesmith18:07:06

it prevents an error clojure.set isn't bound

noisesmith18:07:15

it's the symbolic name of something you want to load

noisesmith18:07:24

you'd get a syntax error if you used it unquoted

SgtZdog18:07:58

Why do we not need it in:

(ns myns
  (:require [otherns :refer :all]))

noisesmith18:07:22

the ns form is a macro, and it doesn't try to evaluate the symbols you put in the require form

noisesmith18:07:39

macros can decide when something is evaluated or not

SgtZdog18:07:44

ah, gotcha

noisesmith18:07:17

when you call a function (eg. require) the args are evaluated first, then passed to the functio

SgtZdog18:07:18

so ns, being a macro doesn't actually itself evaluate anything anyway, unlike (require) which is a function and will evaluate immediately.

noisesmith18:07:49

when you call a macro (eg. ns) the args are passed as a list of symbols and input literals and the macro decides what to do with them

noisesmith18:07:32

I mean a macro usually does evaluate things (clojure macros can use the entire clojure language as needed), but they usually just turn one list (form) into another then return it to be compiled

SgtZdog18:07:10

ty very much for the explanation!

Ahmed Hassan18:07:26

What benefits do we have using ClojureScript for server side instead of Clojure?

noisesmith18:07:24

some people are trying to do something that is easier to do in a js vm rather than jvm

👍 4
noisesmith18:07:46

or are OK with losing performance in exchange for faster startup

👍 4
eskemojoe00720:07:12

Looking to generate some documentation for some website back and front end stuff. I'd like to be able to autogenerate or with some massaging generating dependency/flow diagrams. I know codox is common for pulling docs together, but I didn't see any dependency graph makers. This is what I'm thinking. Any recommendations?

csd21:07:19

Should one prefer tagging fns ^:private to using defn-? (question spurred by https://groups.google.com/forum/#!msg/clojure/zaWlDbOhKCM/sD_iOM_k9WcJ)

hiredman21:07:56

there is a third way

csd21:07:14

what is the third way?

hiredman21:07:38

don't use either

csd21:07:39

that's fair i guess. personally i think a project is easier to understand if you're clear about what's used only within a namespace

SgtZdog21:07:19

a google search yields a split internet, use w/e makes your team happy I'd say (my personal opinion would be explicitly using ^:private since a good IDE will provide auto-completion and this is more consistent with def not having a matching def-)

prabello21:07:36

Hello everyone. I have a doubt regarding Clojure and i couldn’t find a clear answer. Assume i have a bank account with the balance of $100.00 I then receive 2000 request to withdraw these $100.00, usually on a Java code, you would need to lock the resource as soon as the first request arrives, so that only the first one can be made and all the others shall fail. I saw examples using the actor model were you can use a single actor to represent one account, and so the 2000 requests would be made by the account actor, leading to the expected result Does Clojure have any way on helping with this? I saw about the agents but i couldn’t find any information that i could digest on this.

noisesmith21:07:37

@pedrommrabello agents are a straightforward way to do something much like an actor, the important difference being that an actor is a method that you send data to, and an agent is data you send a function to

👍 8
noisesmith21:07:05

but you can set a validator, so that the action won't succeed if it leaves a balance below 0 for example

noisesmith21:07:16

you can also do this with atoms, which are much more intuitive to use

SgtZdog21:07:26

Wouldn't this be an excellent case of storing the value in an atom? Hmm, I suppose that doesn't work for multiple processes?

SgtZdog21:07:34

nvm, you beat me to it.

noisesmith22:07:17

with multiple processes you need inter-process communication which clojure doesn't offer out of the box (unless you meant threads, and threads are why we need things like agents, atoms)

SgtZdog22:07:46

No, I meant processes since they don't share memory.

noisesmith22:07:20

right, in that case nothing in clojure really helps - you'd want some IPC strategy, clojure doesn't really cover it

SgtZdog22:07:06

Sorry, still learning multi-process. What is IPC?

noisesmith22:07:15

inter-process-communication

johnj22:07:22

@pedrommrabello any reason this isn't handled by a database with ACID semantics?

gklijs22:07:53

That would work fine, and for transactions would be fast enough as well.

prabello10:07:02

No, just an example of how it can be dealt, not something happening in a real world scenario

noisesmith22:07:58

right, it's also IPC if you ever reboot your VM (two processes, at different times, working on the same data world)

Alex Miller (Clojure team)22:07:50

you can use refs to coordinate multiple stateful changes https://clojure.org/reference/refs

👍 4
SgtZdog23:07:26

Hey guys, I can't figure out what I'm doing wrong here:

(deftest funcmocktest
  (testing "mock a func"
    (with-redefs-fn {#'clojuretesting.secondnamespace/myfunc (fn [] (print "This is a mock."))}
      (is (= nil (clojuretesting.core/mysecondfuncwrapper))))))

noisesmith00:07:29

I can't really tell what this should be doing that it wouldn't do

noisesmith00:07:40

what do you expect here? what happens instead?

SgtZdog00:07:30

Not an exception. I'm trying to replace the myfunc so that tests of myfuncwrapper can be done without a dependency on myfunc

SgtZdog00:07:43

But it's throwing an exception at me.

noisesmith00:07:52

what exception?

SgtZdog00:07:31

`ERROR in (funcmocktest) (core.clj:7216) Uncaught exception, not in assertion. expected: nil actual: java.lang.ClassCastException: java.lang.Boolean cannot be cast to clojure.lang.IFn`

noisesmith00:07:36

that means that somewhere either true or false is being called as if it were a function

noisesmith00:07:32

it is clearly unrelated to what you show in your example, but you might recognize it in your actual code - usually ... cannot be cast to clojure.lang.IFn is as simple as an extra set of parens somewhere

SgtZdog01:07:34

So, I trimmed down what I've got and it looks like the problem is something to do with how I'm using (with-redefs-fn). (myfunc) was still called and executed instead of the mock function.

SgtZdog01:07:28

(ns clojuretesting.core
  (:gen-class)
  (:require [clojuretesting.secondnamespace :refer :all]))

(defn myfuncwrapper
  []
  (myfunc))

(defn -main
  []
  (myfuncwrapper))

...

(ns clojuretesting.secondnamespace)

(defn myfunc
  []
  (print "myfunc was called"))

...

(ns clojuretesting.core-test
  (:require [clojure.test :refer :all]
            [clojuretesting.core]))

(deftest funcmocktest
  (testing "mock a func"
    (with-redefs-fn {#'clojuretesting.secondnamespace/myfunc (fn [] (print "This is a mock."))}
      (is (= nil (clojuretesting.core/myfuncwrapper))))))

noisesmith01:07:00

the second arg to with-redefs-fn needs to be a function

noisesmith01:07:24

so I was wrong, I forgot how with-redefs-fn works, the is returns a boolean, and it's trying to call the boolean

noisesmith01:07:46

you need #(is ....) (function of no arguments to call with those bindings)

noisesmith01:07:04

or you can use with-redefs which accepts a normal body instead of a function

SgtZdog01:07:16

Ah, got it. Putting the # there fixed it I think

SgtZdog01:07:06

Yes, now my result is the expected printing of "This is a mock". Obviously not a good test, but worked for a quick test of how to use with-redefs-fn

SgtZdog01:07:46

So, with-redefs would be what I would normally want to use though? it just takes a &args that would be commands to run while the funcs in the map are replaced?

noisesmith01:07:39

right - it's one of the macros that takes a & body which means any number of things to call inside the context it creates

noisesmith01:07:00

fn, let, defn, do, all have & body as well

noisesmith01:07:20

and deftest of course

SgtZdog01:07:44

hmm, something isn't right, it didn't replace and myfunc was called instead.

SgtZdog01:07:18

(with-redefs {'clojuretesting.secondnamespace/myfunc mockfunc}
      (is (= nil (clojuretesting.core/myfuncwrapper))))

SgtZdog01:07:22

Is that not right?

noisesmith01:07:49

with-redefs accepts a vector - like let

noisesmith01:07:37

(cmd)user=> (require 'clojure.set)
nil
(cmd)user=> (with-redefs-fn {#'clojure.set/union (constantly 42)} #(clojure.set/union #{:a :b} #{:b :c}))
42
(ins)user=> (with-redefs [clojure.set/union (constantly :OK)] (clojure.set/union 'whatever))
:OK

SgtZdog01:07:30

Ah, the ' leading the myfunc was throwing it off.

SgtZdog01:07:39

Ok, thank you so much for your help.

noisesmith01:07:58

and using {} instead of []

SgtZdog01:07:59

(I had tried with a vector but got an exception)

SgtZdog01:07:43

Why does with-redefs-fn have such a different api?

noisesmith01:07:42

it's a function rather than a macro, so it accepts a mapping for new values, and a function to run in the remapped context

noisesmith01:07:20

you can combine functions more easily than macros, so it's often useful to provide a feature as a function (for extension / programmatic usage) and as a macro (for syntax / readable code)

SgtZdog01:07:16

ah, that makes good sense. And explains the name too then.

SgtZdog01:07:42

So besides the API there's no tangible difference between the two. They are equally capable then?

noisesmith01:07:07

perhaps this answers your question

(ins)user=> (source with-redefs)
(defmacro with-redefs
  "binding => var-symbol temp-value-expr

  Temporarily redefines Vars while executing the body.  The
  temp-value-exprs will be evaluated and each resulting value will
  replace in parallel the root value of its Var.  After the body is
  executed, the root values of all the Vars will be set back to their
  old values.  These temporary changes will be visible in all threads.
  Useful for mocking out functions during testing."
  {:added "1.3"}
  [bindings & body]
  `(with-redefs-fn ~(zipmap (map #(list `var %) (take-nth 2 bindings))
                            (take-nth 2 (next bindings)))
                    (fn [] ~@body)))

noisesmith01:07:51

with-redefs literally turns its body into a valid call to with-redefs-fn

SgtZdog01:07:02

ok, very cool. ;todo replace function dependencies using (with-redefs)

SgtZdog01:07:26

(added to the huge list of things I need to do in this project)