Skip to content

[enhancement] function type checks#3389

Open
line-o wants to merge 31 commits intoeXist-db:developfrom
line-o:feat/function-type-checks
Open

[enhancement] function type checks#3389
line-o wants to merge 31 commits intoeXist-db:developfrom
line-o:feat/function-type-checks

Conversation

@line-o
Copy link
Member

@line-o line-o commented Apr 24, 2020

Description:

BREAKING CHANGE

Internal module functions can now enforce function parameters to adhere to a given signature.

This is added to all HoFs fn:filter, fn:for-each, fn:fold-left, fn:fold-right as well
as their Array and Map function variants.

It is written such that other function values can also be checked array(xs:string) or complex ones like map(xs:integer, function(array(*)) as xs:boolean))

NOTE:

An implementation decision was, to still allow developers to write

filter(1, function ($a) { exists($a) })

instead of having to write

filter(1, function ($a) as xs:boolean { exists($a) })

For function parameters that return item()*, defer the return type check. Throw an error when the first non-matching result is returned.
This later check is currently done in the function implementation itself (see fn:filter).
A better alternative would be:

  1. being able to distinguish between a function with no explicit types set and one that does state it would return item()*
  2. only when types are not specified check the types at runtime.

Examples:

  • fn:filter -> function (item()) as xs:boolean
  • array:for-each-pair -> function (item()*, item()*) as item()*
  • map:for-each -> function (xs:anyAtomicValue, item()*) as item()*

Basex seems to follow the same optimistic approach

Reference:

Result of discussion with @adamretter in #3364
Is related to #1917
supersedes #3361 (the expathrepo related commit will be added here)
fixes #3382
fixes #4596
fixes #6125
related #3370

Type of tests:

All tests pass, two where adapted to match correct behaviour.

@line-o line-o marked this pull request as draft April 24, 2020 09:34
@line-o line-o self-assigned this Apr 24, 2020
@line-o line-o requested a review from a team April 24, 2020 09:34
@line-o line-o added deprecation xquery issue is related to xquery implementation bug issue confirmed as bug labels Apr 24, 2020
@line-o line-o added this to the eXist-6.0.0 milestone Apr 24, 2020
@line-o line-o requested review from joewiz and wolfgangmm April 24, 2020 09:36
Copy link
Member

@JoernT JoernT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

look good to me - very clean

@adamretter adamretter modified the milestones: eXist-6.0.0, eXist-7.0.0 Feb 14, 2022
@line-o line-o force-pushed the feat/function-type-checks branch from d43dff6 to 1403b00 Compare July 18, 2023 13:07
@line-o line-o requested review from a team, dizzzz and reinhapa July 18, 2023 16:58
@line-o
Copy link
Member Author

line-o commented Jul 18, 2023

Nine additional XQTS cases pass:

  • fn-filter-015
  • fn-filter-018
  • filter-901
  • filter-902
  • fn-for-each-pair-037
  • array-for-each-006
  • array-for-each-pair-308
  • array-filter-007
  • array-filter-008

failing XQTS tests are due to

@line-o line-o marked this pull request as ready for review July 18, 2023 17:16
@sonarqubecloud
Copy link

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 11 Code Smells

69.8% 69.8% Coverage
0.0% 0.0% Duplication

idea Catch issues before they fail your Quality Gate with our IDE extension sonarlint SonarLint

@joewiz
Copy link
Member

joewiz commented Jul 24, 2023

As discussed in the Community Call, this PR helps with XQuery conformance, achieves 9 more passing XQTS tests that previously failed, provides better static checking, and lays the groundwork for additional type checking of function parameters and return values.

Copy link
Contributor

@adamretter adamretter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a great start, and I am excited to see it. I do think it requires some work yet to complete it and polish it.

Apart from my specific code comments I am a bit confused by this statement in the PR description:

NOTE:
An implementation decision was, to still allow developers to write

filter(1, function ($a) { exists($a) })

instead of having to write

filter(1, function ($a) as xs:boolean { exists($a) })

Unless I am mistaken, that isn't a decision for an Implementation... It is actually required by the XQuery 3.1 specification, see: https://www.w3.org/TR/xquery-31/#prod-xquery31-InlineFunctionExpr

I am also not sure I understand this statement:

This later check is currently done in the function implementation itself (see fn:filter).

I understand that every function that accepts a function (as a parameter) will need its function signature modified to declare the additional parameter/return types, but
does that also mean that each of those also needs to be modified to perform the actual type checks of the function parameter's parameters? If so, it would seem that this checking should be moved out into something like the DynamicCardinalityCheck class.

* @param primaryType The <strong>Type</strong> of the parameter.
* @param parameterTypes The <strong>parameters</strong> the function(s) must accept.
* @param returnType The <strong>Type</strong> the function(s) needs to return.
* @param cardinality The <strong>Cardinality</strong> of the parameter.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cardinality parameter should follow the primaryType parameter, as they are part of the same sequence type. This is how it currently works in FunctionParameterSequenceType and that should be preserved here, as it makes the code for defining a FunctionSignature more logical to both write and read.

line-o added 23 commits March 14, 2026 00:27
- Static errors in Function.createFunction now throw an
XPathException with error code
`EXXQST0001`.
- remove redundant null initialization
- replace call to PathExpr.getExpression with getSubExpression
- incorporate learnings from eXist-db#5018
- refactor both implementations of checkType
  for readability
- remove unused constructor
- remove unnecessary method arityUnspecified
- fix formatting
- mark getters as nullable that might return null
- remove redundant initializations
- remove dead code

# Conflicts:
#	exist-core/src/main/java/org/exist/xquery/FunctionSignature.java
# Conflicts:
#	exist-core/src/main/java/org/exist/xquery/FunctionSignature.java
Adding some lines to make the intent a little more obvious to the reader.
Also, module.isInternalModule() will not change and should therefore only be called once.
Testcases are much more concise and assert more properties.
- added two new XQSuite tests to ensure this functionality
- Add new Function funParam to FunctionDSL to integrate new FunctionParameterFunctionSequenceType parameters
- FunctionParameterFunctionSequenceType now uses FunctionParameterSequenceType instead of SequenceType for better integration with FunctionDSL
- Use FunctionDSL in MapFunction
- use new switch statement in MapFunction.eval
- add enum for MapFunction similar to ArrayFunction
- adapt FunHigherOrderFun and ArrayFunction
- Refactor ArrayModule and ArrayFunction to use FunctionDSL and export each
  function as a named static export. ArrayFunction.eval also now just dispatches
  to private methods which greatly reduces complexity.
- Make better use of FunctionDSL in MapModule:
  - drop functionSignatures in favour of named exports denoting the arity rather than the position in an array
  - rename function exports for brevity
  - make use of functionDefs
fixes eXist-db#6125

- simplify MapType.get by directly providing the default value
- refactor MapFunction for readability and maintainability by extracting methods for different merge scenarios
  and introducing a method to select the duplicate handling strategy
- add xqsuite test
- use enum for function list
- switch to FunctionDSL for all functions
- eval is just one switch dispatching calls to extracted private methods
- add methods for all function implementations
- return early
- extract method getRealName
- toString method outputs function item types in their most generic but correct form
Used in stacktraces for example
FunctionParameterFunctionSequenceType will now show up with all their information.
Declare variable realNode only when needed.
Improve FunctionDSL
- drop redundant parameters from funParam
- add optFunParam, optManyFunParam

Reorganize constructors or FunctionParameterFunctionSequenceType for ease of use.

Adapt calls to funParam
@line-o line-o force-pushed the feat/function-type-checks branch from aed2487 to 66db85e Compare March 13, 2026 23:37
@dizzzz dizzzz force-pushed the feat/function-type-checks branch from 2195ec7 to 66db85e Compare March 14, 2026 09:33
Copy link
Member

@dizzzz dizzzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@line-o line-o requested a review from JoernT March 16, 2026 08:50
dizzzz and others added 2 commits March 16, 2026 10:22
- expand all imports to single class imports
- move function enum to the end of the class
- reorder function signature definitions
- fix formatting
- reorder the functions, enums and function defitions by the order of
  appearance in the XQuery Functions and Operators specification
- add and improve topmost documentation blocks
@line-o line-o force-pushed the feat/function-type-checks branch from 690ed3b to b4947b5 Compare March 16, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug issue confirmed as bug deprecation enhancement new features, suggestions, etc. xquery issue is related to xquery implementation

Projects

Status: No status

7 participants