Cω - Reference

Lifting

As we have seen above, member access is lifted over nested unlabeled substructures. Member access is similarly lifted over top-level structural types.

Selecting the member ".x" from a stream ps of type Point* (or any other stream type) is a shorthand for applying the selection to every item of the stream, that is, the expression ps.x can be considered as an abbreviation for foreach(Point p in ps){ yield return p.x; }.

  Point* ps = new{  new Point(1,2), new Point(100,200) };
  int* xs = ps.x; // xs = [1,2]

For streams, lifting is also done for method access, so if we have a stream of strings, we can convert each string in that stream to lowercase as follows:

  string* Ss = new{"A","b","C","d","E"};
  string* ss = Ss.ToLower();

For choice types, lifting is limited to fields and property access only to keep the complexity of the rules for member lookup under control (i.e. what would we do in case member lookup returns a method for one alternative and a field for the other?).

Member access for non-methods is lifted over choice{S;T;} by combining the results of casting to either S or T. The most interesting case is when all alternatives of the choice have a matching member. Since choice types are disjoint, we can uniquely determine at runtime which alternative to pick. The result type of lifting over a choice is the choice of the types of the members in each alternative of the choice:

  
class A { public int m = 1; }
class B { public string m = "hello"; }
... 
choice{A;B;} x = new A();
choice{int;string;} ms = x.m; 
Console.WriteLine(ms); // prints 1
x = new B();
ms = x.m;
Console.WriteLine(ms); // prints "hello"

If we have a value x of type choice{Button;string}, the type-checker can prove that only Button has an AllowDrop member, and the expression x.AllowDrop can be specialized to bool? indicating the possibility of returning null in case the dynamic type of x is actually string:

  choice{Button;string;} x = "Hello World!";
  bool? b = x.AllowDrop;

When lifting member access over streams, the result can be a stream of streams. In that case the stream is flattened. The cardinality of the result stream is determined by both streams. (The stream types on which the member access is applied is given by the columns, the return type of the member access on the stream element is given by the rows).
 

  ! ? + *
void void void void void
    ? * *
! ! ? + *
? ? ? * *
+ + * + *
* * * * *


For example if we have a stream of type choice{Button;string;}* and we lift AllowDrop over that stream, the resulting stream has type bool*, i.e. the ? on the result type of the member access gets absorbed by the other stream.

If we do know that the stream is never empty itself, then lifting member access that returns a non-null value returns a non-empty stream as well. However, if the value of the member can be null, then the result is a possibly empty stream, even if the source stream is never null:

  class A { public int? a; }
  A! x = new A(); // x not null
  int? y = x.a;   // but y is null;

When the return type of member access on the element type is void, the result of lifting is void as well, regardless of the cardinality of the stream:

  Button* bs = …;
  bs.BringToFront(); // type void