mini_kraken 0.1.08 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
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