Skip to content

Commit

Permalink
Merge pull request #745 from vsbogd/remove-type-based-duplicates-2
Browse files Browse the repository at this point in the history
Remove type based duplicates
  • Loading branch information
vsbogd authored Jul 23, 2024
2 parents 2470df6 + 64b0848 commit 9c126b1
Show file tree
Hide file tree
Showing 7 changed files with 346 additions and 142 deletions.
16 changes: 1 addition & 15 deletions lib/src/atom/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,24 +1073,10 @@ impl BindingsSet {
}
}

pub trait BindingsResultIter: Iterator<Item=Bindings> {
fn clone_(&self) -> Box<dyn BindingsResultIter>;
}
impl<T: 'static + Clone + Iterator<Item=Bindings>> BindingsResultIter for T {
fn clone_(&self) -> Box<dyn BindingsResultIter> {
Box::new(self.clone())
}
}

/// Iterator over atom matching results. Each result is an instance of [Bindings].
//TODO: A situation where a MatchResultIter returns an unbounded (infinite) number of results
// will hang this implementation, on account of `.collect()`
pub type MatchResultIter = Box<dyn BindingsResultIter>;
impl Clone for MatchResultIter {
fn clone(&self) -> Self {
self.clone_()
}
}
pub type MatchResultIter = Box<dyn Iterator<Item=Bindings>>;

/// Matches two atoms and returns an iterator over results. Atoms are
/// treated symmetrically.
Expand Down
346 changes: 231 additions & 115 deletions lib/src/metta/interpreter_minimal.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/src/metta/runner/stdlib_minimal.metta
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
(: decons-atom (-> Expression Expression))
(: collapse-bind (-> Atom Expression))
(: superpose-bind (-> Expression Atom))
(: metta (-> Atom Type Grounded Atom))

(: id (-> Atom Atom))
(= (id $x) $x)
Expand Down
91 changes: 91 additions & 0 deletions lib/src/metta/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,76 @@ fn get_args(expr: &ExpressionAtom) -> &[Atom] {
&expr.children().as_slice()[1..]
}

/// Returns vector of the types for the given `atom` in context of the given
/// `space`. Returns `%Undefined%` if atom has no type assigned. Returns empty
/// vector if atom is a function call but expected types of arguments are not
/// compatible with passed values.
///
/// # Examples
///
/// ```
/// use hyperon::{Atom, expr, assert_eq_no_order};
/// use hyperon::metta::ATOM_TYPE_UNDEFINED;
/// use hyperon::metta::runner::*;
/// use hyperon::metta::text::SExprParser;
/// use hyperon::metta::types::get_atom_types;
///
/// let metta = Metta::new(None);
/// metta.run(SExprParser::new("
/// (: f (-> A B))
/// (: a A)
/// (: a B)
/// (: b B)
/// ")).unwrap();
///
/// let space = metta.space();
/// assert_eq_no_order!(get_atom_types(&space, &expr!(x)), vec![ATOM_TYPE_UNDEFINED]);
/// assert_eq_no_order!(get_atom_types(&space, &expr!({1})), vec![expr!("i32")]);
/// assert_eq_no_order!(get_atom_types(&space, &expr!("na")), vec![ATOM_TYPE_UNDEFINED]);
/// assert_eq_no_order!(get_atom_types(&space, &expr!("a")), vec![expr!("A"), expr!("B")]);
/// assert_eq_no_order!(get_atom_types(&space, &expr!("a" "b")), vec![expr!("A" "B"), expr!("B" "B"), ATOM_TYPE_UNDEFINED]);
/// assert_eq_no_order!(get_atom_types(&space, &expr!("f" "a")), vec![expr!("B")]);
/// assert_eq_no_order!(get_atom_types(&space, &expr!("f" "b")), Vec::<Atom>::new());
/// ```
#[cfg(not(feature = "old_interpreter"))]
pub fn get_atom_types(space: &dyn Space, atom: &Atom) -> Vec<Atom> {
log::trace!("get_atom_types: atom: {}", atom);
let types = match atom {
// TODO: type of the variable could be actually a type variable,
// in this case inside each variant of type for the atom we should
// also keep bindings for the type variables. For example,
// we have an expression `(let $n (foo) (+ $n $n))`, where
// `(: let (-> $t $t $r $r))`, `(: foo (-> $tt))`,
// and `(: + (-> Num Num Num))`then type checker can find that
// `{ $r = $t = $tt = Num }`.
Atom::Variable(_) => vec![ATOM_TYPE_UNDEFINED],
Atom::Grounded(gnd) => vec![make_variables_unique(gnd.type_())],
Atom::Symbol(_) => {
let mut types = query_types(space, atom);
if types.is_empty() {
types.push(ATOM_TYPE_UNDEFINED)
}
types
},
Atom::Expression(expr) => {
let tuples = get_tuple_types(space, atom, expr);
let applications = get_application_types(space, atom, expr);

let mut types = Vec::new();
if applications == None {
types.extend(tuples);
types.push(ATOM_TYPE_UNDEFINED);
} else {
types.extend(tuples);
applications.into_iter().for_each(|t| types.extend(t));
}
types
},
};
log::debug!("get_atom_types: return atom {} types {:?}", atom, types);
types
}

/// Returns vector of the types for the given `atom` in context of the given
/// `space`. Returns `%Undefined%` if atom has no type assigned. Returns empty
/// vector if atom is a function call but expected types of arguments are not
Expand Down Expand Up @@ -185,6 +255,7 @@ fn get_args(expr: &ExpressionAtom) -> &[Atom] {
/// assert_eq_no_order!(get_atom_types(&space, &expr!("f" "a")), vec![expr!("B")]);
/// assert_eq_no_order!(get_atom_types(&space, &expr!("f" "b")), Vec::<Atom>::new());
/// ```
#[cfg(feature = "old_interpreter")]
pub fn get_atom_types(space: &dyn Space, atom: &Atom) -> Vec<Atom> {
log::trace!("get_atom_types: atom: {}", atom);
let types = match atom {
Expand Down Expand Up @@ -833,8 +904,16 @@ mod tests {
(: b B)
(: b BB)
");
#[cfg(not(feature = "old_interpreter"))]
assert_eq_no_order!(get_atom_types(&space, &atom("(a b)")),
vec![atom("(A B)"), atom("(AA B)"), atom("(A BB)"), atom("(AA BB)"), ATOM_TYPE_UNDEFINED]);
#[cfg(feature = "old_interpreter")]
assert_eq_no_order!(get_atom_types(&space, &atom("(a b)")),
vec![atom("(A B)"), atom("(AA B)"), atom("(A BB)"), atom("(AA BB)")]);
#[cfg(not(feature = "old_interpreter"))]
assert_eq_no_order!(get_atom_types(&space, &atom("(a c)")),
vec![atom("(A %Undefined%)"), atom("(AA %Undefined%)"), ATOM_TYPE_UNDEFINED]);
#[cfg(feature = "old_interpreter")]
assert_eq_no_order!(get_atom_types(&space, &atom("(a c)")),
vec![atom("(A %Undefined%)"), atom("(AA %Undefined%)")]);
assert_eq_no_order!(get_atom_types(&space, &atom("(c d)")), vec![ATOM_TYPE_UNDEFINED]);
Expand Down Expand Up @@ -889,9 +968,21 @@ mod tests {
//assert_eq!(get_atom_types(&space, &expr!("f_gnd" b)), vec![]);

assert_eq!(get_atom_types(&space, &expr!("f_atom" ("b"))), vec![atom("D")]);
// Here and below: when interpreter cannot find a function type for
// expression it evaluates it. Thus any argument expression without
// a function type can potentially suit as a legal argument.
#[cfg(not(feature = "old_interpreter"))]
assert_eq!(get_atom_types(&space, &expr!("f_sym" ("b"))), vec![atom("D")]);
#[cfg(feature = "old_interpreter")]
assert_eq!(get_atom_types(&space, &expr!("f_sym" ("b"))), vec![]);
assert_eq!(get_atom_types(&space, &expr!("f_expr" ("b"))), vec![atom("D")]);
#[cfg(not(feature = "old_interpreter"))]
assert_eq!(get_atom_types(&space, &expr!("f_var" ("b"))), vec![atom("D")]);
#[cfg(feature = "old_interpreter")]
assert_eq!(get_atom_types(&space, &expr!("f_var" ("b"))), vec![]);
#[cfg(not(feature = "old_interpreter"))]
assert_eq!(get_atom_types(&space, &expr!("f_gnd" ("b"))), vec![atom("D")]);
#[cfg(feature = "old_interpreter")]
assert_eq!(get_atom_types(&space, &expr!("f_gnd" ("b"))), vec![]);

assert_eq!(get_atom_types(&space, &expr!("f_atom" {1})), vec![atom("D")]);
Expand Down
9 changes: 6 additions & 3 deletions python/tests/scripts/c1_grounded_basic.metta
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,12 @@
; Custom symbols as arguments of grounded operations
; work similarly
(: ln LN)
!(assertEqualToResult
(== 4 (+ ln 2))
((Error ln BadType)))
; TODO: This test has different behavior in old_interpreter and minimal interpreter.
; In first case it returns (Error ln BadType). In second case it returns
; (Error (+ ln 2) BadType). Uncomment when old_interpreter feature is removed
;!(assertEqualToResult
; (== 4 (+ ln 2))
; ((Error (+ ln 2) BadType)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down
15 changes: 9 additions & 6 deletions python/tests/scripts/d2_higherfunc.metta
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,15 @@
!(assertEqualToResult
(get-type (fmap (curry-a + 2) (Left "5")))
())
!(assertEqualToResult
(get-type (fmap (curry-a + 2) (UntypedC "5")))
())
!(assertEqualToResult
(get-type (fmap (curry-a + 2) (UntypedC (Null) 5)))
())
; TODO: Two examples below are type-checked successfully because, (UntypedC "5")
; can return result which has an appropriate type. Uncomment when old_interpreter
; feature is removed.
;!(assertEqualToResult
; (get-type (fmap (curry-a + 2) (UntypedC "5")))
; ())
;!(assertEqualToResult
; (get-type (fmap (curry-a + 2) (UntypedC (Null) 5)))
; ())

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down
10 changes: 7 additions & 3 deletions python/tests/scripts/d5_auto_types.metta
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ Auto type-checking can be enabled
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; let binds without type checking, but errors when used
(let $x (+ 5 "S") $x)

(: f (-> $t Number))
!(assertEqualToResult
(f (+ 5 "S"))
((Error "S" BadType)))
; TODO: This test has different behavior in old_interpreter and minimal interpreter.
; In first case it returns (Error "S" BadType). In second case it returns
; (Error (+ 5 "S") BadType). Uncomment when old_interpreter feature is removed
;!(assertEqualToResult
; (f (+ 5 "S"))
; ((Error (+ 5 "S") BadType)))

0 comments on commit 9c126b1

Please sign in to comment.