• Top
    • Documentation
    • Books
    • Boolean-reasoning
    • Projects
    • Debugging
    • Std
    • Proof-automation
    • Macro-libraries
      • B*
      • Defunc
      • Fty
        • Deftagsum
        • Defprod
        • Defflexsum
          • Defbitstruct
          • Deflist
          • Defalist
          • Defbyte
          • Deffixequiv
          • Defresult
          • Deffixtype
          • Defoption
          • Fty-discipline
          • Fold
          • Fty-extensions
          • Defsubtype
          • Defset
          • Deftypes
          • Specific-types
          • Defflatsum
          • Deflist-of-len
          • Defbytelist
          • Fty::basetypes
          • Defomap
          • Defvisitors
          • Deffixtype-alias
          • Deffixequiv-sk
          • Defunit
          • Multicase
          • Deffixequiv-mutual
          • Fty::baselists
          • Def-enumcase
          • Defmap
        • Apt
        • Std/util
        • Defdata
        • Defrstobj
        • Seq
        • Match-tree
        • Defrstobj
        • With-supporters
        • Def-partial-measure
        • Template-subst
        • Soft
        • Defthm-domain
        • Event-macros
        • Def-universal-equiv
        • Def-saved-obligs
        • With-supporters-after
        • Definec
        • Sig
        • Outer-local
        • Data-structures
      • ACL2
      • Interfacing-tools
      • Hardware-verification
      • Software-verification
      • Math
      • Testing-utilities
    • Fty
    • Deftypes

    Defflexsum

    Define a (possibly recursive) sum of products type.

    Caveat

    Defflexsum is not very user-friendly or automatic; it is easy to create instances that fail in incomprehensible ways. It is used as a backend to define the deftagsum and defprod type generators, which are easier to use.

    Example

    This is essentially the same as the example in deftagsum. Logically, the way these types work are very similar; only the representation is different.

    (defflexsum arithterm
      (:num :cond (atom x)
            :fields ((val :type integerp :acc-body x))
            :ctor-body val)
      (:plus
       :cond (eq (car x) '+)
       :shape (and (true-listp x) (eql (len x) 3))
       :fields ((left :type arithterm :acc-body (cadr x))
                (right :type arithterm :acc-body (caddr x)))
       :ctor-body (list '+ left right))
      (:minus
       :shape (and (eq (car x) '-)
                   (true-listp x)
                   (eql (len x) 2))
       :fields ((arg :type arithterm :acc-body (cadr x)))
       :ctor-body (list '- arg)))
    
    (define arithterm-eval ((x arithterm-p))
      :returns (xval integerp :rule-classes :type-prescription)
      :measure (arithterm-count x)
      (case (arithterm-kind x)
        (:num (arithterm-num->val x))
        (:plus (+ (arithterm-eval (arithterm-plus->left x))
                  (arithterm-eval (arithterm-plus->right x))))
        (t (- (arithterm-eval (arithterm-minus->arg x)))))
      ///
      (deffixequiv arithterm-eval))
    
    (define arithterm-double ((x arithterm-p))
      :verify-guards nil ;; requires return type theorem first
      :returns (xx arithterm-p)
      :measure (arithterm-count x)
      (arithterm-case x
        :num (arithterm-num (* 2 x.val))
        :plus (arithterm-plus (arithterm-double x.left)
                              (arithterm-double x.right))
        :minus (arithterm-minus (arithterm-double x.arg)))
      ///
      (verify-guards arithterm-double)
    
      (deffixequiv arithterm-double)
    
      (local (include-book "arithmetic/top-with-meta" :dir :system))
    
      (defthm arithterm-double-correct
        (equal (arithterm-eval (arithterm-double x))
               (* 2 (arithterm-eval x)))
        :hints(("Goal" :in-theory (enable arithterm-eval)))))

    Note: when the constructors are actually defined, mbe is used to allow the function to logically apply fixing functions to its arguments without a performance penalty when running with guards checked.

    More on the above Caveat

    defflexsum is less automatic than most other type-defining utilities. It requires certain information to be provided by the user that must then be proved to be self-consistent. For example, the :ctor-body argument in a product spec determines how that product is constructed, and the :acc-body argument to a product field spec determines how that field is accessed. These could easily be inconsistent, or the :ctor-body could produce an object that is not recognized as that product. If either of these is the case, some proof within the defflexsum event will fail and it will be up to the user to figure out what that proof was and why it failed.

    Syntax and Options

    Defflexsum top-level arguments

    Defflexsum takes the following keyword arguments, in addition to a list of products, which are described further below. Default values for keyword arguments may be set by locally adding pairs to the fty::defflexsum-defaults table.

    • :pred, :fix, :equiv, :kind, :case, and :count override the default names for the various generated functions (and case macro). If any of these is not provided, a default name is used instead. If :kind nil is provided, then no -kind function is generated and instead the products are distinguished by their bare :cond fields. If :count nil is provided, then no count function is defined for this type.
    • :xvar sets the variable name used to represent the SUM object. By default, we look for a symbol whose name is "X" that occurs in the product declarations.
    • :measure provides the measure for the predicate, fixing function, and count function. It defaults to (acl2-count x), which is usually appropriate for stand-alone products, but sometimes a special measure must be used, especially when defflexsum is used inside a mutually-recursive deftypes form.
    • :prepwork is a list of events to be submitted at the beginning of the process; usually these are local lemmas needed for the various proofs.
    • :parents, :short, and :long provide xdoc for the type
    • :inline: sets default for inlining of products and determines whether the kind and fixing functions are inlined. This may be :all or a subset of (:kind :fix :acc :xtor), defaulting to (:kind :fix :acc).

    Products

    Each product starts with a keyword naming its kind; this is the symbol that the SUM kind function returns on an object of that product type. The rest of the form is a list of keyword arguments:

    • :cond describes how to tell whether an object is of this product type. To determine the kind of a SUM object, the SUM kind function checks each of the product conditions in turn, returning the name of the first matching condition. So the condition for a given product assumes the negations of the conditions of the previous listed products. The :cond field defaults to t, so typically it can be left off the last product type.
    • :shape adds well-formedness requirements for a product. One purpose these may serve is to make well-formed objects canonical: it must be a theorem that the fixing function applied to a well-formed object is the same object. So if a product is (e.g.) a tuple represented as a list, and the constructor creates it as a true-list, then there should be a requirement that the object be a true-list of the appropriate length; otherwise, an object that had a non-nil final cdr would be recognized as well-formed, but fix to a different value.
    • :fields list the fields of the product; these are described further below.
    • :ctor-body describes how to make a product object from the fields. This must be consistent with the field accessor bodies (described below) and with the :cond and :shape fields of this product and the previous ones (i.e., it can't produce something that could be mistaken for one of the previous products listed). The actual constructor function will have fixing functions added; these should not be present in the :ctor-body argument.
    • :type-name overrides the type-name, which by default is [SUMNAME]-[KIND], e.g. arithterm-plus from the example above. This is used as a base for generating the field accessor names.
    • :ctor-name overrides the name of the product constructor function, which by default is the type-name.
    • :inline, determining whether the constructor and accessors are inlined or not. This may be :all or a subset of (:xtor :acc). Defaults to (:acc) if not overridden.
    • :require adds a dependent type requirement; see the section on this feature in defprod.
    • :count-incr adds the given natural number to the count function for this product, if producing a count function. For backward compatibility this can also be T which is equivalent to 1.

    Product Fields

    Each product field is a name followed by a keyword list, in which the following keywords are allowed:

    • :acc-body must be present: a term showing how to fetch the field from the object.
    • :acc-name overrides the accessor name
    • :type, the type (fixtype name or predicate) of the field; may be empty for an untyped field
    • :default, the default value of the field in the constructor macro
    • :doc will eventually generate xdoc, but is currently ignored
    • :rule-classes, the rule classes for the return type of the accessor function. This is :rewrite by default; you may wish to change it to :type-prescription when the type is something basic like integerp.