I am trying to define the semantics of a programming language in Lean. To do so, I have a bunch of mutually inductive types defining the different term types of the language:
mutual inductive CBlockList, Block, SwitchBody, CExpr, CStatement
with CBlockList : FTContext → set Identifier -> set Identifier -> Type
....
with Block : FTContext → set Identifier → Type
...
with SwitchBody : FTContext → set Identifier → Type
...
with CExpr : FTContext → set Identifier → ℕ → Type
...
with CStatement : FTContext → set Identifier → set Identifier → Type
...
Note that in particular, the different term types take a function typing context and a set of identifier in scope as arguments. Terms that can introduce new variables into scope have a second such argument, defining the set of arguments after the term is evaluated.
However, when defining mutually recursive functions over this mutually recursive data type, lean complains that for some of the recursive calls, it is unable to infer well-foundedness. This occurs in particular when there are calls between functions with different numbers of arguments (because of the different number of arguments of the associated types). Lean seems to try to define the well-founded order over the sum of the dependent product of each function's arguments, but when they have different number of arguments, it, I assume, does not give any order to these elements of the set, hence the problem I have encountered.
I have tried to define a mutually recursive depth function that traverse the mutually inductive data types and returns a limit to the possible depth of recursion to be able to prove well-foundedness myself, however, this function definition runs into the same problem. It seems I'm stuck in a catch-22.
How would I go about solving such a problem?
I am trying to write the following union of types:
FloatVector = Union{Array{Float64,1}, DataArray{Float64,1}, Array{Union{Float64, Missings.Missing},1}};
using the abstract types syntax. Ideally, I would like to do something similar to this link. I tried the below, but unfortunately it does not work:
abstract type FloatVector end
type Array{Float64,1} <: FloatVector end
type DataArray{Float64,1} <: FloatVector end
type Array{Union{Float64, Missings.Missing},1} <: FloatVector end
I am not confident with abstract types and I couldn't find a good reference on a similar problem. I would be glad if you could explain me how to proceed and the advantages over the Union.
It is not possible to do what you want using abstract types in Julia. Union is a way to represent your requirement.
Now to understand why observe that in Julia type system you have three restrictions:
Only abstract types can have subtypes.
A type can have only one supertype.
You have to specify a supertype of a type when defining it.
In this way we know that types create a tree like structure, where the root is Any (with the exception of Union{} which has no values and is subtype of all other types, but you will probably not need this in practice).
Your definition would violate rule #2 and rule #3. Observe that all those types are already defined (either in Base or in a package) - so you cannot redefine them (rule #3). Also they already have a supertype so you cannot add another (rule #2). See for example the result of this call:
julia> supertype(Array{Float64, 1})
DenseArray{Float64,1}
And you see that Array{Float64, 1} is already defined and has a supertype.
Having said that maybe another more general definition will be just enough for you:
const FloatOrMissingVector = AbstractVector{<:Union{Float64, Missing}}
This definition has two differences:
It allows other than dense vectors (which I guess is what you would want anyway as typically you do not care if a vector is dense or sparse)
It would allow e.g. Vector{Missing} as a subtype (so a structure containing only missing values) - if you would want to rule this out you would have to specify union as you have proposed above.
The Data.AVL module of the standard library is parametrized by the value type (among other things):
module Data.AVL
{k v ℓ}
{Key : Set k} (Value : Key → Set v)
{_<_ : Rel Key ℓ}
(isStrictTotalOrder : IsStrictTotalOrder _≡_ _<_)
which of course means the map function it exports can't change the value type:
map : ({k : Key} → Value k → Value k) → Tree → Tree
Is there a way still to transform values stored in a Tree in a non-endomorphic way?
Currently, this seems to be if not impossible then at least very annoying. The internal tree representation, Indexed.Tree, is parameterised by upper and lower bounds of type Key⁺ Value ..., so one would have to convert between Key⁺ V and Key⁺ W for different value types V, W and convince Agda that the conversion doesn't change anything. (It doesn't, of course, since Value is not used in the definition of Key⁺, but proving this is a hassle.)
The easiest way to solve the problem would therefore be to fix the library, moving the Value parameter down a few scopes. I'm sure the Agda devs would accept a patch to that effect.
I have performed the refactoring I was suggesting in the comment to #JannisLimperg's answer. You can readily access it in the refactor-avl branch of the standard library. Edit: it's been merged and is now part of the stdlib!
Here is the summary of the changes as listed in the changelog:
Overhaul of Data.AVL
Splitting out Data.AVL.Key and Data.AVL.Height which should not depend
on the type of Value the tree will contain.
Putting Indexed into its own core module Data.AVL.Indexed following the
example of e.g. Category.Monad.Indexed or Data.Container.Indexed
Giving map a polymorphic type: it is now possible to change the type of
values contained in a tree when mapping over it.
I am trying to create a function
import Language.Reflection
foo : Type -> TT
I tried it by using the reflect tactic:
foo = proof
{
intro t
reflect t
}
but this reflects on the variable t itself:
*SOQuestion> foo
\t => P Bound (UN "t") (TType (UVar 41)) : Type -> TT
Reflection in Idris is a purely syntactic, compile-time only feature. To predict how it will work, you need to know about how Idris converts your program to its core language. Importantly, you won't be able to get ahold of reflected terms at runtime and reconstruct them like you would with Lisp. Here's how your program is compiled:
Internally, Idris creates a hole that will expect something of type Type -> TT.
It runs the proof script for foo in this state. We start with no assumptions and a goal of type Type -> TT. That is, there's a term being constructed which looks like ?rhs : Type => TT . rhs. The ?foo : ty => body syntax shows that there's a hole called foo whose eventual value will be available inside of body.
The step intro t creates a function whose argument is t : Type - this means that we now have a term like ?foo_body : TT . \t : Type => foo_body.
The reflect t step then fills the current hole by taking the term on its right-hand side and converting it to a TT. That term is in fact just a reference to the argument of the function, so you get the variable t. reflect, like all other proof script steps, only has access to the information that is available directly at compile time. Thus, the result of filling in foo_body with the reflection of the term t is P Bound (UN "t") (TType (UVar (-1))).
If you could do what you are wanting here, it would have major consequences both for understanding Idris code and for running it efficiently.
The loss in understanding would come from the inability to use parametricity to reason about the behavior of functions based on their types. All functions would effectively become potentially ad-hoc polymorphic, because they could (say) run differently on lists of strings than on lists of ints.
The loss in performance would come from representing enough type information to do the reflection. After Idris code is compiled, there is no type information left in it (unlike in a system such as the JVM or .NET or a dynamically typed system such as Python, where types have a runtime representation that code can access). In Idris, types can be very large, because they can contain arbitrary programs - this means that far more information would have to be maintained, and computation occurring at the type level would also have to be preserved and repeated at runtime.
If you're wanting to reflect on the structure of a type for further proof automation at compile time, take a look at the applyTactic tactic. Its argument should be a function that takes a reflected context and goal and gives back a new reflected tactic script. An example can be seen in the Data.Vect source.
So I suppose the summary is that Idris can't do what you want, and it probably never will be able to, but you might be able to make progress another way.
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.