mini_kraken 0.1.08 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32a6f92773457338343dffa1343906883ffdbafbf7f8fd9ace170062cc34d345
4
- data.tar.gz: cb7d3e6b88f8bea952bea7f214eaf73af97857de6958f65592a15b7ee7ef2510
3
+ metadata.gz: 79ea33dd475e6dfbed71915177b1581abb5e1ca9a60db04133d11cda7dca9d42
4
+ data.tar.gz: 249bea4319183c929e4c291785c6b74e10606889c789c5e0f2b211197cc6f288
5
5
  SHA512:
6
- metadata.gz: db730590bcc99203f8983fe19b5e0d0b90c77819c07875bc34c100bc843c9b3e5e3e148833149d3264847a190cc9ce5bcf1fa31f74723b8c8e8006a56948d738
7
- data.tar.gz: 4cb5cb8f327e7cb81028621ad67e36afecee8bc90beac1cd6449573b85092191d39d55dfbe2de9f52a5868a66bf4c8288a3223b41815528fb651eead36d3f4fa
6
+ metadata.gz: '086bf0b30750e908dfee1b7e95eb3b0c0e54dff4f7bd700d54bbf28091aaf02080d458c14508ddcaa580248e0234d1da23553869480e3bea930d46708c115d82'
7
+ data.tar.gz: d3ada8d056819b2ae1af0d442056008600f82633ee52ca8aa672708943fdf517adb60522bfff6673f9cea86985c1bf5ce2a7a3ea73f07504945e660f31ebee49
@@ -1,3 +1,55 @@
1
+ ## [0.1.13] - 2020-07-01
2
+ - Cover all frames from Chapter One of "Reasoned Scheme" book.
3
+ - Fix defect for fused variables that remain fresh
4
+
5
+ ### CHANGED
6
+ - Method `Variable#quote` now takes into account of cases when variables are fused.
7
+ - Method `Vocabulary#names_fused` now copes with cases where no variable with given name can be found.
8
+
9
+ ## [0.1.12] - 2020-06-29
10
+ - Supports `conde`, that is, a relation that can take an arbitrary number of arguments.
11
+ - Cover all frames but one from Chapter One of "Reasoned Scheme" book.
12
+
13
+ ### New
14
+ - Class `Conde` a relation that succeeds for each of its successful arguments.
15
+
16
+ ### CHANGED
17
+ - Method `Goal#validated_actuals` add into account polyadic relations (= relations with arbitrary number of arguments)
18
+
19
+ ## [0.1.11] - 2020-06-25
20
+ - Supports `defrel`, that is, the capability to define new relations by combining other relations.
21
+ - Covers frames from "The Reasoned Scheme" book up to frame [1:87]
22
+
23
+ ### New
24
+ - Class `BaseArg` a generalization of goal or goal template argument.
25
+ - Class `DefRelation` A specialization of `Relation` class aimed for user-defined relation.
26
+ - Class `FormalArg` to represent goal template argument(s).
27
+ - Class `FormalRef` an allusion to a formal argument in a goal template.
28
+ - Class `GoalTemplate` a representation of a goal parametrized with formal arguments.
29
+ - Class `KBoolean` a MiniKraken representation of a boolean value.
30
+
31
+ ### CHANGED
32
+ - File `README.md` minor change: added more TODO's.
33
+
34
+ ## [0.1.10] - 2020-06-13
35
+ - Supports frames from "The Reasoned Scheme" book up to frame [1:81]
36
+
37
+ ### New
38
+ - Factory methods `Outcome#failure`, `Outcome#success`
39
+ - Method `Vocabulary#inspect`
40
+ - File `outcome_spec.rb`
41
+
42
+ ### FIXED
43
+ - `Conj2#conjunction` vocabulary wasn't cleared when outcome2 was nil.
44
+
45
+ ## [0.1.09] - 2020-06-06
46
+ - Supports frames from "The Reasoned Scheme" book up to frame [1:76]
47
+
48
+ ### CHANGED
49
+ - Method `FreshEnv#initialize`accepts an array of goals as second argument. This array is transformed into a conjunction of goals.
50
+ - Method `RunStarExpression#initialize` accepts multiple multiple variable names and goals.
51
+ - Method `RunStarExpression#run` can handle solutions with multiple variables.
52
+
1
53
  ## [0.1.08] - 2020-05-30
2
54
  - Fix of nasty bug (object aliasing) that caused flaky failures in specs.
3
55
 
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  ### What is __mini_kraken__ ?
7
7
  An implemention of the [miniKanren](http://minikanren.org/) relational programming language in Ruby.
8
8
  *miniKanren* is a small language for relational (logic) programming.
9
- Based on the reference implementation, in Scheme from the "The Reasoned Schemer" book.
9
+ Based on the reference implementation, in Scheme from the "The Reasoned Schemer" book.
10
10
  Daniel P. Friedman, William E. Byrd, Oleg Kiselyov, and Jason Hemann: "The Reasoned Schemer", Second Edition,
11
11
  ISBN: 9780262535519, (2018), MIT Press.
12
12
 
@@ -15,13 +15,21 @@ ISBN: 9780262535519, (2018), MIT Press.
15
15
  - [X] run\*
16
16
  - [X] fresh
17
17
  - [X] conj2
18
- - [X] disj2
18
+ - [X] disj2
19
+ - [X] defrel
19
20
 
20
21
  ### TODO
21
- - [ ] defrel
22
22
  - [ ] conde
23
23
  - [ ] Occurs check
24
24
 
25
+ List-centric relations from Chapter 2
26
+ - [ ] caro
27
+ - [ ] cdro
28
+ - [ ] conso
29
+ - [ ] nullo
30
+ - [ ] pairo
31
+ - [ ] singletono
32
+
25
33
  ## Installation
26
34
 
27
35
  Add this line to your application's Gemfile:
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'set'
4
4
  require_relative 'atomic_term'
5
- require_relative 'composite_term'
5
+ require_relative 'cons_cell'
6
6
 
7
7
  module MiniKraken
8
8
  module Core
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniKraken
4
+ module Core
5
+ # Abstract class that is a generalization for goal actual arguments or
6
+ # for arguments of goal template.
7
+ class BaseArg
8
+ end # class
9
+ end # module
10
+ end # module
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+ require_relative 'conj2'
5
+ require_relative 'duck_fiber'
6
+ require_relative 'fail'
7
+ require_relative 'goal'
8
+ require_relative 'goal_relation'
9
+ require_relative 'outcome'
10
+
11
+ unless MiniKraken::Core.constants(false).include? :Conde
12
+ module MiniKraken
13
+ module Core
14
+ # A polyadic relation (i.e. it can takes an arbitrary number of argumentt)
15
+ # that behaves as the disjunction of its arguments.
16
+ # It succeeds if at least one of its goal arguments succeeds.
17
+ class Conde < GoalRelation
18
+ include Singleton
19
+
20
+ def initialize
21
+ super('conde', nil)
22
+ end
23
+
24
+ # A relation is polyadic when it accepts an arbitrary number of arguments.
25
+ # @return [TrueClass]
26
+ def polyadic?
27
+ true
28
+ end
29
+
30
+ # @param actuals [Array<Term>] A two-elements array
31
+ # @param anEnv [Vocabulary] A vocabulary object
32
+ # @return [Fiber<Outcome>] A Fiber that yields Outcomes objects
33
+ def solver_for(actuals, anEnv)
34
+ args = *validated_args(actuals)
35
+ Fiber.new { cond(args, anEnv) }
36
+ end
37
+
38
+ # Yields [Outcome, NilClass] result of the disjunction
39
+ # @param goals [Array<Goal>] Array of goals
40
+ # @param voc [Vocabulary] A vocabulary object
41
+ def cond(goals, voc)
42
+ # require 'debug'
43
+ success = false
44
+
45
+ goals.each do |g|
46
+ fiber = nil
47
+
48
+ case g
49
+ when Core::Goal
50
+ fiber = g.attain(voc)
51
+ when Core::Environment
52
+ fiber = g.attain(voc)
53
+ when Array
54
+ conjunct = conjunction(g)
55
+ fiber = conjunct.attain(voc)
56
+ when Core::ConsCell
57
+ goal_array = to_goal_array(g)
58
+ conjunct = conjunction(goal_array)
59
+ fiber = conjunct.attain(voc)
60
+ end
61
+ loop do
62
+ outcome = fiber.resume
63
+ break unless outcome
64
+
65
+ outcome.parent = voc unless outcome.parent
66
+ if outcome.successful?
67
+ success = true
68
+ Fiber.yield outcome
69
+ outcome.clear
70
+ end
71
+ end
72
+ end
73
+
74
+ Fiber.yield Outcome.new(:"#u", voc) unless success
75
+ Fiber.yield nil
76
+ end
77
+
78
+ private
79
+
80
+ def validated_args(actuals)
81
+ result = []
82
+
83
+ actuals.each do |arg|
84
+ case arg
85
+ when Core::Goal
86
+ result << arg
87
+
88
+ when Core::Environment
89
+ result << arg
90
+
91
+ when Array
92
+ result << validated_args(arg)
93
+
94
+ else
95
+ prefix = "#{name} expects goal as argument, found a "
96
+ raise StandardError, prefix + "'#{arg.class}'"
97
+ end
98
+ end
99
+
100
+ result
101
+ end
102
+
103
+ def conjunction(goal_array)
104
+ result = nil
105
+
106
+ loop do
107
+ conjunctions = []
108
+ goal_array.each_slice(2) do |uno_duo|
109
+ if uno_duo.size == 2
110
+ conjunctions << Core::Goal.new(Core::Conj2.instance, uno_duo)
111
+ else
112
+ conjunctions << uno_duo[0]
113
+ end
114
+ end
115
+ if conjunctions.size == 1
116
+ result = conjunctions[0]
117
+ break
118
+ end
119
+ goal_array = conjunctions
120
+ end
121
+
122
+ result
123
+ end
124
+
125
+ def to_goal_array(aCons)
126
+ array = []
127
+ curr_node = aCons
128
+ loop do
129
+ array << curr_node.car if curr_node.car.kind_of?(Core::Goal)
130
+ break unless curr_node.cdr
131
+ break unless curr_node.car.kind_of?(Core::Goal)
132
+
133
+ curr_node = curr_node.cdr
134
+ end
135
+
136
+ array
137
+ end
138
+ end # class
139
+
140
+ Conde.instance.freeze
141
+ end # module
142
+ end # module
143
+ end # unless
@@ -32,11 +32,11 @@ unless MiniKraken::Core.constants(false).include? :Conj2
32
32
  # @param voc [Vocabulary] A vocabulary object
33
33
  def conjunction(g1, g2, voc)
34
34
  # require 'debug'
35
- outcome1 = nil
36
- outcome2 = nil
37
35
  if g1.relation.kind_of?(Fail) || g2.relation.kind_of?(Fail)
38
36
  Fiber.yield Outcome.new(:"#u", voc)
39
37
  else
38
+ outcome1 = nil
39
+ outcome2 = nil
40
40
  f1 = g1.attain(voc)
41
41
  loop do
42
42
  outcome1 = f1.resume
@@ -63,7 +63,9 @@ unless MiniKraken::Core.constants(false).include? :Conj2
63
63
  else
64
64
  Fiber.yield outcome1
65
65
  end
66
- voc.clear if outcome1&.successful? && outcome2&.successful?
66
+ if outcome1.successful? && (outcome2&.successful? || outcome2.nil?)
67
+ voc.clear
68
+ end
67
69
  end
68
70
  end
69
71
 
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'relation'
4
+
5
+ module MiniKraken
6
+ module Core
7
+ # A relation that is parametrized with generic formal arguments
8
+ # and a goal template expression.
9
+ class DefRelation < Relation
10
+ # @return [Array<FormalArg>] formal arguments of this DefRelation
11
+ attr_reader :formals
12
+
13
+ # @return [GoalTemplate] goal template
14
+ attr_reader :goal_template
15
+
16
+ # @param aName [String] name of def relation
17
+ # @param aGoalTemplate [GoalTemplate]
18
+ def initialize(aName, aGoalTemplate, theFormals, alternateName = nil)
19
+ super(aName, alternateName)
20
+ @formals = validated_formals(theFormals)
21
+ @goal_template = validated_goal_template(aGoalTemplate)
22
+ end
23
+
24
+ # Number of arguments for the relation.
25
+ # @return [Integer]
26
+ def arity
27
+ formals.size
28
+ end
29
+
30
+ # @param actuals [Array<Term>] A two-elements array
31
+ # @param anEnv [Vocabulary] A vocabulary object
32
+ # @return [Fiber<Outcome>] A Fiber(-like) instance that yields Outcomes
33
+ def solver_for(actuals, anEnv)
34
+ goal = goal_template.instantiate(formals, actuals)
35
+ goal.attain(anEnv)
36
+ end
37
+
38
+ private
39
+
40
+ def validated_formals(theFormals)
41
+ theFormals
42
+ end
43
+
44
+ def validated_goal_template(aGoalTemplate)
45
+ aGoalTemplate
46
+ end
47
+ end # class
48
+ end # module
49
+ end # module
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'singleton'
4
4
  require_relative 'duck_fiber'
5
+ require_relative 'fail'
5
6
  require_relative 'goal'
6
7
  require_relative 'goal_relation'
7
8
  require_relative 'outcome'
@@ -31,9 +31,9 @@ unless MiniKraken::Core.constants(false).include? :Equals
31
31
  arg2_nil = arg2.nil?
32
32
  if arg1_nil || arg2_nil
33
33
  if arg1_nil && arg2_nil
34
- result = Outcome.new(:"#s", anEnv)
34
+ result = Outcome.success(anEnv)
35
35
  else
36
- result = Failure
36
+ result = Outcome.failure(anEnv)
37
37
  end
38
38
  return result
39
39
  end
@@ -46,6 +46,7 @@ unless MiniKraken::Core.constants(false).include? :Equals
46
46
 
47
47
  private
48
48
 
49
+
49
50
  # table: Unification
50
51
  # | arg1 | arg2 | Criterion || Unification |
51
52
  # | isa? Atomic | isa? Atomic | arg1.eq? arg2 is true || { "s", [] } |
@@ -70,12 +71,12 @@ unless MiniKraken::Core.constants(false).include? :Equals
70
71
  # | | unification(arg1, arg2.value) => "u" || { "u", [] }
71
72
  def do_unification(arg1, arg2, anEnv)
72
73
  # require 'debug'
73
- return Outcome.new(:"#s", anEnv) if arg1.equal?(arg2)
74
+ return Outcome.success(anEnv) if arg1.equal?(arg2)
74
75
 
75
- result = Outcome.new(:"#u", anEnv) # default case
76
+ result = Outcome.failure(anEnv) # default case
76
77
 
77
78
  if arg1.kind_of?(AtomicTerm)
78
- result = BasicSuccess if arg1.eql?(arg2)
79
+ result = Outcome.success(anEnv) if arg1.eql?(arg2)
79
80
  elsif arg1.kind_of?(CompositeTerm)
80
81
  if arg2.kind_of?(CompositeTerm) # AtomicTerm is default case => fail
81
82
  result = unify_composite_terms(arg1, arg2, anEnv)
@@ -84,14 +85,14 @@ unless MiniKraken::Core.constants(false).include? :Equals
84
85
  arg1_freshness = arg1.freshness(anEnv)
85
86
  if arg2.kind_of?(AtomicTerm)
86
87
  if arg1_freshness.degree == :fresh
87
- result = Outcome.new(:"#s", anEnv)
88
+ result = Outcome.success(anEnv)
88
89
  arg1.associate(arg2, result)
89
90
  else
90
- result = Outcome.new(:"#s", anEnv) if arg1.value(anEnv).eql?(arg2)
91
+ result = Outcome.success(anEnv) if arg1.value(anEnv).eql?(arg2)
91
92
  end
92
93
  elsif arg2.kind_of?(CompositeTerm)
93
94
  if arg1_freshness.degree == :fresh
94
- result = Outcome.new(:"#s", anEnv)
95
+ result = Outcome.success(anEnv)
95
96
  arg1.associate(arg2, result)
96
97
  else
97
98
  # Ground case...
@@ -103,11 +104,14 @@ unless MiniKraken::Core.constants(false).include? :Equals
103
104
  when [false, false] # TODO: confirm this...
104
105
  result = unification(arg1.value(anEnv), arg2.value(anEnv), anEnv)
105
106
  when [true, true]
106
- result = Outcome.new(:"#s", anEnv)
107
+ result = Outcome.success(anEnv)
107
108
  if arg1.var_name != arg2.var_name
108
109
  arg1.associate(arg2, result)
109
110
  arg2.associate(arg1, result)
110
111
  end
112
+ when [true, false]
113
+ result = Outcome.success(anEnv)
114
+ arg1.associate(arg2, result)
111
115
  else
112
116
  raise StandardError, "Unsupported freshness combination #{freshness}"
113
117
  end
@@ -123,7 +127,7 @@ unless MiniKraken::Core.constants(false).include? :Equals
123
127
  # @return [Freshness]
124
128
  def unify_composite_terms(arg1, arg2, anEnv)
125
129
  # require 'debug'
126
- result = Outcome.new(:"#u", anEnv)
130
+ result = Outcome.failure(anEnv)
127
131
  children1 = arg1.children
128
132
  children2 = arg2.children
129
133
 
@@ -136,7 +140,7 @@ unless MiniKraken::Core.constants(false).include? :Equals
136
140
  end
137
141
  total_success = subresults.all?(&:successful?)
138
142
  if total_success
139
- memo = Outcome.new(:"#s", anEnv)
143
+ memo = Outcome.success(anEnv)
140
144
  associations = subresults.reduce(memo) do |sub_total, outcome|
141
145
  sub_total.merge(outcome)
142
146
  sub_total
@@ -148,8 +152,8 @@ unless MiniKraken::Core.constants(false).include? :Equals
148
152
  result
149
153
  end
150
154
  end # class
151
-
155
+
152
156
  Equals.instance.freeze
153
157
  end # module
154
158
  end # module
155
- end # unless
159
+ end # unless
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniKraken
4
+ module Core
5
+ # The generalization of any iem that can be
6
+ # passed as arugement to a goal.
7
+ class FormalArg
8
+ # @return [String]
9
+ attr_reader :name
10
+
11
+ def initialize(aName)
12
+ @name = validated_name(aName)
13
+ end
14
+
15
+ private
16
+
17
+ def validated_name(aName)
18
+ aName
19
+ end
20
+ end # class
21
+ end # module
22
+ end # module