Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additions to section #2 #27

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,48 @@ tut2=> (run* [q] (appendo [1 2] q [1 2 3 4]))

Note that `appendo` can infer its inputs!

There’s actually a short hand for writing appendo, we can write it like this. This is pattern matching - it can decrease the amount of boiler plate we have to write for many programs.
`appendo` is a rather pretty mixture of logic and functional programming. It employs
a very functional idiom of using recursion to process list but the result it builds up
is not itself a list but logic goals, e.g. assertions about the contents of the output `o`.
`appendo` works by repeatedly unifying the first element of `l1` with that of `o` (using `conso`)
and then calling itself recursively to do the same for the list tails. When it reaches
the base case, `l1` is the empty list and all that remains is to unify all of `l2` with
the tail of `o`.

`conso` is a relational version of (scheme's) `cons` and `(conso a b o)` can be read as
the goal "unify the result of consing `b` onto `a` with `c`" and although you cannot
mix regular clojure functions with logic variables in this way, it corresponds roughly
to what you'd expect `(== (cons a b) o)` to do.

Before moving on let's get a little taste of `core.logic`'s pattern matching support. Pattern
matching is a powerful and concise way to express behavior and `core.logic` tries to
make good use of its expressive power. The `defne` macro let's you express
conjugations of goals just as `conde` while making use of pattern matching. Here's what
`appendo` looks like:

```clj
(defne appendo [l1 l2 o]
([[] _ _] (== o l2))
([[?a . ?d] _ [?a . ?r]] (appendo ?d l2 ?r))
)
```

First, note the absence of a `fresh` call -- `defne` allows us to define fresh
variables implicitly. By convention these should bear names beginning with '?'.
We have two clauses here and the first corresponds to the base case we saw earlier:
"if l1 is an empty collection (and no matter what `l2` and `o` are) => unify `o` and `l2`"
The second clause uses destructuring syntax instead of `conso` to unify the head
of `l1` and `l2` and then asserts the goal(s) arising from calling `appendo` on
the tails in the same way seen earlier. Let's test it:


```clj
tut2=> (run* [q] (appendo [1 2] q [1 2 3 4]))
((3 4))
```

we got the same answer as before. good.


Zebras
----
Expand Down