Fork me on GitHub
#beginners
<
2018-05-17
>
hawari.rahman1701:05:35

Hi, so I stumbled upon this piece of code in https://github.com/metabase/toucan

(defn do-pre-insert
  "Don't call this directly! Apply functions like `pre-insert` before inserting an object into the DB."
  [model obj]
  (as-> obj <>
    (map-> model <>)
    (pre-insert <>)
    (map-> model <>)
    (apply-type-fns <> :in)
    (apply-property-fns :insert <>)))
I'm curious on what does <> means, does it have a special meaning?

andy.fingerhut01:05:10

No special meaning. You can replace all occurrences of <> with foo or any other Clojure symbol and it would work the same. Whoever wrote that probably just liked the look of <> there, which is a valid Clojure name. Or rather, it works in the implementation, regardless of whether the official docs say it is a valid Clojure name.

hawari.rahman1701:05:32

I see, thank you @andy.fingerhut, I thought it was some kind of common convention that people use like *earmuffs* or fn-bang!

noisesmith01:05:23

I have definitely seen <> as a placeholder, in other languages, but not in clojure before this

dpsutton01:05:58

That's a common one for us at work

andy.fingerhut01:05:59

I looked at 3 uses of as-> in 3 different projects. Some used <>, another used %, one _, one $, one event, ... Doesn't seem to be a commonly used conventional symbol there.

noisesmith02:05:12

_ for something you are not ignoring is weird

noisesmith02:05:57

I typically use x with as->, it's the generic variable after all

dpsutton02:05:11

Yeah and % not in a function literal is misleading to me

andy.fingerhut02:05:51

This one doesn't belong in beginners topic, but the first use of as-> in this code confused the heck out of me until I noticed it was inside of a (-> form): https://crossclj.info/ns/thheller/shadow-cljs/2.3.21/shadow.cljs.devtools.api.html#_check*

marlenefdez02:05:38

Hello! Part of a problem I'm trying to solve involves taking a number and creating a list of the digits. Examples: 123 => [1 2 3] 32542 => [3 2 5 4 2] 12 => [1 2]. Is there an idiomatic way of doing this?

sundarj03:05:12

(map #(Character/getNumericValue %) (str 123))
works

mfikes03:05:56

@marlenefdez FWIW, we worked on this problem on Apropos: https://youtu.be/i3g5bIiTgGM?t=2179

stardiviner03:05:52

In Clojure, how to move a file to a target directory?

mfikes03:05:32

@stardiviner I would probably use Java to rename the file

filosofisto03:05:51

Is it possible to use let for attrib a sequence to variable

filosofisto03:05:06

(let [jobs job-seq(json)] jobs )

stardiviner03:05:11

@mfikes Don't know Java

filosofisto03:05:24

(defn dequeue [json] (let [jobs job-seq(json)] jobs ) )

sundarj03:05:31

job-seq(json) should be (job-seq json)

seancorfield06:05:04

Also @ you should try to get used to "standard" Clojure layout/indentation -- we don't put trailing ) on their own, so your function would be:

(defn dequeue [json]
  (let [jobs (job-seq json)]
    jobs))
Although for something this simple, there's no real need for the let:
(defn dequeue [json]
  (job-seq json))

seancorfield06:05:17

At that point, you could just do

(def dequeue job-seq)
assuming you wanted the alternative name.

seancorfield06:05:57

The community style guide https://github.com/bbatsov/clojure-style-guide talks about syntax, layout, naming, and many other topics -- it's a good read when you're getting started with Clojure (since it's very different to most other languages).

filosofisto11:05:22

Thanks @ for orientatios, I'll try those.

filosofisto03:05:46

I'm receiving this error:

filosofisto03:05:47

Caused by: java.lang.IllegalArgumentException: let requires an even number of forms in binding vector in nubank.core:85

stardiviner03:05:51

@mfikes Like ?

mfikes03:05:31

(.renameTo (io/file "/tmp/foo.txt") (io/file "/tmp/bar.txt"))

mfikes03:05:38

Something along those lines

lee.justin.m03:05:20

(the implementation of rename in that library is exactly what @mfikes suggested

mfikes03:05:00

The “trick” is to know that “move” is the same as “rename”

stardiviner03:05:18

I found "fs" on clojars, but there is https://clojars.org/fs and https://clojars.org/me.raynes/fs which one should I use?

stardiviner03:05:59

I found .renameTo now, I need to (import '( File))

noisesmith11:05:19

FWIW the quote there is redundant - clojure will accept it (outside the ns form at least), but it will also work without the quote

marlenefdez03:05:02

@mfikes that was very helpful, thanks!

vincent.cantin05:05:37

@montanonic @alexmiller @joelsanchez I wrote a 'tree-seq' transducer a while ago. It performs blazing fast and does use the minimum of needed memory (proportional to the depth of the tree, on the call stack). You can copy the source code from there: https://github.com/cgrand/xforms/issues/20

gbouvier05:05:26

(apply max (map :x #{{:x '_}})) is fine but (apply max (map :x #{})) throws ArityException Is there another way I could write this since my set can sometimes be empty?

rauh06:05:40

@gbouvier How should the max be defined for an empty set? IMO the right approach is to use an (if (empty? ..) ...) beforehand

gbouvier06:05:12

That makes sense, thanks. :+1:

cbowdon17:05:54

How best to refactor code like this to extract the dependency on an external source for testing? Here’s a toy example:

(defn fetch-data
  [x]
  (http/get “some url for x”))

(defn score
  [x]
  (->> x
          fetch-data
          do-my-transformations))

;; and several similar functions to score 
Should I pass fetch-data as an argument to score? Or refactor score to take the output of fetch-data as input, composing them elsewhere? Any advice appreciated, unsure of best practice here.

lee.justin.m17:05:13

I, personally, would refactor such that score is totally independent on the source of the data. Keep i/o as isolated from logic as possible and you’ll thank yourself later.

noisesmith17:05:00

if you leave it as is, there's an argument for not testing score, and extensively testing do-my-transformations instead, since all score does is connect some arbitrary data to do-my-transformation

lee.justin.m17:05:18

i was assuming do-my-transformations was a stand-in for a bunch of complex logic. if it is just a functional call, then what @noisesmith is basically the same thing that i’m trying to suggest

noisesmith17:05:47

right, it's effectively doing what you suggesed already

donaldball17:05:15

My general advice is: if you can do all of the i/o work at the topmost layer and just call pure fns to do all the logic, you might be fine just doing tests below the topmost layer. If the impure work pervades the system, you might be happiest putting the impure components behind a protocol, channel, or some other abstraction.

noisesmith17:05:28

funny how having monads would front-load this complexity (that you have to deal with whether you have monads or not naturally)

donaldball17:05:16

If you have a really small number of impure fns and you do want integration tests, you might be fine passing them as args or using with-redefs but personally I get confused pretty quickly going down that road.

noisesmith18:05:40

also with-redefs has some gotchas, it's not thread safe for example

noisesmith18:05:05

(though most people strictly run tests single-threaded so that's less of a problem)

noisesmith18:05:24

and it can cause weird errors when mixed with lazy code

ali41718:05:10

does anyone knows what this means in the function arguments [i j :as point]

noisesmith18:05:50

@ali417 it's destructuring

ali41718:05:04

thank you!

cbowdon18:05:03

Thanks guys - I will refactor so that I/O functions have their own namespace and the complex logic takes their output as input.

alexandre.almosni23:05:37

Hello there - transitioning from Java, I had a bunch of classes "subtypes" inheriting the same interface. Everytime I created a new subtype the IDE forced me to implement all the interface methods. In Clojure I'm running a bunch of multimethods on a map of maps which essentially has the name of each object as main key, and then the subtype and all necessary parameters as subkeys (I dispatch on the subtype). My problem is - when I need a new subtype, I regularly forget to implement one of the multimethods, and the problem only appears at runtime. Is there a best practice to enforce all functions are defined for my new subtype? Thank you,

val_waeselynck08:05:06

You could also create a function (implement-my-type method1 method2 method3 ...) which defers to defmethod, and implement each subtype that way, this way you have less risk of forgetting a method

seancorfield23:05:18

@alexandre.almosni Always write a test for each new subtype first, then implement it?

alexandre.almosni07:05:33

Thanks - I’ll do that

alexandre.almosni08:05:35

I also found out about the methods function, so something like (and (contains? (methods fx) key) ...)

seancorfield17:05:43

TIL! After nearly eight years of Clojure, I've never used that one. Thank you!