Hyperlambda lambda contracts

I realised I kept on repeating myself inside of my Active Events, by checking if some argument was passed in, and if not, throw an exception. After some thinking about the problem, I realised I could create a generalised solution for it, by creating another Active Event, that would accept a “contract”, and traverse the arguments given to an Active Event, and if some parts of the “contract” was missing, it would thrown an exception.

This allows me to declare a “contract” of arguments that an Active Event expects, and if any part of this “contract” is missing, my contract event will throw an exception, with the name of the argument that was missing, and/or did not have the expected type in its value.

Below is first of all an example of some usage of my [micro.contract] event.

/*
 * Creating some dummy Active Event that 
 * has declared a contract.
 */
create-event:foo.bar

  /*
   * Invoking our [micro.contract] event,
   * passing in a "contract" that states that
   * we will at the minimum expect a [foo] and
   * a [bar] node, where the [bar] node's value
   * must be of type "int", and the [foo] node
   * expects a children node named [foo1], having
   * a value of type "string".
   */
  micro.contract:x:/..
    foo
      foo1:string
    bar:int

/*
 * Invoking our [foo.bar] event, such that it
 * throws an exception, since the contract doesn't 
 * match the actual given arguments.
 */
foo.bar
  foo
    foo1:x

  /*
   * This argument will throw an exception, since it doesn't
   * match the "contract". The contract expects an integer,
   * and this value is clearly not convertible to such.
   */
  bar:this-will-throw

Basically, the above code will throw an exception, since the value of our [bar] node cannot be converted into an integer value.

This allows me to much more easily check for existence of arguments inside of my lambda objects, by simply creating a “contract” and invoking [micro.contract] with the root node of the lambda object.

This will be released in the next version of Micro, but if you’re interested in seeing how it looks like today, you can see the code below.

/*
 * Helper Active Event to verify expected "contract" arguments are 
 * given to lambda object.
 *
 * Loops through the given lambda object, given as [_arg], and 
 * verifies all the specified arguments can be found in lambda object.
 *
 * If an argument in contract is not found, an exception will be 
 * thrown, with a text indicating which argument was missing.
 *
 * You can declare the expected type of your argument's value as the 
 * value of your contract argument. You can also declare children 
 * nodes to your arguments, and such declare entire expected 
 * hierarchies of arguments.
 */
create-event:micro.contract

  /*
   * Signal object to separate arguments from the rest of our lambda 
   * object.
   */
  .signal

  /*
   * Looping through each argument given, and verifies that it can 
   * be found accordingly in the given [_arg] lambda object.
   */
  for-each:x:/@.signal/--(!/_arg)/<-

    /*
     * Checking if argument was given at all.
     */
    if:x:/../*/_arg/#/*/{0}
      :x:/@_dp/#?name
      not

      /*
       * Argument was missing in its entirety.
       */
      throw:Argument [{0}] was missing
        :x:/@_dp/#?name

    /*
     * Checking if argument was expected to contain any value,
     * for then to check if argument actually held a value.
     */
    if:x:/@_dp/#?value
      and:x:/../*/_arg/#/*/{0}?value
        :x:/@_dp/#?name
        not

      /*
       * Argument was expected to hold a value, but did not.
       */
      throw:Argument [{0}] did not have a value
        :x:/@_dp/#?name

    /*
     * Checking type of value, and that it can legally be 
     * converted into the expected type.
     */
    if:x:/@_dp/#?value
      and
        p5.types.can-convert:x:/../*/_arg/#/*/{0}?value
          :x:/@_dp/#?name
          type:x:/@_dp/#?value
        not

      /*
       * Value of argument could not be converted into expected type.
       */
      throw:Argument [{0}] count not be converted into '{1}'
        :x:/@_dp/#?name
        :x:/@_dp/#?value

    /*
     * Recursively checking contract beneath currently iterated 
     * contract argument.
     */
    add:x:/+
      src:x:/@_dp/#/*
    micro.contract:x:/../*/_arg/#/*/{0}
      :x:/@_dp/#?name

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s