Fork me on GitHub
#beginners
<
2020-02-23
>
vinnyataide02:02:17

hello, I'm trying to apply reloaded repl in my server with db I'm getting an error with the .stop method when I'm trying to start?

(ns server.db
  (:require [datomic.api :as d]
            [com.stuartsierra.component :as component]))

(defrecord Database [connection]

  component/Lifecycle

  (start [db]
    (let [uri "datomic:"]
      (d/delete-database uri)
      (d/create-database uri)
      (let [conn (d/connect uri)
            schema (load-file "resources/schema.edn")
            seed (load-file "resources/seed.edn")]
        @(d/transact conn schema)
        @(d/transact conn seed)
        (assoc db :connection (d/db conn)))))
  (stop [db]
    (.stop connection)
    (assoc db :connection nil)))

(defn new-db []
  (map->Database {}))
(ns server.server
  (:require [com.stuartsierra.component :as component]
            [aleph.http :as http]
            [yada.yada :as yada]))

(defrecord Server [port connection]

  component/Lifecycle

  (start [component]
    (let [conn (http/start-server  (yada/handler "Hello!") {:port port})]
      (assoc component :connection conn)))

  (stop [component]
    (.close connection)
    (assoc component :connection nil)))

(defn new-server [port]
  (map->Server {:port port}))

(ns server.core
  (:require [server.server :refer [new-server]]
            [server.db :refer [new-db]]
            [com.stuartsierra.component :refer [system-map using]]))

(defn new-system [{:keys [port]}]
  (system-map
   :database (new-db)
   :server (using (new-server port)
                  {:database :database})))

vinnyataide02:02:57

ok it fixed itself I dont know why

vinnyataide02:02:02

just restarted the repl

vinnyataide02:02:07

wait the error keep apearing sometimes

vinnyataide02:02:44

found the error! I forgot to add the database dependency on the server component lol

hindol.adhya05:02:07

Hi, is there an inbuilt function to filter a collection based on a mask? E.g. [1 2 3 4 5] [true false true true false] => [1 3 4].

andy.fingerhut05:02:24

Not that I can think of, but you can combine pairs using a map call with two input sequences, then call the regular filter on the resulting sequence of pairs/whatevers.

seancorfield05:02:32

(map second (filter first (map vector mask data)))

andy.fingerhut05:02:22

The strange thing is ... we are actually two different people 🙂

hindol.adhya06:02:21

Yeah, I thought about map as well as it can sift through multiple seqs in a lockstep fashion. Wondered if there is a hidden gem.

hindol.adhya06:02:57

I was actually thinking having a custom function f as in (map f data mask) and then filter nil. f will output the value or nil depending on mask. But that would remove actual nil values from original collection. Yours is better. @seancorfield

ankoncha08:02:27

If you change the order of data and mask there is a better solution for it

(remove false? (map #(and %1 %2) mask data))

ankoncha08:02:52

It will have lesser iteration to execute

andy.fingerhut08:02:08

Sean's solution works even if data includes false values that you want to keep. That may or may not be an important consideration -- it depends on what kinds of values are in data

ankoncha08:02:07

Yeah certainly that depends on what kind of data @hindol.adhya is trying to work on.

andy.fingerhut08:02:10

If data contains no values equal to false, then agreed your solution will work, too.

hindol.adhya08:02:58

Well, the data has neither nil nor false. But I want to add a generic function in my library that works for all cases.

hindol.adhya08:02:35

That is why I like Sean's solution the most.

reachtarunhere15:02:13

Hi everyone! I am trying to find out what is the idiomatic way to define helper functions inside a function. Should I be using the let expression or not?

nate_clojurians15:02:44

@reachtarunhere ^^^

reachtarunhere15:02:02

curious how is it different from just using let internally?

nate_clojurians15:02:07

it has a less verbose syntax than let

reachtarunhere15:02:36

Thanks. Also idk probably its me coming from other prog langs but I find using additional defn statements pretty clean. Is something like this bad practice:

reachtarunhere15:02:38

(defn my-main-fn [a b]
                    (defn my-helper-fn [n] (* n 2))
                    (+ (my-helper-fn a) (my-helper-fn b)))

nate_clojurians15:02:47

it also makes all function names available to all functions defined in the letfn

nate_clojurians15:02:13

yep, that's bad practice

reachtarunhere15:02:36

Thanks a lot. I'll read up on it 🙂

nate_clojurians15:02:48

that defines a function for the entire namespace

nate_clojurians15:02:56

not just for that scope

nate_clojurians15:02:20

you could just use let as well but letfn has a lot of nice properties

reachtarunhere15:02:10

ah the entire namespace argument makes a lot of sense. I was thinking like a Python programmers 😅

mark54015:02:14

I have heard that it is often idiomatic to just define top-level private functions rather than functions defined with letfn. Just because they are helpers doesn't mean they shouldn't be top-level. Defining them with letfn is only idiomatic when they call each other and/or are closures over locals or params. Just one viewpoint.

hindol.adhya16:02:35

I have the exact opposite viewpoint. Unless a function is shared between two other functions, I don't define top level private fns. I like the encapsulation.

cgdeboer15:02:35

Hey all, I’ve got a legacy server-side MVC application that has tons of “pages”. There are a couple of these that require a little more user interaction. I’m contemplating having smaller cljs “apps” that run on these pages. Maybe 2 or 3 pages that do not have much overlapping functionality. Has anyone here tried: 1. A single cljs project that handles which portion of the project to render depending on the route ? 2. Many small cljs projects (or profiles) that create individual JS bundles for each page. 3. Some third way of maybe accomplishing something similar.

cgdeboer17:02:33

Thanks @ , I’ll give that a look.

nate_clojurians15:02:12

@mark540 @reachtarunhere yeah I should have added that I basically never use letfn myself

nate_clojurians16:02:18

but I also think people should play around and read other code and decide what they like for themselves

hindol.adhya16:02:48

I know a few people who don't like top level private fns. I like the encapsulation of using let/letfn myself, but I don't consider myself an expert.

michael74017:02:08

do you find it's harder to keep functions under 5 or 10 lines? are short functions important to you?

hindol.adhya17:02:18

Yes, it is indeed harder to keep functions small, but that's not the end goal for me. Using let/letfn to define a nested function still separates logic into multiple units that I can think of independently.

hindol.adhya18:02:37

Do you find this very hard to read?

(def prime-seq
  (concat
   [2 3 5 7]
   (lazy-seq
    (let [primes-from (fn primes-from
                        [n [f & r]]
                        (if (some #(zero? (rem n %))
                                  (take-while #(<= (* % %) n) prime-seq))
                          (recur (+ n f) r)
                          (lazy-seq (cons n (primes-from (+ n f) r)))))
          wheel       (cycle [2 4 2 4 6 2 6 4 2 4 6 6 2 6  4  2
                              6 4 6 8 4 2 4 2 4 8 6 4 6 2  4  6
                              2 6 6 4 2 4 6 2 6 4 2 4 2 10 2 10])]
      (primes-from 11 wheel)))))

michael74018:02:31

i do, a bit, but it could just be i'm more used to the java convention of breaking out nested expressions and naming them. i see what you mean about logical grouping; when everything is flattened out it's difficult to see the broader namespace themes

hindol.adhya18:02:42

You can still name them. Here the inner fn is named primes-from. Here is a factorial example, much simpler than the primes example,

(defn factorial-seq
  []
  (letfn [(step [[n f]]
            [(inc n) (*' f (inc n))])]
    (cons [0 1]
          (iterate step [1 1]))))

;; (take 10 (factorial-seq))
;; => ([0 1] [1 1] [2 2] [3 6] [4 24] [5 120] [6 720] [7 5040] [8 40320] [9 362880])
The previous one actually uses other concepts such as lazy sequences.

michael74018:02:16

that isn't bad at all. when it's simpler like this, it seems right to nest. encountering that step helper outside of factorial-seq would be jarring, imo

hindol.adhya18:02:13

Yeah, and that is one more reason for me. I often struggle to find a good name for these contextual one-off helper methods. They rarely make any sense at the namespace level.

matti.uusitalo16:02:08

It is easier to test top level fns

hindol.adhya16:02:28

That is if you test private fns. Some people like to test only the public API. Not disagreeing with anyone's viewpoint, just stating a different viewpoint so that beginners don't get the impression a specific way is the right way.

mark54017:02:01

Could be wrong, but I was under the impression that a lot of people who don't use top-level private fns use top-level fns for "helper" fns, etc, but just don't bother to make them private.

michael74017:02:48

i used to prefer the hierarchy provided by nesting helper functions, but i think it comes at the cost of short, flat functions - things which the clojure community may not value as much as python or java folks, i'm not sure

mkeller0219:02:11

Compiling my-namespace.core
Syntax error macroexpanding at (core.clj:1:1).
Execution error (FileNotFoundException) at my-namespace.core/loading (core.clj:1).
Could not locate my_namespace__init.class, my_namespace.clj or my_namespace.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.
Trying to compile an uberjar. Can anyone identify from past experience what might be happening here? I've seen similar errors online but not quite like what I'm seeing here. Note the my-namespace.core/loading and then when it says it can't locate things it leaves off the "core" part, i.e. my_namepace__init.class Most the errors I've seen don't seem to "lose" part of their namespace name (like "core") in the lower part of the error... In target I can indeed see target/classes/my_namespace/core$loading__6721__auto____171.class ....all the sibling namespace/classes do have the "init" like target/classes/my_namespace/config__init.class ...not sure why core is getting the "loading" treatment. Any help much appreciated.

seancorfield19:02:50

@mkeller02 loading is some top-level definition in my-namespace.core?

seancorfield19:02:56

@mkeller02 Also, please do not cross-post questions from #beginners into #clojure --

seancorfield19:02:18

(I deleted your cross-post in the other channel)

sfyire19:02:04

Just noticed Clojure's reduce doesn't act like I thought it would in regards to empty collections, an empty collection will invoke f without arguments in Clojure but in PHP array_reduce won't invoke f if the collection is empty, Why does Clojure act this way?

sfyire19:02:37

looks like if the collection can be empty it must contain an init value or a f that can handle a zero arity

dpsutton20:02:08

correct. what would PHP return for the equivalent to (reduce + nil)?

dpsutton20:02:26

> If the array is empty and initial is not passed, array_reduce() returns `NULL`.

dpsutton20:02:04

i personally never reduce without an initial value

dpsutton20:02:13

user=> (reduce (constantly :foo) [1])
1
user=> (reduce (constantly :foo) :initial [1])
:foo

andy.fingerhut20:02:17

The completely correct, but not very useful answer, to "Why does Clojure act this way?" is that Rich Hickey designed it to act that way.

alexmiller21:02:21

Actually this is behavior that comes from Common Lisp and Rich regrets pulling that part over but that ship has sailed

andy.fingerhut21:02:57

Regretted design is still design 🙂

andy.fingerhut21:02:40

But I'm the last person to throw a stone from my glass house of poor designs.

andy.fingerhut21:02:14

If you happen to know, and are willing to indulge me, do you know if there are any other parts of reduce other than the call-0-arity-of-provided-fn that Rich regrets about reduce?

alexmiller21:02:02

No that’s the bad part :)

andy.fingerhut20:02:42

And he likely had public discussions on the Clojure Google group 10 years ago about it, but if so, I don't have a link directly to them handy. I can guess. Operations that have a '0' element that makes sense, e.g. 0 for +, 1 for *, the empty sequence for concat, etc. it is convenient to get that value back when reducing over an empty sequence with that operation.

andy.fingerhut20:02:49

And if you are surprised by this, it means that you may not have read the doc string for the function in question. For reduce , I am pretty sure this behavior is described there.

dpsutton20:02:03

I think I remember reading the word “monoid” in the clojure source somewhere

dpsutton20:02:26

yup. in reducers

andy.fingerhut20:02:31

'monoid' is in there. And it exactly captures the examples above, and every other associative operation with a 0 like them.

andy.fingerhut20:02:47

When I say 'it exactly captures', the 'it' I mean is the monoid from mathematics/group theory, which sounds like a scary technical term if you are unfamiliar with it, but is a very simple idea.

mkeller0220:02:30

@seancorfield No, loading is not some top-level definition in my-namespace.core....no word "load" in the namespace. It seems every other class get's built with an __init__ method and core gets loading... EDIT: I don't know what the "$loading" class stuff is about, but the issue was I had an import like [some.namespace :as logging] instead of [some.namespace.logging :as logging] and it was trying to __init__ some.namespace

seancorfield21:02:38

@mkeller02 My next question was going to be to be "please show us the ns form from my-namespace.core 🙂

seancorfield21:02:49

Glad you figured it out.

scott.archer21:02:48

I'm trying to get the most simple / basic ring handler setup that will just echo the request back to the response as JSON, but I'm having problems.

(defn echo-handler
  ([request]
   {:status 200
    :headers {}
    :body request}))
Any idea why this doesn't work? I get
HTTP ERROR 500 com.fasterxml.jackson.core.JsonGenerationException: Cannot JSON encode object of class: class org.eclipse.jetty.server.HttpInputOverHTTP: [email protected][c=0,q=0,[0]=null,s=STREAM]

nate21:02:56

@scott.archer it's because you aren't trying to send back the request body, but the whole request

nate21:02:33

just grab (:body request) and set that as the :body of the response map

nate21:02:54

(defn echo-handler
  ([request]
   {:status 200
    :headers {}
    :body (:body request)}))

nate21:02:08

although that may still error as the body might be an input stream and needs some slurping

scott.archer21:02:43

@nisehl I thought "request" was "just data" in ring?

scott.archer21:02:05

I was trying to just echo back the entire data structure as the body.

scott.archer21:02:53

When I only set the body it works, but when I try to add {:headers (:headers request) :body (:body request)} it gives me that error again.

nate21:02:10

most of it is just data, but usually the :body is an input stream

nate21:02:00

try doing a (println (:headers request)) above the return map and see what is in the map

hiredman21:02:06

https://github.com/ring-clojure/ring/blob/master/SPEC is the spec, it tells you the keys and types of the keys in the spec

scott.archer21:02:35

Thank you! I didn't realize it was an inputstream. I got it to work by doing this:

(defn echo-handler
  ([request]
   {:status 200
    :headers {}
    :body (assoc request :body (slurp (:body request)))
   }
  ))

kelveden23:02:01

Note also that you should be able to replace that assoc with an update instead - i.e. (update request :body slurp).