How can one define a function in Isabelle that has a different definition depending on either the type of its argument, or the type of the context it is used in?
For example, I might want to define a functions is_default with type 'a ⇒ bool, where each different type 'a has a potentially different "default value". (I am also assuming, for the sake of argument, that existing concepts such as zero are not suitable.)
Isabelle supports overloaded definitions by defining a constant name and then later providing the constant with new definitions for different types. This can be done with the consts command to define the constant name, and then the defs (overloaded) command to provide a partial definition.
For example:
consts is_default :: "'a ⇒ bool"
defs (overloaded) is_default_nat:
"is_default a ≡ ((a::nat) = 0)"
defs (overloaded) is_default_option:
"is_default a ≡ (a = None)"
The above will also work without the (overloaded) parameter, but will cause Isabelle to issue a warning.
The defs command is also given a name, which is the name of the theorem generated by Isabelle which contains the definition. This name can then be used in later proofs:
lemma "¬ is_default (Some 3)"
by (clarsimp simp: is_default_option)
More information is available in section "Constants and definitions" in the Isablle/Isar reference manual. Additionally, there is a paper "Conservative Overloading in Higher-Order Logic" by Obua that discusses some of the implementation details and gotchas in having such a framework without sacrificing soundness.
This kind of overloading looks like a perfect fit for type classes. First you define a type class for your desired function is_default:
class is_default =
fixes is_default :: "'a ⇒ bool"
Then you introduce arbitrary instances. E.g., for Booleans
instantiation bool :: is_default
begin
definition "is_default (b::bool) ⟷ b"
instance ..
end
and lists
instantiation list :: (type) is_default
begin
definition "is_default (xs::'a list) ⟷ xs = []"
instance ..
end
Related
I have read in this post that ML dialects do not allow type variables of non-ground kind. E.g. the last statement is not representable:
-- Haskell code
type Ground = Int
type FirstOrder a = Maybe a
type SecondOrder c = c Int -- ML do not allow :c
OCaml has support of higher-kinded only at the level of modules. There are some explanations (here and author's comment here) about which features of OCaml clash with higher-kinded types opportunity.
If I understood it correctly, the main problem is in the following facts:
OCaml does not follow a "freshness" restriction for type definitions: construct type can define both an alias (an the type will remain the same) and a new fresh type
type alias definition can be hidden
AFAIK, Standard ML has different constructs for type definition and aliases: type for aliases and datatype for new fresh types introduction.
Unfortunatelly, I do not know SML well enough -- is it possible to export type aliases with its definition hidden? And can someone please show me if there are any other SML features that still do not go well with an opportunity of higher-kinded types?
Probably there will be some problems with functors -- Could one be so kind to show a code example of it? I've heard several times about such cases but still have not found a complete example of it.
Yes, SML can express the equivalent of higher-kinded types through functors, and can also make them abstract. Useless example:
functor F (type 'a t) :> sig type 'a u end =
struct
type 'a u = ('a t) t
end
However, unlike OCaml, SML does not (officially) have higher-order functors, so per the standard, you can only express second-order type constructors this way.
FWIW, OCaml may use the same keyword for type aliases and generative types (type vs datatype in SML), but they are still distinguished syntactically, by their right-hand side. So that's no real difference to SML.In both languages, an abstract occurring in a signature can be implemented as either a type alias or a generative type. So the problem for type inference that Leo is alluding to exists equally in both. Haskell can get away without that problem because it does not have the same expressiveness regarding type abstraction (i.e., no "sealing" operator for modules).
In datatype declarations, Standard ML will produce an equality type if all of the type arguments to all of the variants are themselves eqtypes.
I've seen comments in a few places lamenting the inability of users to provide their own definition of equality and construct their own eqtypes and unexpected consequences of the SML rules (e.g. bare refs and arrays are eqtypes, but datatype Foo = Foo of (real ref) is not an eqtype).
Source: http://mlton.org/PolymorphicEquality
one might expect to be able to compare two values of type real t, because pointer comparison on a ref cell would suffice. Unfortunately, the type system can only express that a user-defined datatype admits equality or not.
I'm wondering whether it is possible to block eqtyping. Say, for instance, I am implementing a set as a binary tree (with an unnecessary variant) and I want to pledge away the ability to structurally compare sets with each other.
datatype 'a set = EmptySet | SetLeaf of 'a | SetNode of 'a * 'a set * 'a set;
Say I don't want people to be able to distinguish SetLeaf(5) and SetNode(5, EmptySet, EmptySet) with = since it's an abstraction-breaking operation.
I tried a simple example with datatype on = On | Off just to see if I could demote the type to a non-eqtype using signatures.
(* attempt to hide the "eq"-ness of eqtype *)
signature S = sig
type on
val foo : on
end
(* opaque transcription to kill eqtypeness *)
structure X :> S = struct
datatype on = On | Off
let foo = On
end
It seems that transparent ascription fails to prevent X.on from becoming an eqtype, but opaque ascription does prevent it. However, these solutions are not ideal because they introduce a new module and hide the data constructors. Is there a way to prevent a custom type or type constructor from becoming an eqtype or admitting equality without hiding its data constructors or introducing new modules?
Short answer is no. When a type's definition is visible, it's eq-ness is whatever the definition implies. The only way to prevent it being eq then is to tweak the definition such that it isn't, for example, by adding a dummy constructor with a real parameter.
Btw, small correction: your type foo should be an equality type. If your SML implementation disagrees then it has a bug. A different case is real bar when datatype 'a bar = Bar of 'a ref (which is what the MLton manual discusses). The reason that the first one works but the second doesn't is that ref is magic in SML: it has a form of polymorphic eq-ness that user types cannot have.
I'm getting ocamldoc warnings:
Warning: Element MyModule.VariantName not found
when using {!MyModule.VariantName} in doc comments.
The ocamldoc doc says
In this chapter, we use the word element to refer to any of the following parts of an OCaml source file: a type declaration, a value, a module, an exception, a module type, a type constructor, a record field, a class, a class type, a class method, a class value or a class inheritance clause.
and later when explaining text formatting:
∣ {! string } insert a reference to the element named string. string must be a fully qualified element name, for example Foo.Bar.t. The kind of the referenced element can be forced (useful when various elements have the same qualified name) with the following syntax: {! kind : string } where kind can be module, modtype, class, classtype, val, type, exception, attribute, method or section.
Can I reference a type constructor using {! string }?
How do the first group of kinds of elements relate to the second group of kinds of elements?
After looking at the implementation, it looks like it is possible to refer to a variant constructor, but with a rather weird syntax: you must use the {!typename.constrname} syntax or, from another module, {!Modulename.typename.constrname}. With the code example below, that would by {!mylist.Cons} for example. This will generate a hyperlink, but unfortunately the text will still be typename.constrname and not just refer to the constructor name itself.
(I also learned that there is an "explicit" way to tell ocamldoc to which syntactic category the mentioned identifier belong, I suppose to help in some ambiguous case. So just as you could use either {!mylist} or {!type:mylist} to denote a type constructor, you can use either {!mylist.Nil} or {!const:mylist.Nil} to denote the variant constructor.)
Vocabulary note: In the type declaration
type 'a mylist =
| Nil
| Cons of 'a * 'a mylist
the names Nil and Cons are not called "type constructors", but only "constructors" or "variant constructors". The type constructors are the names of parametrized types (or non-parametrized, for constant type constructors) living at the type rather than the value level, mylist in this example.
I often need to declare a type which contains a map or a list, for instance:
type my_type_1 = my_type_0 IntMap.t
type my_type_2 = my_type_0 List
Also I have seen another style of declaration which encapsulates map or list in a record, for instance:
type my_type_1 =
| Bot_1
| Nb_1 of my_type_0 IntMap.t
type my_type_2 =
| Bot_2
| Nb_2 of my_type_0 List
My question is, whether there are some cases where the second style is necessary and better than the first style?
Thank you very much!
The two types you give are not equivalent, because of the Bot constructor added in the second case. This means that the two my_type_1 do not have the same semantics. Incidentally, the construction Bot | Foo of 'a is already provided by the standard type 'a option, with constructors Some and None, so the type my_type_1 of your second sample is equivalent to a my_type_1 option in the first one.
Whether to use an option type or your own constructors names is up to you. In general, I would advise to you an option type if the semantics of your type coincides with the option idea of failure, being absent, or being undefined. Given your name Bot, I assume this is probably what you're doing, but defining your own constructor names is also ok and can be clearer in some circumstances. The matter has been discussed in depth in this blog post from ezyang.
Now, assuming your two types definition were equivalent (that is, in absence of the Bot) constructor, what's the purpose of adding an algebraic datatype layer, a new constructor, instead of using a simple type alias ? Well, it has the effect of making your type distinct from the representation type. For example, if you define type 'a stack = Stack of 'a list, 'a stack and 'a list cannot be confused for each other, and the compiler will raise an error if you do. So that can be used to enforce a (light) type separation, with the constructor acting as a type annotation:
let empty = Stack []
let length (Stack li) = List.length li
I'd say it's mostly a matter of taste, but I would recommend using an algebraic datatype instead of an alias when you want to be sure that there can be no mistake with the original type. The downside is that you have to wrap the operations of the original datatype, as I did in my length function above.
Those are not different styles, but different types: the first type declarations are an abbreviation for a specialized instance (for mytype_0) of the polymorphic List, or IntMap.
The second set of definitions present a "constructed" type, for which Bot_1 (and Bot_2) provide values. Those "alternatives" can be used, for example, to create functions of type T -> my_type_1 which return Bot_1 in a special case where the computation doesn't allow to return a list, in a similar way of what an option type permits. This is impossible with the first set of definitions (who must always provide the required list payload).
The second one isn't a "record" (which is a different thing). It creates an algebraic data type. I'm not sure how to explain it but if you've used Haskell or Standard ML you'll know. It's basically a tagged union. A my_type_1 is either a Bot_1 (which carries no data) or a Nb_1 (which carries a my_type_0 IntMap.t as data).
The first one is simply a type synonym (like a typedef in C).
If I have a type t
type t = C of string;;
And want to explicitly define a type of the variable to be type t:
let b : t = C 'MyString';;
Can I do it in OCaml?
You don't need to be explicit
let b = C mystring
let b = C "a string litteral"
You can be explicit, but it dy oesn't add anything
let b : t = C foo
The preferred way in general is to use type inference without annotating types, and only be explicit about the type of the identifiers exported to other modules, through the associated .mli interface file.
In this case, a type annotation doesn't add anything as the C constructor already is a kind of tag/annotation : C something is necessarily of type t, there is no possible confusion.
You can, using either that syntax, or the alternate one:
let b = (C foo : t)
Adding type constraints in this fashion does not usually serve any purpose in a well-formed program, because the type inference algorithm can handle all of it correctly on its own. There are a few exceptions (mostly involving the object-oriented side), but they're quite rare.
Such annotations are mostly useful when a type error happens and you need to understand why a certain expression has a certain type when you expected it to have another, because you can type-annotate intermediate values to have the type error move up through your source code.
Note that there's another way of annotating types, which is to define a signature for your modules. In your example above, your module body would contain:
let b = C foo
And your module signature would contain :
val b : t
This is especially useful when you need assumptions inside the module to be invisible to other modules. For instance, when using polymorphic variants:
let user_type = `Admin
Here, you only want to handle the administrator account, but you need the rest of your code to be aware that other account types exist, so you would write in the signature that:
val user_type : [`Admin|`Member|`Guest]
This type is technically correct, but could not have been guessed by the type inference algorithm.