Skip to content

Commit ef8e321

Browse files
authored
Update README to reflect the current state
Also add a big section on "advanced usage"
1 parent a1eacd9 commit ef8e321

File tree

1 file changed

+124
-26
lines changed

1 file changed

+124
-26
lines changed

README.md

Lines changed: 124 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,53 +6,69 @@
66
[![Clojars Project](https://img.shields.io/clojars/v/dhall-clj/dhall-clj.svg)](https://clojars.org/dhall-clj/dhall-clj)
77
[![cljdoc badge](https://cljdoc.xyz/badge/dhall-clj/dhall-clj)](https://cljdoc.xyz/d/dhall-clj/dhall-clj/CURRENT)
88

9-
Compiler from Dhall to Clojure.
9+
## Dhall + Clojure = 😍
1010

11-
## Dhall?
11+
This package allows you to compile [Dhall][dhall] expressions to Clojure expressions.
1212

13-
[Dhall][dhall] is a functional programming language that is not Turing complete.
13+
And this is a very useful thing! Why is it so?
1414

15-
*You can think of Dhall as: JSON + functions + types + imports.*
15+
Some use cases for Dhall are:
16+
- **typed configurations**: you need your configuration/data to have a precise type
17+
- **templating**: template things in a sane way (you definitely don't want a Turing complete
18+
templating language..)
19+
- send code on the wire in **safe** and **efficient** way: it's safe because the language is not
20+
Turing complete, and it's efficient because Dhall defines a binary encoding to serialize and
21+
deserialize expressions.
22+
23+
If this sounds useful to you too, then read on! For more inspiration about possible use cases, you can
24+
take a look at the wiki page ["Using Dhall in Production"][dhall-production] tracking the usage of
25+
Dhall in the wild.
1626

17-
For a Dhall Tutorial, see the [README of the project][dhall], or the [full tutorial][dhall-tutorial].
27+
### Sounds nice! But where can I read more about this Dhall?
1828

19-
The purpose of this library is to consume Dhall expressions from Clojure.
29+
First of all, a quick definition: [Dhall][dhall] is a functional programming language that is not
30+
Turing complete, geared towards practical usage as a configuration language.
2031

21-
Example use cases for Dhall are:
22-
- typed configuration files
23-
- templating
24-
- safe exchange format (since the Language Standard defines an efficient binary encoding)
32+
*You can think of Dhall as: JSON + functions + types + imports.*
2533

26-
For more inspiration about possible use cases, see the wiki page
27-
[Using Dhall in Production][dhall-production].
34+
For a more detailed pitch and a live demo, the [Dhall website][dhall] is the best place to start.
35+
If you'd like a Dhall tutorial instead, see the [README of the project][dhall-repo],
36+
or the [full tutorial][dhall-tutorial].
2837

29-
## Dhall version
38+
### Dhall Language Version and compliance to the Standard
3039

3140
*Note: this is quite alpha. Things might be broken, so don't hesitate to [open an issue][issues].*
3241

33-
We basically support all of Dhall `v2.0.0`, except the following:
34-
- [#7](../../issues/7): Http imports
35-
- [#4](../../issues/4): Binary serialization/deserialization
36-
- [#5](../../issues/5): Imports semantic integrity checks (hashing)
37-
- [#8](../../issues/8): Caching of hashed imports
38-
- [#12](../../issues/12): The import alternative operator `?`
39-
- The Dhall compilation works otherwise, but the translation to Clojure data structures is partial for now, e.g. no `Natural/build`
42+
Dhall has a versioned [language specification][dhall-repo], so it's useful to know which version we are using.
43+
44+
This library implements `v5.0.0` of Dhall, except for http imports: that is, you cannot import things via
45+
http URLs yet.
46+
47+
Moreover, the translation to Clojure data structures is somewhat partial for now, so be extra careful when
48+
checking that the data you get from Dhall is what you expected.
49+
50+
For more information on the aspects in which this implementation is not compliant with the Standard, see
51+
the issues labeled with ["standard compliance"][standard-compliance-issues].
4052

4153
## HOWTO
4254

4355
```clojure
44-
(require [dhall-clojure.core :refer [input input-ast]])
56+
(require '[dhall-clojure.core :refer [input input-ast]])
4557

46-
;; We can run Dhall expression with the `input` function
58+
;; We can run compile and run Dhall expression in Clojure with the `input` function.
59+
;; Note that the result of the evaluation is a Clojure value
4760

4861
(input "True && False")
49-
5062
;; => false
5163

5264

53-
;; We can even import functions from Dhall, and execute them:
65+
;; We can even import functions from Dhall..
66+
;; (the following compiles a Dhall function into a Clojure function)
5467

5568
(def build-info (input "λ(major : Natural) → { version = \"${Natural/show major}.0\" }"))
69+
70+
;; ..and run them in Clojure!
71+
5672
(build-info 1)
5773

5874
;; => {"version" "1.0"}
@@ -71,6 +87,86 @@ We basically support all of Dhall `v2.0.0`, except the following:
7187
;; => #dhall_clj.ast.NaturalLit {:n 2}
7288
```
7389

90+
## HOWTO - Advanced usage
91+
92+
There are cases in which you'd need to run single compiler phases, e.g. if you wish to
93+
serialize expressions.
94+
95+
If we follow the lifetime of a Dhall expression being compiled, we'll see the following
96+
phases happening:
97+
- **parsing**: here we go from "text" to Abstract Syntax Tree (AST), that is going to be the
98+
representation we'll use for the Dhall expression in all the next phases.
99+
That is, all the following operations are defined as manipulations on the AST.
100+
- **import resolution**: a Dhall expression might contain local and remote imports (files,
101+
environment variables, http URLs), so we should try to resolve these imports and incorporate
102+
them into the expression before going forward (as we cannot verify the type of the expression
103+
if we don't know what the import will contain).
104+
So this phase will transform the AST containing imports into a import-free AST
105+
- **typechecking**: once we have the full expression, we have to verify that the types are correct,
106+
e.g. we'll check that if a function takes an `Integer` it's not being passed a `Text`, and so on.
107+
- **execution/normalization**: this is the final phase of the compilation, and effectively "runs"
108+
the computation. We do this by "reducing to a normal form" (all Dhall expression have a "normal form",
109+
and this means that all expressions *always* terminate), where we basically apply all the functions
110+
we can apply.
111+
- (optionally) **emit Clojure**: this phase is not strictly included in the compilation, but it will
112+
transform the Dhall AST from the normalization into Clojure datastructures. E.g. Dhall Lists will
113+
become Clojure vectors, Records will become Clojure maps, Dhall functions will become Clojure functions,
114+
etc.
115+
116+
Let's see how to use this knowledge to do interesting things (like serializing Dhall expressions to binary):
117+
```clojure
118+
;; There are different namespaces for every compilation phase:
119+
;; - "parsing"
120+
(require '[dhall-clj.parse :refer [parse expr]])
121+
122+
;; - "import resolution" (the `state` ns is for the in-memory cache for imports)
123+
(require '[dhall-clj.import :refer [resolve-imports]])
124+
(require '[dhall-clj.state :as s])
125+
126+
;; - "typechecking"
127+
(require '[dhall-clj.typecheck :refer [typecheck]])
128+
129+
;; - "normalization"
130+
(require '[dhall-clj.beta-normalize :refer [beta-normalize]])
131+
132+
;; - "emit Clojure"
133+
(require '[dhall-clj.emit :refer [emit]])
134+
135+
;; See the implementation of `dhall-clj.core/input` to see how to compose them together properly
136+
;; But we can say that a basic re-implementation of `input` would be:
137+
138+
(defn my-input [dhall-text]
139+
(let [parse-tree (parse dhall-code)
140+
ast (expr parse-tree)
141+
ast (resolve-imports ast (s/new))
142+
type (typecheck ast {})
143+
ast (beta-normalize ast)
144+
clj-sexp (emit ast)]
145+
(eval clj-sexp)))
146+
147+
148+
;; Let's say we now want to serialize a Dhall expression to binary data.
149+
;; We'll take the `build-info` function we used in the previous section to demonstrate this
150+
151+
(def dhall-source "λ(major : Natural) → { version = \"${Natural/show major}.0\" }")
152+
153+
(require '[dhall-clj.binary :refer [encode decode]])
154+
155+
(def serialized (-> dhall-source input-ast encode))
156+
157+
;; Now `serialized` will contain the serialized expression in a ByteArray.
158+
;; You can now send this around, and when you want to _deserialize_ an encoded
159+
;; expression you can run `decode` on it:
160+
161+
(def build-info (-> serialized decode emit eval))
162+
163+
;; ...and you can now run this code as well!
164+
165+
(build-info 1)
166+
167+
;; => {"version" "1.0"}
168+
```
169+
74170
## Catching exceptions
75171

76172
Sometimes the evaluation will fail, and we'd like to catch the error condition to handle that.
@@ -121,10 +217,12 @@ Distributed under the
121217
[Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html),
122218
the same as Clojure.
123219

124-
[dhall]: https://github.com/dhall-lang/dhall-lang
125-
[dhall-tutorial]: http://hackage.haskell.org/package/dhall-1.17.0/docs/Dhall-Tutorial.html
220+
[dhall]: https://dhall-lang.org
221+
[dhall-repo]: https://github.com/dhall-lang/dhall-lang
222+
[dhall-tutorial]: http://hackage.haskell.org/package/dhall-1.20.1/docs/Dhall-Tutorial.html
126223
[dhall-production]: https://github.com/dhall-lang/dhall-lang/wiki/Dhall-in-production
127224
[issues]: https://github.com/f-f/dhall-clj/issues
128225
[ex]: https://github.com/mpenet/ex
129226
[ex-info]: https://clojuredocs.org/clojure.core/ex-info
130227
[fail]: ./src/dhall_clj/fail.clj
228+
[standard-compliance-issues]: https://github.com/f-f/dhall-clj/issues?q=is%3Aissue+is%3Aopen+label%3A%22standard+compliance%22

0 commit comments

Comments
 (0)