- From: <bugzilla@jessica.w3.org>
- Date: Thu, 14 Aug 2014 19:32:38 +0000
- To: public-qt-comments@w3.org
https://www.w3.org/Bugs/Public/show_bug.cgi?id=26585 Bug ID: 26585 Summary: Proposal: fn:apply Product: XPath / XQuery / XSLT Version: Working drafts Hardware: PC OS: All Status: NEW Severity: normal Priority: P2 Component: XQuery 3.1 Assignee: jonathan.robie@gmail.com Reporter: marc.van.grootel@gmail.com QA Contact: public-qt-comments@w3.org XQuery 3.0 introduced functions that are derived from functional programming. Function items can be passed as arguments to functions like fn:fold-left(). Together they allow a more functional style of coding. Most of these functions can be used to perform map, filter, reduce on sequences. However, one important function that is missing and for which I do not know a reasonable workaround for is "apply" [1]. The wikipedia page shows that many languages have ways to achieve this and Clojure has a function with the same name [2]. In XQuery we can bind a variable to a function item. But when we want to use it we are limited to one argument. $f = function my-add($a,$b,$c) { $a + $b + $c } $args = (1,2,3) $f($args) == $f((1,2,3)) If we had an apply function this would do it. fn:apply($f, $args) == $f(1,2,3) Of course we could rewrite the function my-add to something like: $f = function my-add($seq) { sum($seq) } $f($args) == 6 But this is only practical for a limited number of cases and the function body would need to unpack the sequence, and we also lose the type checking. This limitation also shows with the proposed arrow operator (=>). Someone coming from a language like Clojure, Lisp or Scheme would interpret the following (1,2,3) => $f to mean $f(1,2,3) but in XQuery it would mean $f((1,2,3)) The arrow operator is similar to Clojure's threading operator (->) [3]. Clojure: (-> (1 2 3) f1 f2) == (f2 (f1 1 2 3)) Note that in Clojure -> is a macro which re-writes the form before the compiler will see it. But I digress. XQuery: (1,2,3) => $f1 => $f2 $f2($f1((1,2,3)) Maybe this is a consequence of the fact that sequences cannot be nested, and are effectively flattened. I cannot oversee all the consequences but like a map may contain other maps arrays can be nested so they could be used for "apply" semantics. Currently we have: (1,2,3) => $f $f((1,2,3)) With array apply semantics: [1,2,3] => $f1 => $f2 == $f2($f1(1,2,3)) [(1,2),(3,4)] => $f1 == $f1((1,2),(3,4)) == fn:apply($f1, [(1,2),(3,4)]) On the surface this looks like an elegant solution to me and is closer to the apply semantics in other languages. A particular use case for fn:apply arose when I tried implementing Clojure Ring and Compojure libraries in XQuery. These are comparable to WSGI in Python and Rack in Ruby. Below I simplified things but I hope it illustrates a concrete use case. I have a function for defining a routing handler which maps an HTTP GET request to a handler function. The def-route() signature is function( $method as xs:string, $url-template as xs:string, $params as array(*), $handler as function(*) ) as function(map(*)) as map(*) This means that a route handler function is something that takes a request map as input and produces a response map as output. $handler = function($name, $age) { 'Person: ' || $name || ' (age ' || $age ')' } $route = def-route('GET', '/person/{name}/{age}', ['name', 'age'], $handler) $route(request('GET', '/person/Joe/10')) When a GET request comes in on '/person/Joe/10'. The route-handler $route matches and the 'name' and 'age' parameters are parsed from the URL and added to the request map. The third argument is an array that specifies which keys need to be taken from the request map and builds the $params array: ['Joe', '10']. Finally it can invoke the $handler using fn:apply($handler, $params) or $params => $handler == ['Joe', '10'] => $handler This results in a response('Person: Joe (age 10)') which is then rendered to the browser. Note that the handler does not need to know where to find the parameters because this is set up by the $route handler thus achieving a clean separation of concerns and the possibility of using $handler in other situations because it is not tied to the plumbing/routing logic. As I said earlier, I may not be able to oversee all the consequences as I'm not super intimate with all the specs involved. Or, my pseudo code is confusing. Mea culpa. --Marc [1] http://en.wikipedia.org/wiki/Apply [2] http://clojuredocs.org/clojure_core/clojure.core/apply [3] http://clojuredocs.org/clojure_core/clojure.core/-%3E -- You are receiving this mail because: You are the QA Contact for the bug.
Received on Thursday, 14 August 2014 19:32:57 UTC