Syntactic abbreviation for types with in Isabelle/HOL? - isabelle

Is there a way to define syntactic replacements for types in Isabelle/HOL?
I want to do something like this:
syntax my_short_list :: "type" ("my-list")
translations "my_short_list" ⇌ (type) "'a list" ― ‹Could not find syntax to express this ...›
locale foo =
fixes blub :: "my-list ⇒ my-list"
And want this to be interpreted like this:
locale foo =
fixes blub :: "'a list ⇒ 'a list"
(all occurrences of my-list are replaced with the text 'a list)
The above produces the following error:
Error in syntax translation rule: rhs contains extra variables
"my_short_list" ↝ ("\<^type>List.list" 'a)
So I am looking for a variant that is a purely syntactic replacement without any macro hygiene checks.
Some background to understand the underlying problem:
I have a locale with 5 type parameters and a datatype X that takes all 5 type parameters, so for every use I have to write ('a, 'b, 'c, 'd, 'e) X. Obviously the names are longer in practice so this becomed even more unreadable.
Other approaches I tried:
Defining the types or a type_synonym in my locale.
This is not allowed (see https://stackoverflow.com/a/19281758/303637).
Just using another type parameter to represent ('a, 'b, 'c, 'd, 'e) X and assumes for all the required properties of the data type.
This is a lot of boilerplate since I need to write down all the properties that are usually automatically generated for datatype definitions.

Related

How can I fake existential types in Isabelle/HOL?

Consider the following Isabelle/HOL definition of a simple process language:
typedecl channel
datatype process = Put channel char process | Get "char ⇒ process" | Stop
This languages supports sending and receiving of characters via channels.
Now I’d like to have typed channels. The channel type should have the type of values it can transport as a parameter:
typedecl 'a channel
The Put and Get data constructors should have the following (polymorphic) types:
Put :: "['a channel, 'a, process] ⇒ process"
Get :: "['a channel, 'a ⇒ process] ⇒ process"
However, this requires support for existential quantification in data types, which Isabelle/HOL doesn’t have.
I tried to fake existential quantification and came up with the following attempt:
typedecl put
axiomatization put :: "['a channel, 'a] ⇒ put" where
put_inject: "put a x = put b y ⟷ a = b ∧ x = y"
bnf_axiomatization 'r get
axiomatization get :: "['a channel, 'a ⇒ 'r] ⇒ 'r get" where
get_inject: "get a f = get b g ⟷ a = b ∧ f = g"
datatype process = Put put process | Get "process get" | Stop
Unfortunately, this results in the following error message:
Type definition with open dependencies, use "typedef (overloaded)" or enable configuration option "typedef_overloaded" in the context.
Type: process
Deps:
map_get(process.process_IITN_process
⇒ (process_pre_process_bdT + process_pre_process_bdT process_pre_process) set ⇒ bool,
(process_pre_process_bdT + process_pre_process_bdT process_pre_process) set ⇒ bool),
bd_get,
set_get(process.process_IITN_process
⇒ (process_pre_process_bdT + process_pre_process_bdT process_pre_process) set ⇒ bool)
The error(s) above occurred in typedef "process"
Is my attempt reasonable, and, if yes, how can I solve this problem? Is there a better solution?
Indeed, the axiom for get is inconsistent with the bnf_axiomatization. However, if you restrict yourself to countable types 'a, then such a type exists. As soon as you fix such a cardinality bound, you do not even have to restort to axiomatization. Existential types can then be emulated within HOL using encoding and decoding functions into/from a universal domain.
For example, for countable types, the natural numbers can be used as the universal domain. This has been used, e.g., in Imperative_HOL to model a heap that can store typed values, see the paper from 2008. Huffman has done something similar for HOLCF, Isabelle's domain theory library.
With such an encoding in place, you'd construct the datatype of processes using untyped channels and then create a type-safe view on the datatype using the encoding and decoding functions as necessary.

quotient_type warning "no map function"

When using the quotient_type command I get the following warning: "No map function defined for Example.A. This will cause problems later on".
Here is a minimal example to trigger the warning(tested with Isabelle2017).
theory Example
imports
Main
begin
datatype 'a A = B "'a A" | C
(*for map: map *) (* uncommenting doesn't fix the warning*)
quotient_type 'a Q = "'a A" / "op ="
by (rule identity_equivp)
end
So my questions are:
What is meant by a map function in this context (I only do know the concept of a map function in the context of functors in functional programming)?
What does it have to do with the datatype packages map functions, like one that would be generated by the commented line?
Which problems will one get later on?
The datatype command does not by default register the generated map function with the quotient package because there may be more general mappers (in case there are dead type variables). You therefore must do the functor declaration manually:
functor map_A
by(simp_all add: A.map_id0 A.map_comp o_def)
The mapper and its theorems are needed if you later want to lift definitions through the quotient type. This has been discussed on the Isabelle mailing list.

Using syntax/translations wiith locales

Is there any known hack that allows custom syntax for definitions inside a given locale, using the syntax/translation mechanism? All of my attempts at an "obvious" solution are generating type errors, which I am led to believe is caused by syntax/translation not yet being made "locale-aware".
Raw AST transformations with syntax and translations cannot be used inside locales in Isabelle2016. There is a workaround for constants and types whose declaration does not depend on locale parameters. You merely have to issue the syntax declaration outside of the locale for the appropriate constant from background theory. Below is a proof of concept:
locale test = fixes a :: nat begin
definition foo :: "nat ⇒ nat" where "foo x = x"
end
syntax "_foo" :: "nat ⇒ bool" ("FOO")
translations "FOO" ↽ "CONST test.foo"
context test begin
term foo
This workaround does not work for constants which depend on parameters of the locale, because then constant in the background theory takes these parameters as additional arguments and the locale installs an abbreviation, which is folded before the custom syntax translation fires.

Can I overload the notation for operators that are assigned to bool and list?

(NOTE: If I can get rid of the warning I show below, then I say a bunch of extraneous stuff. As part of asking a question, I also do some opinionating. I guess that's sort of asking the question "Why am I wrong here in what I say?")
It seems that 6 of the symbols used for bool operators should have been assigned to syntactic type classes, and bool instantiated for those type classes. In particular, these:
~, &, |, \<not>, \<and>, \<or>.
Because type annotation of terms is a frequent requirement for HOL operators, I don't think it would be a great burden to have to use bool annotations for those 6 operators.
I would like to overload those 6 symbols for other logical operators. Not having the usual symbols for an application can result in there being no good solution for notation.
In the following example source, if I can get rid of the warnings, then the problem is solved (unless I would be setting a trap for myself):
definition natOP :: "nat => nat => nat" where
"natOP x y = x"
definition natlistOP :: "nat list => nat list => nat list" where
"natlistOP x y = x"
notation
natOP (infixr "&" 35)
notation
natlistOP (infixr "&" 35)
term "True & False"
term "2 & (2::nat)"
term "[2] & [(2::nat)]" (*
OUTPUT: Ambiguous input produces 3 parse trees:
...
Fortunately, only one parse tree is well-formed and type-correct,
but you may still want to disambiguate your grammar or your input.*)
Can I get rid of the warnings? It seems that since there's a type correct term, there shouldn't be a problem.
There are actually other symbols I also want, such as !, used for list:
term "[1,2,3] ! 1"
Here's the application for which I want the symbols:
Verilog HDL Operators.
Update
Based on Brian Huffman's answer, I unnotate &, and switch & to a syntactic type class. It'll work out, or it won't, indeed, binary logic, so diversely applicable. My general rule is "don't mess with default Isabelle/HOL".
(*|Unnotate; switch to a type class; see someday why this is a bad idea.|*)
no_notation conj (infixr "&" 35)
class conj =
fixes syntactic_type_classes_are_awesome :: "'a => 'a => 'a" (infixr "&" 35)
instantiation bool :: conj
begin
definition syntactic_type_classes_are_awesome_bool :: "bool => bool => bool"
where "p & q == conj p q"
instance ..
end
term "True & False"
value "True & False"
declare[[show_sorts]]
term "p & q" (* "(p::'a::conj) & (q::'a::conj)" :: "'a::conj" *)
You can "undeclare" special syntax using the no_notation command, e.g.
no_notation conj (infixr "\<and>" 35)
This infix operator is then available to be used with your own functions:
notation myconj (infixr "\<and>" 35)
From here on, \<and> refers to the constant myconj instead of the HOL library's standard conjunction operator for type bool. You should have no warnings about ambiguous syntax. The original HOL boolean operator is still accessible by name (conj), or you can give it a different syntax if you want with another notation command.
For the no_notation command to work, the pattern and fixities must be exactly the same as they were declared originally. See src/HOL/HOL.thy for the declarations of the operators you are interested in.
I should warn about a potential pitfall: Subsequent theory merges can bring the original syntax back into scope, causing ambiguous syntax again. For example, say your theory A.thy imports Main and redeclares the \<and> syntax. Then your theory B.thy imports both A and another library theory, say Complex_Main. Then in theory B, \<and> will be ambiguous. To prevent this, make sure to put all your external theory imports in the one theory file where you change the syntax; then have all of your other theories import this one.

Can I do `type 'a entry = string * 'a * 'a entry;;` in OCaml?

It seems I can't.
I wanna understand why I can't.
It is sure that I can do type 'a entry = Empty | Value of string * 'a * 'a entry;;, so if I want to recursively define a type, I only can go the union route?
Your question is confusing because it only mentions the type you don't want to use! However, I see from your title that you want to use a directly recursive type.
If you want directly recursive types, you need to turn on -rectypes on the command line. This is not something you really want to do most of the time, however. It allows too many types that really aren't what you want.
$ ocaml
OCaml version 4.00.0
# type 'a entry = string * 'a * 'a entry;;
Error: The type abbreviation entry is cyclic
# ^D
$ ocaml -rectypes
OCaml version 4.00.0
# type 'a entry = string * 'a * 'a entry;;
type 'a entry = string * 'a * 'a entry
#
The reason -rectypes isn't enabled by default is that it allows types to be given to many expressions that are actually coding errors. On balance, the extra flexibility isn't worth giving up the extra error detection.
Here's an example I found in an old OCaml mailing list message:
$ ocaml -rectypes
OCaml version 4.00.0
# let f x = x :: x;;
val f : ('a list as 'a) -> 'a list = <fun>
#
With -rectypes turned on, this fairly absurd function is accepted. But really it only works on rather strange list types. Most likely the programmer meant to append the list to itself using # rather than ::. Unfortunately this error won't be detected until much later when -rectypes is on.
As a side comment, and as newacct points out, you would probably soon find that the type in the title isn't so useful. It really can only represent cyclic or infinite structures. This is another reason that working without -rectypes isn't too difficult.
When you do type something = another type, you are doing a type synonym, like a typedef in C. It makes an alias that you can use as a shortcut to refer to another type. It does not define a new type by itself. Hence, it cannot be recursive.
When you do type something = SomeContructor of ..., that defines a new algebraic data type. This can be recursive.
(Haskell and SML are more clear about this; they use type for type synonyms and data (Haskell)/datatype(SML) to define new algebraic data types.)
An algebraic data type does not necessarily need to have more than one alternative. It is syntactically valid to do
type 'a entry = Value of string * 'a * 'a entry
However, in this case, this is probably not useful, because, how could it end?

Resources