redplanetlabs / rama-clj-kondo

clj-kondo hooks for Rama code
Apache License 2.0
11 stars 0 forks source link

Since Rama defines it's own dataflow language as a DSL in Clojure, linting it is a difficult problem. This repo defines the rules for how Rama code should be re-written such that clj-kondo can provide helpful warnings and errors as effectively as possible.

Setting up

The clj-kondo extensions for Rama come bundled with the Rama jar when installing dependencies. This means your editor should import the clj-kondo rules for you automatically, but in case it doesn't or you favour using clj-kondo from a terminal, you can get clj-kondo to import the linting rules for Rama by running

You can get clj-kondo to import the linting rules for Rama by running:

clj-kondo --lint "$(clojure -Spath)" --copy-configs --skip-lint

or

clj-kondo --lint "$(lein classpath)" --copy-configs --skip-lint

If you're using leiningen.

Note that if your dependency on Rama is specified under a specific alias or profile, you need to make sure to include that in the clojure or lein command. Otherwise the Rama jar won't be on the classpath.

Roadmap

There are a number of Rama features that are known to not lint correctly. Since Rama segments get transformed into a graph, reorganizing the code to successfully lint everything is difficult, and as such skips out on a lot of that at the moment.

(my-ramaop :> *out :error> *ex)
(anchor> <X>)
(<<branch <X>
  (anchor> <Y>))
(<<branch <Y>)
(my-ramaop :> *out :error> <error> *ex)

(<<branch <error> 
  (println *ex))

How it works

Rama dataflow code is written in, what appears to be, a linear style, compared to Clojure where everything is lexically scoped. As such, the general premise of this is to transform Rama code so that emits are rewritten as nested lets.

For example,

(?<- 
  (identity 1 :> *x)
  (identity 2 :> *y)
  (println (+ *x *y)))

Would be rewritten as

(let [*x (identity 1)]
  (let [*y (identity 2)]
    (println (+ *x *y))))

The tests contain extensive examples of how every type of form is rewritten such that it can be interpreted as regular Clojure code.

Since having access to the following forms is important for the transformation rules, hooks in the config.edn are only defined for top-level forms. Transformation rules for special Rama forms, such as <<if or batch<- are written as extensions of the split-form or handle-form methods in rama_hooks.clj.