I am so grateful for Clojure


I am so grateful for Clojure

Thanks to everyone involved for creating Clojure and it's entire ecosystem (even Oracle/Sun for the jvm and lets not forget emacs). It's all saving me a tonne of time. And making my dreams possible.

Submitted October 21, 2017 at 05:06AM by daneelvt
via reddit http://ift.tt/2xbzM35

Advertisements

Solutions for structuring spec definitions?


Solutions for structuring spec definitions?

My team is currently in discussions for how to move forward with structuring our specs, and we've identified something we feel is a limitation with the current implementation.

I fully expect these issues to have a clear solution that is thought through by the spec developers, I'm just not sure what it is yet. Please, go easy.

The main problem we've run into is when a single namespace has code that works with different maps of data that have identical unqualified keys, but different underlying specs. We feel this is a relatively common problem.


Definitions:

  • Namespace – Generally matches the file where the code is actually located at. foo.bar.test is located at src/foo/bar/test.clj.

  • Specspace – (my definition) – The namespace portion of a qualified keyword, such as the spec keyword identifier, which may or may not be associated with an actual namespace. Example :foo.core/type has foo.core as specspace. As spec is a global registry, this does not need to match a namespace, and I'll refer to this as a "logical specspace", when it denotes a logical grouping rather than a namespace. One key aspect is that this piece is dropped when specified as unqualified in certain specs like keys, where :foo.core/type will match to a keyword :type rather than requiring the data map has a specspaced :foo.core/type keyword.

  • Qualified keyword – keyword with a specspace, e.g. :foo.core/type. The spec space could be either a namespace or logical.

  • Unqualified keyword – A bare keyword such as :type.


Advantages of using Namespace as Specspace:

You get to use the :: shortcut. If you use ::type in the foo.core namespace, it automatically expands to :foo.core/type. This also works for alias, so if you require [project.alice :as a], then you can reference a ::type spec defined inside that namespace as ::a/type. This makes navigating to the code easy, and enforces a clear path between the usage of a spec and it's definition. It also works to prevent accidentally redefining a spec twice, especially in a larger codebase, where a logical specspace.

Limitations of using Namespace as Specspace:

Certain functions in spec, like the req-un directive of the keys spec, derive valid keywords by dropping the specspace. This means that you cannot have specs in a single namespace that describe two maps that contain the same key. If there are two map types in a given namespace, and one map uses :type as a string? and another map uses :type as an integer?, for instance.


Solutions within current spec landscape:

Mix and Match

This lets you use the shortcuts while also being able to define the same unqualified keyword twice in a namespace via logical specspaces, but I feel this has a huge cost of confusing whether the specspace is navigable and what exactly it is referencing. The logical specspaces would not show up in a namespace's require statement, but still would require that the namespace containing the logical specspaces is required, effectively using specs as hidden globals, which can break tooling. The namespace required would look like it isn't being used, and there would be no link between the usage of the spec and knowing where it came from…. obviously not ideal.

Only use namespace specspaces

This means you can never two specs for the same unqualified keyword within the same namespace, which means you need to split into new namespaces anytime this happens. The downside is that it enforces an abstraction and file-level separation that is arguably not logically necessary and hurts locality / readability.

Only use logical specspaces.

This would allow you to keep your spec definitions local to the code that uses them. The downsides are numerous: You lose navigability and the relationship between spec usage and definition, effectively using specs as globals. You lose the ability to use the :: shortcut for the current namespace, though using it with aliases could be worked around with explicit alias such as (alias 'logical-specspace 'shortcut), though that functionality isn't there yet and aliases require real namespaces. This also increases the risk of overwriting a spec on a large project if you have a logical spec space collision, if you keep specs local with their code rather than in one big spec file.

Possible solutions requiring development:

Allow a logical component of specspace in addition to namespace.

Example: A spec ::/bar/type declared in the foo.core namespace that could be referenced as :foo.core/bar/type

This would allow us to enforce namespaces for all specspaces, while also allowing us to have multiple specs for a given unqualified keyword within a single namespace.

Rewrite any spec functions that derive an unqualified keyword by dropping specspace to allow aliasing.

This would separate the spec name from the expected key inside the checked data. Ideally, this would be paired with forcing all specspaces to be namespaces, and separates the name of the spec from the key it is checking, possibly causing confusion.

Example: (s/keys :req-un [:foo.core/second-type]) would become `(s/keys :req-un [[:foo.core/second-type :as :type]]

This allows us to define multiple specs sharing an unqualified keyword in a given namespace, at the expense of requiring a name like second-type or third-type that is mapped backed to type when used.

Allow the definition of a spec to define it's own unqualified keyword.

Example: (s/def ::second-type keyword? :as :type)

This would let you specify what unqualified keys are valid in a given map, while still allowing the spec to be named uniquely. This might not be compatible with spec and is in many ways just a way of adding an extra logical component to the specspace, just handled in extra data rather than the name.

Submitted October 20, 2017 at 06:56AM by eriktjacobsen
via reddit http://ift.tt/2gvDyRU

ClojureScript libs for async programming on Node.js and Lumo.


ClojureScript libs for async programming on Node.js and Lumo.

Shrimp Shrimp-Chain

Hi everyone! I would like to receive some feedback relative to two experiments recently released, it's two small libs targeted mostly at the use with Lumo.
Shrimp is some kind of "poor-man" core.async lib based on Red Lobster promise library, with extras for helping with async testing.
Shrimp-Chain is an experiment inspired quite some time ago by a post on this subreddit (i could be wrong, couldn't find it), it's an attempt to adapt core Clojure macros to work with async functions.
This version of Shrimp-Chain is built on top of Shrimp, but I plan to release a core.async version as well, originally was on top of core.async actually but now that version requires some changes. It introduces overhead because of the machinery behind the macros, so I'm curious to know if you people think is a good idea or not.

Submitted October 20, 2017 at 05:44AM by peppezer
via reddit http://ift.tt/2yUWbWP