Timeline

← ← ←
 

Docs

This is an overview of Timeline's documentation. If this is your first time here, it might be helpful to understand how time travel works.

Language Overview

Timeline is a relatively simple language, syntactically. All code is executed in response to events, and all such code contains only logic or the triggering of other events. There is no I/O, logging, functions, subroutines, promises, callbacks, or anything like that.

Progressive Example

This will build up a sophisticated example showing language features in context.

All code starts with registering an event handler via on, and it's customary to have the first bit of code inside the start event, which is the first even fired:

on start [] {
  «code here»
}

Inside start, you would trigger the trip details event, which tells the Time Space Manifold where you are going, when, and what's going through the vortex. Because there is no I/O, there can be no interactive input, configuration files or the like, so you trigger everything in an event. Since our flight plan is for a single trip only, hard-coding values is not only OK, it is expected.

In this code, we include a destination using the International Celestial Reference Frame coordinate system, or ICRF. We also describe the travelers weight and a cube that completely encloses the traveler, to give a sense of size.

on start [] {
  send trip details [
    destination:[
      ascension:60°23'42"
      declination:75°02'12"
      distance:384,402km
      duration:1,296,000s
    ]
    traveler:[
      weight:90kg
      dimensions: [
        width:1m
        height:2m
        depth:0.3m
      ]
    ]
  ] now

  send ping [] now
}

All event triggers match this pattern: send «event name» [ «params» ] «when». In this case, we send it immediately, using the keyword now. Note that the values we are given are not naked numbers. Timeline prefers the use of well-defined data types, so instead of using 34.21222 to represent an angle, we can use the canonical encoding of an angle: 34°12'44". This value is unambiguously an Angle, which makes the code much more readable than using a floating point value that you have to guess at its meaning.

You can further note that large numeric values like 1,296,000km are typed (the km suffix tells us it's a Distance), but we also use commas to make the number more readable. These are required to ensure the correct transcoding of large values into the program.

You may have noticed we are also sending a ping, which, if there are objects ahead of us in the vortex, will trigger the collision imminent event. This event receives parameters:

on collision imminent [
  distance:
  position:
  dimensions:
] {
  buffer          is 2ms
  adjustment      is distance   - buffer
  adjustment-back is adjustment + 4ms
  direction       is 360°       - position

  send adjust vortex direction [
    direction:direction
  ] in adjustment

  send adjust vortex direction [
    direction:direction
  ] in adjustment-back

  send ping [] in adjustment-back
}

You can consult the documentation for the types of the parameters (there's no need for your code to re-state what they are), but to save you time, distance is a Time, position is an Angle, and dimensions is a Dimension.

As we look more into this code, we can see a variable assignment. The variable buffer is given the value 2ms, which is a Time. The assignment happens via the is keyword. The equals sign (=) used in many languages is somewhat confusing, especially when dealing with math. In math, “=” represents equality, not assignment. Some languages create operators like := to allay this confusion, or even use arrows, e.g. direction ← 45°, but we prefer the succinct and unambiguous is. There can be no clearer way to state that the value of buffer is 2ms than how we have designed it.

Lastly, you'll notice the expressions in use. We assign adjustment to the result of subtracting buffer from distance. Since both buffer and distance are Times, and subtraction is defined on Time to produce a Time, the compiler (and us!) knows that adjustment is of type Time. There's no need to restate this information in the code.

Suppose we wish to slow down before making our turn. We can do this by checking the value of distance. If it's less than 10ms, we trigger a custom event to slow down before the turn and speed up after.

on collision imminent [
  distance:
  position:
  dimensions:
] {
  buffer          is 2ms
  adjustment      is distance   - buffer
  adjustment-back is adjustment + 4ms
  direction       is 360°       - position

  too-close?      is distance < 10ms

  if too-close? {
    send slow down then speed up [
      slow-in:1ms
      speed-up-in:adjustment-back
    ] now
  }
  else {
    do nothing
  }

  send adjust vortex direction [
    direction:direction
  ] in adjustment

  send adjust vortex direction [
    direction:direction
  ] in adjustment-back

  send ping [] in adjustment-back
}

Here we see our control structure via an if and else. All if statements must have a corresponding else, even if there is no logic needed. This creates general consistency in all code (you never have to wonder if the omitted else is intentional), and also ensures the programmer has thought through both cases, using the do nothing construct to explicitly indicate there is no code for the alternate case.

You'll also note that the variable too-close? has a question mark in it. Booleans are special, since they control the flow of code. Thus, they must always end in a question mark which immediately calls out their specialness. You might think we could omit the use of this boolean in our if statement, but if statements may only be based upon boolean literals or variables of type Boolean. Expressions are not allowed. This forces us to name all expressions.

Lastly, we're triggering a custom event called slow down then speed up. How does that work? We can define it the same way as we handle built in events. The types are derived from the callsite and assumed in the code.

on slow down then speed up [
  slow-in:
  speed-up-in:
] {
  send adjust vortex speed [
    direction:90%
  ] in slow-in

  send adjust vortex direction [
    direction:110%
  ] in speed-up-in
}

You may be wondering why variable and parameter names are using dashes instead of underscores. We can all agree that delimiting words in names using camel-case results in a mess of unreadability, but underscores solve this nicely. Underscores are somewhat difficult to type in a QWERTY layout, requiring both pinky fingers (or a pinky and a ring finger). This results in numerous typos. Dashes are easier to execute, so we use that. When subtracting values, you must surround the hyphen with spaces to disambiguate (this also makes the code more readable).

Next, let's see the definition of the language's syntax. Note that there is significant semantic relevance to the language's constructs, for example, the syntax allows any variable to have a question mark at the end of its name, but this is only semantically allowed for booleans.

Language Syntax Reference

<flight plan>           ::= <event handler> | <event handler> <flight plan>
<event handler>         ::= on <inline-space> <event name> <inline-space> [ <whitespace> <parameter list> <whitespace> ] <whitespace> { <whitespace> <code> <whitespace> }
<inline-space>          ::= " " | " " <inline-space>
<optional-inline-space> ::= "" | <inline-space>
<whitespace>            ::= <inline-space> | <newline> | <inline-space> <whitespace> | <newline> <whitespace>
<event name>            ::= <word> | <word> " " <event name>
<word>                  ::= "" | <word> <letter>
<letter>                ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
<code>                  ::= <whitespace> <statement> | <whitespace> <statement> <newline> <code>
<newline>               ::= "\n"
<statement>             ::= <variable assignment> | <variable reassignment> | <control structure> | <send event> | "do nothing"
<variable assignment>   ::= <variable name> <inline-space> "is" <inline-space> <expression>
<variable reassignment> ::= <variable name> <inline-space> "is now" <inline-space> <expression>
<control structure>     ::= "if" <inline-space> <variable> <inline-space> "{" <newline> <code> <newline> "}" <newline> "else {" <newline> <code> <newline> "}"
<expression>            ::= <variable-or-literal> <operator> <variable-or-literal>
<variable-or-literal>   ::= <variable> | <literal>
<variable>              ::= <word> | <word> "-" <variable>
<literal>               ::= <non-whitespace> | <non-whitespace> <literal>
<non-whitespace>        ::= «any non space symbol»
<send event>            ::= "send" <inline-whitespace> <event name> <inline-whitespace> "[" <inline-whitespace> <argument list>  <inline-whitespace> "]" <scheduled time>
<scheduled time>        ::= "now" | "in" <inline-whitespace> <variable-or-literal>
<parameter list>        ::= "" | <parameter name> ":" <optional-inline-space> <parameter list>
<argument list>         ::= "" | <parameter name> ":" <parameter value> <optional-inline-space> <parameter value>
<parameter name>        ::= <word> | <word> "-" <parameter name>
<parameter value>       ::= <variable-or-literal>

Language Semantic Reference

Referencing the tokens in the BNF above:

  • <literal> must conform to a known literal format. See Data Types for more.
  • <scheduled time> must be a Time value
  • <send event> must reference a known event or one defined in the current flight plan.
  • <variable assignment> (e.g. direction is 45°) may only occur when declaring a variable the first time in a scope
  • <variable reassignment> (e.g. direction is now 33°) may not occur to declare a variable, but must occur after a variable has been declared with is
  • <word>, which is used in variable names, event names, argument names, and parameter names must be in the dictionary

Standard Library

Timeline's standard library consists of events and types. In most cases, events are designed to either be triggered by your code or handled by it, but not both.

Events

Events are the lifeblood of your flight plan, and all your code revolves around responding to events (or handling them), or sending events (or triggering).

To Handle

These are events you respond to in your flight plan. You would typically not fire these events. Future versions of Timeline may prevent you from doing so.

start

[

]

The first event triggered. This is where you should set up your trip details.

collision imminent

[
  distance: Time
  position: Angle
  dimensions: Dimension
]

Triggered if a ping detects an object ahead of the traveler, where a collision will happen in distance time.

The object is at position inside the vortex and a box of size dimensions would enclose the object.

traveler entered

[

]

Triggered when the traveler enters the vortex and the trip begins. Any distances-as-time would start when this is triggered. This is a good place to set up any pre-determined vortex adjustments, or to send pings.

vortex established

[

]

Triggered when the vortex has been successfully established. This can be used for setup after the connection has been made, however you are more likely to want to handle the traveler entered event, since that is triggered when the traveler actually enters.

To Trigger

These are events you trigger to affect the outcome of your flight plan. You should not write handlers for them. Future versions of Timeline may prevent you from doing so.

adjust vortex direction

[
  direction: Angle
]

Manipulates the vortex along the given direction.

adjust vortex speed

[
  speed: Percent
]

Manipulates the vortex to slow the traveler down, or speed it up, by the given percentage.

ping

[

]

Send a ping ahead of the traveler. If anything is detected, various events will be triggered. collision imminent is the most common.

trip details

[
  destination: Destination
  traveler: Traveler
]

Contains the details of your trip, including where to go, when to go, and the size of the traveler. When this is handled by the Time-Space Manifold, it will trigger the vortex established event (unless a hardware problem prevents it).

Data Types

These are the data types provided by the language runtime. Currently you cannot create your own data types, so these are somewhat magical.

Angle

[
  degrees: Integer
  minutes: Integer
  seconds: Integer
]

Supported Literals

  • %{degrees}° e.g. 45°
  • %{degrees}°%{minutes}' e.g. 45°12'
  • %{degrees}°%{minutes}'%{seconds}" e.g. 45°12'34"

Supported Operators

  • Angle - Angle → Angle
  • Angle + Angle → Angle

An angle in degrees

Destination

[
  ascension: Angle
  declination: Angle
  distance: Distance
  duration: Time
]

No Literals

    No Operators

      A destination in time and space—where you want to go.

      Dimension

      [
        width: Distance
        height: Distance
        depth: Distance
      ]

      No Literals

        No Operators

          A cube that describes the outer-most dimensions of an object.

          Distance

          [
            kilometers: Integer
          ]

          Supported Literals

          • %{kilometers}km e.g. 12km
          • %{meters}m e.g. 12,345m

          Supported Operators

          • Distance + Distance → Distance
          • Distance - Distance → Distance

          A physical distance.

          Integer

          [
          
          ]

          Supported Literals

          • %{value} e.g. 123,456,789

          No Operators

            Any integer. Generally you won't use this on its own, but use a type literal or a constructor.

            Percent

            [
              percentage: Integer
            ]

            Supported Literals

            • %{percentage}% e.g. 34%

            No Operators

              A percentage as a whole number, so e.g. 90% is 0.90.

              Time

              [
                milliseconds: Integer
              ]

              Supported Literals

              • %{milliseconds}ms e.g. 150ms

              No Operators

                An amount of time in seconds.

                Traveler

                [
                  weight: Weight
                  dimensions: Dimension
                ]

                No Literals

                  No Operators

                    What is going to travel in time. It has a weight and a cube that describes it's largest dimensions.

                    Weight

                    [
                      kilograms: Integer
                    ]

                    Supported Literals

                    • %{kilograms}kg e.g. 12kg

                    No Operators

                      A weight or mass

                      Simulator

                      The simulator is where you spend most of your time. The simulator will run a Monte Carlo simulation on your flight plan and tell you the risks involved in taking the trip, based on the plan.

                      > timeline --help
                      
                      SYNOPSIS
                        timeline [--black-box-for=<failure mode>] \
                                 [--details=<failure mode>] \
                                 [--seed=<seed value>] \
                                 <timeline file>
                      
                      DESCRIPTION
                      
                        Simulate the execution of a flight plan.  Your
                        typical workflow will be to simulate your plan,
                        examine the risks output by this simulator and
                        then either write code to mitigate those risks
                        (and repeat), or to accept the risks and take
                        the trip, or to abandon the trip altogether.
                      
                        This is called "Risk Driven Development" or
                        RDD and is the primary way in which Timeline
                        code is written
                      
                      OPTIONS
                      
                        <timeline file>
                          The file containing your flight plan.
                      
                        --black-box-for=<failure mode>
                      
                          Re-run the simulation, producing a black box
                          for a sample run in which <failure mode>
                          occurred.  This can be helpful for diagnosing
                          very specific failures and mitigating risks
                          too specific for a summary.
                      
                          Often, the use of --details will suggest this
                          option as details are only limited to broader
                          failures.
                      
                        --details=<failure mode>
                          Re-run the simulation showing more details
                          about the given <failure mode>.
                          This is useful if you want to see more ways
                          in which a failure mode manifests itself to
                          mitigate more specific risks.  Often used
                          with --seed to recreate a previous run
                      
                        --seed=<seed value>
                           Re-use <seed value> for the random
                           seed to recreate exactly a previous
                           simulated run.
                      
                      EXAMPLES
                      
                         o Run a flight plan
                      
                           $ timeline plan.timeline
                      
                         o Re-Run a flight plan
                      
                           $ timeline --seed=9474837473738 plan.timeline
                      
                         o Figure out what known object you are
                           colliding with
                      
                           $ timeline --details="Known Object Collision" \
                               plan.timeline
                      
                         o Get a black box for radioactive interference
                      
                           $ timeline \
                               --black-box-for="Radioactive Interference" \
                               plan.timeline