[mercury-users] The Logic of Mercury
Richard A. O'Keefe
ok at hermes.otago.ac.nz
Wed Sep 8 10:44:25 AEST 1999
Haskell and Clean are wonderful languages.
I find SML's inability to make up its mind about currying vexatious
(for example, built-in operators like + are NOT curried).
Lee Naish is a Very Smart Person and one of logic programming's
long-time Good Guys.
So it's with some trepidation that I strongly disagree with Lee
about the desirability of currying in logic programs.
To keep things neutral, let's consider a sort of hybrid SML/Clean
rather than Mercury.
f :: A -> B
There are no partial applications of f. All we can do is give it an
A and sit back.
g :: (A,B) -> C
There are TWO ways to partially apply g. Let's define
c2a :: ((a,b)->c, a) -> (b->c)
c2a(f,x) = \y.f(x,y)
c2b :: ((a,b)->c, b) -> (a->c)
c2b(f,y) = \x.f(x,y)
Now we can do c2a(g, an_a) and c2b(g, a_b). To lay such heavy stress
on currying as Lee does is to assert that c2a (supply left operand)
is *much* more useful than c2b (supply right operand). That just
isn't true. In Haskell, I find myself using (++x) or (-x) more
often than (x++) or (x-).
h :: (A,B,C) -> D
There are THREE ways to partially apply h to one argument,
and THREE ways to partially apply it to two arguments. Currying
gives you only one of each. Let's define
c3a :: ((a,b,c)->d, a) -> ((b,c)->d)
c3a(f,x) = \(y,z).f(x,y,z)
c3b :: ((a,b,c)->d, b) -> ((a,c)->d)
c3b(f,y) = \(x,z).f(x,y,z)
c3c :: ((a,b,c)->d, c) -> ((a,b)->d)
c3c(f,z) = \(x,y).f(x,y,z)
c3ab :: ((a,b,c)->d, a, b) -> (c->d)
c3ab(f,x,y) = \z.f(x,y,z)
c3ac :: ((a,b,c)->d, a, c) -> (b->d)
c3ac(f,x,z) = \y.f(x,y,z)
c3bc :: ((a,b,c)->d, b, c) -> (a->d)
c3bc(f,y,z) = \x.f(x,y,z)
To lay such heavy stress on currying as Lee does is to assert
that c3a and c3ab are *much* more useful than the other four
forms. This is intrinsically implausible.
Just to double-check, let's take some functions from the SML library.
I picked OS.Path as the module to look in because that's what I've
been looking at most recently. Most of the multiargument functions
therein don't just take uncurried (tuple) arguments, they take an
argument *record* so as to get named arguments.
OS.Path.joinDirFile {dir = d, file = f}
is a good example. (By the way, how DO you get named arguments
in a heavily curried language? They'd be nice in Mercury, wouldn't
they. Erlang (ab)uses records for much the same reason.)
For example, OS.Path.joinDirFile {dir="foo", file="bar"}
yields "foo/bar" in UNIX or "foo\\bar" in Windows.
Suppose it were
join_dir_file :: (String,String) -> String
join_dir_file(dir,file) = ...
Then c2a(join_dir_file,"/tmp") would be a useful function for
naming files in the tmp directory, BUT
exists(c2b(OS.FileSys.exists,A_READ),
map(c2b(join_dir_file,"profile"), directory_list))
would be a way of looking for a file called "profile" in each
of a list of directories. Note that OS.FileSys.exists has
its arguments in a natural order, but is MORE likely to be
partially applied to its second argument than its first.
OS.Path.joinBaseExt {base = b, ext = e}
is just such another.
OS.Path.mkAbsolute(name, reference)
OS.Path.mkRelative(name, reference)
These two functions take a file name and an absolute reference.
The name is interpreted relative to the reference, unless it is
already absolute. mkAbsolute returns an absolute result.
mkRelative returns a path relative to the reference, e.g.
mkRelative("/a/b/c/d", "/a/b") -> "c/d"
mkRelative("/c/a/b", "c/d") -> "../a/b"
I would expect most partial applications of these functions to
supply the SECOND argument, not the first.
What happens with a function that has four arguments?
There are FOUR ways to supply one argument, SIX ways to
supply two arguments, and FOUR ways to supply three arguments,
for a total of 14 partial applications, only three of which are
supported by currying.
I am *not* saying that currying is useless, or that languages based on
it do not have their pleasant side. What I *am* saying is that
- in an uncurried polymorphic functional language (such as the
functional sublanguage of Mercury) it is possible to define a
kit of currying functions that can be applied to provide all
of the combinations directly supported by built-in currying and then
some, with only minor inconvenience.
- in my experience with SML, Haskell, and Clean, I have found that
about half of the partial applications I want in practice are NOT
supported by currying so that I have to use currying combinators
or explicit lambda-expressions, so that curried Mercury would only
save about half of the notational effort
- requiring people to use currying combinators like c3ac is NOT any
kind of barrier to re-use, and if it were, Haskell would have the
*same* barrier.
What about predicates? Well, the average predicate has rather more
arguments than the average function. Allowing predicate definitions
to be nested reduces that, but predicates still have somewhat more
than one argument more than functions, which exponentially increases
the number of potentially useful partial applications, and correspondingly
dilutes the practical utility of curried definitions.
--------------------------------------------------------------------------
mercury-users mailing list
post: mercury-users at cs.mu.oz.au
administrative address: owner-mercury-users at cs.mu.oz.au
unsubscribe: Address: mercury-users-request at cs.mu.oz.au Message: unsubscribe
subscribe: Address: mercury-users-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------
More information about the users
mailing list