I'm looking at the syntax for function composition -- (f:g x) -- and I have two quibbles, both regarding conventions in other languages. First, the notation for this in math looks more like a * or . in ascii. Haskell
uses a standalone . character, but the convention of using f.g for attribute or
method access in other languages is so strong that (f.g x) would probably be
unnecessarily counterintuitive for most of us. Which is the second quibble: The
: character shows up in other places in math (e.g. defining sets) and some
object-oriented languages. It makes me think of namespaces, actually, like in
XML. The main strike against * I see is that it's also an existing operator, so
how would you compose another function with multiplication? And this opens up two more cans of worms: 1. Consider the left-to-right flow of a normal Lisp function: You start reading at
the left, see the symbol you're defining, its inputs, a flow-control operator,
and then maybe a calculation -- at which point you scan for the calculation's
arguments, and trace backwards until you're back at the top of the calculation.
With the compose operator the nesting of parentheses is less distracting, but
your eye still jumps to the last function in the chain and works right-to-left. The pipe operator in the shell is handy and intuitive -- it's concise, and
it reads left-to-right, so the pipeline is easy to follow visually. How about
using a pipe for reverse function composition? Here's an example function (taking a list of numbers) -- see what I mean by left-to-right reading: ;; Scheme
(define (dist-from-orig pt)
(sqrt (apply + (map (lambda (n) (expt n 2)) pt))))
;; Arc + pipes
(def sum (arr) (apply + arr))
(def dist-from-orig (pt)
(map|sum|sqrt [expt _ 2] pt))
Now, the same pipe character could also mean "or" or "such that"; there might
be a really effective way to use those concepts on list-processing functions,
too, that would be worth spending an infix operator on. However, I don't know
of any other language that already uses a pipe character that way (anyone here
know APL or K?); "or" is already handled by the language and "such that" would
probably appear in a much different context, like pattern matching. And as for
the existing compose operator, it already breaks the usual assumption in Lisp
that the order of function application is completely defined by parentheses.
For stream-of-consciousness hacking, I think this adaptation of the shell's
condition is a better use of syntax.[NB: Most of the code in SICP has to be rearranged a bit to make any use of the
compose or reverse-compose operators, which makes me think maybe it's not such
a good idea to go too crazy with function composition.] 2. Namespaces: good or bad? They work pretty well for me in Python. If they're
left out, I can picture some folks doing it manually, similar to the way it's
sometimes done in C: ;; In mylib.arc, profanities:
(def mylib:read (fd) ...
(def mylib:write (fd data) ...
;; In order to do this elsewhere:
(import 'mylib)
(mylib:write myfd "User kludge")
Versus: ;; mylib.arc:
(export '(read write))
(def read (fd) ...
(def write (fd data) ...
;; elsewhere.arc:
(import 'mylib)
(mylib:write mydf "Erlang has it right")
;; or:
(import 'mylib '(read write))
(write mydf "Possibly clobbering read and write")
;; or even:
(import 'mylib '*)
(write mydf "Python has a nice shortcut")
Better? |