% crochet

// Types of types
abstract g-abstract-type;
singleton g-singleton-type;
type g-regular-type;
type g-regular-type-parameters(a, b, c);

// Extensions on types
abstract g-list;
type g-list-node(value, rest is g-list) is g-list;
singleton g-list-end is g-list;
type g-list-a is g-list;

singleton g-list1;
type g-list-node1(value, rest is g-list1) is g-list1;
singleton g-list-end1 is g-list1;
type g-list-a1 is g-list1;

type g-list2;
type g-list-node2(value, rest is g-list2) is g-list2;
singleton g-list-end2 is g-list2;
type g-list-a2 is g-list2;

// Using types
test "Constructing types" do
  let A = new g-regular-type;
  let B = new g-regular-type-parameters(1, 2, 3);

  let C = new g-list-node(1, g-list-end);

  // FIXME: allow assert failures
  // assert fail new g-abstract-type;
  // assert fail new g-singleton-type;
end

test "Projecting from types" do
  let A = new g-regular-type-parameters(1, 2, 3);
  
  assert A.a =:= 1;
  assert A.b =:= 2;
  assert A.c =:= 3;

  //FIXME: re-implement select?
  //assert A.(a, b) =:= [a -> 1, b -> 2];
  //assert A.(a as x, b as y) =:= [x -> 1, y -> 2];

  //assert [A, A].(a, b as c) =:= [[a -> 1, c -> 2], [a -> 1, c -> 2]];
end

// Defining commands on types
command g-list-node count = 1 + self.rest count;
command g-list-end count = 0;
command #g-list empty = g-list-end;

test "Dispatch on types" do
  let A = new g-list-node(1, new g-list-node(2, g-list-end));

  assert A count =:= 2;
  assert A.rest count =:= 1;
  assert g-list-end count =:= 0;
  assert #g-list empty count =:= 0;
end

// Local types
local type a;
local singleton b;
local abstract c;

command a name = 1;
command b name = 2;
command #c name = 3;

test "Local types are usable" do
  let A = new a;
  let B = b;

  assert A name =:= 1;
  assert B name =:= 2;
  assert #c name =:= 3;
end

// FIXME: check that local types can't be accessible in the global registry

// Types that have public fields
type g-point(global x is integer, global y is integer);

command #g-point x: X y: Y = new g-point(X, Y);

test "Public fields create accessor methods" do
  let P = #g-point x: 1 y: 2;

  assert P x =:= 1;
  assert P y =:= 2;
end