On this page:
5.1 Basic
_
gen
sep
collect
esc
clos
5.2 Predicates
one-of?
all
any
none
and
or
not
and%
or%
5.3 Boolean Algebra
NOT
!
AND
&
OR
NOR
NAND
XOR
XNOR
any?
all?
none?
inverter
5.4 Routing
ground
X
crossover
==
relay
==*
relay*
-<
tee
fanout
1>
2>
3>
4>
5>
6>
7>
8>
9>
select
block
bundle
group
sieve
partition
5.5 Conditionals
if
when
unless
gate
5.6 Loops
loop
loop2
feedback
5.7 Exceptions
try
5.8 Higher-order Flows
><
amp
pass
<<
>>
ε
effect
apply
5.9 Binding
as
5.9.1 Variable Scope
5.10 Identifiers
5.11 Literals
5.12 Templates and Partial Application
5.13 Utilities
count
live?
rectify
5.14 Language Extension
qi:  *

5 The Qi Language🔗ℹ

The core syntax of the Qi language. These forms may be used in any flow. Flows may be specified in Racket via the language interface.

    5.1 Basic

    5.2 Predicates

    5.3 Boolean Algebra

    5.4 Routing

    5.5 Conditionals

    5.6 Loops

    5.7 Exceptions

    5.8 Higher-order Flows

    5.9 Binding

      5.9.1 Variable Scope

    5.10 Identifiers

    5.11 Literals

    5.12 Templates and Partial Application

    5.13 Utilities

    5.14 Language Extension

5.1 Basic🔗ℹ

syntax

_

The symbol _ means different things depending on the context, but when used on its own, it is the identity flow or trivial transformation, where the outputs are the same as the inputs. It is equivalent to Racket’s values.

Examples:
> (( _) 3)

3

> (( _) 1 2)

1

2

> (( (-< _ _)) 3)

3

3

syntax

(gen expr ...)

A flow that generates the values of the provided Racket expressions expr, ignoring any input values provided to it at runtime. This is the most common way to translate an ordinary value into a flow.

Note that literals are transparently wrapped with gen during expansion and don’t need to be explicitly wrapped.

gen generalizes const in the same way that values generalizes identity.

Also see Using Racket Values in Qi Flows.

Examples:
> (( (gen 1)) 3)

1

> (( (gen (string-append "hello" " " "there")))
   3 4 5)

"hello there"

> (( (gen 1 2)) 3)

1

2

syntax

syntax

sep

syntax

( flo)

syntax

(sep flo)

Separate the input list into its component values. This is the inverse of .

When used in parametrized form with a presupplied flo, this flow accepts any number of inputs, where the first is expected to be the list to be unraveled. In this form, the flow separates the list into its component values and passes each through flo along with the remaining input values.

and often allow you to use functions directly where you might otherwise need to use an indirection like apply or list.

Examples:
> (( (~>  +)) (list 1 2 3 4))

10

> (( (~>  (>< sqr) )) (list 1 2 3 4))

'(1 4 9 16)

> (( (~> ( +) )) (list 1 2 3) 10)

'(11 12 13)

> (struct kitten (name age) #:transparent)
> (( (~> ( kitten) ))
   (list "Ferdinand" "Imp" "Zacky") 0)

(list (kitten "Ferdinand" 0) (kitten "Imp" 0) (kitten "Zacky" 0))

syntax

syntax

collect

Collect the input values into a list. This is the inverse of .

and often allow you to use functions directly where you might otherwise need to use an indirection like apply or list.

Examples:
> (( (~>  (string-join ""))) "a" "b" "c")

"abc"

> (( (~>  (>< sqr) )) (list 1 2 3 4))

'(1 4 9 16)

syntax

(esc expr)

Escape to the host language to evaluate expr which is expected to yield a flow. ( (esc ( expr))) is equivalent to ( expr).

Also see Using Racket to Define Flows.

Example:
> (( (esc (λ (x) (+ 2 x)))) 3)

5

syntax

(clos flo)

A flow that generates a flow as a value. Any inputs to the clos flow are available to flo when it is applied to inputs, i.e. it is analogous to a closure in Racket.

We typically describe flows using Qi, while esc allows us to describe a flow using the host language. In either case, the flow simply operates on runtime inputs. In some cases, though, we need to generate a flow as a value to be used later, for instance, when the flow is parametrized by inputs not available until runtime. clos allows us to define and produce such a flow using Qi.

Without clos, we could still accomplish this by using esc and producing a function value (i.e. a lambda) as the result of the function we define – but this would mean that we must employ the host language to describe the flow rather than Qi.

When used within a threading form (i.e. ~> or ~>>), clos incorporates the pre-supplied input in accordance with the threading direction at the site of its definition.

See Converting a Function to a Closure in the field guide for more tips on using closures.

Examples:
> (( (~> (-< (~> first (clos *)) rest) map)) (list 5 4 3 2 1))

'(20 15 10 5)

> (~> ("a" (list "b" "c" "d")) (== (clos string-append) _) map)

'("ab" "ac" "ad")

> (~> ("a" (list "b" "c" "d")) (== (~>> (clos string-append)) _) map)

'("ba" "ca" "da")

5.2 Predicates🔗ℹ

syntax

(one-of? expr ...)

Is the input one of the indicated values?

Example:
> (( (one-of? 'a 'b 'c)) 'b)

#t

syntax

(all flo)

Do all of the inputs satisfy the predicate flo?

Examples:
> (( (all positive?)) 1 2 3)

#t

> (( (all positive?)) 1 -2 3)

#f

syntax

(any flo)

Do any of the inputs satisfy the predicate flo?

Examples:
> (( (any positive?)) -1 2 -3)

#t

> (( (any positive?)) -1 -2 -3)

#f

syntax

(none flo)

Output true if none of the inputs satisfy the predicate flo.

Examples:
> (( (none positive?)) -1 2 -3)

#f

> (( (none positive?)) -1 -2 -3)

#t

syntax

(and flo ...)

Output true if the inputs, when considered together, satisfy each of the flo predicates.

Examples:
> (( (and positive? odd?)) 9)

#t

> (( (and (all odd?) <)) 3 7 11)

#t

syntax

(or flo ...)

Output true if the inputs, when considered together, satisfy any of the flo predicates.

Examples:
> (( (or positive? odd?)) 8)

#t

> (( (or (all odd?) <)) 3 8 12)

#t

syntax

(not flo)

Output true if the inputs, when considered together, do not satisfy the predicate flo.

Examples:
> (( (not positive?)) -9)

#t

> (( (not <)) 8 3 12)

#t

syntax

(and% flo ...)

Output true if the inputs, when considered independently or "in parallel," satisfy each of the respective flo predicates. Equivalent to (~> (== flo ...) AND), except that the identifier _ when used in the present form indicates that you "don’t care" about the corresponding input in determining the result (whereas it ordinarily indicates the identity transformation).

Examples:
> (( (and% positive? negative?)) 3 -9)

#t

> (( (and% positive? negative?)) -3 -9)

#f

syntax

(or% flo ...)

Output true if any of the inputs, when considered independently or "in parallel," satisfies its corresponding flo predicate. Equivalent to (~> (== flo ...) OR), except that the identifier _ when used in the present form indicates that you "don’t care" about the corresponding input in determining the result (whereas it ordinarily indicates the identity transformation).

Examples:
> (( (or% positive? negative?)) -3 -9)

#t

> (( (or% positive? negative?)) -3 9)

#f

5.3 Boolean Algebra🔗ℹ

syntax

NOT

syntax

!

A Boolean NOT gate, this negates the input. This is equivalent to Racket’s not.

Examples:
> (( NOT) #t)

#f

> (( NOT) #f)

#t

syntax

AND

syntax

&

A Boolean AND gate, this outputs the conjunction of the inputs.

Examples:
> (( AND) #t #t #t)

#t

> (( AND) #t #f #t)

#f

syntax

OR

syntax

A Boolean OR gate, this outputs the disjunction of the inputs.

Note that the symbol form uses Unicode 0x2225 corresponding to LaTeX’s \parallel. We do not use the easier-to-type || symbol (that was formerly used here in older versions of Qi) as that is treated as the empty symbol by Racket’s reader, which could cause problems in some cases.

Examples:
> (( OR) #t #f #t)

#t

> (( OR) #f #f #f)

#f

syntax

NOR

syntax

NAND

syntax

XOR

syntax

XNOR

Flows corresponding to the identically-named Boolean gates.

syntax

any?

syntax

all?

syntax

none?

Output true if any, all, or none (respectively) of the inputs are truthy.

Examples:
> (( any?) #t #f #t)

#t

> (( any?) #f #f #f)

#f

> (( all?) #t #t #t)

#t

> (( all?) #t #f #t)

#f

> (( none?) #f #t #f)

#f

> (( none?) #f #f #f)

#t

syntax

inverter

Negate each input in parallel. Equivalent to (>< NOT).

Example:
> (( inverter) #f #t #f)

#t

#f

#t

5.4 Routing🔗ℹ

syntax

syntax

ground

Extinguish the input values, yielding no output at all.

Example:
> (( ) 1 2 3)

syntax

(~> flo ...)

syntax

(~>> flo ...)

Compose flows in sequence, from left to right. In the metaphor of an analog electrical circuit, you could think of this as a wire.

~> "threads" the arguments in the leading position, while ~>> threads them in the trailing position. Argument positions may also be explicitly indicated via a template, either individually or en masse.

Examples:
> (( (~> + sqr)) 1 2 3)

36

> (( (~>> (string-append "a" "b"))) "c" "d")

"abcd"

syntax

X

syntax

crossover

Invert the order of the inputs, so that the last output is the first input, the second-to-last output is the second input, and so on.

Examples:
> (( X) 1 2 3)

3

2

1

> (( (~> X string-append)) "a" "b" "c")

"cba"

syntax

(== flo ...)

syntax

(relay flo ...)

syntax

==

syntax

relay

Compose flows in parallel, so that inputs are passed through the corresponding flo’s individually. The number of flos must be the same as the number of runtime inputs.

In the common case of 1 × 1 flos (i.e. where the flows each accept one input and produce one output), the number of outputs will be the same as the number of inputs, but as flows can be nonlinear, this is not necessarily the case in general.

When used in identifier form simply as ==, it behaves identically to ><.

See also the field guide entry on the relationship between bindings and nonlinearity.

Example:
> (( (== add1 sub1)) 1 2)

2

1

syntax

(==* flo ...)

syntax

(relay* flo ...)

Similar to == and analogous to list*, this passes each input through the corresponding flo individually, until it encounters the last flo. This last one receives all of the remaining inputs.

Example:
> (( (==* add1 sub1 +)) 1 1 1 1 1)

2

0

3

syntax

(-< flo ...)

syntax

(tee flo ...)

A "tee" junction, this forks the input values into multiple copies, each set of which is passed through a flo "in parallel." Equivalent to ( (~> (fanout N) (== flo ...))), where N is the number of flo’s.

Examples:
> (( (-< add1 sub1)) 3)

4

2

> (( (-< + *)) 3 5)

8

15

syntax

fanout

syntax

(fanout N)

Split the inputs into N copies of themselves.

When used in identifier form simply as fanout, it treats the first input as N, and the remaining inputs as the values to be fanned out.

Examples:
> (( (fanout 3)) 5)

5

5

5

> (( (fanout 2)) 3 7)

3

7

3

7

> (~> (3 "hello?") fanout)

"hello?"

"hello?"

"hello?"

syntax

1>

syntax

2>

syntax

3>

syntax

4>

syntax

5>

syntax

6>

syntax

7>

syntax

8>

syntax

9>

Aliases for inputs, by index. Equivalent to (select N), for index N. If you need to select more than one input, use select directly.

Example:
> (( 4>) 'a 'b 'c 'd 'e 'f)

'd

syntax

(select index ...)

syntax

(block index ...)

Select or block inputs by index, outputting the selection or remainder, respectively. index is 1-indexed, that is, for instance, in order to select the first and the third input, we would use (select 1 3).

Examples:
> (( (select 1 4)) 'a 'b 'c 'd 'e 'f)

'a

'd

> (( (block 1 2 4 6)) 'a 'b 'c 'd 'e 'f)

'c

'e

syntax

(bundle (index ...) selection-flo remainder-flo)

Divide the set of inputs into two groups or "bundles" based on provided indexes, passing the selection to selection-flo and the remainder to remainder-flo.

Example:
> (( (bundle (1 3) + *)) 1 2 3 4 5)

4

40

syntax

group

syntax

(group number selection-flo remainder-flo)

Divide the set of inputs into two groups by number, passing the first number inputs to selection-flo and the remainder to remainder-flo.

In the context of a loop, this is a typical way to do "structural recursion" on flows (see "pare"), and in this respect it is the values analogue to car and cdr for lists.

When used in identifier form simply as group, it treats the first three inputs as number, selection-flo and remainder-flo, respectively, and the remaining as the data inputs to be acted upon.

Example:
> (( (group 2 + *)) 1 2 3 4 5)

3

60

syntax

sieve

syntax

(sieve condition-flo selection-flo remainder-flo)

Divide the set of inputs into two groups by condition, passing the inputs that satisfy condition-flo (individually) to selection-flo and the remainder to remainder-flo.

When used in identifier form simply as sieve, it treats the first three inputs as condition-flo, selection-flo and remainder-flo, respectively, and the remaining as the data inputs to be acted upon.

Example:
> (( (sieve positive? max min)) 1 -2 3 -4 5)

5

-4

syntax

(partition [condition-flo body-flo] ...)

A form of generalized sieve, passing all the inputs that satisfy each condition-flo to the corresponding body-flo.

Examples:
> (( (partition)))
> (( (partition [positive? max])) 1 -2 3 -4 5)

5

> (( (partition [positive? max]
                 [negative? min]))
      1 -2 3 -4 5)

5

-4

> (( (partition [positive? max]
                 [negative? min]))
      1 -2 3 -4 5)

5

-4

> (( (partition [positive? max]
                 [zero? (-< count (gen "zero"))]
                 [negative? min]))
      1 -2 3 -4 5)

5

0

"zero"

-4

> (( (partition [(and positive? (> 1)) max]))
      1 -2 3 -4 5)

5

5.5 Conditionals🔗ℹ

syntax

(if condition-flo consequent-flo alternative-flo)

syntax

(when condition-flo consequent-flo)

syntax

(unless condition-flo alternative-flo)

The flow analogue of if, this is the basic conditional, passing the inputs through either consequent-flo or alternative-flo, depending on whether they satisfy condition-flo.

when is shorthand for (if condition-flo consequent-flo ) and unless is shorthand for (if condition-flo alternative-flo).

Examples:
> (( (if positive? add1 sub1)) 3)

4

> (( (when positive? add1)) 3)

4

> (( (unless positive? add1)) 3)

syntax

(switch maybe-divert-expr switch-expr ...)

 
maybe-divert-expr = (divert condition-gate-flow consequent-gate-flow)
  | (% condition-gate-flow consequent-gate-flow)
     
switch-expr = [flow-expr flow-expr]
  | [flow-expr (=> flow-expr)]
  | [else flow-expr]
The flow analogue of cond, this is a dispatcher where the condition and consequent expressions are all flows which operate on the switch inputs.

Typically, each of the component flows – conditions and consequents both – receives all of the original inputs to the switch. This can be changed by using a divert clause, which takes two flow arguments, the first of whose outputs go to all of the condition flows, and the second of whose outputs go to all of the consequent flows. This can be useful in cases where multiple values flow, but only some of them are predicated upon, and others (or all of them) inform the actions to be taken. Using (divert _ _) is equivalent to not using it. % is a symbolic alias for divert – parse it visually not as the percentage sign, but as a convenient way to depict a "floodgate" diverting values down different channels.

When the => form is used in a consequent flow, the consequent receives N + 1 inputs, where the first input is the result of the predicate flow, and the remaining N inputs are the a priori inputs to the consequent flow (which are typically the original inputs to the switch, unless modulated with a divert clause). This form is analogous to the => symbol when used in a cond. Note that while switch can direct any number of values, we can unambiguously channel the result of the predicate to the first input of the consequent here because it is guaranteed to be a single value (otherwise it wouldn’t be a predicate).

If none of the conditions are met, this flow produces the input values, unchanged, except if a divert clause is specified, in which case it produces the input values transformed under consequent-gate-flow. If you need a specific value such as (void) or would prefer to output no values, indicate this explicitly via e.g. [else void] or [else ].

Examples:
> (( (switch [positive? add1]
              [else sub1]))
   3)

4

> (( (switch [(member (list 1 2 3)) (=> 1> (map - _))]
              [else 'not-found]))
   2)

'(-2 -3)

> (( (switch (% 1> _)
        [number? cons]
        [list? append]
        [else 2>]))
   (list 3)
   (list 1 2))

'(3 1 2)

syntax

(gate condition-flo)

Allow the inputs through unchanged if they satisfy condition-flo, otherwise, ground them so that there is no output.

Examples:
> (( (gate <)) 3 5)

3

5

> (( (gate <)) 5 1)

5.6 Loops🔗ℹ

syntax

(loop condition-flo map-flo combine-flo return-flo)

syntax

(loop condition-flo map-flo combine-flo)

syntax

(loop condition-flo map-flo)

syntax

(loop map-flo)

syntax

loop

A simple loop for structural recursion on the input values, this applies map-flo to the first input on each successive iteration and recurses on the remaining inputs, combining these using combine-flo to yield the result as long as the inputs satisfy condition-flo. When the inputs do not satisfy condition-flo, return-flo is applied to the inputs to yield the result at that terminating step. If the condition is satisfied and there are no further values, the loop terminates naturally.

If unspecified, condition-flo defaults to #t, combine-flo defaults to _, and return-flo defaults to .

When used in identifier form simply as loop, this behaves the same as the fully qualified version, except that the flows parametrizing the loop are expected as the initial four inputs (in the same order), and the data inputs being acted upon are expected to follow.

Examples:
> (( (loop (* 2))) 1 2 3)

2

4

6

> (( (loop #t _ +)) 1 2 3 4)

10

syntax

(loop2 condition-flo map-flo combine-flo)

A "tail-recursive" looping form, this passes the result at each step as a flow input to the next, alongside the inputs to the subsequent step, simply evaluating to the result flow on the last step.

Example:
> (( (loop2 (~> 1> (not null?))
             sqr
             cons))
   (list 1 2 3) null)

'(9 4 1)

syntax

feedback

syntax

(feedback flo)

syntax

(feedback N flo)

syntax

(feedback N (then then-flo))

syntax

(feedback N (then then-flo) flo)

syntax

(feedback (while cond-flo))

syntax

(feedback (while cond-flo) flo)

syntax

(feedback (while cond-flo) (then then-flo))

syntax

(feedback (while cond-flo) (then then-flo) flo)

Pass the inputs N times through flo by "feeding back" the outputs each time. If a while clause is specified in place of a value, then the outputs are fed back as long as cond-flo is true. If the optional then form is specified, then-flo will be invoked on the outputs at the end after the loop has completed.

If used as (feedback flo), the first input to the feedback block will be expected to be N and will not be "fed back" with the rest of the inputs. If flo is not specified, then it will be expected as the first input, with the remaining inputs being treated as the inputs being acted upon. If used in identifier form simply as feedback, it treats the first two inputs as N and flo, respectively, and both are expected. The remaining inputs are treated as the data inputs being acted upon.

For practical advice on using feedback, see Effectively Using Feedback Loops in the field guide.

Examples:
> (( (feedback 3 add1)) 5)

8

> (( (feedback (while (< 50)) sqr)) 2)

256

5.7 Exceptions🔗ℹ

syntax

(try flo [error-predicate-flo error-handler-flo] ...)

A dispatcher for handling exceptions that works similarly to switch, this attempts flo and simply produces its results if successful. Otherwise, if an exception is raised, the exception is provided to each of the error-predicate-flo’s in turn, and for the first predicate that returns true, the corresponding error-handler-flo is invoked with the input arguments.

Examples:
> (~> (9) (try (/ 3) [exn:fail? 0]) add1)

4

> (~> (9)
      (try (/ 0)
        [exn:fail:contract:arity? 0]
        [exn:fail:contract:divide-by-zero? _])
      add1)

10

5.8 Higher-order Flows🔗ℹ

syntax

><

syntax

(>< flo)

syntax

amp

syntax

(amp flo)

The flow analogue to map, this maps each input individually under flo. As flows may generate any number of output values, unlike map, the number of outputs need not equal the number of inputs here.

If used in identifier form simply as ><, it treats the first input as flo.

Examples:
> (( (>< sqr)) 1 2 3)

1

4

9

> (( ><) sqr 1 2 3)

1

4

9

> (( (>< (-< _ _))) 1 2 3)

1

1

2

2

3

3

syntax

pass

syntax

(pass condition-flo)

The flow analogue to filter, this filters the input values individually under condition-flo, yielding only those values that satisfy it.

If used in identifier form simply as pass, it treats the first input as condition-flo and the remaining inputs as the values to be filtered.

Examples:
> (( (pass positive?)) 1 -2 3)

1

3

> (( pass) positive? 1 -2 3)

1

3

syntax

(<< flo init-flo)

syntax

(<< flo)

syntax

(>> flo init-flo)

syntax

(>> flo)

The flow analogues to foldr and foldl (respectively – the side on which the symbols "fold" corresponds to the type of fold), these fold over input values rather than an input list.

flo processes one input value at a time. It receives the current input value in the first position, followed by the accumulated values, and may generate any number of output values. These output values are fed back as accumulated values for the next iteration if input values remain to be processed; otherwise, they are produced as the output of the flow.

image

init-flo is expected to be a flow that will generate the initial values for the fold, and will be invoked with no inputs for this purpose at runtime. It is done this way to support having multiple initial values or no initial values, rather than specifically one. Specifying init-flo is optional; if it isn’t provided, flo itself is invoked with no arguments to obtain the init value, to borrow a convention from the Clojure language.

Examples:
> (( (<< +)) 1 2 3 4)

10

> (( (<< string-append)) "a" "b" "c" "d")

"abcd"

> (( (>> string-append)) "a" "b" "c" "d")

"dcba"

> (( (<< string-append "☯")) "a" "b" "c" "d")

"abcd☯"

> (( (<< cons '())) 1 2 3 4)

'(1 2 3 4)

> (( (<< + (gen 2 3))) 1 2 3 4)

15

> (( (>> (-< (block 1)
              (~> 1> (fanout 2)))
          )) 1 2 3)

1

1

2

2

3

3

syntax

(ε side-effect-flo flo)

syntax

(ε side-effect-flo)

syntax

(effect side-effect-flo flo)

syntax

(effect side-effect-flo)

Pass the inputs through flo but also independently to side-effect-flo. The results of the latter, if any, are grounded, so they would have no effect on downstream flows, which would only receive the results of flo. Equivalent to (-< (~> side-effect-flo ) flo).

Use (ε side-effect-flo) to just perform a side effect without modifying the input, equivalent to (-< (~> side-effect-flo ) _).

Remember that, as the side-effect flow is based on a tee junction, it must handle as many inputs as the main flow. For instance, if you were to use displayln as the side-effect, it wouldn’t work if more than one value were flowing, and you’d get an inscrutable error resembling:

; displayln: contract violation
;   expected: output-port?
;   given: 1
;   argument position: 2nd
;   other arguments...:
;    1

As displayln expects a single input, you’d need to use (>< displayln) for this side-effect in general.

If you are interested in using effect to debug a flow, see the section on Debugging in the field guide for more strategies.

Examples:
> (( (~> (ε displayln sqr) add1)) 3)

3

10

> (( (~> (ε (>< displayln) *) add1)) 3 5)

3

5

16

syntax

apply

Analogous to apply, this treats the first input as a flow and passes the remaining inputs through it, producing the output that the input flow would produce if the argument flows were passed through it directly.

Example:
> (( apply) + 1 2 3)

6

5.9 Binding🔗ℹ

syntax

(as v ...)

A flow that binds an identifier v to the input value. If there are many input values, than there should be as many identifiers as there are inputs. Aside from introducing bindings, this flow produces no output.

Examples:
> (( (~> (-< (~> list (as vs))
              +)
          (~a "The sum of " vs " is " _)))
   1 2)

"The sum of (1 2) is 3"

> (( (~> (-< + count)
          (as total number)
          (/ total number)))
   1 2 3 4 5)

3

5.9.1 Variable Scope🔗ℹ

We will use (gen v) as an example of a flow referencing a binding, to illustrate variable scope.

In general, bindings are scoped to the outermost threading form, and may be referenced downstream.

> (~> (5) (as v) (gen v))

5

> (~> (5) (-< (~> sqr (as v))
              _) (gen v))

25

A tee junction binds downstream flows in a containing threading form, with later tines shadowing earlier tines.

> (~> () (-< (~> 5 (as v))
             (~> 6 (as v))) (gen v))

6

A relay binds downstream flows in a containing threading form, with later tines shadowing earlier tines.

> (~> (5 6)
      (== (as v)
          (as v))
      (gen v))

6

In an if conditional form, variables bound in the condition bind the consequent and alternative flows, and do not bind downstream flows.

> (on ("Ferdinand")
    (if (-< (~> string-titlecase (as name))
            (string-suffix? "cat"))
        (gen name)
        (gen (~a name " the Cat"))))

"Ferdinand the Cat"

Analogously, in a switch, variables bound in each condition bind the corresponding consequent flow.

> (switch ("Ferdinand the cat")
    [(-< (~> string-titlecase (as name))
         (string-suffix? "cat")) (gen name)]
    [else "dog"])

"Ferdinand The Cat"

As switch compiles to if, technically, earlier conditions bind all later switch clauses (and are shadowed by them), but this is considered an incidental implementation detail. Like if, switch bindings are unavailable downstream.

5.10 Identifiers🔗ℹ

Identifiers in a flow context are interpreted as variables whose values are expected to be functions. In other words, any named function may be used directly as a flow.

More precisely, for instance, add1 in a flow context is equivalent to (esc add1).

Examples:
> (( +) 1 2 3)

6

> (( add1) 3)

4

5.11 Literals🔗ℹ

Literals and quoted values (including syntax-quoted values) in a flow context are interpreted as flows generating them. That is, for instance, 5 in a flow context is equivalent to (gen 5).

Example:
> (( "hello") 1 2 3)

"hello"

5.12 Templates and Partial Application🔗ℹ

A parenthesized expression that isn’t one of the Qi forms is treated as partial function application, where the syntactically-indicated arguments are pre-supplied to yield a partially applied function that is applied to the input values at runtime.

Usually, the _ symbol indicates the trivial or identity flow, simply passing the inputs through unchanged. Within a partial application, however, the underscore indicates argument positions. If the expression includes a double underscore, __, then it is treated as a blanket template such that the runtime arguments (however many there may be) are passed starting at the position indicated by the placeholder. Another type of template is indicated by using one or more single underscores. In this case, a specific number of runtime arguments are expected (corresponding to the number of blanks indicated by underscores). Note that this could even include the function to be applied, if that position is templatized. This more fine-grained template is powered under the hood by Fancy App: Scala-Style Magic Lambdas.

Examples:
> (( (* 3)) 7)

21

> (( (string-append "c")) "a" "b")

"abc"

> (( (string-append "a" _ "c")) "b")

"abc"

> (( (< 5 _ 10)) 7)

#t

> (( (< 5 _ 7 _ 10)) 6 9)

#t

> (( (< 5 _ 7 _ 10)) 6 11)

#f

> (( (~> (clos *) (_ 3))) 10)

30

> (( (< 5 __ 10)) 6 7 8)

#t

> (( (< 5 __ 10)) 6 7 11)

#f

5.13 Utilities🔗ℹ

syntax

count

Analogous to length for lists, but for values, this counts the values in the flow.

Examples:
> (( count) 5)

1

> (( (~> (-< _ _ _) count)) 5)

3

syntax

live?

Check if there are any values flowing – evaluates to true if there are, and false otherwise.

Examples:
> (( live?) 5)

#t

> (~> (5) (-< _ _ _) live?)

#t

> (~> (5) (-< _ _ _)  live?)

#f

> (~> (8 "hello" 3 'boo 4) (pass number?) (if live? min #f))

3

> (~> ("hello" 'boo) (pass number?) (if live? min #f))

#f

syntax

(rectify v ...)

Check if the flow is live?, and if it isn’t, then ensure that the values v ... are produced. Equivalent to (if live? _ (gen v ...)).

A flow may sometimes produce no values. In such cases, depending on how the output of the flow is used, it may be desirable to ensure that it returns some default value or values instead. rectify produces the original output of the flow unchanged if there is any output, and otherwise outputs v ....

Examples:
> (~> (5) (rectify #f))

5

> (~> (5)  (rectify #f))

#f

> (~> (8 "hello" 3 'boo 4) (pass number?) (rectify 0) min)

3

> (~> ("hello" 'boo) (pass number?) (rectify 0) min)

0

5.14 Language Extension🔗ℹ

This way to extend the language is intended for use in legacy versions of Racket only, that is, versions 8.2 or earlier. Qi now (as of Racket 8.3) offers "first class" macro extensibility where there is no need for the special prefix, "qi:". See Qi Macros for more. Prefix-based macros (i.e. the kind discussed in the present section) should be considered deprecated on versions 8.3 or later.

syntax

(qi:* expr ...)

A form with a name starting with "qi:" is treated in a special way – it is simply left alone during the macro expansion phase as far as Qi is concerned, as it is expected to be a macro written by a third party that will expand into a Racket expression that is usable as a flow (i.e. similar to the esc form). That is, such a form must expand either to a flow specified using Qi and wrapped with , or to a lambda containing arbitrary Racket code.

This allows you to extend the Qi language locally without requiring changes to be made in the library itself.

Examples:
> (define-syntax-rule (qi:square flo)
    ( (feedback 2 flo)))
> (~> (2 3) + (qi:square sqr))

625