Question 1: Can I write a function $f$ with the type
$\forall\alpha,\;(\alpha\to\alpha\to\mathbb{B})\to(\alpha\to t)$
such that $f\;e$ is an injection for
all equivalence relations $e$?
The answer is negative. Neel Krishnaswami noticed that $f\;(=_{t'})$ is an injection only if $|t'|\le|t|$, which is not the case when $t'$ is $t\to\mathbb{B}$. Tsuyoshi Ito noticed that an even stronger statment must be true: $f\;e$ is a constant function.
Here is a proof that was shown to me by Rasmus Petersen. Go to the free theorem generator and type in "(a->a->Bool)->(a->Integer)". (The last type is Integer
instead of $t$ so that the website knows that it is a type, not a type variable.) The free theorem for this type is $$\forall t\,t'\,\forall R\,\forall p\,p',\;\bigl(\forall (x,x')\,(y,y'),\;p\;x\;y=p'\;x'\;y'\bigr)\Rightarrow\bigl(\forall (x,x'),\;f\;p\;x=f\;p'\;x'\bigr).$$ Here, $t$ and $t'$ are types; $R$ is a relation on these types; (in our case) $p$ and $p'$ are equivalence relations on $t$ and, respectively, $t'$; and we have $xRx'$ and $yRy'$. We now pick a relation $R$ such that the premise is satisfied and the right hand side of the conclusion reduces to a constant: $R=\{(x,())\}$, where $x$ is some fixed but arbitrary element of $t$, and $t'=\mathbb{U}=\{()\}$ is the unit type. $$f\;p\;x=f\;(=_\mathbb{U})\;()$$
Q.E.D. The notes I posted in a comment contain a longer proof, and more explanations.
Question 2: Can I implement it if $f$
keeps some state?
Neel's observation still holds. You can't.
Question 3: Huh? But I did implement
a version that works slow!
First, I believe your implementation is not polymorphic.
Second, and more important, even if there is no injection from $\mathbb{Z}\to\mathbb{B}$ to $\mathbb{Z}$, there is one from the elements of $\mathbb{Z}\to\mathbb{B}$ that your program constructs in some finite time.
(If you give up polymorphism but not state, then there is a way to find a counterexample to any potential implementation. I learned this from Paulo Oliva, but I don't know how yet.)
Question 4: That's all good and
theoretical. Can you also tell me
something actually helps me write my
program?
After all those impossibility results, I think it's reasonable to settle for the question: "How do I implement a hash function without collisions for some given type $t$?'' The standard solution is hash-consing, a technique first described by Ershov in 1957 (in Russian, and in 1958 in English) that should be much better known than it is.
If the equivalence relation is (reference) equality, then there is a very simple hash: Just take the address of the object. So we can recast the problem as follows: "How do we ensure that we build at most one element from each equivalence class?" Let's use structural equality as equivalence relation. If we want to build an object whose parts are $x_1,\ldots,x_n$ we call $\mathit{mk}\;x_1\ldots x_n$. In that function, we first lookup the tuple $(x_1,\ldots,x_n)$ in a dictionary and, if a value is found, we return it. (Since $x_1,\ldots,x_n$ were built before, they are class representatives.) Otherwise, we construct a new data structure.
For a different equivalence relation you need to adapt hash-consing, exploiting properties of your particular equivalence.
The implementation is a bit trickier than this description might suggest. Some (hidden) test data is here.
PS: I mentioned some people from which I learned some parts of the answer. Naturally, if those parts are wrong, it is likely I misunderstood what I've been told.
int
is really ℤ, not 32-bit int (or something slightly larger). Then the question looks related to a free theorem. I believe that for every functionF
that has the signature∀obj. ((obj, obj) -> bool) -> obj -> int
,F Eq a
evaluates to a constantint
value independent ofEq
anda
(because I think that your implicit assumption is thatEq(a, a)
always evaluates to true). I am not familiar with the theory of programming languages to actually prove it. $\endgroup$