Vesa Karvonen

2005-07-12 15:50:02 UTC

Compared to the preludes or standard libraries of many other functional

languages, the Standard ML Basis library is rather frugal when it comes to

basic combinators for function composition, function application and infix

operators. I for one would like to see some general purpose operators for

notational convenience to be added to the Basis library. Towards that

goal, I briefly describe a family of simple function application operators

that I have found convenient (some I have used longer than others). None

of these operators is novel, but the family has some synergetic properties

and a degree of completeness.

Let's first take a look at the definitions of the operators:

infix 2 <\ (* Left section *)

fun (x <\ f) y = f (x, y)

infix 2 \> (* Left application *)

fun f \> y = f y

infixr 2 /> (* Right section *)

fun (f /> y) x = f (x, y)

infixr 2 </ (* Right application *)

fun x </ f = f x

infix 1 >| (* Left pipe *)

val op>| = op</

infixr 1 |< (* Right pipe *)

val op|< = op\>

The left and right section operators, <\ and />, are useful in SML for

partial application of infix operators. (Paulson's ML for the Working

Programmer (2nd. ed.) describes curried functions `secl' and `secr' for

the same purpose on pages 179-181.) For example,

List.map (op- /> y)

is a function for subtracting `y' from a list of integers and

List.exists (x <\ op=)

is a function for testing whether a list contains an `x'.

The left and right sectioning operators, <\ and />, together with the left

and right application operators, \> and </, provide a way to treat any

binary function (i.e. a function whose domain is a pair) as an infix

operator. Namely,

x0 <\f1\> x1 <\f2\> x2 ... <\fN\> xN = fN (... f2 (f1 (x0, x1), x2) ..., xN)

and

xN </fN/> ... x2 </f2/> x1 </f1/> x0 = fN (xN, ... f2 (x2, f1 (x1, x0)) ...) .

As the treatment of fixity declarations is a bit problematic in SML, this

technique can be an attractive alternative to the duplication of fixity

declarations or pollution of the top-level with ad-hoc infix identifiers.

These operators essentially approximate the Haskell syntax

x `f` y

for infix application.

The left and right application operators may also provide some notational

convenience on their own. In general,

f \> x1 \> ... \> xN = f x1 ... xN

and

xN </ ... </ x1 </ f = f x1 ... xN .

If nothing else, both of them can eliminate parentheses. For example,

foo (1 + 2) (3 * 4) = foo \> 1 + 2 \> 3 * 4 .

The left and right piping operators, >| and |<, are the same as the right

and left application operators, respectively, except the associativities

are reversed and the binding strength is lower. They are useful for piping

data trough a sequence of operations. In general,

x >| f1 >| ... >| fN

= fN |< ... |< f1 |< x

= fN (... (f1 x) ...)

= (fN o ... o f1) x

(modulo obvious differences in evaluation order).

The right piping operator, |<, is provided by the Haskell prelude as $.

The left piping operator is not provided by the Haskell prelude, AFAIK.

Much more could be spelled out about the use of these operators, but they

are probably best learned through personal use. I doubt there is any

chance of getting them to the Basis library, but at least you can't blame

me for not trying at all. ;)

-Vesa Karvonen

languages, the Standard ML Basis library is rather frugal when it comes to

basic combinators for function composition, function application and infix

operators. I for one would like to see some general purpose operators for

notational convenience to be added to the Basis library. Towards that

goal, I briefly describe a family of simple function application operators

that I have found convenient (some I have used longer than others). None

of these operators is novel, but the family has some synergetic properties

and a degree of completeness.

Let's first take a look at the definitions of the operators:

infix 2 <\ (* Left section *)

fun (x <\ f) y = f (x, y)

infix 2 \> (* Left application *)

fun f \> y = f y

infixr 2 /> (* Right section *)

fun (f /> y) x = f (x, y)

infixr 2 </ (* Right application *)

fun x </ f = f x

infix 1 >| (* Left pipe *)

val op>| = op</

infixr 1 |< (* Right pipe *)

val op|< = op\>

The left and right section operators, <\ and />, are useful in SML for

partial application of infix operators. (Paulson's ML for the Working

Programmer (2nd. ed.) describes curried functions `secl' and `secr' for

the same purpose on pages 179-181.) For example,

List.map (op- /> y)

is a function for subtracting `y' from a list of integers and

List.exists (x <\ op=)

is a function for testing whether a list contains an `x'.

The left and right sectioning operators, <\ and />, together with the left

and right application operators, \> and </, provide a way to treat any

binary function (i.e. a function whose domain is a pair) as an infix

operator. Namely,

x0 <\f1\> x1 <\f2\> x2 ... <\fN\> xN = fN (... f2 (f1 (x0, x1), x2) ..., xN)

and

xN </fN/> ... x2 </f2/> x1 </f1/> x0 = fN (xN, ... f2 (x2, f1 (x1, x0)) ...) .

As the treatment of fixity declarations is a bit problematic in SML, this

technique can be an attractive alternative to the duplication of fixity

declarations or pollution of the top-level with ad-hoc infix identifiers.

These operators essentially approximate the Haskell syntax

x `f` y

for infix application.

The left and right application operators may also provide some notational

convenience on their own. In general,

f \> x1 \> ... \> xN = f x1 ... xN

and

xN </ ... </ x1 </ f = f x1 ... xN .

If nothing else, both of them can eliminate parentheses. For example,

foo (1 + 2) (3 * 4) = foo \> 1 + 2 \> 3 * 4 .

The left and right piping operators, >| and |<, are the same as the right

and left application operators, respectively, except the associativities

are reversed and the binding strength is lower. They are useful for piping

data trough a sequence of operations. In general,

x >| f1 >| ... >| fN

= fN |< ... |< f1 |< x

= fN (... (f1 x) ...)

= (fN o ... o f1) x

(modulo obvious differences in evaluation order).

The right piping operator, |<, is provided by the Haskell prelude as $.

The left piping operator is not provided by the Haskell prelude, AFAIK.

Much more could be spelled out about the use of these operators, but they

are probably best learned through personal use. I doubt there is any

chance of getting them to the Basis library, but at least you can't blame

me for not trying at all. ;)

-Vesa Karvonen