2005-07-12 15:50:02 UTC
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
x0 <\f1\> x1 <\f2\> x2 ... <\fN\> xN = fN (... f2 (f1 (x0, x1), x2) ..., xN)
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
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. ;)