eXist-db / exist

eXist Native XML Database and Application Platform
https://exist-db.org
GNU Lesser General Public License v2.1
428 stars 179 forks source link

[BUG] HoF function parameters parameter types not checked #3754

Open line-o opened 3 years ago

line-o commented 3 years ago

Describe the bug

When a function parameter is typed, the type must be enforced. That is why the example above will throw err:XPTY0004 "xs:integer(1) is not a sub-type of xs:string".

(function ($parameter as xs:string) { $parameter })(1)

The parameter types of arguments of Higher-order-functions however are not enforced.

for-each(1, function ($parameter as xs:string) { $parameter })

returns xs:integer(1)

for-each((1, "2"), function ($parameter) as xs:string { $parameter })

does throw on the other hand. So return types are enforced.

This seems to be a known issue, see https://github.com/eXist-db/exist/issues/3382#issuecomment-617155618 A clear and concise description of what the bug is.

Interestingly, user defined higher order functions are not affected. That is a clear hint that the underlying implementation of higher order functions in Java needs to be changed.

declare function local:string-id ($item as xs:string) { $item };
declare function local:my-hof($item, $f as function(*)) { $f($item) };
local:my-hof(1, local:string-id#1)

Expected behavior

The parameter types of arguments of Higher-order-functions however are enforced and err:XPTY0004 is raised whenever a value of a type is passed to a function parameter that does not match the expected type.

To Reproduce

xquery version "3.1";

module namespace t="http://exist-db.org/xquery/test";

declare namespace test="http://exist-db.org/xquery/xqsuite";

declare %private function t:my-apply($item, $f as function(*)) { $f($item) };

declare
    %test:assertError("XPTY0004")
function t:my-hof() {
    t:my-apply(1, function ($item as xs:string) { $item })
};

declare
    %test:assertError("XPTY0004")
function t:for-each() {
    for-each((1, "2"), function ($item as xs:string) { $item })
};

declare
    %test:assertError("XPTY0004")
function t:filter() {
    filter((1, "2"), function ($item as xs:string) { exists($item) })
};

declare
    %test:assertError("XPTY0004")
function t:fold-left() {
    fold-left((1, "2"), "", 
        function ($result, $next as xs:string) { concat($result, $next) })
};

declare
    %test:assertError("XPTY0004")
function t:apply() {
    apply(function ($item as xs:string) { $item }, [1])
};

Context (please always complete the following information):

Additional context

line-o commented 3 years ago

And as the below test indicates @adamretter was right that the type-checks are not enforced at compile time

declare
    %test:assertXPath("/error")
function t:compile-my-hof() {
    util:compile-query(``[
        declare function local:my-apply($item, $f as function(*)) {
            $f($item)
        };
        local:my-apply(1, function ($item as xs:string) { $item })
    ]``, "xmldb:exist://")
};