92 Matching Annotations
  1. Jun 2023
  2. Dec 2022
  3. Nov 2022
  4. Sep 2022
  5. Jun 2022
  6. Mar 2022
  7. Feb 2022
  8. Nov 2021
  9. Oct 2021
  10. Aug 2021
    1. Clojure Vars can have thread-local bindings. binding uses these, while with-redefs actually alters the root binding (which is someting like the default value) of the var. Another difference is that binding only works for :dynamic vars while with-redefs works for all vars.

      binding vs. with-redefs

      user=> (def ^:dynamic *a* 1)
      #'user/*a*
      user=> (binding [*a* 2] *a*)
      2
      user=> (with-redefs [*a* 2] *a*)
      2
      user=> (binding [*a* 2] (doto (Thread. (fn [] (println "*a* is " *a*))) (.start) (.join)))
      *a* is  1
      #<Thread Thread[Thread-2,5,]>
      user=> (with-redefs [*a* 2] (doto (Thread. (fn [] (println "*a* is " *a*))) (.start) (.join)))
      *a* is  2
      #<Thread Thread[Thread-3,5,]>
      

      意思是说

      • 通过 binding 绑定的值是线程间不可见的
      • 通过 with-redefs 绑定的值是线程间可见的

      binding 可以构造 thread-local var; with-redefs 构造的是 thread-shareing var .

    1. dotoclojure.coreAvailable since 1.0 (source) (doto x & forms)Evaluates x then calls all of the methods and functions with the value of x supplied at the front of the given arguments. The forms are evaluated in order. Returns x. (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))

      doto 不同于 do

      • doto(doto x & forms) 计算第一个表达式 x,一般是构建对象或者线程。之后的所有表达式都是对 x 的调用: x.expr1 x.expr2, 并且最后返回的仍然是 x.

      doto 把 x 对象作为后面方法的所属对象(一般对象调用方法,在 clojure 的写法中都是 (.method object &arguments))。进行调用,整体表达式仍然返回 x 对象。

      因此这种方法通常用来对 x 对象的属性,进行批量修改(如果 x 对象有多个属性),或者是调用 x 的方法,对 x 进行某些改变。

      • do(do & forms) 只是按顺序执行每一个表达式,并返回最后一个表达式的结果
  11. Jul 2021
    1. Clojure’s four reference types are listed across the top, with their features listed down the left. Atoms are for lone synchronous objects. Agents are for asynchronous actions. Vars are for thread-local storage. Refs are for synchronously coordinating multiple objects.

      四种mutable object分别适用不同的并发处理情形:

      1. vars : 线程隔离
      2. refs: 多对象协调同步
      3. atoms: 单对象同步
      4. agents: 异步

      四种对象特色综述

      • Coordinate :意思是读写多个refs可以保障无冲突

      • Asynchronous : 意思是更新请求会在另一个线程排队等待稍后执行。

      • Retriable : 对 reference value 的更新如果遇到异常会重试

      • Thread-local: 每个线程有单独的数据拷贝,对状态的更改都是独立的

    2. Time—The relative moments when events occur State—A snapshot of an entity’s properties at a moment in time Identity—The logical entity identified by a common stream of states occurring over time

      clojure 状态管理和更改模型,包含三个核心概念:

      • 时间——事件发生的相对时刻
      • 状态——某个时刻实体属性的快照
      • 身份——逻辑实体, 亦即一段时间内的公共状态流
    1. (set! var-symbol expr) Assignment special form. When the first operand is a symbol, it must resolve to a global var. The value of the var’s current thread binding is set to the value of expr. Currently, it is an error to attempt to set the root binding of a var using set!, i.e. var assignments are thread-local. In all cases the value of expr is returned. Note - you cannot assign to function params or local bindings. Only Java fields, Vars, Refs and Agents are mutable in Clojure.

      在 clojure 中只有四种 mutable 对象:

      1. Java fields
      2. var
      3. ref
      4. agent

      可以通过 (set! var-symbol expr) 对他们进行 thread-loca 更改。

  12. Apr 2021
  13. Mar 2021
  14. Feb 2021
  15. Oct 2020
  16. Sep 2020
  17. Aug 2020
  18. Jul 2020
  19. May 2020
    1. Programming languages These will probably expose my ignorance pretty nicely.

      When to use different programming languages (advice from an Amazon employee):

      • Java - enterprise applications
      • C# - Microsoft's spin on Java (useful in the Microsoft's ecosystem)
      • Ruby - when speed is more important then legibility or debugging
      • Python - same as Ruby but also for ML/AI (don't forget to use type hinting to make life a little saner)
      • Go/Rust - fresh web service where latency and performance were more important than community/library support
      • Haskell/Erlang - for very elegant/mathematical functional approach without a lot of business logic
      • Clojure - in situation when you love Lisp (?)
      • Kotlin/Scala - languages compiling to JVM bytecode (preferable over Clojure). Kotlin works with Java and has great IntelliJ support
      • C - classes of applications (operating systems, language design, low-level programming and hardware)
      • C++ - robotics, video games and high frequency trading where the performance gains from no garbage collection make it preferable to Java
      • PHP/Hack - testing server changes without rebuilding. PHP is banned at Amazon due to security reasons, but its successor, Hack, runs a lot of Facebook and Slack's backends
  20. Apr 2020
  21. Mar 2020
  22. Feb 2020
  23. Jan 2020
  24. Dec 2019
    1. Clojure has a programmatic macro system which allows the compiler to be extended by user code. Macros can be used to define syntactic constructs which would require primitives or built-in support in other languages. Many core constructs of Clojure are not, in fact, primitives, but are normal macros.
  25. Nov 2019
    1. They answer the two chief complaints about Lisp syntax: too many parentheses and “unnatural” ordering

      they do that, but I don't think that's their primary rationale and a deeper win more important they neatly express certain structure / shape of computation

  26. Oct 2019
    1. In 2017, I rewrote it again as a ClojureScript application, and it was only 500 lines of code! Holy cow!!!

      going from 3k obj-c to 1,5k js to 0.5k in cljs!

    1. For example the following pattern: (let [x true y true z true] (match [x y z] [_ false true] 1 [false true _ ] 2 [_ _ false] 3 [_ _ true] 4)) ;=> 4 expands into something similar to the following: (cond (= y false) (cond (= z false) (let [] 3) (= z true) (let [] 1) :else (throw (java.lang.Exception. "No match found."))) (= y true) (cond (= x false) (let [] 2) :else (cond (= z false) 3 (= z true) 4 :else (throw (java.lang.Exception. "No match found.")))) :else (cond (= z false) (let [] 3) (= z true) (let [] 4) :else (throw (java.lang.Exception. "No match found.")))) Note that y gets tested first. Lazy pattern matching consistently gives compact decision trees. This means faster pattern matching. You can find out more in the top paper cited below.
  27. Sep 2019
    1. The problem with the annotation notion is that it's the first time that we consider a piece of data which is not merely a projection of data already present in the message store: it is out-of-band data that needs to be stored somewhere.

      could be same, schemaless datastore?

    2. There are objects, sets of objects, and presentation tools. There is a presentation tool for each kind of object; and one for each kind of object set.

      very clojure-y mood, makes me think of clojure REBL (browser) which in turn is inspired by the smalltalk browser and was taken out of datomic (which is inspired by RDF, mentioned above!)

    1. That said, most Clojure programs begin life as text files, and it is the task of the reader to parse the text and produce the data structure the compiler will see

      clojure compiler sees (real) clojure i.e. programs as clojure data structures (we) humans see clojure in their (semi-incidental) representation in text readers bridges the gap

      Now, why semi- incidental? It's not necessary for clojure to be text but it is necesserry for it to be represented as some kind of symbolic representations for humans. It's pretty much always text

  28. May 2019
    1. Although regular macros work on programs in the form of trees, a special type of macro, called a read macro, operates on the raw characters that make up your program.

      this make me wonder how is this different from pre-processors? if I'm working on text before compile, is being in the same language giving me any advantage

    2. Not only does lisp provide direct access to code that has been parsed into a cons cell structure, but it also provides access to the characters that make up your programs before they even reach that stage.

      reader macros user extendible read macros are not present in clojure there is a project to provide that outside vanilla clojure

  29. Apr 2019
    1. deps.edn - install level deps.edn file, with some default deps (Clojure, etc) and provider config

      /usr/local/Cellar/clojure/<version> for brew installed

    1. :deps ; to get access to clojure.tools.deps.alpha.repl/add-lib ;; - now you can add new deps to a running REPL: ;; (require '[clojure.tools.deps.alpha.repl :refer [add-lib]]) ;; (add-lib 'some/library {:mvn/version "RELEASE"}) ;; - and you can git deps too; here's how to get the master version of a lib: ;; (require '[clojure.tools.gitlibs :as gitlibs]) ;; (defn load-master [lib] ;; (let [git (str "https://github.com/" lib ".git")] ;; (add-lib lib {:git/url git :sha (gitlibs/resolve git "master")}))) ;; - e.g., using the GitHub path (not the usual Maven group/artifact): ;; (load-master 'clojure/tools.trace) {:extra-deps {org.clojure/tools.deps.alpha {:git/url "https://github.com/clojure/tools.deps.alpha" :sha "e160f184f051f120014244679831a9bccb37c9de"}}}

      add-lib etc

    2. ;; - see https://github.com/bhauman/rebel-readline ;; - start a Rebel Readline REPL: :rebel {:extra-deps {com.bhauman/rebel-readline {:mvn/version "RELEASE"}} :main-opts ["-m" "rebel-readline.main"]}

      run bh's rebel from anywhere

    1. However, we found a few trade-offs when using clojure.spec: clojure.spec requires registering a spec for each key in every path that you care about in a data structure, which can be verbose, especially for deeply-nested data structures. We felt this pain the most when specifying the incoming webhook payloads from the providers. We didn't specify the full payloads, we only specified the values that we actually needed, some of which were nested 7-8 levels deep. To work around this, we used data-spec (part of the spec-tools project) to define the payload specs as a mirror of the shape of the actual data. clojure.spec's error output is concise, and it's not always immediately apparent where the validation failure is within the data, especially when validating a large data structure. To help with this, we used expound to generate friendlier error messages at the REPL and in tests.

      spec gotchas

    2. This system is the first significant project on which we have used clojure.spec, and it proved useful in a few ways: Specifying the inputs to each layer in one place made it easier to visualize the shape of the data as it flowed through the subsystem, and proved valuable when describing the behavior to other engineers.Instrumenting the inputs and outputs of our layer boundary functions (via Orchestra), during testing, enabled us to quickly catch cases where we deviated from the spec, allowing us to see the issue at that boundary instead of as a random error within the implementation. Validating the layer inputs against our specs in production allows us to quickly diagnose and isolate faults to the layer where it was triggered, and helps us catch when our assumptions about the shape of the webhook event are incorrect. Having specs allowed for more straightforward tests, since we didn't need to assert on the shape of the data.

      benefits of spec

    1. ([a b] (if (and (map? a) (map? b)) (merge-with deep-merge a b) b))

      deep-merge nice recursive solution piggybacking on merge-with

    2. ([m k f] (if-let [kv (find m k)] (assoc m k (f (val kv))) m))

      update-existing good use of find & combination of if-let and find!

    3. ([pred coll] (reduce (fn [_ x] (if (pred x) (reduced x))) nil coll))

      find-first

    1. (def current "Get current process PID" (memoize (fn [] (-> (java.lang.management.ManagementFactory/getRuntimeMXBean) (.getName) (string/split #"@") (first)))))

      getting current proces id (PID) more importantly: java.lang.management.ManagementFactory/getRuntimeMXBean

    1. (defn- file? [f] (instance? java.io.File f)) (defn- find-files-in-dir [dir] (filter #(.isFile ^File %) (file-seq dir)))

      finding files nice declarative way file-seq of this dir

    1. (s/def :ring.http/field-name (-> (s/and string? not-empty field-name-chars?) (s/with-gen #(gen/not-empty (gen-string field-name-chars)))))

      clean way of using with gen!

    2. (defn- char-range [a b] (map char (range (int a) (inc (int b))))) (def ^:private lower-case-chars (set (char-range \a \z)))

      nice way of defining character range

  30. Mar 2019
  31. 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

  32. 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

  33. 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

  34. 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"

  35. 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

  36. 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

  37. 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