Skip to main content

Unifier

Struct Unifier 

Source
pub struct Unifier { /* private fields */ }
Expand description

Holds the inference variables of a type problem and the substitution that unification builds over them.

A Unifier is the working state of type inference. It mints fresh TyVars with fresh, makes two types equal with unify — recording the variable bindings that equality requires — and reads a type back under those bindings with resolve. A variable belongs to the unifier that minted it.

Unification is the standard first-order algorithm: two constructors match only if their heads and arity agree, in which case their arguments are unified pairwise; a variable unifies with any type by being bound to it, guarded by an occurs check so a variable can never be bound to a type that contains it. The substitution it produces is a most-general unifier — it constrains a variable only as far as making the two types equal demands.

§Examples

use type_lang::{TyCon, Type, Unifier};

const FUNCTION: TyCon = TyCon::new(0);
const INT: TyCon = TyCon::new(1);

let mut unifier = Unifier::new();
let arg = unifier.fresh();
let ret = unifier.fresh();

// Unify the inferred signature  (?arg) -> ?ret  with  (int) -> int.
let inferred = Type::app(FUNCTION, [Type::var(arg), Type::var(ret)]);
let known = Type::app(FUNCTION, [Type::con(INT), Type::con(INT)]);
unifier.unify(&inferred, &known).expect("same constructor and arity");

// Both variables are now known to be int.
assert_eq!(unifier.resolve(&Type::var(arg)), Type::con(INT));
assert_eq!(unifier.resolve(&Type::var(ret)), Type::con(INT));

Implementations§

Source§

impl Unifier

Source

pub const fn new() -> Self

Creates an empty unifier with no variables.

§Examples
use type_lang::Unifier;

let unifier = Unifier::new();
assert!(unifier.is_empty());
Source

pub fn with_capacity(vars: usize) -> Self

Creates an empty unifier with room for vars variables preallocated.

A hint only: it sizes the internal table so that minting up to vars variables does not reallocate. Use it when the variable count is known up front — for instance, one per binding in the scope being checked.

§Examples
use type_lang::Unifier;

let mut unifier = Unifier::with_capacity(8);
for _ in 0..8 {
    let _ = unifier.fresh();
}
assert_eq!(unifier.var_count(), 8);
Source

pub fn fresh(&mut self) -> TyVar

Mints a fresh, unbound inference variable.

Variables are numbered in creation order from 0 and stay valid for the life of the unifier. A fresh variable stands for “some type not yet known”; it gains a binding only when unify requires one.

§Examples
use type_lang::Unifier;

let mut unifier = Unifier::new();
let a = unifier.fresh();
let b = unifier.fresh();
assert_ne!(a, b);
assert_eq!(unifier.var_count(), 2);
Source

pub fn var_count(&self) -> usize

Returns the number of variables this unifier has minted.

Source

pub fn is_empty(&self) -> bool

Returns true if no variables have been minted.

Source

pub fn unify(&mut self, a: &Type, b: &Type) -> Result<(), TypeError>

Unifies two types, binding variables as needed to make them equal.

On success the unifier’s substitution is extended so that a and b resolve to the same type. On failure the call returns a TypeError describing why, and any bindings made before the conflict was reached are kept — unification is not transactional. A caller that needs to try a unification speculatively should clone the unifier first and keep the clone only if it succeeds.

§Errors
  • TypeError::Mismatch if the two types are built from different constructors or the same constructor at a different arity.
  • TypeError::Occurs if making them equal would require binding a variable to a type that contains it (an infinite type).
§Examples
use type_lang::{TyCon, Type, TypeError, Unifier};

const INT: TyCon = TyCon::new(0);
const BOOL: TyCon = TyCon::new(1);

let mut unifier = Unifier::new();
let v = unifier.fresh();

// A variable unifies with a concrete type by being bound to it.
unifier.unify(&Type::var(v), &Type::con(INT)).unwrap();
assert_eq!(unifier.resolve(&Type::var(v)), Type::con(INT));

// Two different constructors do not unify.
let err = unifier.unify(&Type::con(INT), &Type::con(BOOL)).unwrap_err();
assert!(matches!(err, TypeError::Mismatch { .. }));
Source

pub fn resolve(&self, ty: &Type) -> Type

Resolves a type fully under the current substitution.

Every bound variable in ty is replaced by what it was bound to, all the way down, leaving a type whose only variables are still unbound. A type with no bound variables resolves to an equal copy of itself.

The walk is proportional to the size of the resolved result, which — for a substitution that maps a variable to a type mentioning it twice — can be larger than the input. Resolve once you are done constraining a type, rather than after every step.

§Examples
use type_lang::{TyCon, Type, Unifier};

const LIST: TyCon = TyCon::new(0);
const INT: TyCon = TyCon::new(1);

let mut unifier = Unifier::new();
let element = unifier.fresh();
let collection = unifier.fresh();

// ?collection = List<?element>, then ?element = int.
unifier
    .unify(&Type::var(collection), &Type::app(LIST, [Type::var(element)]))
    .unwrap();
unifier.unify(&Type::var(element), &Type::con(INT)).unwrap();

// Resolving the outer variable substitutes all the way down.
assert_eq!(
    unifier.resolve(&Type::var(collection)),
    Type::app(LIST, [Type::con(INT)]),
);

// An unbound variable resolves to itself.
let free = unifier.fresh();
assert_eq!(unifier.resolve(&Type::var(free)), Type::var(free));

Trait Implementations§

Source§

impl Clone for Unifier

Source§

fn clone(&self) -> Unifier

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl Debug for Unifier

Source§

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

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

impl Default for Unifier

Source§

fn default() -> Unifier

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

impl<'de> Deserialize<'de> for Unifier

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for Unifier

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

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
Source§

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

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

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,

Source§

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>,

Source§

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>,

Source§

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.