Re: Switch devops environments via namespaces

2023-05-18 Thread aditya....@gmail.com
So from an ops point of view, I like your first approach; viz. pass the 
environment parameter explicitly.

I prefer being explicit about every single invocation of any ops task.

The namespace solution, while being "legal", causes the configuration to 
become implicit, one has to look at the REPL prompt to "know", rather than 
be confident that we told the function exactly where to call.

>From a Clojure sensibilities point of view, what was stateless (explicit 
param) becomes stateful (implicit global state with def server). This can 
cause all manner of subtle issues, e.g. accidentally referring to the var 
instead of the resolved var in some service function.

Personally, I would go with your original approach, because it forces me to 
slow down a little and pay attention, while I'm typing, every single time. 
This is in service of human error mitigation. I would say the explicit 
workflow is close to the "pointing and calling" method 
https://en.wikipedia.org/wiki/Pointing_and_calling

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/clojure/b6713010-47cc-4bd6-8073-d9d5d681d62cn%40googlegroups.com.


Re: [ANN] Discontinuing 4clojure.com

2021-07-05 Thread aditya....@gmail.com
Thanks to all the 4clojure maintainers and contributors, and special thanks 
to you, Alan for keeping the lights on for so long. 

4clojure helped me as I muddled through learning Clojure almost eight years 
ago (good grief, eight years!). At the time it was perhaps the only 
beginner-accessible _and fun!_ way to get introduced to the language. Being 
able to see other peoples solutions was a huge part of the learning process 
--- exposure to other peoples' ideas and thought process is so precious.

It's been a great run. I feel happy-sad but also hopeful that, like great 
art, it finds a new life in a new way.

Live long and prosper \\//_
On Monday, July 5, 2021 at 1:56:05 AM UTC+5:30 Alan Malloy wrote:

> TL;DR: Turning off 4clojure.com by the end of July 2021
>
> Hello, 4clojure problem solvers. You've probably noticed SSL errors on 
> 4clojure.com over the last week. The old decrepit system 4clojure runs on 
> has finally gotten out of date enough that I can't even figure out how to 
> get it recent enough that SSL certs will auto-renew anymore.
>
> In principle I could start from scratch on a new server and move 4clojure 
> over, but I won't. 4clojure has been piggybacking along on a server that I 
> use for personal reasons, and over the years I have less and less reason to 
> keep paying for that server - it's now pretty much just 4clojure costing me 
> an embarrassing amount of money every month because I haven't wanted to 
> disappoint the community by shutting it down. This SSL thing is just what 
> made me finally pull the trigger.
>
> I don't have a specific EOL date in mind, but sometime near the end of the 
> month, since that's the billing cycle. Until that time, 4clojure still 
> works, as long as you don't mind clicking through the security warnings - 
> it really is still me hosting the site, and since the connection is still 
> HTTPS (albeit with an invalid cert) I think that means your data is still 
> safe. If you have solutions to problems you're proud of, you've still got 
> some time to print them out and put them up on your refrigerator.
>
> I'm not seeking new maintainers. I'd feel uncomfortable handing over a 
> database with so many email addresses and password hashes in it to anyone. 
> The service has had a good run - just over a decade since the first 
> release 
> .
>  
> I hope you enjoyed it during that time.
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/clojure/79ae9a04-adab-4fdd-8ca1-2c7a81867139n%40googlegroups.com.


Re: Idiomatic program for someone new to Clojure

2020-12-15 Thread aditya....@gmail.com
On Wednesday, December 16, 2020 at 3:12:22 AM UTC+5:30 jamesl...@gmail.com 
wrote:

> So I think I can answer my first question. That particular line in 
> `pipeline-build-count` is doing associative destructing 
> <https://clojure.org/guides/destructuring#_associative_destructuring>. 
> And you didn't have to do the `as response` but like you said in your 
> comment, you are just doing that to sort of document the method. That sound 
> about right?
>

Yes it's "associative destructuring". The main purpose of destructuring is 
to conveniently bind a name to a value found somewhere inside a data 
structure. The additional benefit is that it also serves to document the 
function API, which IDE tooling can surface.

The term "destructuring" sounded heavy when I first tried to grok it. It 
got easier when I started thinking of it like visually matching shapes (of 
name bindings) to shapes (of data). Sort of like a fancy mask or a cookie 
cutter pattern. To illustrate:

;; If I see this:

(let [[a b c & others]
  [1 2 3 4 5 6]]
  ;; do something
  )

;; I visualise the effect of destructuring as:

1  2  34 5 6
|  |  |   ( )
v  v  v  v
[a  b  c & others]
 

> On Tuesday, December 15, 2020 at 2:33:08 PM UTC-6 James Lorenzen wrote:
>
>> Also, your program works great though it only returns a list of the 
>> latest build numbers without the project name. I'm trying to think of how I 
>> could include that but I'm not seeing it. The fetch pipeline response does 
>> include the project name so I could just return it and the counter; so I 
>> could return a tuple? or just two values (I think clojure can support 
>> that). I'd rather just thread the project-name through though.
>>
>
There are at least two ways to do this. In both cases, I'd favour returning 
a hash-map, instead of tuples. 

One way is to modify `pipeline-build-count` like this:

(defn pipeline-build-count
  [{:keys [body] :as response}]
  {:project-name (:the-key-for-project-name body)
   :latest-build-count (-> body
   (parse-string true)
   :pipelines
   first
   :counter)})

The other way is in the fetch-pipeline-counts function. Something like this:

(defn fetch-pipeline-counts-alt
  [pipeline-fetcher project-names]
  (reduce (fn [builds project-name]
(conj builds
  {:project-name project-name
   :latest-build-count (-> project-name
   pipeline-api-endpoint
   pipeline-fetcher
   pipeline-build-count)}))
  []
  project-names))

If the name is the only relevant detail that matters, then either way is 
fine. However, I'd modifying the pipeline-build-count function, because it 
has access to all the information about a pipeline, and we may want to 
process and/or pass through more of it later.

 

> Thanks for all the help! I'm learning a ton.
>>
>
Cheers :)

On Monday, December 14, 2020 at 11:35:15 PM UTC-6 aditya@gmail.com 
>> wrote:
>>
>>> I'd try to separate the "I/O or side-effecting" parts from the "purely 
>>> data processing" parts. This makes the program much easier to test --- the 
>>> "purer" the code, the better it is. This also helps tease apart 
>>> domain-agnostic parts from domain-specialised parts, which is useful, 
>>> because domain-agnostic parts tend to be generalisable and thus more 
>>> reusable.
>>>
>>> I'd also avoid mixing lazy functions like `map` with effectful things 
>>> like `GET` requests, because : 
>>> https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects
>>>
>>> I've taken the liberty to rearrange the code, and rename functions to 
>>> illustrate what I mean.
>>>
>>> (defn pipeline-api-endpoint ;; lifted out of `fetch-pipeline` 
>>>   [project-name]
>>>   ;; knows how to translate, or perhaps more generally, map, a project 
>>> name to a project URL format
>>>   (str "https://example.com/go/api/pipelines/; project-name "/history"))
>>>
>>> (defn http-get-basic-auth ;; instead of `fetch-pipeline', because now 
>>> this operation doesn't care about a specific type of URL  
>>>   [well-formed-url] ;; it can simply assume someone gives it a 
>>> well-formed URL.
>>>   (client/get well-formed-url
>>>   {:basic-auth "username:password"})) ;; we'll see about 
>>> this hard-coding later
>>>
>>> (defn pipeline-build-count ;; now this only cares about looking up 
>&

Re: Idiomatic program for someone new to Clojure

2020-12-14 Thread aditya....@gmail.com
I'd try to separate the "I/O or side-effecting" parts from the "purely data 
processing" parts. This makes the program much easier to test --- the 
"purer" the code, the better it is. This also helps tease apart 
domain-agnostic parts from domain-specialised parts, which is useful, 
because domain-agnostic parts tend to be generalisable and thus more 
reusable.

I'd also avoid mixing lazy functions like `map` with effectful things like 
`GET` requests, because 
: https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects

I've taken the liberty to rearrange the code, and rename functions to 
illustrate what I mean.

(defn pipeline-api-endpoint ;; lifted out of `fetch-pipeline` 
  [project-name]
  ;; knows how to translate, or perhaps more generally, map, a project name 
to a project URL format
  (str "https://example.com/go/api/pipelines/; project-name "/history"))

(defn http-get-basic-auth ;; instead of `fetch-pipeline', because now this 
operation doesn't care about a specific type of URL  
  [well-formed-url] ;; it can simply assume someone gives it a well-formed 
URL.
  (client/get well-formed-url
  {:basic-auth "username:password"})) ;; we'll see about this 
hard-coding later

(defn pipeline-build-count ;; now this only cares about looking up build 
count, so no "GET" semantics
  ;; assumes it gets a well-formed response
  [{:keys [body] :as response}] ;; destructuring for convenience and 
function API documentation
  (-> body
  (parse-string true)
  :pipelines
  first
  :counter))

(defn fetch-pipeline-counts! ;; ties all the pieces together
  [project-names]
  (reduce (fn [builds project-name] 
   ;; uses reduce, not map, 
because: https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects
(conj builds
  (-> project-name
  pipeline-api-endpoint
  http-get-basic-auth
  pipeline-build-count)))
  []
  project-names))


Now... It turns out that fetch-pipeline-counts! is a giant effectful 
process, tied directly to http-get-basic-auth. We could try to lift out the 
effectful part, and try to make it a pure function.

(defn http-get-basic-auth
  [well-formed-url username password]
  (client/get well-formed-url
  {:basic-auth (str username ":" password)}))

(defn http-basic-auth-getter
  "Given basic auth credentials, return a function that takes an HTTP 
endpoint, and GETs data from there."
  [username password]
  (fn [well-formed-url]
(http-get-basic-auth well-formed-url
 username
 password)))

(defn fetch-pipeline-counts-alt
  [pipeline-fetcher project-names]
  ;; Easier to unit test. We can pass a mock fetcher that doesn't call over 
the network.
  ;; In fact, we can now use any kind of "fetcher", even read from a DB or 
file where we may have dumped raw GET results.
  (reduce (fn [builds project-name]
(conj builds
  (-> project-name
  pipeline-api-endpoint
  pipeline-fetcher
  pipeline-build-count)))
  []
  project-names))

(comment
  (fetch-pipeline-counts-alt (http-basic-auth-getter "username" "password")
 ["projectA"
  "projectB"
  "projectC"
  "projectD"])
 )

A closer look might suggest that we're now describing processes that could 
be much more general than fetching pipeline counts from an HTTP endpoint...

Enjoy Clojuring! :)

On Monday, December 14, 2020 at 10:51:52 PM UTC+5:30 jamesl...@gmail.com 
wrote:

> Very cool everyone. This is exactly the kind of feedback I was hoping for. 
> I'm going through Clojure for the Brave and I hadn't made it to the macros 
> chapter yet. That single threading macro is pretty sweet!
>
> Thanks everyone!
>
> On Monday, December 14, 2020 at 11:00:02 AM UTC-6 brando...@gmail.com 
> wrote:
>
>> Hey James,
>>
>> Another small suggestion is you can just pass println to map, since it 
>> takes 1 argument in your case.
>>
>> (map println (sort builds))
>>
>> But here, since you just want to perform side effects, maybe run! would 
>> be a better function to use.
>>
>> (run! println (sort builds))
>>
>> This would cause it to return just one nil. Clojure is a functional 
>> language, and every function returns a value. You'll see the return in 
>> your REPL, but if the program is run in another way, such as packaged as a 
>> jar, you would just see the print output.
>>
>> - Brandon
>>
>> On Mon, Dec 14, 2020 at 8:42 AM Justin Smith  wrote:
>>
>>> a small suggestion: you don't need to nest let inside let, a clause
>>> can use previous clauses:
>>>
>>> (defn get-latest-build
>>>   [pipeline]
>>>   (let [response (fetch-pipeline pipeline)
>>> json (parse-string (:body response) true)
>>>[pipeline] (:pipelines json)]
>>>   (:counter