Trait/Object

eu.cdevreeze.yaidom.queryapi

ElemLike

Related Docs: object ElemLike | package queryapi

Permalink

trait ElemLike extends ElemApi

API and implementation trait for elements as containers of elements, as element nodes in a node tree. This trait knows very little about elements. It does not know about names, attributes, etc. All it knows about elements is that elements can have element children (other node types are entirely out of scope in this trait).

The purely abstract API offered by this trait is eu.cdevreeze.yaidom.queryapi.ElemApi. See the documentation of that trait for examples of usage, and for a more formal treatment. Below follows an even more formal treatment, with proofs by induction of important properties obeyed by methods of this API. It shows the mathematical rigor of the yaidom query API. API users that are only interested in how to use the API can safely skip that formal treatment.

ElemLike more formally

In order to get started using the API, this more formal section can safely be skipped. On the other hand, this section may provide a deeper understanding of the API.

The only abstract method is findAllChildElems. Based on this method alone, this trait offers a rich API for querying elements. This is entirely consistent with the semantics defined in the ElemApi trait. Indeed, the implementation of the methods follows the semantics defined there.

In the ElemApi trait, some (simple) provable laws were mentioned. Some proofs follow below.

1. Proving property about filterElemsOrSelf

Below follows a proof by structural induction of one of the laws mentioned in the documentation of trait ElemApi.

First we make a few assumptions, for this proof, and (implicitly) for the other proofs:

Based on these assumptions, we prove by induction that:

elm.filterElemsOrSelf(p) == elm.findAllElemsOrSelf.filter(p)

Base case

If elm has no child elements, then the LHS can be rewritten as follows:

elm.filterElemsOrSelf(p)
immutable.IndexedSeq(elm).filter(p) ++ (elm.findAllChildElems flatMap (_.filterElemsOrSelf(p))) // definition of filterElemsOrSelf
immutable.IndexedSeq(elm).filter(p) ++ (Seq() flatMap (_.filterElemsOrSelf(p))) // there are no child elements
immutable.IndexedSeq(elm).filter(p) ++ Seq() // flatMap on empty sequence returns empty sequence
immutable.IndexedSeq(elm).filter(p) // property of concatenation: xs ++ Seq() == xs
(immutable.IndexedSeq(elm) ++ Seq()).filter(p) // property of concatenation: xs ++ Seq() == xs
(immutable.IndexedSeq(elm) ++ (elm.findAllChildElems flatMap (_ filterElemsOrSelf (e => true)))) filter p // flatMap on empty sequence (of child elements) returns empty sequence
(immutable.IndexedSeq(elm).filter(e => true) ++ (elm.findAllChildElems flatMap (_ filterElemsOrSelf (e => true)))) filter p // filtering with predicate that is always true
elm.filterElemsOrSelf(e => true) filter p // definition of filterElemsOrSelf
elm.findAllElemsOrSelf filter p // definition of findAllElemsOrSelf

which is the RHS.

Inductive step

For the inductive step, we use the following (general) properties:

(xs.filter(p) ++ ys.filter(p)) == ((xs ++ ys) filter p) // referred to below as property (a)

and:

(xs flatMap (x => f(x) filter p)) == ((xs flatMap f) filter p) // referred to below as property (b)

If elm does have child elements, the LHS can be rewritten as:

elm.filterElemsOrSelf(p)
immutable.IndexedSeq(elm).filter(p) ++ (elm.findAllChildElems flatMap (_.filterElemsOrSelf(p))) // definition of filterElemsOrSelf
immutable.IndexedSeq(elm).filter(p) ++ (elm.findAllChildElems flatMap (ch => ch.findAllElemsOrSelf filter p)) // induction hypothesis
immutable.IndexedSeq(elm).filter(p) ++ ((elm.findAllChildElems.flatMap(ch => ch.findAllElemsOrSelf)) filter p) // property (b)
(immutable.IndexedSeq(elm) ++ (elm.findAllChildElems flatMap (_.findAllElemsOrSelf))) filter p // property (a)
(immutable.IndexedSeq(elm) ++ (elm.findAllChildElems flatMap (_ filterElemsOrSelf (e => true)))) filter p // definition of findAllElemsOrSelf
(immutable.IndexedSeq(elm).filter(e => true) ++ (elm.findAllChildElems flatMap (_ filterElemsOrSelf (e => true)))) filter p // filtering with predicate that is always true
elm.filterElemsOrSelf(e => true) filter p // definition of filterElemsOrSelf
elm.findAllElemsOrSelf filter p // definition of findAllElemsOrSelf

which is the RHS.

This completes the proof. Other above-mentioned properties can be proven by induction in a similar way.

2. Proving property about filterElems

From the preceding proven property it easily follows (without using a proof by induction) that:

elm.filterElems(p) == elm.findAllElems.filter(p)

After all, the LHS can be rewritten as follows:

elm.filterElems(p)
(elm.findAllChildElems flatMap (_.filterElemsOrSelf(p))) // definition of filterElems
(elm.findAllChildElems flatMap (e => e.findAllElemsOrSelf.filter(p))) // using the property proven above
(elm.findAllChildElems flatMap (_.findAllElemsOrSelf)) filter p // using property (b) above
(elm.findAllChildElems flatMap (_ filterElemsOrSelf (e => true))) filter p // definition of findAllElemsOrSelf
elm.filterElems(e => true) filter p // definition of filterElems
elm.findAllElems filter p // definition of findAllElems

which is the RHS.

3. Proving property about findTopmostElemsOrSelf

Given the above-mentioned assumptions, we prove by structural induction that:

(elm.findTopmostElemsOrSelf(p) flatMap (_.filterElemsOrSelf(p))) == (elm.filterElemsOrSelf(p))

Base case

If elm has no child elements, and p(elm) holds, then LHS and RHS evaluate to immutable.IndexedSeq(elm).

If elm has no child elements, and p(elm) does not hold, then LHS and RHS evaluate to immutable.IndexedSeq().

Inductive step

For the inductive step, we introduce the following additional (general) property, if f and g have the same types:

((xs flatMap f) flatMap g) == (xs flatMap (x => f(x) flatMap g)) // referred to below as property (c)

This is also known as the "associativity law for monads". (Monadic types obey 3 laws: associativity, left unit and right unit.)

If elm does have child elements, and p(elm) holds, the LHS can be rewritten as:

(elm.findTopmostElemsOrSelf(p) flatMap (_.filterElemsOrSelf(p)))
immutable.IndexedSeq(elm) flatMap (_.filterElemsOrSelf(p)) // definition of findTopmostElemsOrSelf, knowing that p(elm) holds
elm.filterElemsOrSelf(p) // definition of flatMap, applied to singleton sequence

which is the RHS. In this case, we did not even need the induction hypothesis.

If elm does have child elements, and p(elm) does not hold, the LHS can be rewritten as:

(elm.findTopmostElemsOrSelf(p) flatMap (_.filterElemsOrSelf(p)))
(elm.findAllChildElems flatMap (_.findTopmostElemsOrSelf(p))) flatMap (_.filterElemsOrSelf(p)) // definition of findTopmostElemsOrSelf, knowing that p(elm) does not hold
elm.findAllChildElems flatMap (ch => ch.findTopmostElemsOrSelf(p) flatMap (_.filterElemsOrSelf(p))) // property (c)
elm.findAllChildElems flatMap (_.filterElemsOrSelf(p)) // induction hypothesis
immutable.IndexedSeq() ++ (elm.findAllChildElems flatMap (_.filterElemsOrSelf(p))) // definition of concatenation
immutable.IndexedSeq(elm).filter(p) ++ (elm.findAllChildElems flatMap (_.filterElemsOrSelf(p))) // definition of filter, knowing that p(elm) does not hold
elm.filterElemsOrSelf(p) // definition of filterElems

which is the RHS.

4. Proving property about findTopmostElems

From the preceding proven property it easily follows (without using a proof by induction) that:

(elm.findTopmostElems(p) flatMap (_.filterElemsOrSelf(p))) == (elm.filterElems(p))

After all, the LHS can be rewritten to:

(elm.findTopmostElems(p) flatMap (_.filterElemsOrSelf(p)))
(elm.findAllChildElems flatMap (_.findTopmostElemsOrSelf(p))) flatMap (_.filterElemsOrSelf(p)) // definition of findTopmostElems
elm.findAllChildElems flatMap (ch => ch.findTopmostElemsOrSelf(p) flatMap (_.filterElemsOrSelf(p))) // property (c)
elm.findAllChildElems flatMap (_.filterElemsOrSelf(p)) // using the property proven above
elm.filterElems(p) // definition of filterElems

which is the RHS.

5. Properties used in the proofs above

There are several (unproven) properties that were used in the proofs above:

(xs.filter(p) ++ ys.filter(p)) == ((xs ++ ys) filter p) // property (a); filter distributes over concatenation

(xs flatMap (x => f(x) filter p)) == ((xs flatMap f) filter p) // property (b)

// Associativity law for monads
((xs flatMap f) flatMap g) == (xs flatMap (x => f(x) flatMap g)) // property (c)

Property (a) is obvious, and stated without proof. Property (c) is known as the "associativity law for monads". Property (b) is proven below.

To prove property (b), we use property (c), as well as the following property (d):

(xs filter p) == (xs flatMap (y => if (p(y)) List(y) else Nil)) // property (d)

Then property (b) can be proven as follows:

xs flatMap (x => f(x) filter p)
xs flatMap (x => f(x) flatMap (y => if (p(y)) List(y) else Nil))
(xs flatMap f) flatMap (y => if (p(y)) List(y) else Nil) // property (c)
(xs flatMap f) filter p

Implementation notes

Methods findAllElemsOrSelf, filterElemsOrSelf, findTopmostElemsOrSelf and findElemOrSelf use recursion in their implementations, but not tail-recursion. The lack of tail-recursion should not be a problem, due to limited XML tree depths in practice. It is comparable to an "idiomatic" Scala quicksort implementation in its lack of tail-recursion. Also in the case of quicksort, the lack of tail-recursion is acceptable due to limited recursion depths. If we want tail-recursive implementations of the above-mentioned methods (in particular the first 3 ones), we either lose the ordering of result elements in document order (depth-first), or we lose performance and/or clarity. That just is not worth it.

Linear Supertypes
ElemApi, AnyElemApi, AnyRef, Any
Known Subclasses
Ordering
  1. Alphabetic
  2. By inheritance
Inherited
  1. ElemLike
  2. ElemApi
  3. AnyElemApi
  4. AnyRef
  5. Any
  1. Hide All
  2. Show all
Visibility
  1. Public
  2. All

Type Members

  1. abstract type ThisElem <: Aux[ThisElem]

    Permalink

    The element type itself.

    The element type itself. It must be restricted to a sub-type of the query API trait in question.

    Concrete element classes will restrict this type to that element class itself.

    Definition Classes
    ElemLikeElemApiAnyElemApi

Abstract Value Members

  1. abstract def findAllChildElems: IndexedSeq[ThisElem]

    Permalink

    Core method that returns all child elements, in the correct order.

    Core method that returns all child elements, in the correct order. Other operations can be defined in terms of this one.

    Definition Classes
    ElemLikeElemApi
  2. abstract def thisElem: ThisElem

    Permalink

    This element itself.

    This element itself.

    Definition Classes
    AnyElemApi

Concrete Value Members

  1. final def !=(arg0: Any): Boolean

    Permalink
    Definition Classes
    AnyRef → Any
  2. final def ##(): Int

    Permalink
    Definition Classes
    AnyRef → Any
  3. final def ==(arg0: Any): Boolean

    Permalink
    Definition Classes
    AnyRef → Any
  4. final def \(p: (ThisElem) ⇒ Boolean): IndexedSeq[ThisElem]

    Permalink

    Shorthand for filterChildElems(p).

    Shorthand for filterChildElems(p). Use this shorthand only if the predicate is a short expression.

    Definition Classes
    ElemLikeElemApi
  5. final def \\(p: (ThisElem) ⇒ Boolean): IndexedSeq[ThisElem]

    Permalink

    Shorthand for filterElemsOrSelf(p).

    Shorthand for filterElemsOrSelf(p). Use this shorthand only if the predicate is a short expression.

    Definition Classes
    ElemLikeElemApi
  6. final def \\!(p: (ThisElem) ⇒ Boolean): IndexedSeq[ThisElem]

    Permalink

    Shorthand for findTopmostElemsOrSelf(p).

    Shorthand for findTopmostElemsOrSelf(p). Use this shorthand only if the predicate is a short expression.

    Definition Classes
    ElemLikeElemApi
  7. final def asInstanceOf[T0]: T0

    Permalink
    Definition Classes
    Any
  8. def clone(): AnyRef

    Permalink
    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  9. final def eq(arg0: AnyRef): Boolean

    Permalink
    Definition Classes
    AnyRef
  10. def equals(arg0: Any): Boolean

    Permalink
    Definition Classes
    AnyRef → Any
  11. final def filterChildElems(p: (ThisElem) ⇒ Boolean): IndexedSeq[ThisElem]

    Permalink

    Returns the child elements obeying the given predicate.

    Returns the child elements obeying the given predicate. This method could be defined as:

    def filterChildElems(p: ThisElem => Boolean): immutable.IndexedSeq[ThisElem] =
      this.findAllChildElems.filter(p)
    Definition Classes
    ElemLikeElemApi
  12. final def filterElems(p: (ThisElem) ⇒ Boolean): IndexedSeq[ThisElem]

    Permalink

    Returns the descendant elements obeying the given predicate.

    Returns the descendant elements obeying the given predicate. This method could be defined as:

    this.findAllChildElems flatMap (_.filterElemsOrSelf(p))
    Definition Classes
    ElemLikeElemApi
  13. final def filterElemsOrSelf(p: (ThisElem) ⇒ Boolean): IndexedSeq[ThisElem]

    Permalink

    Returns the descendant-or-self elements obeying the given predicate.

    Returns the descendant-or-self elements obeying the given predicate. This method could be defined as:

    def filterElemsOrSelf(p: ThisElem => Boolean): immutable.IndexedSeq[ThisElem] =
      Vector(this).filter(p) ++ (this.findAllChildElems flatMap (_.filterElemsOrSelf(p)))

    It can be proven that the result is equivalent to findAllElemsOrSelf filter p.

    Definition Classes
    ElemLikeElemApi
  14. def finalize(): Unit

    Permalink
    Attributes
    protected[java.lang]
    Definition Classes
    AnyRef
    Annotations
    @throws( classOf[java.lang.Throwable] )
  15. final def findAllElems: IndexedSeq[ThisElem]

    Permalink

    Returns all descendant elements (not including this element).

    Returns all descendant elements (not including this element). This method could be defined as filterElems { e => true }. Equivalent to findAllElemsOrSelf.drop(1).

    Definition Classes
    ElemLikeElemApi
  16. final def findAllElemsOrSelf: IndexedSeq[ThisElem]

    Permalink

    Returns this element followed by all descendant elements (that is, the descendant-or-self elements).

    Returns this element followed by all descendant elements (that is, the descendant-or-self elements). This method could be defined as filterElemsOrSelf { e => true }.

    Definition Classes
    ElemLikeElemApi
  17. final def findChildElem(p: (ThisElem) ⇒ Boolean): Option[ThisElem]

    Permalink

    Returns the first found child element obeying the given predicate, if any, wrapped in an Option.

    Returns the first found child element obeying the given predicate, if any, wrapped in an Option. This method could be defined as filterChildElems(p).headOption.

    Definition Classes
    ElemLikeElemApi
  18. final def findElem(p: (ThisElem) ⇒ Boolean): Option[ThisElem]

    Permalink

    Returns the first found (topmost) descendant element obeying the given predicate, if any, wrapped in an Option.

    Returns the first found (topmost) descendant element obeying the given predicate, if any, wrapped in an Option. This method could be defined as filterElems(p).headOption.

    Definition Classes
    ElemLikeElemApi
  19. final def findElemOrSelf(p: (ThisElem) ⇒ Boolean): Option[ThisElem]

    Permalink

    Returns the first found (topmost) descendant-or-self element obeying the given predicate, if any, wrapped in an Option.

    Returns the first found (topmost) descendant-or-self element obeying the given predicate, if any, wrapped in an Option. This method could be defined as filterElemsOrSelf(p).headOption.

    Definition Classes
    ElemLikeElemApi
  20. final def findTopmostElems(p: (ThisElem) ⇒ Boolean): IndexedSeq[ThisElem]

    Permalink

    Returns the descendant elements obeying the given predicate that have no ancestor obeying the predicate.

    Returns the descendant elements obeying the given predicate that have no ancestor obeying the predicate. This method could be defined as:

    this.findAllChildElems flatMap (_.findTopmostElemsOrSelf(p))
    Definition Classes
    ElemLikeElemApi
  21. final def findTopmostElemsOrSelf(p: (ThisElem) ⇒ Boolean): IndexedSeq[ThisElem]

    Permalink

    Returns the descendant-or-self elements obeying the given predicate, such that no ancestor obeys the predicate.

    Returns the descendant-or-self elements obeying the given predicate, such that no ancestor obeys the predicate. This method could be defined as:

    def findTopmostElemsOrSelf(p: ThisElem => Boolean): immutable.IndexedSeq[ThisElem] =
      if (p(this)) Vector(this)
      else (this.findAllChildElems flatMap (_.findTopmostElemsOrSelf(p)))
    Definition Classes
    ElemLikeElemApi
  22. final def getChildElem(p: (ThisElem) ⇒ Boolean): ThisElem

    Permalink

    Returns the single child element obeying the given predicate, and throws an exception otherwise.

    Returns the single child element obeying the given predicate, and throws an exception otherwise. This method could be defined as findChildElem(p).get.

    Definition Classes
    ElemLikeElemApi
  23. final def getClass(): Class[_]

    Permalink
    Definition Classes
    AnyRef → Any
  24. def hashCode(): Int

    Permalink
    Definition Classes
    AnyRef → Any
  25. final def isInstanceOf[T0]: Boolean

    Permalink
    Definition Classes
    Any
  26. final def ne(arg0: AnyRef): Boolean

    Permalink
    Definition Classes
    AnyRef
  27. final def notify(): Unit

    Permalink
    Definition Classes
    AnyRef
  28. final def notifyAll(): Unit

    Permalink
    Definition Classes
    AnyRef
  29. final def synchronized[T0](arg0: ⇒ T0): T0

    Permalink
    Definition Classes
    AnyRef
  30. def toString(): String

    Permalink
    Definition Classes
    AnyRef → Any
  31. final def wait(): Unit

    Permalink
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  32. final def wait(arg0: Long, arg1: Int): Unit

    Permalink
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )
  33. final def wait(arg0: Long): Unit

    Permalink
    Definition Classes
    AnyRef
    Annotations
    @throws( ... )

Inherited from ElemApi

Inherited from AnyElemApi

Inherited from AnyRef

Inherited from Any

Ungrouped