Struct polytype::Context

source ·
pub struct Context<N: Name = &'static str> { /* private fields */ }
Expand description

A type environment. Useful for reasoning about Types (e.g unification, type inference).

Contexts track substitutions and generate fresh type variables.

Implementations§

source§

impl<N: Name> Context<N>

source

pub fn substitution(&self) -> &IndexMap<Variable, Type<N>>

The substitution managed by the context.

source

pub fn len(&self) -> usize

The number of constraints in the substitution.

source

pub fn is_empty(&self) -> bool

true if the substitution has any constraints, else false.

source

pub fn clean(&mut self)

Clears the substitution managed by the context.

Examples
let mut ctx = Context::default();

// Get a fresh variable
let t0 = ctx.new_variable();
let t1 = ctx.new_variable();

let clean = ctx.clone();

if let Type::Variable(N) = t0 {
    ctx.extend(N, tp![t1]);
    let dirty = ctx.clone();

    ctx.clean();

    assert_eq!(clean, ctx);
    assert_ne!(clean, dirty);
}
source

pub fn rollback(&mut self, n: usize)

Removes previous substitutions added to the Context until there are only n remaining.

source

pub fn extend(&mut self, v: Variable, t: Type<N>)

Create a new substitution for Type::Variable number v to the Type t.

source

pub fn new_variable(&mut self) -> Type<N>

Create a new Type::Variable from the next unused number.

Examples
let mut ctx = Context::default();

// Get a fresh variable
let t0 = ctx.new_variable();
assert_eq!(t0, Type::Variable(0));

// Instantiating a polytype will yield new variables
let t = ptp!(0, 1; @arrow[tp!(0), tp!(1), tp!(1)]);
let t = t.instantiate(&mut ctx);
assert_eq!(t.to_string(), "t1 → t2 → t2");

// Get another fresh variable
let t3 = ctx.new_variable();
assert_eq!(t3, Type::Variable(3));
source

pub fn unify( &mut self, t1: &Type<N>, t2: &Type<N> ) -> Result<(), UnificationError<N>>

Create constraints within the context that ensure t1 and t2 unify.

Examples
let mut ctx = Context::default();

let t1 = tp!(@arrow[tp!(int), tp!(0)]);
let t2 = tp!(@arrow[tp!(1), tp!(bool)]);
ctx.unify(&t1, &t2).expect("unifies");

let t1 = t1.apply(&ctx);
let t2 = t2.apply(&ctx);
assert_eq!(t1, t2);  // int → bool

Unification errors leave the context unaffected. A UnificationError::Failure error happens when symbols don’t match:

let mut ctx = Context::default();

let t1 = tp!(@arrow[tp!(int), tp!(0)]);
let t2 = tp!(@arrow[tp!(bool), tp!(1)]);
let res = ctx.unify(&t1, &t2);

if let Err(UnificationError::Failure(left, right)) = res {
    // failed to unify t1 with t2.
    assert_eq!(left, tp!(int));
    assert_eq!(right, tp!(bool));
} else { unreachable!() }

An UnificationError::Occurs error happens when the same type variable occurs in both types in a circular way. Ensure you instantiate your types properly, so type variables don’t overlap unless you mean them to.

let mut ctx = Context::default();

let t1 = tp!(1);
let t2 = tp!(@arrow[tp!(bool), tp!(1)]);
let res = ctx.unify(&t1, &t2);

if let Err(UnificationError::Occurs(v)) = res {
    // failed to unify t1 with t2 because of circular type variable occurrence.
    // t1 would have to be bool -> bool -> ... ad infinitum.
    assert_eq!(v, 1);
} else { unreachable!() }
source

pub fn unify_fast( &mut self, t1: Type<N>, t2: Type<N> ) -> Result<(), UnificationError<N>>

Like unify, but may affect the context even under failure. Hence, use this if you discard the context upon failure.

source

pub fn confine(&mut self, keep: &[Variable])

Confines the substitution to those which act on the given variables.

Examples
let mut ctx = Context::default();
let v0 = ctx.new_variable();
let v1 = ctx.new_variable();
ctx.unify(&v0, &tp!(int));
ctx.unify(&v1, &tp!(bool));

{
    let sub = ctx.substitution();
    assert_eq!(sub.len(), 2);
    assert_eq!(sub[&0], tp!(int));
    assert_eq!(sub[&1], tp!(bool));
}

// confine the substitution to v1
ctx.confine(&[1]);
let sub = ctx.substitution();
assert_eq!(sub.len(), 1);
assert_eq!(sub[&1], tp!(bool));
source

pub fn merge( &mut self, other: Context<N>, sacreds: Vec<Variable> ) -> ContextChange

Merge two type contexts.

Every Type (TypeScheme) that corresponds to the other context must be reified using ContextChange::reify_type (ContextChange::reify_typescheme). Any Variable in sacreds will not be changed by the context (i.e. reification will ignore it).

Examples

Without sacred variables, which assumes that all type variables between the contexts are distinct:

let mut ctx = Context::default();
let a = ctx.new_variable();
let b = ctx.new_variable();
ctx.unify(&Type::arrow(a, b), &tp!(@arrow[tp!(int), tp!(bool)])).unwrap();
// ctx uses t0 and t1

let mut ctx2 = Context::default();
let pt = ptp!(0, 1; @arrow[tp!(0), tp!(1)]);
let mut t = pt.instantiate(&mut ctx2);
ctx2.extend(0, tp!(bool));
assert_eq!(t.apply(&ctx2).to_string(), "bool → t1");
// ctx2 uses t0 and t1

let ctx_change = ctx.merge(ctx2, vec![]);
// rewrite all terms under ctx2 using ctx_change
ctx_change.reify_type(&mut t);
assert_eq!(t.to_string(), "t2 → t3");
assert_eq!(t.apply(&ctx).to_string(), "bool → t3");

assert_eq!(ctx.new_variable(), tp!(4));

With sacred variables, which specifies which type variables are equivalent in both contexts:

let mut ctx = Context::default();
let a = ctx.new_variable();
let b = ctx.new_variable();
ctx.unify(&Type::arrow(a, b), &tp!(@arrow[tp!(int), tp!(bool)])).unwrap();
// ctx uses t0 and t1

let mut ctx2 = Context::default();
let a = ctx2.new_variable();
let b = ctx2.new_variable();
let mut t = Type::arrow(a, b);
ctx2.extend(0, tp!(bool));
assert_eq!(t.apply(&ctx2).to_string(), "bool → t1");
// ctx2 uses t0 and t1

// t1 from ctx2 is preserved *and* constrained by ctx
let ctx_change = ctx.merge(ctx2, vec![1]);
// rewrite all terms under ctx2 using ctx_change
ctx_change.reify_type(&mut t);
assert_eq!(t.to_string(), "t2 → t1");
assert_eq!(t.apply(&ctx).to_string(), "bool → bool");

assert_eq!(ctx.new_variable(), tp!(4));

Trait Implementations§

source§

impl<N: Clone + Name> Clone for Context<N>

source§

fn clone(&self) -> Context<N>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<N: Debug + Name> Debug for Context<N>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<N: Name> Default for Context<N>

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<N: Name> PartialEq for Context<N>

source§

fn eq(&self, other: &Self) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl<N: Name> Eq for Context<N>

Auto Trait Implementations§

§

impl<N = &'static str> !RefUnwindSafe for Context<N>

§

impl<N> Send for Context<N>
where N: Send + Sync,

§

impl<N> Sync for Context<N>
where N: Send + Sync,

§

impl<N> Unpin for Context<N>
where N: Unpin,

§

impl<N = &'static str> !UnwindSafe for Context<N>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.