Cω - Samples

Transitive Member Access Tutorial

This tutorial shows how to perform transitive member access in a simple program.

Sample Files

To run this tutorial, you may use the following project and source files:

These files are located in the \samples\Basics\Foreach subdirectory under the path where you installed Cω, which by default is C:\Program Files\Comega.

Further Reading

Tutorial

This tutorial is divided into the following sections:

Transitive Member Access in General

Normal member access such as b.Text or hello.Body selects only the direct members of a singleton or stream of object instances. On the other hand transitive member access selects all recursively reachable accessible members (where access also lifts over streams). For example using transitive member access, we can rewrite the expression hello.Body.P as hello...P, which selects all accessible P members from hello, no matter where in the object graph of hello they appear.

Filters

Every once in a while we receive urgent messages that promise to make us rich quick and risk-free, and we definitively do not want to miss the opportunity to make a quick 9.5 million dollars:

<Email>
  <Header>
    <From>assdgsdgsdg@hotmail.com</From>
    <To>undisclosed recipients</To>
    <Subject>URGENT ASSISTANCE NEEDED</Subject>
  </Header>
  <Body>
   ...
  <P>
    WITH OUR POSITIONS, WE HAVE SUCCESSFULLY SECURED
    FOR OURSELVES THE SUM OF THIRTY ONE MILLION,
    FIVE HUNDRED THOUSAND UNITED STATES DOLLARS (US$31.5M).
    THIS AMOUNT WAS CAREFULLY MANIPULATED
    BY OVER-INVOICING OF AN OLD CONTRACT.
 </P>
  ...
  <P>
   IT HAS BEEN AGREED THAT THE OWNER OF THE ACCOUNT
   WILL BE COMPENSATED WITH 30% OF THE REMITTED FUNDS,
   WHILE WE KEEP 60% AS THE INITIATORS AND 10% WILL BE
   SET ASIDE TO OFFSET EXPENSES AND PAY THE NECESSARY TAXES.
  </P>
  ...
  <P>THIS TRANSACTION IS 100% RISK FREE.</P>
  ...
  </Body>
</Email>; 

The code below uses Cω filters, and type-based transitive member access to define the MustRead virtual method on the type Email that we can use to filter interesting messages from our mailbox.

A message is interesting if it contains certain trigger words. The function IsInteresting checks if a given string contains one of those interesting words:

  bool IsInteresting (string s){
    return
      (  s.IndexOf("URGENT") >= 0
      || s.IndexOf("YOUR ASSISTANCE") >= 0
      || s.IndexOf("MILLION") >= 0
      || s.IndexOf("100% RISK FREE") >= 0
      );
  }

Given a message msg, the transitive member access expression msg...string::* selects all recursively accessible members of type string in msg. In this case, the stream of strings returned by ms...string::* is the stream consisting of the strings this.Header.From, this.Header.To, this.Header.Subject, and this.Body.P. Using the IsInteresting predicate we can filter out all interesting words from a message:

  public virtual string* MustRead () {
    bool IsInteresting (string s){
      return
        (  s.IndexOf("URGENT") >= 0
        || s.IndexOf("YOUR ASSISTANCE") >= 0
        || s.IndexOf("MILLION") >= 0
        || s.IndexOf("100% RISK FREE") >= 0
        );
    }

    return this...string::*[IsInteresting(it)];
  }

This code is noteworthy in two aspects. First, IsInteresting is defined as a local function declaration inside the MustRead method. Second, square brackets that are usually used for indexing are here used for filtering. Within the filter the string variable it is successively bound to all values of the transitive query.

Apply to All

Filters are expressions and thus compose easily, i.e. we can have one filter after another: the result is still an expression. However sometimes we do not only want to filter information but we also want to apply a method or a code block to all stream elements. For instance, you might want to lower all strings contained in an Email msg. Normally we would write a foreach loop. But loops aren't expressions and thus don't compose. Therefore, Cω provides a foreach loop on the expression level (as we have already seen being used several times before):

  string* lowers = msg...string::*.{ it.ToLower() };

An alternative way to write this as a block is:

  string* lowers = msg...string::*.{ s = it.ToLower(); return s; };

The expression or block is applied to all elements of the stream; within them it is successively bound to the elements of the stream.

Example

The following is a complete Cω program that demonstrates the concepts discussed above.

using System;

public class Test {
    static void Main() {
      xs = GetStrings();

      // Implicitly convert stream of structs to stream of strings
      string* ys = xs;
      // print all strings in UPPERCASE
      ys.{ Console.WriteLine("it = {0}.",it); };

      // Convert all strings to lowercase
      lowers = xs...string::*.{ it.ToLower() };

      // Print all strings in lowercase
      lowers.{ Console.WriteLine("it = {0}.",it); };

      System.Console.ReadLine();
    }

    static struct{string Text;}* GetStrings() {
       yield return new{Text = "THE"};
       yield return new{Text = "QUICK"};
       yield return new{Text = "BROWN"};
       yield return new{Text = "FOX"};
       yield return new{Text = "JUMPED"};
       yield return new{Text = "OVER"};
       yield return new{Text = "THE"};
       yield return new{Text = "LAZY"};
       yield return new{Text = "DOG!"};
    }
}

Output

it = THE.
it = QUICK.
it = BROWN.
it = FOX.
it = JUMPED.
it = OVER.
it = THE.
it = LAZY.
it = DOG!.
it = the.
it = quick.
it = brown.
it = fox.
it = jumped.
it = over.
it = the.
it = lazy.
it = dog!.