The Isabelle reference manual describes to ways to perform type-based overloading of constants: "Adhoc overloading of constants" in section 11.3, and "Overloaded constant definitions" in section 5.9.
It seems that 5.9 overloading requires all type parameters to be known before it decides on an overloaded constant, whereas 11.3 (adhoc) overloading decides on an overloaded constant if there is only one matching:
consts
c1 :: "'t ⇒ 'a set"
c2 :: "'t ⇒ 'a set"
definition f1 :: ‹'a list ⇒ 'a set› where
‹f1 s ≡ set s›
adhoc_overloading
c1 f1
overloading
f2 ≡ ‹c2 :: 'a list ⇒ 'a set›
begin
definition ‹f2 w ≡ set w›
end
context
fixes s :: ‹int list›
begin
term ‹c1 s› (* c1 s :: int set *)
term ‹c2 s› (* c2 s :: 'a set *)
end
What's the difference between the two? When would I use one over the other?
Overloading is a core feature of Isabelle's logic. It allows you to declare a single constant with a "broad" type that can be defined on specific types. There's rarely a need for users to do that manually. It is the underlying mechanism used to implement type classes. For example, if you define a type class as follows:
class empty =
fixes empty :: 'a
assumes (* ... *)
Then, the class command will declare the constant empty of type 'a', and subsequent instantiations merely provide a definition of empty for specific types, like nat or list.
Long story short: overloading is – for most purposes – an implementation detail that is managed by higher-level commands. Occasionally, the need for some manual tweaking arises, e.g. when you have to define a type that depends on class constraints.
Ad-hoc overloading is, in my opinion, a misleading name. As far as I understand, it stems from Haskell (see this paper from Wadler and Blott). There, they use it to describe precisely the type class mechanism that in Isabelle would be coined as just "overloading". In Isabelle, ad-hoc overloading means something entirely different. The idea is that you can use it to define abstract syntax (like do-notation for monads) that can't accurately be capture by Isabelle's ML-style simple type system. As in overloading, you'd define a constant with a "broad" type. But that constant never receives any definitions! Instead, you define new constants with more specific types. When Isabelle's term parser encounters the use of the abstract constant, it will try to replace it with a concrete constant.
For example: you can use do-notation with option, list, and a few other types. If you write something like:
do { x <- foo; bar }
Then Isabelle sees:
Monad_Syntax.bind foo (%x. bar)
In a second step, depending on the type of foo, it will translate it to one of these possible terms:
Option.bind foo (%x. bar)
List.bind foo (%x. bar)
(* ... more possibilities ...*)
Again, users probably don't need to deal with this concept explicitly. If you pull in Monad_Syntax from the library, you'll get one application of ad-hoc overloading readily configured.
Long story short: ad-hoc overloading is a mechanism for enabling "fancy" syntax in Isabelle. Newbies may get confused by it because error messages tend to be hard to understand if there's something wrong in the internal translation.
Trying to create my first Coq definitions after doing many tutorials. Wondering how to define something simple like an alphabet, if the definition is:
Σ is an alphabet iff it's a finite nonempty set of symbols.
Got this much:
Require Import Coq.Lists.ListSet.
Definition alphabet := set.
But how do you specify the "must be a finite, non-empty set" part?
Since you choose your alphabet to be set, it is by definition finite, since set is defined as an instance of list, and inductive types are always finite.
The ListSet library you are using defines emptyset so your first option would be to state that
Definition not_empty (a: alphabet) : Prop := a <> empty_set.
Or you could rely on the fact that your set is list and pattern match on the expression:
Definition not_empty (a: alphabet) : bool := match a with
| nil => false
| _ => true
end.
(You can also define the latter in Prop instead of bool by using False and True.)
EDIT: Some clarification asked by Arthur (Simplification here, grab a real text-book about inductive types if you want a more precise explanation):
An inductive type can be inhabited by:
a finite number of elements (for example, bool),
an infinite number of elements (for example, nat)
no element at all (for example False).
However, any element of an inductive type is by construction finite. For example you can write any natural number by composing a finite number of time the constructor S but you have to use O at some point and ''stop'' the construction of your term. Same goes for lists: you can build an arbitrary long list, but its length will always be finite.
I want to abbreviate create a synonym for a type class name. Here's how I'm doing it now:
class fooC = linordered_idom
instance int :: fooC
proof qed
definition foof :: "'a::fooC ⇒ 'a" where
"foof x = x"
term "foof (x::int)"
value "foof (x::int)"
This works fine if there's not a better way to do it. The disadvantage is that I have to instantiate int, and the class command takes time to implement itself.
Update 140314
This update is to clarify for Makarius what it is I want, to explain my purpose in wanting it, and give a list of commands that I'm familiar with for creating notation, abbreviations, and synonyms, but commands which I couldn't get to work for what I want.
My initial choice of "abbreviation" rather than "synonym"
I guess "synonym" would have been a better word, but I chose "abbreviation" because it describes what I want, which is to be able to create a shorter name for for a type class, like renaming linordered_semidom to losdC. Though Isar abbreviation has some of the attributes of definition, it also just defines syntax. So, because "abbreviate" describes what I want, and abbreviation just defines syntax, I chose "abbreviation" instead of "synonym" or "alias".
Synonym/alias, Isar commands I couldn't get to work for that
"Alias" would describe what I want. As to the sentence "If you just want to save typing in the editor, you could use some abbreviations there," here are the commands I've experimented with to try and rename linordered_idom, but I couldn't get them to work for me:
type_notation
type_synonym
notation
abbreviation
syntax
Rather than explain what I've tried, and try to remember what I tried, I just list them. I did searches on "class" and only found the Isar commands class and classes. I thought maybe locale commands might be applicable, but I didn't find anything.
What I want is simple, like how type_synonym is used to define synonyms for types.
The purpose
There is my general desire to shorten type class names such as linordered_idom, because eventually, I plan on using the algebra type classes extensively.
However, there is a second reason, and that is to rename something like linordered_semidom to be part of a naming scheme of three types.
For any algebraic type class, such as linordered_semidom, I can use that type class, along with quotient_type, to create what I'll call a number system, such as how nat is used to define int.
Using Int.thy as a template, I did that with linordered_semidom, and then instantiated it as comm_ring_1, which is as far as I have time to go these days.
Additionally, with typedef, for any algebraic type class which has the dependencies of zero and one (and others such as ord), I can define a type of all elements greater than or equal to zero, and another one for all elements greater than zero. I did that for linordered_idom, but then I figured out that I actually needed to go the quotient_type route, to get things that model rat.
That's the long explanation. Eventually, I'll start working with numerous algebraic type classes, and from one type class, I'll get two more. If I do that for 20 type classes, and also use them, then long, descriptive names don't work, and renaming type classes will help me in knowing what type classes go together.
Here would be the scheme for linordered_semidom, where I don't know how this will actually work out, until I'm able to try it all:
linordered_semidom is the base class. I rename it to losdC. It's the numbers greater than or equal to zero for these three types.
losdQ is defined from losdC using quotient_type. It gives me the negative numbers, and the ability to coerce losdC to losdQ.
losd1 is defined using typedef, and is the numbers greater than zero.
I need a consistent naming scheme, to keep it all straight: losdC, losdQ, and losd1.
Finally, eventually even 4 types instead of 3 types
I haven't completely worked and thought things out (I'm not even close), but analogously, it's all related to implementing, for algebra type classes, the basic relationship between nat, int, and rat, where real might eventually come into play. Additionally, it's about getting a type, from these types, of the non-negative or positive members, if those don't come by default.
There is nat used for int, and int used for rat.
With nat used for int, we get the non-negative integers by default, which is nat.
With int used for rat, we don't get the non-negative members of rat, we get fractions. (Again, I'm talking about a type of non-negatives and positives, not a set of non-negatives and positives.)
So, if I use linordered_idom and quotient_type to define fractions, then I have to use typedef twice to get the non-negative and positive members of those fractions, which means I would have 4 types to keep track of, liodC, liodQ, liod0, and liod1.
If there's a simple solution to renaming type classes, then I've unnecessarily said about 600 words.
A definition is not an abbreviation, it introduces a separate term that is logical equal. That works for term constants.
A type class is semantically a predicate over types, and thus connected to some predicate (term constant), but in practice you rarely access that.
So what exactly means to "abbreviate a type class"?
For example, you might want to manipulate the class name space to get an alias for it, which is in principle possible. But what is the purpose?
If you just want to save typing in the editor, you could use some abbreviations there.
Another possibility, within the formal system, is to introduce genuine aliases in the name space. Isabelle provides some facilities for that, which are not very much advertized, because there is a real danger of obscuring libraries and preventing anyone else from understanding them, if names are changed too much.
This is how it works, using some friendly Isabelle/ML within the theory source:
class foobar = ord + fixes foobar :: 'a
setup {* Sign.class_alias #{binding f} #{class foobar} *}
typ "'a::f"
instantiation nat :: f
begin
definition foobar_nat :: nat where "foobar_nat = 0"
instance ..
end
Note that Sign.class_alias only refers to the type class name space in the narrow sense. A class is many things at the same time: locale, const (the prodicate), type class. You can see this in the following examples where the class is used as "target" for local definitions and theorems:
definition (in foobar) "fuzz = foobar"
theorem (in foobar) "fuzz = foobar" by (simp add: fuzz_def)
Technically, the locale name space used above could support aliases as well, but this is not done. Only basic Sign.class_alias, Sign.type_alias, Sign.const_alias are exposed for unusual situations, to address problems with legacy libraries.
I want to find theorems. I have read the section on find_theorems in the Isabelle/Isar reference manual:
find_theorems criteria
Retrieves facts from the theory or proof context matching all of given search
criteria. The criterion name: p selects all theorems whose fully qualified
name matches pattern p, which may contain "*" wildcards. The criteria intro,
elim, and dest select theorems that match the current goal as introduction,
elimination or destruction rules, respectively. The criterion solves returns
all rules that would directly solve the current goal. The criterion simp: t
selects all rewrite rules whose left-hand side matches the given term. The
criterion term t selects all theorems that contain the pattern t -- as usual,
patterns may contain occurrences of the dummy "_" , schematic variables, and
type constraints.
Criteria can be preceded by "-" to select theorems that do not match. Note
that giving the empty list of criteria yields all currently known facts. An
optional limit for the number of printed facts may be given; the default is 40.
By default, duplicates are removed from the search result. Use with_dups to
display duplicates.
As far as I understand, find_theorems is used in the find window of Isabelle/jEdit. The above does not help me finding relevant theorems for the following situation (Lambda is a theory of the Nominal Isabelle extension. The tarball is here):
theory First
imports Lambda
begin
theorem "Lam [x].(Lam [y].(App (Var x)(Var y))) = Lam [y].(Lam [x].(App (Var y)(Var x)))"
When I try the search expression Lam Isabelle/jedit says
Inner syntax error: unexpected end of input
Failed to parse term
How can I make it look for all the theorems that contain the constant Lam?
Since Lam like the ordinary lambda (%) is not a term on its own, you should add the remaining parts to get a proper term, which may contain wildcards. In your example, I would perform
find_theorems "Lam [_]. _"
which gives lots of answers.
Typically this happens whenever special syntax was defined for some constant. But there is (almost) always an underlying ("raw") constant. To find out which constant provides the Lam [_]. _ syntax. You can Ctrl-click Lam (inside a proper term) within Isabelle/jEdit. This will jump to the definition of the underlying constant.
For Lam there is the additional complication that the binder syntax uses exactly the same string as the underlying constant, namely Lam, as can be seen at the place of definition:
nominal_datatype lam =
Var "name"
| App "lam" "lam"
| Lam x::"name" l::"lam" binds x in l ("Lam [_]. _" [100, 100] 100)
In such cases you can use the long name of the constant by prefixing it with the theory name, i.e., Lambda.Lam.
Note: The same works for binders like ALL x. P x (with underlying constant All), but not for the built-in %x. x.
I've found a great example of type checking in LispWorks Hyper Spec, but the "type specifier" link leads to a mere glossary not the denotation, and I got a little confused with the syntax.
In (check-type n (integer 0 *) "a positive integer") what does (integer 0 *) mean? I assume it means inclusive range from 0 to infinity, but is this so?
Yes you can use type specifiers in common lisp, they can be very powerful if your compiler chooses to use them. While you may find uses for check-type, the most common kinds of type specifications come in the form of declarations.
The declare expression is not only used for types however it has a number of declaration identifiers and common lisp implementations are actually free to add their own.
The bit you are interested in though is 'types' and more specifically than that 'Type Specifiers'. That page will give you the low-down on a variety of ways to specify types, including the way you mentioned in your question.
Again be aware that your implementation doesn’t have to use the declarations it could just ignore them! Here is some more info on that.
And for some example code, here is the example that got me understanding the basics of how this works. Here and more here.
From the 4.2.3 Type Specifiers:
If a type specifier is a list, the car of the list is a symbol, and
the rest of the list is subsidiary type information. Such a type
specifier is called a compound type specifier. Except as explicitly
stated otherwise, the subsidiary items can be unspecified. The
unspecified subsidiary items are indicated by writing *. For example,
to completely specify a vector, the type of the elements and the
length of the vector must be present.
(vector double-float 100)
The following leaves the length unspecified:
(vector double-float *)
The following leaves the element type unspecified:
(vector * 100)