Hi again,

> Indeed, I think a general point can be made here, and not just about code 
> generation. In the last couple of years, we've run into situations in 
> Quickcheck, Nitpick, the code generator, and perhaps more, where we felt the 
> need for sets as different from functions. However, there is no guarantee 
> that all the people who talked about "sets vs. functions" meant, or needed, 
> the same thing!

> I want to emphasize that the limitation of the code generator mentioned here 
> not only applies to sets-as-predicates but also to maps-as-functions and 
> other abstract types that are often specified in terms of functions (finite 
> maps, almost constant maps, etc.). Thus, having good old 'a set back is does 
> not fully solve this problem as a whole, just one (important) instance of it.
> My view on this whole topic is outlined in the report I recently sent around 
> (http://www4.in.tum.de/~krauss/papers/refinement.pdf, not updated since last 
> time). In the long run, I would prefer to see flexible transport machinery to 
> move stuff between isomorphic types. 

It is important to note that the motivation to have set back again as a
type constructor is not code generation itself, but fundamental flaws in
the current situation.  Indeed I fully agree that the driving force
behind the development should always be specification and proof, with
derived application at a secondary level.  Also in our case, code
generation is a side effect, although many technical points of this
discussion here are related to code generation, I guess mainly since a
couple of people involved in this story actually want to see progress here.

What is a different – and in itself valuable – thing is the matter of
»transport« mentioned by Alex, something which also I am eager to see
one day.

But now back to sets.  In answer to some statements made, I have
collected evidence why the current situation indeed is problematic:

> Like any definition of a primitive operator, mem_def is not really intended
> to be unfolded by the user, so don't be surprised if your proof is a mess
> after unfolding this definition. You wouldn't unfold the definitions of
> logical operators like "conj" or "disj" either, except for proving
> introduction- or elimination rules for these operators.

> Can this claim be made more concrete (or even quantitative)? Or is it merely 
> a conjecture?
> From what Jasmin wrote, this does not seem to hold for sledgehammer, and 
> simp/auto/blast should not run into the "wrong world" when configured 
> properly.
> I understand that this is true for naive users... but automated tools??? 

> So your point is actually a different one from Florian's: Users unfold 
> mem_def in order to gain automation, which may work in that moment, but is 
> usually a bad idea later on. I understand this point.
> (Aside: It would be interesting to explore why users actually do this. Is 
> there something missing in the automation for sets that works better for 
> predicates?)
> My understanding of Florian's point above was that sets-as-predicates 
> actually hinder automation by enlarging the search space. But it seems to me 
> that this is only a theoretical consideration, and that we do not actually 
> observe this effect in practice.

A typical example for what I was alluding to can be found at:

http://www4.in.tum.de/~haftmann/cgi-bin/hgweb.cgi/isabelle_set/rev/6e3e9c6c5062#l1.37

Observe the following proof:

lemma part_equivpI:
  "(\<exists>x. R x x) \<Longrightarrow> symp R \<Longrightarrow> transp
R \<Longrightarrow> part_equivp R"
  apply (simp add: part_equivp_def)
  -- {* (I) "sane" proof state *}
  apply auto
  -- {* (II) "insane" proof state *}
  apply (auto elim: sympE transpE)
  -- {* (III) does not even terminate; adding "simp add: mem_def"
succeeds! *}

The second »auto« imposes a »\<in>« on predicates, although mem_def,
Collect_def or similar do not appear in the proof text (push to the
»wrong world«).  Worse, the final auto does not even terminate, this is
what I had in mind when referring to an enlarged search space.  Here,
the way the system is build forces me to use »simp add: mem_def«.

(Well, joking you could also argue that there are three thing which
LISP, Java and Isabelle have in common: strange syntax, not writable
without special tool support, choking as soon as higher-order things
come in.)

>> * The logical identification of sets and predicates prevents some
>> > reasonable simp rules on predicates: e.g. you cannot have (A |inf| B) x
>> > = A x & B x
>> > because then expressions like "set xs |inter| set ys" behave strangely
>> > if the are eta-expanded (e.g. due to induction).
> This sounds more like a problem with the underlying infrastructure that should
> be fixed, rather than working around the problem by introducing new type 
> constructors.
> Can you give an example of a proof by induction, where eager eta expansion 
> leads
> to an unnecessarily complicated proof?

theory Scratch
imports Main "~~/src/HOL/Library/Lattice_Syntax"
begin

declare List.set_append [simp del]

thm sup_apply
declare sup_apply [simp]

lemma set_append: "set (xs @ ys) = (set xs \<union> set ys)"
  apply (induct xs)
  apply simp_all
  apply auto -- {* non-termination! *}

The declaration of »sup_apply« as »[simp]« smashes, seemingly natural, a
proof of a fundamental property on sets.  The bitter irony is that what
once was envisaged as a follow-up to the abolishment of the set type
constructor, the unifying of set and lattice operations, has been
complicated by the abolishment rather than facilitated.  Also observe
the annoying non-termination of »auto« again.

> We use map functions to lift coercions between base types to coercions 
> between type constructors. E.g. "nat list" is subtype of "int list" with the 
> coercion "map int", provided that "int" is declared as a coercion and "map" 
> as map function for "'a list". This is a typical example of a covariant map 
> function (i.e. the lifting does not invert the direction of the subtype 
> relation).
> On the other hand, the generic map function for "'a => 'b" ("% f g h x. g (h 
> (f x)) :: ('a => 'b) => ('c => 'd) => ('b => 'c) => ('a => 'd)") is 
> contravariant in the first argument. Concretely that means that "int => bool" 
> is a subtype of "nat => bool", provided the above map function and the 
> coercion "int". In contrast, the corresponding map function for sets as 
> predicates ("image :: ('a => 'b) => ('a => bool) => ('b => bool)") is, as one 
> would expect, covariant in the first argument.
> The current implementation of subtyping allows to declare only one map 
> function for a type constructor. Thus, we can have either the contravariant 
> or the covariant map for the function type, but not both at the same time. 
> The introduction of set as an own datatype would resolve this issue. 

This is an interesting oberservation which also applies to the
instantiation of sets for type classes: in 'a => bool, you have to think
about 'a in a contravariant fashion, whereas in 'a set it is covariant.

Rgds.,
        Florian

-- 

Home:
http://www.in.tum.de/~haftmann

PGP available:
http://home.informatik.tu-muenchen.de/haftmann/pgp/florian_haftmann_at_informatik_tu_muenchen_de

Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
isabelle-dev mailing list
isabelle-...@in.tum.de
https://mailmanbroy.informatik.tu-muenchen.de/mailman/listinfo/isabelle-dev

Reply via email to