I think dynamic typing is a lot more problematic in imperative/OO languages. One problem is that the data is mutable, and you pass things around by reference. Even if you knew the shape of the data originally, there's no way to tell whether it's been changed elsewhere via side effects. The other problem is that OO encourages proliferation of types in your code. Keeping track of that quickly gets out of hand.
Clojure embraces immutability, and any changes to the data happen explicitly in a known context. This makes it much easier to know exactly what the shape of the data is at any one place in your application.
Meanwhile, all the data is structured using a set of common data structures. Any iterator function such as `map`, `filter`, or `reduce` can iterate any data structure, and it's completely agnostic regarding the concrete types. The code that cares about the types is passed in as a parameter. Pretty much any data transformations in Clojure are accomplished by chaining functions from the standard library together, with domain specific code bubbling up to a shallow layer at the top.
There's also the culture of using Spec and Schema in Clojure, so you often have assertions at the interface level. For example, there's ring-spec https://github.com/ring-clojure/ring-spec/blob/master/src/ri... providing the specification for Ring.
(defn parse-seq [parse-el form] (when (sequential? form) (reduce #(if-let [parsed (parse-el %2)] (conj %1 parsed) (reduced nil)) [] form)))
without knowing really clojure i take it "[parse-el form]" are the arguments of this function. How is it possible with minimal effort to know what am I supposed to pass as parse-el and form? what are they?
https://github.com/tonsky/datascript/blob/master/src/datascr...
"parse-el" is used as a function, which takes one argument, the first argument when invoked from reduce (the accumulator), which is first [] then another vector (because of conj).
So "parse-seq" parses a sequence (named "form") and produces a vector, or nil. It applies "parse-el" to each form using reduce. When the result of any form gives a nil value, the whole result is also nil (see https://clojuredocs.org/clojure.core/reduced). The result is also nil if form is not sequential.
There is no other contract about "parse-el", it just should be able to "parse" whatever occurs in "form". You could use it on your own with any kind of data that has a corresponding parse function.
A documentation string would have been nice, or different names ("parse-el"?)
Now, if you look at the usage of "parse-seq", you can see for example:
(let [vars* (parse-seq parse-variable vars)
....
And it looks like "parse-variable" parses a form and returns an instance of Variable. (defn parse-variable [form]
(when (and (symbol? form)
(= (first (name form)) \?))
(Variable. form)))An interesting corollary to this problem though is the wandering type-path, where when consuming (again, poorly documented) a Java library, I know the type of the function I want to call, but not how to get that type. Usually it's:
"I have B value [byte-array, int, whatever] and I need Z from this library so I can call `Z.foo`... Z's constructor takes Y, which takes X, which takes..."
Types help there, but they also hinder quite a bit if I have the correct underlying data type, but can't get Java to accept it.
OK, there aren't any. Pull requests accepted?
Well, javascript does not have some of the features that other dynamic programming languages have, that help you manage complexity and create successful projects of large size.