Beta

Toying with Core.logic

Description:

Toying with Core.logic

Core.logic is a Clojure implementation of the miniKanren logic programming language.

In this kata you will practice simple logic programming problems through core.logic. With that said, this kata only features beginner level problems and aims to be a reasonable excuse for Clojure hobbyists to have a taste of logic programming; users with previous experiences with miniKanren or Prolog might find the problems trivial.

Learning Core.logic

Clojure features quite some complete introductions. Core.logic much less so. Here are some well-written resources that would get you started (and googling around would reveal more):

Beyond the Most Basic

This kata will use some features of core.logic that is beyond what has been mentioned in some online tutorials. I will try to run a quick recap of basic core.logic and then introduce the features.

Setup

In this section, we assume that we are in a namespace with the setup below:

(ns logic-playground.core
  [:refer-clojure :exclude [==]]
  [:require
    [clojure.core.logic :refer :all]
    [clojure.core.logic.fd :as fd]])

Recap

Recall the unification operator ==, and the run variants:

(run 1 [q]
    (== q 3)) ;; (3)

Recall the conde operator:

(run* [q]
    (conde
        [(== q :banana)]
        [(== q :wire)]
        [fail]
        [succeed])) ;; (:banana :wire _0)

Recall that fresh creates new logic variables:

(run* [q]
    (fresh [p]
        (== p q)
        (== p 3))) ;; (3)

Recall that defn can be used to define new goals

(defn fake-firsto [l x]
    (== (first l) x))

(run* [q]
    (fresh [p]
        (fake-firsto [q 10 100] p))) ;; (_0)

Recall the following goals:

  • conso
  • resto
  • membero
  • distincto

core.logic.fd

The information in this section is all based on https://github.com/clojure/core.logic/wiki/Features

Arithmetic functions + - * are not by default defined for logic variables. Thankfully instead of using project or building our own natural number system, core.logic.fd defines the arithmetic operations for us.

(run* [q]
    (fresh [a b]
        (fd/in a b (fd/interval 1 100))
        (fd/* a b 30)
        (fd/<= a b)
        (== q [a b]))) ;; ([1 30] [2 15] [3 10] [5 6])

defne

defne is a macro extending defn, providing pattern-matching for logic goals.

A contrived example is that we are to define addition in the ℤ2 ring, or equivalently the xor function.

(defne z2+ [a b c]
    ([0 0 0])
    ([0 1 1])
    ([1 1 0])
    ([1 0 1]))

The definition is just listing all the possibilities of a b c: 0 + 0 = 0, 0 + 1 = 1, 1 + 1 = 0, 1 + 0 = 1.

defne's strength becomes more obvious when matching on sequences.

For example, suppose we are to write a relation called (lasto l x) that succeeds when x is the last element of l.

A way to write this "from scratch" is to write it in the traditional functional programming way via pattern-matching:

(defne lasto
        [xs x]
        ([[x . ()] _])
        ([[_ . a] _]
            (lasto a x)))

The pattern [x . ()] matches cons lists in the form x :: nil, a one-element list that has only the element x (the same x in the arguments).

Unfortunately, this does not work as well as firsto in reality: (lasto v 1) can yield infinite many results (as opposed to one result in the case of the built-in firsto). For this kata, however, we wil only extract and test a finite amount of results, so this problem does not matter.

As the last example, here is a naive implementation of the fibonacci sequence:

(defne fib [input output]
    ([0 1])
    ([1 1])
    ([n r]
        (fresh [n1 n2 r1 r2]
            (fd/- n 1 n1)
            (fd/- n 2 n2)
            (fib n1 r1)
            (fib n2 r2)
            (fd/+ r1 r2 r))))

Goals

Implement the following goals

;; ntho, "coll" will only be vectors in the input
(ntho coll i ele) ;; (= ele (nth coll i))

;; reverseo, "a" and "b" will only be vectors in the input
(reverseo a b) ;; (= b (reverse a))

;; sets implemented as vectors, A must be a subset of B to succeed, both inputs only contain distinct elements
(subseto a b)

Note that the core.logic library used in this kata only contains the clojure.core.logic and clojure.core.logic.fd namespace. The others are not included.

Logic Programming
Fundamentals

More By Author:

Check out these other kata created by TheBookmarkKnight

Stats:

CreatedNov 17, 2017
PublishedNov 17, 2017
Warriors Trained65
Total Skips1
Total Code Submissions40
Total Times Completed8
Clojure Completions8
Total Stars3
% of votes with a positive feedback rating100% of 6
Total "Very Satisfied" Votes6
Total "Somewhat Satisfied" Votes0
Total "Not Satisfied" Votes0
Total Rank Assessments5
Average Assessed Rank
3 kyu
Highest Assessed Rank
1 kyu
Lowest Assessed Rank
5 kyu
Ad
Contributors
  • TheBookmarkKnight Avatar
Ad