How can I use functions and terms defined in locale after interpretation? - typeclass

(This question is a continuation from How can I have multiple arbitrary types in a typeclass definiton in Isabelle? I posted a new question because I thought it was appropriate to. Apologies if this violates Stack Overflow guidelines!)
So the accepted answer from the above link proposed using a locale for the question asked with code:
text ‹Create the locale and define equalParams within the body of the locale›
locale mylocale =
fixes name :: "'a => 'b"
and params :: "'a => 'p"
begin
fun equalParams :: "'a => 'a => bool" where
"equalParams a b = (params a = params b)"
end
datatype ('b, 'c, 'd) actionT1 = Act (name: 'b) (c: 'c) (d: 'd)
abbreviation params where
‹params a ≡ (c a, d a)›
text ‹Instantiate the locale with our parameters.›
text ‹First in a named interpretation:›
interpretation action: mylocale where name = name and params = params
by unfold_locales
term action.equalParams
text ‹Second in a unnamed interpretation:›
interpretation mylocale where name = name and params = params
by unfold_locales
term equalParams
What I would like to do is use the functions name, params and equalParams outside of the locale, and in functions that have types assumed to be part of the given locale. For example, suppose I have the function
fun f :: "'a :: mylocale => 'b => 'c"
.
. <equations go here>
.
Since type 'a is restricted to locale mylocale, I should be able to assume that the generic function names from mylocale can be called on elements of type 'a. However, this cannot be done since the output complains that mylocale is not a class. What I want in the end is for mylocale to be analogous to a Haskell typeclass with multiple type parameters whose instances have access to functions defined in the class. (Multiple type parameters in type class?). Could I please have some help with this?
Thanks in advance.

Locales are a contract, not additional information on types. So 'a :: mylocale does not make sense.
The idea is that you define every function in your locale, for example with:
context mylocale
begin
fun f :: "'a => 'b => 'c" where
...
end
and then all your definitions theorems are instantiated when you interprete (i.e., replace the concrete with concrete values) the locale.
I strongly advise to read the locale documentation to get more details (I linked the 2021 version, replace 2021 by the current year to get the latest answer).

Related

Instantiate type classes in locale contexts

Suppose I have some locale where a type-class can be inferred from the assumptions.
locale some_locale =
fixes xs :: "'x list"
assumes xs_contains_UNIV: "set xs = UNIV"
begin
lemma finite_type: "OFCLASS('x, finite_class)"
proof (intro class.finite.of_class.intro class.finite.intro)
have "finite (set xs)" ..
then show "finite (UNIV :: 'x set)" unfolding xs_contains_UNIV .
qed
Can I then instantiate the type-class in some way?
Direct instantiation (instance, instantiation) does not work in locale contexts.
I also had no luck with interpretation, as finite has no fixed constants,
so I cannot specify the type I want to interpret.
interpretation finite (* subgoal: "class.finite TYPE('a)" for an arbitrary type 'a *)
proof
show "finite (UNIV :: 'x set)" (* "Failed to refine any pending goal", because of the type mismatch *) sorry
show "finite (UNIV :: 'a set)" (* would work, but impossible to prove for arbitrary type 'a *) oops
instance 'x :: finite (* not possible in locale context (Bad context for command "instance") *)
I know that I could simply fixes xs :: "'x::finite list",
which is probably the solution I will have to accept,
but that just seems redundant.
It's currently (Isabelle2021-1) impossible to instantiate a type class inside a locale. However, you can interpret the local context of the type class inside a locale. This makes all the theorems and definitions available that have been made inside the type class context, but not the definitions and theorems that have been proven outside of this context.
interpretation finite
proof
show "finite (UNIV :: 'a set)"
If that is not enough for your use case, then adding the sort constraint to the locale declaration is the way to go.
Note that this only works if the type variable 'x in the locale is renamed to 'a, as the interpretation command for unknown reasons insists on using the type variable 'a.

How do I extract useful information from the payload of a GADT / existential type?

I'm trying to use Menhir's incremental parsing API and introspection APIs in a generated parser. I want to, say, determine the semantic value associated with a particular LR(1) stack entry; i.e. a token that's been previously consumed by the parser.
Given an abstract parsing checkpoint, encapsulated in Menhir's type 'a env, I can extract a “stack element” from the LR automaton; it looks like this:
type element =
| Element: 'a lr1state * 'a * position * position -> element
The type element describes one entry in the stack of the LR(1) automaton. In a stack element of the form Element (s, v, startp, endp), s is a (non-initial) state and v is a semantic value. The value v is associated with the incoming symbol A of the state s. In other words, the value v was pushed onto the stack just before the state s was entered. Thus, for some type 'a, the state s has type 'a lr1state and the value v has type 'a ...
In order to do anything useful with the value v, one must gain information about the type 'a, by inspection of the state s. So far, the type 'a lr1state is abstract, so there is no way of inspecting s. The inspection API (§9.3) offers further tools for this purpose.
Okay, cool! So I go and dive into the inspection API:
The type 'a terminal is a generalized algebraic data type (GADT). A value of type 'a terminal represents a terminal symbol (without a semantic value). The index 'a is the type of the semantic values associated with this symbol ...
type _ terminal =
| T_A : unit terminal
| T_B : int terminal
The type 'a nonterminal is also a GADT. A value of type 'a nonterminal represents a nonterminal symbol (without a semantic value). The index 'a is the type of the semantic values associated with this symbol ...
type _ nonterminal =
| N_main : thing nonterminal
Piecing these together, I get something like the following (where "command" is one of my grammar's nonterminals, and thus N_command is a string nonterminal):
let current_command (env : 'a env) =
let rec f i =
match Interpreter.get i env with
| None -> None
| Some Interpreter.Element (lr1state, v, _startp, _endp) ->
match Interpreter.incoming_symbol lr1state with
| Interpreter.N Interpreter.N_command -> Some v
| _ -> f (i + 1)
in
f 0
Unfortunately, this is puking up very confusing type-errors for me:
File "src/incremental.ml", line 110, characters 52-53:
Error: This expression has type string but an expression was expected of type
string
This instance of string is ambiguous:
it would escape the scope of its equation
This is a bit above my level! I'm pretty sure I understand why I can't do what I tried to do above; but I don't understand what my alternatives are. In fact, the Menhir manual specifically mentions this complexity:
This function can be used to gain access to the semantic value v in a stack element Element (s, v, _, _). Indeed, by case analysis on the symbol incoming_symbol s, one gains information about the type 'a, hence one obtains the ability to do something useful with the value v.
Okay, but that's what I thought I did, above: case-analysis by match'ing on incoming_symbol s, pulling out the case where v is of a single, specific type: string.
tl;dr: how do I extract the string payload from this GADT, and do something useful with it?
If your error sounds like
This instance of string is ambiguous:
it would escape the scope of its equation
it means that the type checker is not really sure if outside of the pattern matching branch the type of v should be a string, or another type that is equal to string but only inside the branch. You just need to add a type annotation when leaving the branch to remove this ambiguity:
| Interpreter.(N N_command) -> Some (v:string)

Add recursive function to class as member

I'm having trouble adding a recursive function to a class.
I can declare the function private no problem above my member declarations using let.
But when I try to make it public using member, it does not compile.
member this.rec mux xs ys =
match xs with
| [] -> ys
| x::xt -> x :: mux ys xt
Thanks for correcting me and pointing me to a proper resource online. I have been reading many tutorials but I can't find this info.
Member functions are always recursive, no rec keyword required:
member this.mux xs ys =
(and even if there was a rec keyword, it would go before this, same way as private - member rec this.mux ...)
But once you've declared it as a member, you have to reference it as a member as well - i.e. this.mux instead of mux:
member this.mux xs ys =
match xs with
| [] -> ys
| x::xt -> x :: this.mux ys xt
Why member functions are always recursive, while let-bound functions are not
(in response to comment)
let-bound functions can shadow previously defined identifiers. For example:
let f x = x+5
let f x = x-2
let a = f 5 // a = 3, not 10
This is a perfectly legal thing to do (except at top level in modules), and is frequently used for utilitarian purposes, e.g. sanitizing parameters:
let sendEmail email subject body =
let email = canonicalize email
...
Notice how in this function the first thing I do is clean up the email, and then proceed to do whatever (NOTE: this is not "assigning" new value to the parameter email, but defining a whole new value that just happens to have the same name).
This new definition of email is used in the rest of the function instead of the original parameter email. This is called "shadowing".
Now, notice how this new definition of email references the old definition of email. This is only possible because email is not recursive: the compiler knows that the word email within the definition of email refers to the previously defined value, not the one being defined right now.
"But wait" - you say - "what does it even mean for email to be recursive? Doesn't the term "recursive" only apply to functions?". Well, no. Values can be recursive too, but that's a topic for another time. For now, here's a different example:
let notifyUsers sendEmail log =
let sendEmail name =
log ("Notifying " + name)
sendEmail (name + "#contoso.com")
sendEmail "John"
sendEmail "Mark"
sendEmail "Matthew"
sendEmail "Luke"
In this example, I "shadowed" the function sendEmail with a new definition that logs the name of the user before calling the original sendEmail. If I defined my "new" sendEmail as recursive (i.e. let rec sendEmail name = ...), this program would result in an infinite loop: the function would just endlessly call itself. But because the function is not recursive, it is able to reference previously-defined value of the same name.
Member functions don't have this problem: you can't shadow a class method, that would be meaningless.
This problem is addressed differently in different languages.
For example, in languages with everything mutable by default, this problem doesn't arise at all: you just mutate the values and that's it... Unless you want to change the type, too - then you're screwed.
For another example, in Haskell, all values are always recursive, and shadowing will cause a warning. As a result, people are forced to use ticks or get creative with naming, or even introduce a monad where none is required.

How to add instances of sub types that have common base class to the same list in FSharp?

I have a base abstract class named Tokenand some sub types like NumToken,StrToken.
I want to put their instances to the same list.
I can't declare a variable use let l = list<'a when 'a :> Token>
Then, I write
let extractToken<'a when 'a :> Token>(lineNum:int, line:string) : 'a list option =
let mutable result : 'a list = []
It works, but can not add element.result <- new NumToken(lineNum, value) :: result just say it needs 'a but here is NumToken
Now I can use new NumToken(lineNum, value) :> Token and declare Token list.
It wokrs but looks ugly(I know fsharp doesn't do auto up cast..).
list<_ :> Token> doesn't work too, it only accepts one sub type.
Thx for help.
When you model tokens using a class hierarchy and you create a list of tokens, the type of the list needs to be specific. You can either return list<Token> or list<NumToken>.
The flexible types with when constraints are useful only quite rarely - typically, when you have a function that takes some other function and returns whatever the other function produces, so I do not think you need them here. You can use list<Token> and write:
result <- (NumToken(lineNum, value) :> Token) :: result
That said, modelling token in F# using a class hierarchy is not very good idea. F# supports discriminated unions, which are much better fit for this kind of problem:
type Token =
| NumToken of int
| StrToken of string
Then you can write a function that returns list<Token> just by writing
result <- (NumToken 42) :: result
Although, depending on what you are doing, it might be also a good idea to avoid the mutation.

what is use cases of F# explicit type parameters?

As I know, explicit type parameters in value definitions is a one way to overcome "value restriction" problem.
Is there another cases when I need to use them?
Upd: I mean "explicitly generic constructs", where type parameter is enclosed in angle brackets, i.e.
let f<'T> x = x
Polymorphic recursion is another case. That is, if you want to use a different generic instantiation within the function body, then you need to use explicit parameters on the definition:
// perfectly balanced tree
type 'a PerfectTree =
| Single of 'a
| Node of ('a*'a) PerfectTree
// need type parameters here
let rec fold<'a,'b> (f:'a -> 'b) (g:'b->'b->'b) : 'a PerfectTree -> 'b = function
| Single a -> f a
| Node t -> t |> fold (fun (a,b) -> g (f a) (f b)) g
let sum = fold id (+)
let ten = sum (Node(Node(Single((1,2),(3,4)))))
This would likely be rare, but when you want to prevent further generalization (§14.6.7):
Explicit type parameter definitions on value and member definitions can affect the process of type inference and generalization. In particular, a declaration that includes explicit generic parameters will not be generalized beyond those generic parameters. For example, consider this function:
let f<'T> (x : 'T) y = x
During type inference, this will result in a function of the following type, where '_b is a type inference variable that is yet to be resolved.
f<'T> : 'T -> '_b -> '_b
To permit generalization at these definitions, either remove the explicit generic parameters (if they can be inferred), or use the required number of parameters, as the following example shows:
let throw<'T,'U> (x:'T) (y:'U) = x
Of course, you could also accomplish this with type annotations.
Most obvious example: write a function to calculate the length of a string.
You have to write:
let f (a:string) = a.Length
and you need the annotation. Without the annotation, the compiler can't determine the type of a. Other similar examples exist - particularly when using libraries designed to be used from C#.
Dealing with updated answer:
The same problem applies - string becomes A<string> which has a method get that returns a string
let f (a:A<string>) = a.get().Length

Resources