Namespace funnyqt.bidi

Bidirectional transformations (BX).

Other Namespaces

Co-Evolution transformations on TGraphs.
Printing/persisting and reading/loading query results and transformation traces as EDN.
Core functions for accessing and manipulating EMF models.
Specify models extensionally.
Generic protocols extended upon many different types, and generic functions.
In-place transformation stuff.
Rule-base out-place transformations similar to ATL or QVTo.
Graph Pattern Matching on arbitrary models.
Polymorphic functions dispatching on types of model elements.
Generic query functions like regular path expressions & quantified expressions.
EMF-specific query functions
TG-specific query functions
Relational Model Querying.
Core functions for accessing and manipulating TGraphs.
Generic utility functions, e.g., for signaling errors, debugging, and profiling,
Model visualization functions.
Convert XML to DOM-like TGraphs.
Index Page
Alphabetic Var Index

Public Vars

Usage Documentation

Bidirectional transformations (BX).
Back to top

Details of Public Vars

Dynamic Var: *t-relation-bindings*

  Only for internal use.
  A map with the following structure:

    {:related   {t-relation1 bindings, t-relation2 bindings, ...}
     :unrelated {t-relation1 bindings, t-relation2 bindings, ...}}

  where t-relationN is a keyword denoting a t-relation and bindings is:

    #{{:?lsym1 lval1, :?rsym1 rval2, ...}

  Access this information with the relation `relateo`.
Back to top View Source

Dynamic Var: *target-direction*

  Only for internal use.
  The current direction of the transformation execution.
  Either :left or :right.
Back to top View Source

Dynamic Var: *target-model*

  Only for internal use.
  The current target model of the transformation execution.
  Depends on `*target-direction*, so it's either the :right or the :left modes.
Back to top View Source

Macro: deftransformation


   [left right & args]


  Creates a new bidirectional transformation with the given `name` on the
  models `left` and `right` with possibly additional `args`.  The
  transformation may extend other transformation using an `extends-clause` (see
  Transformation Inheritance below).  If the transformation should delete
  target elements that aren't required, add a `target-deletion-clause` (see
  Target Deletion Clause below), and finally there can be synthetic ids (see
  Synthetic Identities below).

  The signature of the defined transformation (a plain function) is

    (transformation-name left-model right-model direction & args)

  `direction` is the direction in which to execute the transformation,
  either :right or :left for enforcement mode, or :right-checkonly
  or :left-checkonly for checkonly mode.

  Defining Transformation Relations

  In the transformation specification, `t-relations` is a sequence of
  transformation relation definitions.  Every such t-relation has the following

      :left [...]
      :right [...])

  The values of :left and :right are vectors of goals.  Logical variables with
  the same name in :left and :right define the correspondence between the left
  and right elements.  Logical variables have to start with a question mark.
  Those are used for elements and attribute values, e.g., (+name left ?left-el
  ?foo) in :left and (+id right ?right-el ?foo) in :right specifies that
  ?left-el's name and ?right-el's id values have to be equal.

  The semantics when transforming from left to right are: For every set of
  elements in the `left` model for which all :left goals succeed, there must be
  at least one set of elements in the `right` model for which all :right goals

  ^:top metadata can be added to t-relation names.  Such top-level t-relations
  are enforced or checked automatically by the transformation in their
  declaration order.  Non-top-level t-relations have to be called explicitly
  from another t-relation using :where clauses (see Postconditions below).

  Target Deletion Clause

  By default, FunnyQT won't delete elements in the target model which are not
  required by some source model element and some t-relation, i.e., unmatched
  target model elements.  That is, after the transformation has been enforced,
  for any element (considered by the transformation's t-relations) in the
  source model, there exists some element in the target model.  However, there
  still might be additional elements in the target model without some source
  model element correspondence.

  In general, this is good.  Given two models, if you enforce a transformation
  first in one and then the other direction, then both models will be in sync
  with missing elements created in either one.  This would not be possible if
  enforcing a transformation in a given direction would automatically delete
  unmatched target model elements.

  Anyway, since that's usefull nevertheless in some situations, you can enable
  automatic deleteion of unmatched target model elements with a target deletion
  clause of the form:

    :delete-unmatched-target-elements true

  Target deletion only happens in enforcement mode, i.e., not in the case of
  the direction being :right-checkonly or :left-checkonly.

  Return Value and Traceability

  The return value of a bidirectional transformation is its complete
  traceability information represented as a map with the following structure:

    {:related   {:t-relation1 bindings, :t-relation2 bindings, ...}
     :unrelated {:t-relation1 bindings, :t-relation2 bindings, ...}}

  The inner map being the value of the :related key contains the actual
  traceability information, i.e., it is a map from the transformation's
  t-relations (as keywords) to the corresponding bindings in the left and right
  models.  Concretely, bindings is a set of maps of the form {:llvar1?
  lval1, :rlvar1? rval1, ...} where :llvar1 and :rlvar1 correspond to some
  logic variables in the :left or :right goals of the corresponding t-relation,
  and lval1 and rval1 are their values.

  This information can be accessed during the transformation.  E.g., a goal

    (foo2bar :?foo ?foo :?bar ?bar)

  in a t-relation's :left, :right, or :when clause or in a plain relation
  unifies ?foo and ?bar with all possible bindings where ?foo and ?bar have
  been related previously by the foo2bar t-relation.  This is only a shorthand
  for the low-level traceability relation `relateo`, i.e., the above is
  equivalent to

    (relateo :foo2bar :?foo ?foo :?bar ?bar)

  The map being the value of the :unrelated key contains the bindings for which
  no corresponding target model match exists.  This map is only populated in
  checkonly mode since in enforcement mode, target elements are created or
  modified in order to guarantee the existence of a match.


  A relation spec may also contain a :when precondition:

      :left  [...]
      :right [...]
      :when  [...])

  It is also a vector of goals.  Usually, the goals are used to retrieve and
  bind elements created by previous t-relations using traceability goals such
  as (other-t-rel :?foo ?f :?bar ?b) which use `relateo` internally.  The :left
  to :right semantics are: For all solutions satisfying the conjunction
  of :left and :when goals, a corresponding solution of the :right goals has to
  exist, i.e., the :when goals are simply added to the clause of the current
  source model.

  Target Clauses

  T-relations may have a :target clause:

      :left   [...]
      :right  [...]
      :target [...])

  As said above, the goals of :when clauses are simply added to the current
  source clause, e.g., :left when transforming into the direction of the right
  model.  The :target clause is essentially the inverse, i.e., its goals are
  added to the current target clause, e.g., to the :right clause when
  transforming into the :right direction.

  The use-case this clause serves is the handling of non-bijective mappings in
  attribute value goals which may change the value (i.e., the generated attr*
  relations).  For example, in a hypothetical class diagram to database schema
  transformation, the attribute types INT and LONG both correspond to the
  database type INTEGER, and the database types VARCHAR and TEXT both
  correspond to the class diagram type STRING.  Thus, there are choices how to
  map STRING attributes and INTEGER columns.  In such cases, the mapping can be
  defined by a plain relation (see below)

    (cd-type2db-type [cdt dbt]
        [(all (== cdt "LONG")   (== dbt "INTEGER"))]
        [(all (== cdt "INT")    (== dbt "INTEGER"))]
        [(all (== cdt "STRING") (== dbt "TEXT"))]
        [(all (== cdt "STRING") (== dbt "VARCHAR"))]))

  where the order of clauses defines that we prefer the larger types LONG and
  TEXT here.  This should be used in the :target clause like so.

       :left   [...
                (cd/type* cd ?attr ?atype)]
       :right  [...
                (db/type* db ?col ?ctype)]
       :target [(cd-type2db-type ?atype ?ctype)])

  The problem the :target clause solves is the following: if it were placed in
  the :when clause (or either :left or :right), then (depending on direction)
  either cdt or dbt would be fresh.  Thus, when synchronizing between existing
  models, always the first of the two conda-clauses which could match will
  match because unifying a fresh variable with some literal always works.  The
  effect is that, e.g., all INT attributes will be changed to LONG.

  If the goal is in the :target clause, then both cdt and dbt are ground if the
  corresponding attribute and column have their type set already.  Thus, the
  INT/INTEGER and STRING/VARCHAR clauses will match for the respective
  attribute types, and the corresponding attribute and column types won't be


  A relation spec may also contain a :where postcondition:

      :left  [...]
      :right [...]
      :where [...])

  It is a vector of relation-calls (not goals!).  The semantics is: For any set
  of elements in the `left` model for which a corresponding set of elements in
  the `right` model has been enforced, the relations in :where also need to be
  enforced afterwards.

  Note that the :where relations are not called until all enforcements of
  foo2bar have been applied, e.g., the evaluation is breadth-first, not
  depth-first.  That is, in the above definition, the relations in the :where
  clause may assume that foo2bar already holds for all matching elements.

  Both :left and :right are optional.  If omitted, they are equivalent to
  `:left [ccl/succeed]` and `:right [ccl/succeed]`, i.e., they simply succeed.
  Clearly, omitting those clauses makes only sense if there're :when and :where
  clauses.  For example, there might be a final ^:top relation that matches
  elements only in terms of `:when [(relateo ...)...]` and then invokes other
  relations using its :where clause.

  Transformation Relation Inheritance

  A t-relation spec may also contain an :extends clause:

      :extends [(a2b :?a ?foo :?b ?bar)]
      :left [(rel-with l ?foo) ...]
      :right [(rel-with r ?bar) ...])
    (^:abstract a2b
      :left [(rel-with l ?a) ...]
      :right [(rel-with r ?b) ...])

  The foo2bar relation includes all :left/:right/:when/:where clauses of the
  a2b rule, where ?a is substituted by ?foo, and ?b is substituted by ?bar.
  Usually, this feature is useful for refactoring relations on common

  A t-relation may extend multiple other relations, and extension works
  transitively.  The mustn't be extension cycles.

  A t-relation that's not called explicitly in a :where clause and is only
  extended by others should be declared ^:abstract like a2b above.  Then, no
  code is generated for it.

  Hint: The clauses of the relation that is extended may have
  `clojure.core.logic/succeed` in their :left/:right/:when clauses.  This is a
  no-op but gives a hint where the corresponding clauses of the extending
  t-relation should be filled in.  If no succeed goal is in the clauses, the
  extending t-relation's clauses are simply appended.

  Debugging Transformation Relations

  For debugging purposes, relations may also contain the following clauses:

    :debug-entry    (println ...) ;; code being executed when a relation is invoked
    :debug-src      (println ...) ;; code being executed when the source domain
                                  ;; and :when clause has been matched
    :debug-trg      (println ...) ;; code being executed when the target domain has
                                  ;; been matched
    :debug-enforced (println ...) ;; code being executed when the target domain has
                                  ;; been enforced

  The value may be arbitrary forms that are inserted at the corresponding

  Plain Relations

  A bidirectional transformation definition may also define plain, local
  relations which can then be used inside the :left, :right, and :when clauses
  of t-relations.  The syntax is that of local functions as per `letfn`.

    (my-relation [arg1 arg2 ...]
        (goal1 arg1)
        (goal2 arg2 arg1 ...)))

  Of course, plain relation may also be specified outside of the

  Transformation Inheritance

  A transformation may extend other transformations using an :extends clause
  following the argument vector.  Its value is a symbol or a vector of symbols
  denoting other bidirectional transformations.

    (deftransformation a2b [l r]
      :extends a2b-base

  The transformation a2b extends a2b-base here.  This means that a2b contains
  its own relations plus the ones defined in a2b-base.  If a2b defines a
  relation that's already defined by a2b-base, then a2b's version overrides the
  version from a2b-base.  Likewise, a2b calls all top-level relations defined
  by itself and a2b-base.

  a2b is allowed to use different names for the left and right model.  However,
  when overriding a relation, the logical variables (those beginning with
  question mark) should have the same names as in the overridden relation, at
  least if some other inherited relation calls the overridden relation
  in :where or accesses its trace in terms of `relateo` in :when.  That's
  because the logical variables act as named parameters and are also
  represented in the transformation trace.

  Synthetic Identities

  FunnyQT bidirectional transformations have forall-there-exists semantics.
  This also means that if in one model there are two completely equal elements
  (same type, attribute values, references), FunnyQT won't see a need to create
  two different elements in the other model.  If you wanted to change that,
  you'd need to add some kind of identity attribute to your element's metamodel
  class and assign unique values to your model elements.  Of course, extending
  a metamodel just for the sake of a transformation tool is cumbersome, so
  FunnyQT bidirectional transformations provide synthetic ids for you.

  Every bidirectional transformation implicitly declares a plain relation `id`.
  Goals have the form (id ?elem ?id) where the element ?elem has the given ?id.
  The ids can be defined before the start of the transformation using
  a :id-init-fn which is a function that receives the same arguments as the
  transformation itself (the left model, the right model, the direction, and
  possibly additional arguments) and returns a map from elements to their
  id (which may be chosen freely).  There is a  default :id-init-fn
  (`number-all-source-model-elements`) which simply numbers all elements in the
  current source model and uses that number as id.

  Here is an example: If we don't assume that the name of a Family in
  combination with the first name of a FamilyMember, and the name of a Person
  uniquely determine an element, then we need to use synthetic ids.

  (bx/deftransformation families2persons [f p]
    :id-init-fn number-all-source-model-elements
    (^:abstract member2person
     :left  [(f/->families f ?fam-reg ?family)
             (f/Family f ?family)
             (f/name f ?family ?last-name)
             (f/FamilyMember f ?member)
             (f/name f ?member ?first-name)
             (id ?member ?id)]
     :right [(p/->persons p ?per-reg ?person)
             (p/Person p ?person)
             (rel/stro ?last-name ", " ?first-name ?n)
             (p/name p ?person ?n)
             (id ?person ?id)])
Back to top View Source

Function: existing-elemento?


  (existing-elemento? elem)


  Succeeds iff `elem` is a ground and existing model element that has been
  matched by the transformation.  Throws an exception if `elem` is ground but
  no model element.
Back to top View Source

Function: new-elemento?


  (new-elemento? elem)


  Succeeds iff `elem` is a ground model element to be created by the transformation.
  Throws an exception if `elem` is ground but no model element.
Back to top View Source

Function: number-all-source-model-elements


  (number-all-source-model-elements left right dir & args)


  A simple :id-init-fn that simply numbers all source model elements so that
  every source element has a unique id (this number).
Back to top View Source

Function: relateo


  (relateo t-relation & keyvals)


  A relation that succeeds if there's a correspondence between `keyvals` in
  `t-relation` (given as keyword).  `keyvals` is a sequence of keywords with
  values that relate elements from the left and right domains of `t-relation`.


    (relateo :class2table :?class ?subclass :?table ?subtable)
Back to top View Source

Function: target-directiono


  (target-directiono dir)


  A relation where the transformation direction is `dir`.
  Can be used for conditionalizing non-bijective transformations, e.g., when
  one wants some t-relation to be only enforced in one direction.  Then, just
  put (target-directiono :left) in a :when clause.

  target-directiono only unifies with :left or :right, i.e., even if the target
  direction argument of the transformation was :right-checkonly, the actual
  direction considered by target-directiono is :right.
Back to top View Source

Function: unseto?


  (unseto? f elem ref refed-elem)


  Succeeds if `elem`s reference `ref` is unset or is set to `refed-elem`.
  All arguments must be ground.  `ref` may be a keyword or top-level reference
  relation.  Non-relational.

  Useful in conda-goals like these:

  ;; Prefer adding to the father role.  If that's already set, add to the sons
  ;; role.
     (unseto? family f/->father member)
     (f/->father f family member))]
   [(f/->sons f family member)])
Back to top View Source