23 Matching Annotations
  1. Feb 2019
    1. :ok

      nice, return value: keyword!

    2. (fn [system] (throw (ex-info "initializer not set, did you call `set-init`?" {}))))

      interesting pattern, default function throws kind of like abstract base classes methods in python

  2. Dec 2018
    1. Full disclosure: I’m a co-maintainer of clj-time and I’m pretty vocal about encouraging people not to use clj-time when starting a new project: use Java Time instead. Conversion from an existing, clj-time-heavy project is another matter tho’, unfortunately.

      sean cornfield co-mainainter of clj-time use Java.Time

  3. Nov 2018
    1. One quick trick for making it easier for debugging/understanding your threading macros is to put print statements in between some of the steps. The important thing to remember is that all the print functions in clojure return nil so you need to make a little helper function (I like to call mine ?) that prints and then returns the original input, like this: (defn ? [x] (prn x) x)

      debugging trick

    2. I don't think passing the entire http client is very idiomatic, but what is quite common is to pass the entire "environment" (aka runtime configuration) that your app needs through every function. In your case if the only variant is the URL then you could just pass the URL as the first parameter to get-data. This might seem cumbersome to someone used to OO programming but in functional programming it's quite standard. You might notice when looking at example code, tutorials, open source libraries etc. that almost all code that reads or writes to databases expects the DB connection information (or an entire db object) as a parameter to every single function. Another thing you often see is an entire "env" map being passed around which has all config in it such as endpoint URLs.

      passing state down the call stack configuration, connection, db--pretty common in FP

    3. Something that I've found helps greatly with testing is that when you have code with lots of nested function calls you should try to refactor it into a flat, top level pipeline rather than a calling each function from inside its parent function. Luckily in clojure this is really easy to do with macros like -> and friends, and once you start embracing this approach you can enter a whole new world of transducers. What I mean by a top level pipeline is that for example instead of writing code like this: (defn remap-keys [data] ...some logic...) (defn process-data [data] (remap-keys (flatten data))) (defn get-data [] (let [data (http/get *url*)] (process-data data))) you should make each step its own pure function which receives and returns data, and join them all together at the top with a threading macro: (defn fetch-data [url] (http/get url)) (defn process-data [data] (flatten data)) (defn remap-keys [data] ...some logic...) (defn get-data [] (-> *url* fetch-data process-data remap-keys)) You code hasn't really changed, but now each function can be tested completely independently of the others, because each one is a pure data transform with no internal calls to any of your other functions. You can use the repl to run each step one at a time and in doing so also capture some mock data to use for your tests! Additionally you could make an e2e tests pipeline which runs the same code as get-data but just starts with a different URL, however I would not advise doing this in most cases, and prefer to pass it as a parameter (or at least dynamic var) when feasible.

      testing flat no deep call stacks, use pipelines

    4. One thing Component taught me was to think of the entire system like an Object. Specifically, there is state that needs to be managed. So I suggest you think about -main as initializing your system state. Your system needs an http client, so initialize it before you do anything else

      software design state on the outside, before anything else lessions from Component

    5. For the sweet spot you're looking for, I suggest being clear about if you're designing or developing. If you're designing and at the REPL, force yourself to step away with pen and paper after you've gotten some fast feedback.

      designing vs developing!

    1. n

      Using the syntax #'a.b/d is a shortcut for (var a.b/d), with returns the "var" which points to the "function" a.b/d. When Clojure sees the var, it automatically substitutes the function before evaluation. I found this (mostly undocumented) behavior quite confusing for several years. – Alan Thompson May 26 '16 at 21:41

    1. Re-open libraries for exploration I use in-ns to jump into library namespaces and re-define their vars. I insert bits of println statements to help understand how data flows through a library. These monkey-patches only exist in the running REPL. I usually put them inside a comment form. On a REPL restart, the library is back at its pristine state. In this example below, I re-open clj-http.headers to add tracing before the header transformation logic: [source] ;; set us up for re-opening libraries (require 'clj-http.headers) (in-ns 'clj-http.headers) (defn- header-map-request [req] (let [req-headers (:headers req)] (if req-headers (do (println "HEADERS: " req-headers) ;; <-- this is my added print (-> req (assoc :headers (into (header-map) req-headers) :use-header-maps-in-response? true))) req))) ;; Go back to to the user namespace to test the change (in-ns 'user) (require '[clj-http.client :as http]) (http/get "http://www.example.com") ;; This is printed in the REPL: ;; HEADERS: {accept-encoding gzip, deflate} An astute observer will notice this workflow is no different from the regular clojure workflow. Clojure gets out of your way and allows you to shape & experiment in the code in the REPL. You can use this technique to explore clojure.core too!

      explore library code in the repl in-ns and the redefinition

    2. Unmap namespaces during experimentation I use ns-unmap and ns-unalias to remove definitions from my namespace. These are the complementary functions of require and def. While exploring, you namespace will accrue failed experiments, especially around naming. Instead of using a giant hammer [tools.namespace], you can opt for finer-grained tools like these. Example: (require '[clojure.string :as thing]) (ns-unalias *ns* 'thing) ; *ns* refers to the current namespace

      cleaning up the namespace fro repl experimentation

    1. That is using a specific tool for a specific use case. You don’t actually have a table view of your data. Once it’s in a table, man, you’re good. That is the modeling. A sequel database table, you have this amazing high-level language for doing all sorts of cool operations with it.To turn this into some class hierarchy, it’s almost criminal. There, I said it. It’s like you’re throwing away the power that you have.

      about a situation when you sometime want an is-a relationship but in most cases just have it as loosely structured (table-like) data format

    1. clone the repo, then lein install and use whatever that snapshot version in project.clj is where you want to use it (edited)

      how to use specific version of library you can also use checkouts where you just symlink to a local copy from your-project/checkouts

  4. Oct 2018
    1. TIL this is meant to work in Clojure and ClojureScript. Wow… cool! (keys (filter (comp pos? val) {:a 0, :b 1}))

      filter returns mapentries so not a map yet keys works on that!

    1. function coll-of allows a :count key to specify the required number of elements:

      good to know that there is the keyword directive :count

    2. On the flip side, it can go further than mere types, including emulating dependent types and programming-by-contract.

      spec though it's used at runtime (not compile time)

      • hence: not a replacement for types as such BUT
      • enables dependent types
      • programming by contract
    1. Following Christopher Strachey,[2] parametric polymorphism may be contrasted with ad hoc polymorphism, in which a single polymorphic function can have a number of distinct and potentially heterogeneous implementations depending on the type of argument(s) to which it is applied. Thus, ad hoc polymorphism can generally only support a limited number of such distinct types, since a separate implementation has to be provided for each type.

      kind of like clojure multimethods but those can dispatch on arbitary function hence arbitrary "property"

  5. Sep 2017
    1. Hiccup forms are plain Clojure vectors: you don’t need to learn the Hiccup syntax you don’t need to write a preprocessor you don’t need to write IDE plugins there are no edge cases you can test part of your components as plain clojure functions you can parse your hiccup code

      clojure

  6. Aug 2017
    1. Since Clojure uses the Java calling conventions, it cannot, and does not, make the same tail call optimization guarantees. Instead, it provides the recur special operator, which does constant-space recursive looping by rebinding and jumping to the nearest enclosing loop or function frame. While not as general as tail-call-optimization, it allows most of the same elegant constructs, and offers the advantage of checking that calls to recur can only happen in a tail position.

      Clojure's answer to the JVM's lack to tail call optimization

  7. Dec 2015
    1. Clojure Design Patterns

      Intro Episode 1. Command Episode 2. Strategy Episode 3. State Episode 4. Visitor Episode 5. Template Method Episode 6. Iterator Episode 7. Memento Episode 8. Prototype Episode 9. Mediator Episode 10. Observer Episode 11. Interpreter Episode 12. Flyweight Episode 13. Builder Episode 14. Facade Episode 15. Singleton Episode 16. Chain of Responsibility Episode 17. Composite Episode 18. Factory Method Episode 19. Abstract Factory Episode 20. Adapter Episode 21. Decorator Episode 22. Proxy Episode 23. Bridge