Skip to content

Commit

Permalink
Merge pull request #16 from StrykerKKD/prepare030
Browse files Browse the repository at this point in the history
Prepare for 0.3.0 release
  • Loading branch information
StrykerKKD authored Feb 9, 2020
2 parents b7415ef + 9648205 commit bbe3496
Show file tree
Hide file tree
Showing 25 changed files with 182 additions and 41 deletions.
116 changes: 93 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,66 @@ Logical is a minimalistic logic programming inspired by [microKanren](http://min

## How does it work?
To understand Logical first you need to understands it's basic building blocks which are the following:
- Type
- Value
- State
- Goal

### Value
Logical is basically could be seen as a embeded programming language(or DSL), which allows us to use logic programming in Ocaml. It also means that it has it own (value) type system:
### Type
Logical is basically could be seen as a embedded programming language(or DSL), which allows us to use logic programming in Ocaml. It also means that it has it own type system:
```ocaml
type variable = string
type variable_name = string
type value =
type t =
| Int of int
| Float of float
| Str of string
| Bool of bool
| Var of variable
| Set of value Base.Set.t
| Var of variable_name
```
You can see from the type declaration Logical has all the base types as Ocaml(`Int`, `Float`, `Str`, `Bool`), but also has `Var` for variable declarations.

### Value
The value module is responsible for making it easier to create value's based on Logical's type system and has the following constructor functions:
```ocaml
val int value : int -> int Type.t
val float value : float -> float Type.t
val str value = : string -> string Type.t
val bool value = : bool -> bool Type.t
val var variable_name : variable_name -> variable_name Type.t
```
You can see from the simplified code that Logical has all the base types as Ocaml(`Int`, `Float`, `Str`, `Bool`), but also has `Var` for variable declarations and `Set` for declaring sets in Logical.

### State
State represents the current state of your program in Logical by storing the value of every used variable.
State represents the current state of your program in Logical. State is a map, which stores the value of every currently used variable by using a `variable name -> variable value` association.
It's kind of the same as stack frames in other languages.

State's signature:
```ocaml
type state = (variable * value) list
type state = (variable_name, value) Base.Map.t
```
You can clearly see this in state's type declaration, where `variable_name` is the key of the Map and the `value` is the associated value. You can think of state as a list of variable assignments.

State module's most relevant function are the following:
```ocaml
(* represents an empty state *)
val empty : state
(* creates a new state based on the input association list, which gives back None for duplicated variable names *)
val create : (variable_name * t) list -> state option
(* creates a new state based on the input association list, which gives back an exception for duplicated variable names *)
val create_exn : (variable_name * t) list -> state
(* gives back the requested logical value based on the provided state *)
val value_of : state -> t -> t
```
You can clearly see this in state's type decleration, where `variable` is a variable name and `value` is it's value. You can also see that we are storing multiple variables, because the assignments are stored in a list.

### Goal
Goal is basically a function, which takes in a state and generates a stream of new states based on that state. The only twist is that sometimes we end up in invalid state(or deadend state), which is why sometimes we can't produce new state based on the input state.
Goal is a function, which takes in a state and generates a stream of new states based on that state. The only twist is that sometimes we end up with invalid state(or deadend state), which is why sometimes we can't produce new state based on the input state.

You can see this in Goal's signature:
```ocaml
Expand All @@ -54,16 +83,19 @@ val either_multi : goal list -> goal (* or [A,B,C,..,Y]*)
val both : goal -> goal -> goal (* A and B *)
val both_multi : goal list -> goal (* and [A,B,C,..,Y]*)
val in_set : value -> value -> goal (* A in (A,B,C,...,Y) *)
```
You can see from the type declarations that there are two kinds of goals:
- Basic building block, which expects 2 `value` and generates a `goal`. You can think of them as constructors for goals.
- Goal combinators, which expects 2 `goals` and generates based on that a one new `goal`
- Goal combinators, which expects 2 `goals` and generates new goals based on that.

## How to use it?
General rules for using Logical:
- Goal constructors are the most basic building block of every goal
- Goals can be made bigger and more complex by combining them with goal combinators
- Goals can be grouped by creating custom goals
- Goals are inactive until you give them an initial state

### equal
Expand Down Expand Up @@ -91,12 +123,12 @@ In this case `state_list` has two states where:
let a_goal = Goal.equal (Value.var "a") (Value.int 42)
let b_goal = Goal.equal (Value.var "b") (Value.int 21)
let goal_list = [a_goal; b_goal]
let either_goal = Goal.either_multi goal_list
let state_list = either_goal State.empty |> Base.Sequence.to_list
let either_multi_goal = Goal.either_multi goal_list
let state_list = either_multi_goal State.empty |> Base.Sequence.to_list
(* state_list is [ Some[("a",Value.Int 42)]; Some[("b",Value.Int 21)] ]*)
```

### both
### both and both_multi
```ocaml
let a_goal = Goal.equal (Value.var "a") (Value.int 42)
let b_goal = Goal.equal (Value.var "b") (Value.int 21)
Expand All @@ -108,25 +140,62 @@ In this case `state_list` has a state with two assignments where:
- `a` is equal with `42`
- `b` is equal with `21`

`both_multi` is the same as `both` only more general, because it expects a list of goals.
```ocaml
let a_goal = Goal.equal (Value.var "a") (Value.int 42)
let b_goal = Goal.equal (Value.var "b") (Value.int 21)
let goal_list = [a_goal; b_goal]
let both_multi_goal = Goal.both_multi goal_list
let state_list = both_multi_goal State.empty |> Base.Sequence.to_list
(* state_list is [ Some[("b",Value.Int 21); ("a",Value.Int 42)] ]*)
```

### in_set
in_set goal is basically a sintactic sugar for an `either_multi` where every goal has the same variable.
in_set goal is basically a syntactic sugar for an `either_multi` where every goal has the same variable.
```ocaml
let my_set = Base.Set.of_list (module Value.Comparator) [Value.int 42; Value.int 21]
let in_set_gaol = Goal.in_set (Value.var "a") (Value.set my_set)
let state_list = in_set_gaol State.empty |> Base.Sequence.to_list
let my_set = Base.Set.of_list (module Type) [Value.int 42; Value.int 21]
let in_set_goal = Goal.in_set (Value.var "a") my_set
let state_list = in_set_goal State.empty |> Base.Sequence.to_list
(* state_list is [ Some[("a",Value.Int 42)]; Some[("a",Value.Int 21)] ]*)
```
In this case `state_list` has two states with the same variable(`a`) with two different values: `42` and `21`

`in_set` goal is useful, when you want negation like `x != 6`, which is basically the same as `x in (-infinity,..,5,7,...,infinity)`. From this example you can also see that in_set is only really useful on small finite domains, where the universal set is small and well defined.

### Custom goals
Goal is basically just a function, which accepts a state and based on that generate a sequence of states, so in order to create custom goals you only have to follow the type definition of the goal function, which is:
```ocaml
type goal = state -> state option Base.Sequence.t
```
For example:
```ocaml
let my_simple_goal state =
(*my_simple_goal's implementation*)
let my_complex_goal custom_input state =
(*my_complex_goal's implementation based on custom_input*)
let my_big_goal state =
Goal.both_multi [
my_simple_goal;
my_complex_goal custom_input;
(fun state -> my_simple_goal state) (*Lambda goal*)
(fun state -> my_simple_goal State.empty) (*Independent lambda goal*)
] state
```

Custom goals are excellent tools for breaking up bigger complex goals into smaller ones and they are also really great fit for generating or creating goals based on some custom extra input parameters. One thing you should be aware is that you are in control of what state you run your custom goals, which gives you a great deal of flexibility.

**Warning**: using `State.value_of` inside of a custom goal could make it dependent on the order of your other goals.

### Note
If you like reading code more than guides than you can find the example in the `bin` folder in this repository.
If you like reading code more than guides than you can find the examples in the `bin` folder in this repository.

## How to run the example?
## How to run the examples?
Prerequisite: Install [Esy](https://esy.sh/)

Run it: `esy x example`
Run it: `esy x <example name>`

For instance: `esy x example`

## How to for contributors
Prerequisite: Install [Esy](https://esy.sh/)
Expand All @@ -143,4 +212,5 @@ Update docs folder: `esy update-doc`

## Notable resources
- [Hello, declarative world](https://codon.com/hello-declarative-world)
- [miniKanren](http://minikanren.org/)
- [miniKanren](http://minikanren.org/)
- [Solving murder with Prolog](https://xmonader.github.io/prolog/2018/12/21/solving-murder-prolog.html)
16 changes: 12 additions & 4 deletions bin/example.ml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ let either_result = Goal.either
State.empty
let () = Util.print_state_stream "either_result" either_result

let either_result_multi = Goal.either_multi
[Goal.equal (Value.var "a") (Value.int 1); Goal.equal (Value.var "b") (Value.int 2);
Goal.equal (Value.var "b") (Value.int 3);]
State.empty
let either_result_multi = Goal.either_multi [
Goal.equal (Value.var "a") (Value.int 1);
Goal.equal (Value.var "b") (Value.int 2);
Goal.equal (Value.var "b") (Value.int 3);
] State.empty
let () = Util.print_state_stream "either_result_multi" either_result_multi

let both_result = Goal.both
Expand All @@ -21,6 +22,13 @@ let both_result = Goal.both
State.empty
let () = Util.print_state_stream "both_result" both_result

let both_result_multi = Goal.both_multi [
Goal.equal (Value.var "a") (Value.int 1);
Goal.equal (Value.var "b") (Value.int 2);
Goal.equal (Value.var "c") (Value.int 3)
] State.empty
let () = Util.print_state_stream "both_result_multi" both_result_multi

let my_set = Base.Set.of_list (module Type) [Value.int 1; Value.int 2; Value.int 3]
let in_set_result = Goal.in_set (Value.var "a") my_set State.empty
let () = Util.print_state_stream "in_set_result" in_set_result
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<div class="by-name">
<h2>OCaml package documentation</h2>
<ol>
<li><a href="logical/index.html">logical</a> <span class="version">0.2.0</span></li>
<li><a href="logical/index.html">logical</a> <span class="version">0.3.0</span></li>
</ol>
</div>
</main>
Expand Down
2 changes: 1 addition & 1 deletion docs/logical/Logical/Goal/index.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Goal (logical.Logical.Goal)</title><link rel="stylesheet" href="../../../odoc.css"/><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../../../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div class="content"><header><nav><a href="../index.html">Up</a><a href="../../index.html">logical</a> &#x00BB; <a href="../index.html">Logical</a> &#x00BB; Goal</nav><h1>Module <code>Logical.Goal</code></h1></header><dl><dt class="spec value" id="val-equal"><a href="#val-equal" class="anchor"></a><code><span class="keyword">val</span> equal : <a href="../Value/Type/index.html#type-t">Value.Type.t</a> <span>&#45;&gt;</span> <a href="../Value/Type/index.html#type-t">Value.Type.t</a> <span>&#45;&gt;</span> <a href="../Value/index.html#type-goal">Value.goal</a></code></dt><dt class="spec value" id="val-either"><a href="#val-either" class="anchor"></a><code><span class="keyword">val</span> either : <a href="../Value/index.html#type-goal">Value.goal</a> <span>&#45;&gt;</span> <a href="../Value/index.html#type-goal">Value.goal</a> <span>&#45;&gt;</span> <a href="../Value/index.html#type-goal">Value.goal</a></code></dt><dt class="spec value" id="val-either_multi"><a href="#val-either_multi" class="anchor"></a><code><span class="keyword">val</span> either_multi : <span><a href="../Value/index.html#type-goal">Value.goal</a> list</span> <span>&#45;&gt;</span> <a href="../Value/index.html#type-goal">Value.goal</a></code></dt><dt class="spec value" id="val-both"><a href="#val-both" class="anchor"></a><code><span class="keyword">val</span> both : <a href="../Value/index.html#type-goal">Value.goal</a> <span>&#45;&gt;</span> <a href="../Value/index.html#type-goal">Value.goal</a> <span>&#45;&gt;</span> <a href="../Value/index.html#type-goal">Value.goal</a></code></dt><dt class="spec value" id="val-in_set"><a href="#val-in_set" class="anchor"></a><code><span class="keyword">val</span> in_set : <a href="../Value/Type/index.html#type-t">Value.Type.t</a> <span>&#45;&gt;</span> <a href="../Value/Type/index.html#type-t">Value.Type.t</a> <span>&#45;&gt;</span> <a href="../Value/index.html#type-goal">Value.goal</a></code></dt></dl></div></body></html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Goal (logical.Logical.Goal)</title><link rel="stylesheet" href="../../../odoc.css"/><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../../../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div class="content"><header><nav><a href="../index.html">Up</a><a href="../../index.html">logical</a> &#x00BB; <a href="../index.html">Logical</a> &#x00BB; Goal</nav><h1>Module <code>Logical.Goal</code></h1></header><dl><dt class="spec value" id="val-equal"><a href="#val-equal" class="anchor"></a><code><span class="keyword">val</span> equal : <a href="../Type/index.html#type-t">Type.t</a> <span>&#45;&gt;</span> <a href="../Type/index.html#type-t">Type.t</a> <span>&#45;&gt;</span> <a href="../Type/index.html#type-goal">Type.goal</a></code></dt><dt class="spec value" id="val-either"><a href="#val-either" class="anchor"></a><code><span class="keyword">val</span> either : <a href="../Type/index.html#type-goal">Type.goal</a> <span>&#45;&gt;</span> <a href="../Type/index.html#type-goal">Type.goal</a> <span>&#45;&gt;</span> <a href="../Type/index.html#type-goal">Type.goal</a></code></dt><dt class="spec value" id="val-either_multi"><a href="#val-either_multi" class="anchor"></a><code><span class="keyword">val</span> either_multi : <span><a href="../Type/index.html#type-goal">Type.goal</a> list</span> <span>&#45;&gt;</span> <a href="../Type/index.html#type-goal">Type.goal</a></code></dt><dt class="spec value" id="val-both"><a href="#val-both" class="anchor"></a><code><span class="keyword">val</span> both : <a href="../Type/index.html#type-goal">Type.goal</a> <span>&#45;&gt;</span> <a href="../Type/index.html#type-goal">Type.goal</a> <span>&#45;&gt;</span> <a href="../Type/index.html#type-goal">Type.goal</a></code></dt><dt class="spec value" id="val-both_multi"><a href="#val-both_multi" class="anchor"></a><code><span class="keyword">val</span> both_multi : <span><a href="../Type/index.html#type-goal">Type.goal</a> list</span> <span>&#45;&gt;</span> <a href="../Type/index.html#type-goal">Type.goal</a></code></dt><dt class="spec value" id="val-in_set"><a href="#val-in_set" class="anchor"></a><code><span class="keyword">val</span> in_set : <a href="../Type/index.html#type-t">Type.t</a> <span>&#45;&gt;</span> <span><span>(<a href="../Type/index.html#type-t">Type.t</a><a href="../Type/index.html#type-comparator_witness">Type.comparator_witness</a>)</span> Base.Set.t</span> <span>&#45;&gt;</span> <a href="../Type/index.html#type-goal">Type.goal</a></code></dt></dl></div></body></html>
2 changes: 1 addition & 1 deletion docs/logical/Logical/State/index.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>State (logical.Logical.State)</title><link rel="stylesheet" href="../../../odoc.css"/><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../../../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div class="content"><header><nav><a href="../index.html">Up</a><a href="../../index.html">logical</a> &#x00BB; <a href="../index.html">Logical</a> &#x00BB; State</nav><h1>Module <code>Logical.State</code></h1></header><dl><dt class="spec value" id="val-empty"><a href="#val-empty" class="anchor"></a><code><span class="keyword">val</span> empty : <span><span class="type-var">'a</span> list</span></code></dt><dt class="spec value" id="val-value_of"><a href="#val-value_of" class="anchor"></a><code><span class="keyword">val</span> value_of : <a href="../Value/index.html#type-state">Value.state</a> <span>&#45;&gt;</span> <a href="../Value/Type/index.html#type-t">Value.Type.t</a> <span>&#45;&gt;</span> <a href="../Value/Type/index.html#type-t">Value.Type.t</a></code></dt><dt class="spec value" id="val-unify"><a href="#val-unify" class="anchor"></a><code><span class="keyword">val</span> unify : <a href="../Value/index.html#type-state">Value.state</a> <span>&#45;&gt;</span> <a href="../Value/Type/index.html#type-t">Value.Type.t</a> <span>&#45;&gt;</span> <a href="../Value/Type/index.html#type-t">Value.Type.t</a> <span>&#45;&gt;</span> <span><a href="../Value/index.html#type-state">Value.state</a> option</span></code></dt><dt class="spec value" id="val-to_string"><a href="#val-to_string" class="anchor"></a><code><span class="keyword">val</span> to_string : <a href="../Value/index.html#type-state">Value.state</a> <span>&#45;&gt;</span> string</code></dt></dl></div></body></html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>State (logical.Logical.State)</title><link rel="stylesheet" href="../../../odoc.css"/><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../../../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div class="content"><header><nav><a href="../index.html">Up</a><a href="../../index.html">logical</a> &#x00BB; <a href="../index.html">Logical</a> &#x00BB; State</nav><h1>Module <code>Logical.State</code></h1></header><dl><dt class="spec value" id="val-empty"><a href="#val-empty" class="anchor"></a><code><span class="keyword">val</span> empty : <a href="../Type/index.html#type-state">Type.state</a></code></dt><dt class="spec value" id="val-create"><a href="#val-create" class="anchor"></a><code><span class="keyword">val</span> create : <span><span>(<a href="../Type/index.html#type-variable_name">Type.variable_name</a> * <a href="../Type/index.html#type-t">Type.t</a>)</span> list</span> <span>&#45;&gt;</span> <span><a href="../Type/index.html#type-state">Type.state</a> option</span></code></dt><dt class="spec value" id="val-create_exn"><a href="#val-create_exn" class="anchor"></a><code><span class="keyword">val</span> create_exn : <span><span>(<a href="../Type/index.html#type-variable_name">Type.variable_name</a> * <a href="../Type/index.html#type-t">Type.t</a>)</span> list</span> <span>&#45;&gt;</span> <a href="../Type/index.html#type-state">Type.state</a></code></dt><dt class="spec value" id="val-value_of"><a href="#val-value_of" class="anchor"></a><code><span class="keyword">val</span> value_of : <a href="../Type/index.html#type-state">Type.state</a> <span>&#45;&gt;</span> <a href="../Type/index.html#type-t">Type.t</a> <span>&#45;&gt;</span> <a href="../Type/index.html#type-t">Type.t</a></code></dt><dt class="spec value" id="val-unify"><a href="#val-unify" class="anchor"></a><code><span class="keyword">val</span> unify : <a href="../Type/index.html#type-state">Type.state</a> <span>&#45;&gt;</span> <a href="../Type/index.html#type-t">Type.t</a> <span>&#45;&gt;</span> <a href="../Type/index.html#type-t">Type.t</a> <span>&#45;&gt;</span> <span><a href="../Type/index.html#type-state">Type.state</a> option</span></code></dt><dt class="spec value" id="val-to_string"><a href="#val-to_string" class="anchor"></a><code><span class="keyword">val</span> to_string : <a href="../Type/index.html#type-state">Type.state</a> <span>&#45;&gt;</span> string</code></dt></dl></div></body></html>
Loading

0 comments on commit bbe3496

Please sign in to comment.