Expand description

Type-level implementation of SF. Prooving that type-system turing-complete. Higtly inspired by this article.

Features

typed-sf supports “no_std” feature with limitations. Runtime representation of List’s and State’s unavaliable (they are using Vec to represent theyself).

Full list of unsupported features:

Also where is “require_docs” feature which forbids undocumented pub items. If You want to contrubute, feel free to turn off this default feature.

How it works

First, where’s some traits what i’m calling type-level enums (Bit, List). Have a look at it:

enum Bit {
    True,
    False
}

impl Bit {
    fn neg(self) -> Bit {
        match self {
            Bit::True => Bit::False,
            Bit::False => Bit::True
        }
    }
}

assert_eq!(Bit::True, Bit::False.neg());
assert_eq!(Bit::False, Bit::True.neg());
trait Bit {
    type Neg: Bit;
    fn real() -> bool;
}
struct True;
struct False;

impl Bit for True {
    type Neg = False;
    fn real() -> bool { true }
}

impl Bit for False {
    type Neg = True;
    fn real() -> bool { false }
}

assert_eq!(True::real(), <False as Bit>::Neg::real());
assert_eq!(False::real(), <True as Bit>::Neg::real());

Of course, what’s more bolierplate, but also instead of matching in runtime, compiler solves type-equasions in compile-time for us.

To type-encode functions i’m using something like this:

// A and B are arguments to 'function'.
trait Call<A, B> { type Return; }

struct Sum;
struct Diff;

impl<A, B> Call<A, B> for Sum
where
    A: Add<B>
{
    type Return = <A as Add<B>>::Output;
}

impl<A, B> Call<A, B> for Diff
where
    A: Sub<B>
{
    type Return = <A as Sub<B>>::Output;
}

type Apply<A, Fn, B> = <Fn as Call<A, B>>::Return;

What we can use later as:

struct X;
struct Y;
struct XaddY;
struct XsubY;

impl Add<Y> for X {
    type Output = XaddY;
}
impl Sub<Y> for X {
    type Output = XsubY;
}

/* Add `::val() -> Self` function to all types... */

assert_eq!(
    <Apply<X, Sum, Y>>::val(),
    XaddY::val()
);

assert_eq!(
    <Apply<X, Diff, Y>>::val(),
    XsubY::val()
);

That’s basically how Runner<Left, Value, Right> works. But commands such as Left takes an generic argument, so it runs next command on modified state which does same and so on…

Examples

Basic usage

type SetTrue<Next = EOF> = Cycle<Flip, Flip<Next>>;
// [*<<[*]*>>>] // Move any-sized chunk of True's 2 cells left
type prog = Cycle<Flip<Left<Left<SetTrue<Right<Right<Right>>>>>>>;
type result = Run<
    prog,
    State<Nil, True, Cons<True, Nil>>
>;

assert_eq!(
    <result as StateTrait>::val(),
    (vec![true, true, false, false], false, Vec::new())
); //                 ^^^^^^^^^^^^
   // Some waste, honestly don't why it is here...

Structs

Node of list, consists of value (Bit) and tail – List.

Cycling ([ <Body> ] <Next> in SF) command. If current Bit is False – executes Next Command, otherwise – executes Body and itself again.

Default state, alias for State<Nil, Value, Nil>.

End-of-file command. Returns current state.

Type-level False value, Bit.

Flip current Bit (* <Next> in SF) command. Actually, just uses Bit::Neg type.

Go left (< <Next> in SF) command. If left List is Nil, current bit will be Filler argument value (default – False)

End of List.

Go right (> <Next> in SF) command. If right List is Nil, current bit will be Filler argument value (default – False)

Zipper-list, representing current state of program. Consists of left-hand List, Value – Bit and right-hand List.

Type-level True value, Bit.

Traits

Type-level enum of True and False.

Type-level enum of commands. Supertrait of Runner

Type-level linked list, enum-like of Nil and Cons

Represents result of running some Command on State via generic arguments.

Single-type trait for State type, used to get Left, Value and Rigth types.

Type Definitions

Alias for running Program on State

Real value of any State.

Sets current bit to False.

Sets current bit to True.