I'm trying to use Coq for some simple kinds of philosophical predicate logic. Suppose, for instance, that I wanted to express the statement "if a being is human, it is not perfect" in Coq. I will first have to define what the terms 'being', 'human', and 'perfect' are. The natural approach, it seems, is to define the first as a type, and others as unary predicates of that type.
Inductive being : Type :=
| b : nat -> being.
Inductive human : being -> Prop :=
| h : forall ( b : being ), human b.
Inductive perfect : being -> Prop :=
| p : forall ( b : being ), perfect b.
(being is indexed with numbers to ensure there are multiple distinct beings.)
With this definition, the statement can be expressed as
Lemma humans_are_imperfect :
forall ( b : being ), human b -> ~ perfect b.
Admitted.
The problem with this approach is that it allows proofs of nonsense, such as
Lemma humans_are_perfect :
forall ( b : being ), human b -> perfect b.
intros b H. apply ( p b ). Qed.
Obviously, there is a problem with the definition of human and perfect, because it indescriminately takes any being and asserts its humanity and perfection. What is needed is a definition of human and perfect that only specifies their types, while remaining ambiguous about which beings they apply to.
This problem could be avoided altogether if the truth condition of perfect was built into its definition, e.g. the constructor only took nonhuman beings as arguments. But supplying all information upfront like that is not always feasible in philosophical arguments. Very often you have to take some type or predicate as a given, and slowly build up its details with further premises.
I have a feeling that what I'm trying to do does not fit very well into the inductive basis of Coq. Maybe I'm better off learning a logic programming language like Prolog, but hopefully I'm wrong.
What you want to do can be perfectly expressed in Coq using just higher-order predicate logic, without the need for inductive definitions. You can work with your theorems just by postulating the existence of a type being, predicates human and perfect, and inference rules relating those.
Section SomePhilosophy.
Variable being : Type.
Variable human perfect : being -> Prop.
Hypothesis human_not_perfect : forall b, human b -> ~ perfect b.
(* ... some theorems about the above notions ... *)
End SomePhilosophy.
After closing the section, all of your definitions will become parametric with respect to choices of being, human and perfect that satisfy your hypothesis. In your original approach, you constrained your theory by fixing upfront what being is, since you gave its definition. Here, being can by anything.
Related
I'm trying to wrap my head around this blog post about the ConstraintKinds extension.
There was a post in the comment section which I totally did not understand. Here it is:
Adam M says: 14 September 2011 19:53 UTC
Wow, this sounds great. Is it scheduled to be part of the official GHC 7.4?
Also, does this mean that you've introduced a third production in the in System FC2 grammar for Kinds? Currently it has * and k~>k as the only alternatives where k1~>k2 is (basically) the kind of (forall a::k1 . (t::k2)). It sounds like this would add k1==>k2 which is the kind of (a::k1 => (t::k2)). Or are the two kinds actually the same?
Could someone, please analyze this step-by-step or at least provide some links which would help me wrap my head around this myself. Some key moments I should pinpoint:
What is a "System FC2 grammar for Kinds"? (Probably the main and the most general one, whose answer would embed the two other ones.)
I tried explaining why "k1~>k2 is (basically) the kind of (forall a::k1 . (t::k2))"? As far as I understand, ~> is some special notation for -> in kinds, as * and k1 -> k2 the only inhabitants of the standard Haskell's kind system (fits their description: "Currently it has * and k~>k as the only alternatives"). Thus, the (forall a::k1 . (t::k2)) formula means that if we take an inhabited type k1, it can be mapped onto another k2 iff it is inhabited (due to Curry-Howard working for kinds the same way it works for types). Is that right? (P.S.: I see how this intuition fails if I do not understand the notion of inhabitance for kinds; do kinds correspond to True provable formulae (see comments) when they have an inhabited type as an inhabitant or an arbitrary type? The intuition fails in the second case.)
What does the => mean in the formula for k1==>k2, namely (a::k1 => (t::k2))?
The response this comment got:
Max says:
14 September 2011 21:11 UTC
Adam: it's not that complicated! It just adds the base kind Constraint to the grammar of kinds. This is a kind of types inhabited by values, just like the existing kinds * and #.
So the author claims that Adam M overcomplicated the extension. Their response is quite easy to understand. Anyway, even if Adam M's comment is not true, I think it is totally worth attention as it introduced some unfamiliar concepts to me.
"System FC2" is a term coined by Weirich et al in their 2010 paper "Generative type abstraction and type-level computation" (link). It refers to the addition of "roles" to System FC and formed the basis for the implementation in GHC described in the 2016 paper "Safe Zero-cost Coercions for Haskell. System FC, in turn, is the system originally described in this paper (or actually an earlier paper of which this is post-publication extended version), which extended the usual polymorphic lambda calculus of System F with type equalities.
However, I think Adam M was probably using the term "System FC2" less formally to refer to whatever type system GHC was implementing at the time the comment was written. So, the meaning of the phrase:
introduced a third production in the System FC2 grammar for Kinds
is really:
added a third production rule to the grammar of kinds, as kinds are currently implemented in GHC
His claim was that the grammar for kinds currently had two production rules:
* is a kind
If k1 and k2 are kinds, then k1 ~> k2 is a kind.
and he was asking if this extension gave a third production rule:
If k1 and k2 are kinds, then k1 ==> k2 is a kind.
As you've guessed, he introduced the operator ~> to differentiate the kind-level arrow from the type-level arrow. (In GHC, both the kind-level and type-level arrow operators are written the same way ->.) He gave a definition of ~> as:
where k1~>k2 is (basically) the kind of (forall a::k1 . (t::k2)).
which is interpretable, but very imprecise. He was trying to use forall here as a sort of type-level lambda. It's not, but you can imagine that if you had a type forall a. t, you could instantiate it at a specific type a, and if for all a :: k1 you get t :: k2, then this polymorphic type sort of represents an implicit type function of kind k1 ~> k2. But the polymorphism / universal quantification is irrelevant here. What's important is how a appears in the expression t, and the extent to which you can express the type-level expression t as, say, a type-level function:
type Whatever a = t
or if Haskell had type-level lambdas, a type-level lambda with a as an argument and t as its body:
Lambda a. t
You won't get anywhere by trying to seriously consider forall a. t as having kind k1 -> k2.
Based on this loose interpretation of ~>, he tried to ask if there was a new, kind-level operator ==> such that the relationship between the kind-level operator ~> and the type-level expression forall a. b was the same as the relationship between a new hypothetical kind-level operator ==> and the type-level expression a => b. I think the only reasonable way to interpret this question is to imagine that he wanted to consider the type expression a => b as being parameterized by a, the same way he was imagining forall a. b as being parameterized by a, so he wanted to consider a type-level function of the form:
type Something a = a => b
and consider the kind of Something. Here, the kind of Something is Constraint ~> *. So, I guess the answer to his final question is, "the two kinds are actually the same", and no other kind-level operator besides ~> is needed.
Max's reply explained that the extension didn't add any new kind-level operator but merely added a new primitive kind, Constraint at the same grammatical level as the kinds * and #. The kind-level ~> operator has the same relationship to type-level application f a whether the primitive kinds involved are * or # or Constratin. So, for example, given:
{-# LANGUAGE ConstraintKinds, RankNTypes #-}
type Whatever a = Maybe [a]
type Something a = a => Int
the kinds of Whatever and Something are both expressed in terms of the kind operator ~> (in GHC, written simply ->):
λ> :kind Whatever
Whatever :: * -> *
λ> :kind Something
Something :: Constraint -> *
If I understand Curry-Howard's isomorphism correctly, every dependent type correspond to a theorem, for which a program implementing it is a proof. That means that any mathematical problem, such as a^n + b^n = c^n can be, somehow, expressed as a type.
Now, suppose I want to design a game which generates random types (theorems), and on which plays must try to implement programs (proofs) of those types (theorems). Is it possible to have control over the difficulty of those theorems? I.e., an easy mode would generate trivial theorems while a hard mode would generate much harder theorems.
A one-way function is a function that can be calculated in polynomial time, but that does not have a right inverse that can be calculated in polynomial time. If f is a one-way function, then you can choose an argument x whose size is determined by the difficulty setting, calculate y = f x, and ask the user to prove, constructively, that y is in the image of f.
This is not terribly simple. No one knows whether there are any one-way functions. Most people believe there are, but proving that, if true, is known to be at least as hard as proving P /= NP. However, there is a ray of light! People have managed to construct functions with the strange property that if any functions are one-way, then these must be. So you could choose such a function and be pretty confident you'll be offering sufficiently hard problems. Unfortunately, I believe all known universal one-way functions are pretty nasty. So you will likely find it hard to code them, and your users will likely find even the easiest proofs too difficult. So from a practical standpoint, you might be better off choosing something like a cryptographic hash function that's not as thoroughly likely to be truly one-way but that's sure to be hard for a human to crack.
If you generate just types, most of them will be isomorphic to ⊥. ∀ n m -> n + m ≡ m + n is meaningful, but ∀ n m -> n + m ≡ n, ∀ n m -> n + m ≡ suc m, ∀ n m -> n + m ≡ 0, ∀ n m xs -> n + m ≡ length xs and zillions of others are not. You can try to generate well-typed terms and then check, using something like Djinn, that the type of a generated term is not inhabited by a much simpler term. However many generated terms will be either too simple or just senseless garbage even with a clever strategy. Typed setting contains less terms than non-typed, but a type of just one variable can be A, A -> A, A -> B, B -> A, A -> ... -> E and all these type variables can be free or universally quantified. Besides, ∀ A B -> A -> B -> B and ∀ B A -> A -> B -> B are essentially the same types, so your equality is not just αη, but something more complex. The search space is just too big and I doubt a random generator can produce something really non-trivial.
But maybe terms of some specific form can be interesting. Bakuriu in comments suggested theorems provided by parametricity: you can simply take Data.List.Base or Function or any other basic module from Agda standard library and generate many theorems out of thin air. Check also the A computational interpretation of parametricity paper which gives an algorithm for deriving theorems from types in a dependently typed setting (though, I don't know how it's related to Theorems for free and they don't give the rules for data types). But I'm not sure that most produced theorems won't be provable by straightforward induction. Though, theorems about functions that are instances of left folds are usually harder than about those which are instances of right folds — that can be one criteria.
This falls into an interesting and difficult field of proving lower bounds in proof complexity. First, it very much depends on the strenght of the logic system you're using, and what proofs it allows. A proposition can be hard to prove in one system and easy to prove in another.
Next problem is that for a random proposition (in a reasonably strong logic system) it's even impossible to decide if it's provable or not (for example the set of provable propositions in first-order logic is only recursively enumerable). And even if we know it's provable, deciding its proof complexity can be extremely hard or undecidable (if you find a proof, it doesn't mean it's the shortest one).
Intuitively it seems similar to Kolmogorov complexity: For a general string we can't tell what's the shortest program that produces it.
For some proof systems and specific types of formulas there are known lower bounds. Haken proved in 1989:
For a sufficiently large n, any Resolution proof of PHP^n{n-1}_ (Pigeon hole principle) requires length 2^{\Omega(n)}.
These slides give an overview of the theorem. So you could generate propositions using such a schema, but that'd probably won't be very interesting for a game.
I have pretty decent intuition about types Haskell prohibits as "impredicative": namely ones where a forall appears in an argument to a type constructor other than ->. But just what is predicativity? What makes it important? How does it relate to the word "predicate"?
The central question of these type systems is: "Can you substitute a polymorphic type in for a type variable?". Predicative type systems are the no-nonsense schoolmarm answering, "ABSOLUTELY NOT", while impredicative type systems are your carefree buddy who thinks that sounds like a fun idea and what could possibly go wrong?
Now, Haskell muddies the discussion a bit because it believes polymorphism should be useful but invisible. So for the remainder of this post, I will be writing in a dialect of Haskell where uses of forall are not just allowed but required. This way we can distinguish between the type a, which is a monomorphic type which draws its value from a typing environment that we can define later, and the type forall a. a, which is one of the harder polymorphic types to inhabit. We'll also allow forall to go pretty much anywhere in a type -- as we'll see, GHC restricts its type syntax as a "fail-fast" mechanism rather than as a technical requirement.
Suppose we have told the compiler id :: forall a. a -> a. Can we later ask to use id as if it had type (forall b. b) -> (forall b. b)? Impredicative type systems are okay with this, because we can instantiate the quantifier in id's type to forall b. b, and substitute forall b. b for a everywhere in the result. Predicative type systems are a bit more wary of that: only monomorphic types are allowed in. (So if we had a particular b, we could write id :: b -> b.)
There's a similar story about [] :: forall a. [a] and (:) :: forall a. a -> [a] -> [a]. While your carefree buddy may be okay with [] :: [forall b. b] and (:) :: (forall b. b) -> [forall b. b] -> [forall b. b], the predicative schoolmarm isn't, so much. In fact, as you can see from the only two constructors of lists, there is no way to produce lists containing polymorphic values without instantiating the type variable in their constructors to a polymorphic value. So although the type [forall b. b] is allowed in our dialect of Haskell, it isn't really sensible -- there's no (terminating) terms of that type. This motivates GHC's decision to complain if you even think about such a type -- it's the compiler's way of telling you "don't bother".*
Well, what makes the schoolmarm so strict? As usual, the answer is about keeping type-checking and type-inference doable. Type inference for impredicative types is right out. Type checking seems like it might be possible, but it's bloody complicated and nobody wants to maintain that.
On the other hand, some might object that GHC is perfectly happy with some types that appear to require impredicativity:
> :set -Rank2Types
> :t id :: (forall b. b) -> (forall b. b)
{- no complaint, but very chatty -}
It turns out that some slightly-restricted versions of impredicativity are not too bad: specifically, type-checking higher-rank types (which allow type variables to be substituted by polymorphic types when they are only arguments to (->)) is relatively simple. You do lose type inference above rank-2, and principal types above rank-1, but sometimes higher rank types are just what the doctor ordered.
I don't know about the etymology of the word, though.
* You might wonder whether you can do something like this:
data FooTy a where
FooTm :: FooTy (forall a. a)
Then you would get a term (FooTm) whose type had something polymorphic as an argument to something other than (->) (namely, FooTy), you don't have to cross the schoolmarm to do it, and so the belief "applying non-(->) stuff to polymorphic types isn't useful because you can't make them" would be invalidated. GHC doesn't let you write FooTy, and I will admit I'm not sure whether there's a principled reason for the restriction or not.
(Quick update some years later: there is a good, principled reason that FooTm is still not okay. Namely, the way that GADTs are implemented in GHC is via type equalities, so the expanded type of FooTm is actually FooTm :: forall a. (a ~ forall b. b) => FooTy a. Hence to actually use FooTm, one would indeed need to instantiate a type variable with a polymorphic type. Thanks to Stephanie Weirich for pointing this out to me.)
Let me just add a point regarding the "etymology" issue, since the other answer by #DanielWagner covers much of the technical ground.
A predicate on something like a is a -> Bool. Now a predicate logic is one that can in some sense reason about predicates -- so if we have some predicate P and we can talk about, for a given a, P(a), now in a "predicate logic" (such as first-order logic) we can also say ∀a. P(a). So we can quantify over variables and discuss the behavior of predicates over such things.
Now, in turn, we say a statement is predicative if all of the things a predicate is applied to are introduced prior to it. So statements are "predicated on" things that already exist. In turn, a statement is impredicative if it can in some sense refer to itself by its "bootstraps".
So in the case of e.g. the id example above, we find that we can give a type to id such that it takes something of the type of id to something else of the type of id. So now we can give a function a type where an quantified variable (introduced by forall a.) can "expand" to be the same type as that of the entire function itself!
Hence impredicativity introduces a possibility of a certain "self reference". But wait, you might say, wouldn't such a thing lead to contradiction? The answer is: "well, sometimes." In particular, "System F" which is the polymorphic lambda calculus and the essential "core" of GHC's "core" language allows a form of impredicativity that nonetheless has two levels -- the value level, and the type level, which is allowed to quantify over itself. In this two-level stratification, we can have impredicativity and not contradiction/paradox.
Although note that this neat trick is very delicate and easy to screw up by the addition of more features, as this collection of articles by Oleg indicates: http://okmij.org/ftp/Haskell/impredicativity-bites.html
I'd like to make a comment on the etymology issue, since #sclv's answer isn't quite right (etymologically, not conceptually).
Go back in time, to the days of Russell when everything is set theory— including logic. One of the logical notions of particular import is the "principle of comprehension"; that is, given some logical predicate φ:A→2 we would like to have some principle to determine the set of all elements satisfying that predicate, written as "{x | φ(x) }" or some variation thereon. The key point to bear in mind is that "sets" and "predicates" are viewed as being fundamentally different things: predicates are mappings from objects to truth values, and sets are objects. Thus, for example, we may allow quantifying over sets but not quantifying over predicates.
Now, Russell was rather concerned by his eponymous paradox, and sought some way to get rid of it. There are numerous fixes, but the one of interest here is to restrict the principle of comprehension. But first, the formal definition of the principle: ∃S.∀x.S x ↔︎ φ(x); that is, for our particular φ there exists some object (i.e., set) S such that for every object (also a set, but thought of as an element) x, we have that S x (you can think of this as meaning "x∈S", though logicians of the time gave "∈" a different meaning than mere juxtaposition) is true just in case φ(x) is true. If we take the principle exactly as written then we end up with an impredicative theory. However, we can place restrictions on which φ we're allowed to take the comprehension of. (For example, if we say that φ must not contain any second-order quantifiers.) Thus, for any restriction R, if a set S is determined (i.e., generated via comprehension) by some R-predicate, then we say that S is "R-predicative". If every set in our language is R-predicative then we say that our language is "R-predicative". And then, as is often the case with hyphenated prefix things, the prefix gets dropped off and left implicit, whence "predicative" languages. And, naturally, languages which are not predicative are "impredicative".
That's the old school etymology. Since those days the terms have gone off and gotten lives of their own. The ways we use "predicative" and "impredicative" today are quite different, because the things we're concerned about have changed. So it can sometimes be a bit hard to see how the heck our modern usage ties back to this stuff. Honestly, I don't think knowing the etymology really helps any in terms of figuring out what the words are really about (these days).
How would you explain inductive predicates? What are they used for? What's the theory behind them? Are they only present in dependent type systems, or in other systems as well? Are they related to GADT's in some way? Why are they true by default in Coq?
This is an example from Coq:
Inductive even : nat -> Prop :=
| even0 : even 0
| evens : forall p:nat, even p -> even (S (S P))
How would you use this definition? Is it a datatype or a proposition?
I think we call inductive predicates objects that are defined inductively and sorted in Prop.
They are used for defining properties inductively (duh). The theory behind can probably be found in the literature for inductive constructions. Search papers about CIC (the Calculus of Inductive Constructions) for instance.
They are somewhat related to GADTs, though with dependent types they can express more things. If I am not mistaken, with GADTs each constructor lives in one particular family, whereas the addition of dependent types allows constructors to inhabit different families based on their arguments.
I do not know what you mean by "true by default in Coq".
even as you define it is an inductive datatype. It is not a proposition yet, as the type nat -> Prop indicates. A proposition would be even 0 or even 1. It can be inhabited (provable) as in even 0, or not as in even 1.
To answer another part of the question. Inductive predicates/definitions are not only present in dependent type systems (e.g., Isabelle/HOL also has them). In principle they are much older and just describe the least predicate (or set) that is closed under some given inference rules. Whether this is well-defined or not depends on the rules. There are sufficient conditions which are easy to check (e.g., that the defined predicate itself only occurs positively in premises of rules). Then the Knaster-Tarski theorem yields the desired result.
If I remember correctly the book The Formal Semantics of Programming Languages: An Introduction by Glynn Winskel gives an introduction to the mathematical foundations.
OK, so I realise that I will probably regret this for the rest of my life, but... How does Djinn actually work?
The documentation says that it uses an algorithm which is "an extension of LJ" and points to a long confusing paper about LJT. As best as I can tell, this is a big complicated system of highly formalised rules for figuring out which logical statements are true or false. But this doesn't even begin to explain how you turn a type signature into an executable expression. Presumably all the complicated formal reasoning is involved somehow, but the picture is crucially incomplete.
It's a bit like that time I tried to write a Pascal interpretter in BASIC. (Don't laugh! I was only twelve...) I spent hours trying to figure it out, and in the end I had to give up. I just couldn't figure out how the heck you get from a giant string containing an entire program, to something you can compare against known program fragments in order to decide what to actually do.
The answer, of course, is that you need to write a thing called a "parser". Once you comprehend what this is and what it does, suddenly everything becomes obvious. Oh, it's still not trivial to code it, but the idea is simple. You just have to write the actual code. If I'd known about parsers when I was twelve, then maybe I wouldn't have spent two hours just staring at a blank screen.
I suspect that what Djinn is doing is fundamentally simple, but I'm missing some important detail which explains how all this complicated logical gymnastics relates to Haskell source code...
Djinn is a theorem prover. It seems your question is: what does theorem proving have to do with programming?
Strongly typed programming has a very close relationship to logic. In particular, traditional functional languages in the ML tradition are closely related to Intuitionist Propositional Logic.
The slogan is "programs are proofs, the proposition that a program proves is its type."
In general you can think of
foo :: Foo
as saying that foo is a proof of the formula Foo. For example the type
a -> b
corresponds to functions from a to b, so if you have a proof of a and a proof of a -> b you have a proof of b. So, function correspond perfectly to implication in logic. Similarly
(a,b)
Corresponds to conjunction (logic and). So the logic tautology a -> b -> a & b corresponds to the Haskell type a -> b -> (a,b)
and has the proof:
\a b -> (a,b)
this is the "and introduction rule"
While, fst :: (a,b) -> a and snd :: (a,b) -> b correspond to the 2 "and elimination rules"
similarly, a OR b corresponds to the Haskell type Either a b.
This correspondence is sometimes referred to as the "Curry-Howard Isomorphism" or "Curry-Howard Correspondence" after Haskell Curry and William Alvin Howard
This story is complicated by non-totality in Haskell.
Djinn is "just" a theorem prover.
If you are interested in trying to write a clone, the first page of google results for "Simple Theorem Prover" has this paper which describes writing a theorem prover for LK that appears to be written in SML.
Edit:
as to "how is theorem proving possible?" The answer is that in some sense it isn't hard. It is just a search problem:
Consider the problem restated as this: we have a set of propositions we know how to prove S, and a proposition we want to prove P. What do we do?
First of all, we ask: do we already have a proof of P in S? If so, we can use that, if not we can pattern match on P
case P of
(a -> b) -> add a to S, and prove b (-> introduction)
(a ^ b) -> prove a, then prove b (and introduction)
(a v b) -> try to prove a, if that doesn't work prove b (or introduction)
if none of those work
for each conjunction `a ^ b` in S, add a and b to S (and elimination)
for each disjunction `a v b` in S, try proving `(a -> P) ^ (b -> P)` (or elimination)
for each implication `a -> P` is S, try proving `a` (-> elimination)
Real theorem provers have some smarts, but the idea is the same. The research area of "Decision Procedures" examines strategy for finding proofs to certain kinds of formula that are guaranteed to work. On the other hand "Tactics" looks at how to optimally order the proof search.
As to: "How can proofs be translated into Haskell?"
Each inference rule in a formal system corresponds to some simple Haskell construct, so if you have a tree of inference rules, you can construct a corresponding program--Haskell is a proof language after all.
Implication introduction:
\s -> ?
Or introduction
Left
Right
And introduction
\a b -> (a,b)
And elimination
fst
snd
etc
augustss says in his answer that they way he implemented this in Djinn is a little tedious for an SO answer. I bet though, that you can figure it how to implement it on your own.
In the most general terms, according to the Curry-Howard isomorphism there is a correspondence between types and propositions and also values and proofs. Djinn uses this correspondence.
Do be more concrete, say that you want to find a Haskell term of type (a, b) -> (b, a). First you translate the type to a statement in logic (Djinn uses propositional logic, i.e., no quantifiers). The logic statement goes (A and B) is true implies (B and A) is true. The next step is to prove this. For propositional logic it is always possible to mechanically prove or disprove a statement. If we can disprove it, then that means that there can be no corresponding term in (terminating) Haskell. If we can prove it, then there is a Haskell term of that type, and furthermore, the Haskell term has the exact same structure as the proof.
The last statement has to be qualified. There are different sets of axioms and inference rules you can choose use to prove the statement. There is only a correspondence between the proof and the Haskell term if you pick a constructive logic. The "normal", i.e., classical logic has things like A or (not A) as an axiom. That would correspond to the Haskell type Either a (a -> Void), but there's no Haskell term of this type so we can't use classical logic.
It is still true that any propositional statement can be proven or disproven in constructive propositional logic, but it's considerably more involved doing so than in classical logic.
So to recap, Djinn works by translating the type to a proposition in logic, then it uses a decision procedure for constructive logic to prove the proposition (if possible), and finally the proof is translated back to a Haskell term.
(It's too painful to illustrate how this works here, but give me 10 minutes at a white board and it will be crystal clear to you.)
As a final comment for you to ponder: Either a (a -> Void) can be implemented if you have Scheme's call/cc. Pick a more concrete type like Either a (a -> Int) and figure out how.
Perhaps I'm looking at this all wrong. Perhaps all this formal logic stuff is just a distraction. Rather than staring at the deduction rules for LJT or whatever, maybe the thing I should be doing is looking at Haskell.
There are, what, 6 possible kinds of expression in Haskell? And each one places different type constraints on the variables it uses, right? So, maybe I just generate one new variable for each argument in the function's type, and start seeing what expressions I can construct.
It's not even like you have to generate all possible expressions in a brute-force search. If none of your arguments have function types, there's no point trying function applications. If all of your arguments are polymorphic type variables, case expressions aren't going to help you. And so on. The types available tell you which sorts of expressions might work.
Things get a bit more interesting if you're allowing your code to call existing top-level functions. Aside from the amusing scoping problems with polymorphic types, there's the question of figuring out which functions will or won't help you.
Clearly I will have to go away and think about this for a while...