Practical is a strong word. Though many things start out as abstract academic nonsense and then someone actually turns them into something practical. Rust demonstrates that!
But if you want to see some of the sorts of things where this sort of thing appears in my code... enjoy!
(At some point I should probably use macros for de-duplication)
use std::marker::PhantomData;
use std::fmt::Display;
struct Proof<T: ?Sized>(PhantomData<fn(&T) -> &T>);
trait Top {}
impl<T:?Sized> Top for T {}
trait WithTop<T: ?Sized> {
type Output;
fn go(self) -> Self::Output where T:Top;
}
trait IsTopProof<T: ?Sized> {
fn go<Output>(&self, callback : impl WithTop<T,Output=Output>) -> Output;
}
impl<T: Top + ?Sized> IsTopProof<T> for () {
fn go<Output>(&self, callback : impl WithTop<T,Output=Output>) -> Output {
callback.go()
}
}
impl<T: ?Sized> IsTopProof<T> for ! {
fn go<Output>(&self, _ : impl WithTop<T,Output=Output>) -> Output {
*self
}
}
struct TopProof<T: ?Sized>(Proof<T>);
impl<T: ?Sized> IsTopProof<T> for TopProof<T> {
fn go<Output>(&self, callback: impl WithTop<T,Output=Output>) -> Output {
let boxed: Box<dyn WithTop<T,Output=Output>> = Box::new(callback);
//SAFETY: Hoping the vtables line up...
let alt_boxed : Box<dyn FnOnce() -> Output> = unsafe { std::mem::transmute(boxed) };
alt_boxed()
}
}
impl<T: ?Sized> TopProof<T> {
pub unsafe fn unsafe_mk() -> TopProof<T> {
TopProof(Proof(PhantomData))
}
pub fn mk() -> TopProof<T> where T : Top {
TopProof::from_proof(())
}
pub fn from_proof(val : impl IsTopProof<T>) -> TopProof<T> {
struct Mk<Inner: ?Sized>(PhantomData<fn() -> TopProof<Inner>>);
impl<Inner: ?Sized> WithTop<Inner> for Mk<Inner> {
type Output=TopProof<Inner>;
fn go(self) -> TopProof<Inner> {
unsafe { TopProof::unsafe_mk() }
}
}
val.go(Mk(PhantomData))
}
}
trait WithDisplay<T: ?Sized> {
type Output;
fn go(self) -> Self::Output where T:Display;
}
trait IsDisplayProof<T: ?Sized> {
fn go<Output>(&self, callback : impl WithDisplay<T,Output=Output>) -> Output;
}
impl<T: Display + ?Sized> IsDisplayProof<T> for () {
fn go<Output>(&self, callback : impl WithDisplay<T,Output=Output>) -> Output {
callback.go()
}
}
impl<T: ?Sized> IsDisplayProof<T> for ! {
fn go<Output>(&self, _ : impl WithDisplay<T,Output=Output>) -> Output {
*self
}
}
struct DisplayProof<T: ?Sized>(Proof<T>);
impl<T: ?Sized> IsDisplayProof<T> for DisplayProof<T> {
fn go<Output>(&self, callback: impl WithDisplay<T,Output=Output>) -> Output {
let boxed: Box<dyn WithDisplay<T,Output=Output>> = Box::new(callback);
//SAFETY: Hoping the vtables line up...
let alt_boxed : Box<dyn FnOnce() -> Output> = unsafe { std::mem::transmute(boxed) };
alt_boxed()
}
}
impl<T: ?Sized> DisplayProof<T> {
pub unsafe fn unsafe_mk() -> DisplayProof<T> {
DisplayProof(Proof(PhantomData))
}
pub fn mk() -> DisplayProof<T> where T : Display {
DisplayProof::from_proof(())
}
pub fn from_proof(val : impl IsDisplayProof<T>) -> DisplayProof<T> {
struct Mk<Inner: ?Sized>(PhantomData<fn() -> DisplayProof<Inner>>);
impl<Inner: ?Sized> WithDisplay<Inner> for Mk<Inner> {
type Output=DisplayProof<Inner>;
fn go(self) -> DisplayProof<Inner> {
unsafe { DisplayProof::unsafe_mk() }
}
}
val.go(Mk(PhantomData))
}
}
impl<T: ?Sized> DisplayProof<T> {
pub fn print(&self, val : &T) {
struct Callback<'a,Inner: ?Sized>(&'a Inner);
impl<'a, Inner: ?Sized> WithDisplay<Inner> for Callback<'a, Inner> {
type Output = ();
fn go(self) where Inner:Display{
print!("{}",self.0)
}
}
self.go(Callback(val))
}
}
trait WithSend<T: ?Sized> {
type Output;
fn go(self) -> Self::Output where T:Send;
}
trait IsSendProof<T: ?Sized> {
fn go<Output>(&self, callback : impl WithSend<T,Output=Output>) -> Output;
}
impl<T: Send + ?Sized> IsSendProof<T> for () {
fn go<Output>(&self, callback : impl WithSend<T,Output=Output>) -> Output {
callback.go()
}
}
impl<T: ?Sized> IsSendProof<T> for ! {
fn go<Output>(&self, _ : impl WithSend<T,Output=Output>) -> Output {
*self
}
}
struct SendProof<T: ?Sized>(Proof<T>);
impl<T: ?Sized> IsSendProof<T> for SendProof<T> {
fn go<Output>(&self, callback: impl WithSend<T,Output=Output>) -> Output {
let boxed: Box<dyn WithSend<T,Output=Output>> = Box::new(callback);
//SAFETY: Hoping the vtables line up...
let alt_boxed : Box<dyn FnOnce() -> Output> = unsafe { std::mem::transmute(boxed) };
alt_boxed()
}
}
impl<T: ?Sized> SendProof<T> {
pub unsafe fn unsafe_mk() -> SendProof<T> {
SendProof(Proof(PhantomData))
}
pub fn mk() -> SendProof<T> where T : Send {
SendProof::from_proof(())
}
pub fn from_proof(val : impl IsSendProof<T>) -> SendProof<T> {
struct Mk<Inner: ?Sized>(PhantomData<fn() -> SendProof<Inner>>);
impl<Inner: ?Sized> WithSend<Inner> for Mk<Inner> {
type Output=SendProof<Inner>;
fn go(self) -> SendProof<Inner> {
unsafe { SendProof::unsafe_mk() }
}
}
val.go(Mk(PhantomData))
}
}
trait WithSync<T: ?Sized> {
type Output;
fn go(self) -> Self::Output where T:Sync;
}
trait IsSyncProof<T: ?Sized> {
fn go<Output>(&self, callback : impl WithSync<T,Output=Output>) -> Output;
}
impl<T: Sync + ?Sized> IsSyncProof<T> for () {
fn go<Output>(&self, callback : impl WithSync<T,Output=Output>) -> Output {
callback.go()
}
}
impl<T: ?Sized> IsSyncProof<T> for ! {
fn go<Output>(&self, _ : impl WithSync<T,Output=Output>) -> Output {
*self
}
}
struct SyncProof<T: ?Sized>(Proof<T>);
impl<T: ?Sized> IsSyncProof<T> for SyncProof<T> {
fn go<Output>(&self, callback: impl WithSync<T,Output=Output>) -> Output {
let boxed: Box<dyn WithSync<T,Output=Output>> = Box::new(callback);
//SAFETY: Hoping the vtables line up...
let alt_boxed : Box<dyn FnOnce() -> Output> = unsafe { std::mem::transmute(boxed) };
alt_boxed()
}
}
impl<T: ?Sized> SyncProof<T> {
pub unsafe fn unsafe_mk() -> SyncProof<T> {
SyncProof(Proof(PhantomData))
}
pub fn mk() -> SyncProof<T> where T : Sync {
SyncProof::from_proof(())
}
pub fn from_proof(val : impl IsSyncProof<T>) -> SyncProof<T> {
struct Mk<Inner: ?Sized>(PhantomData<fn() -> SyncProof<Inner>>);
impl<Inner: ?Sized> WithSync<Inner> for Mk<Inner> {
type Output=SyncProof<Inner>;
fn go(self) -> SyncProof<Inner> {
unsafe { SyncProof::unsafe_mk() }
}
}
val.go(Mk(PhantomData))
}
}
//Fully safe code!
fn sync_to_send<'a, T: ?Sized>(proof : SyncProof<T>) -> SendProof<&'a T> {
struct MkSendProof<Inner: ?Sized>(PhantomData<fn() -> SendProof<Inner>>);
impl<'a_inner, Inner: ?Sized> WithSync<Inner> for MkSendProof<&'a_inner Inner> {
type Output = SendProof<&'a_inner Inner>;
fn go(self) -> SendProof<&'a_inner Inner> where Inner:Sync {
SendProof::mk()
}
}
proof.go(MkSendProof(PhantomData))
}
//The reverse can't be done with safe code
/*
fn send_to_sync<'a, T: ?Sized>(proof : SendProof<&'a T>) -> SyncProof<T> {
struct MkSyncProof<Inner: ?Sized>(PhantomData<fn() -> SyncProof<Inner>>);
impl<'a_inner, Inner: ?Sized> WithSend<&'a_inner Inner> for MkSyncProof<Inner> {
type Output = SyncProof<Inner>;
fn go(self) -> SyncProof<Inner> where &'a_inner Inner:Send {
SyncProof::mk()
}
}
proof.go(MkSyncProof(PhantomData))
}
*/
// But we could do it with unsafe code, if we wanted!
// Is this actually sound?
fn send_to_sync<'a, T: ?Sized>(_ : SendProof<&'a T>) -> SyncProof<T> {
unsafe { SyncProof::unsafe_mk() }
}
struct NotDisplay;
fn main() {
//It actually works!
DisplayProof::<u8>::mk().print(&5);
if false {
// THIS IS BAD
unsafe { DisplayProof::unsafe_mk().print(&NotDisplay) };
}
}
Since trait proofs can now be represented as values we can just move around, I'm hoping to extend this system to extracting and recombining dyn metadata, and possibly trying to implement working polymorphic recursion using some terrible abuse of vtable overlap.
Obviously none of this is... how about we use the word 'sane'. None of this is sane.
But rust is already capable of some representation here!
Each of those ZSTs asserts the existence of a vtable. With things like std::ptr::DynMetadata
we can then further manipulate those vtables themselves.
However, if I want abstract category theory manipulation of the category of rust traits and rust vtables, top and bottom naturally fall out of that (in fact it would arguably be more proper for me to be casting to WithTop
rather than FnOnce
, it is only because FnOnce
lets me get around the Box
issue I do it).
If I want to represent the trait hierarchy properly, this is a must. For example, BottomProof<T> <-> DerefMutProof<&'a T, T>
is, I believe, the correct function for lifting the trait hierarchy to the trait level, given how things are currently argued in things like Pin
. Though actually that is debatable (Should &! : DerefMut
be valid? There is talk about making &!
itself an uninhabited type in terms of even validity rules, and even without that it is clearly already uninhabited for safety rules).
With this (horrible abomination of a system) we can represent all these questions as about the safety of various rust functions! We can even, I believe, offer things like From<!> for T
without further coherence work (though at so my boilerplate that one might question why do so)!