Skip to content

Commit

Permalink
transform special functions to classes
Browse files Browse the repository at this point in the history
  • Loading branch information
jitsedesmet committed Oct 23, 2023
1 parent a5a9cfd commit fb036a4
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 154 deletions.
4 changes: 1 addition & 3 deletions packages/expression-evaluator/lib/expressions/Expressions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IEvalContext, FunctionApplication } from '@comunica/types';
import type { FunctionApplication } from '@comunica/types';
import type * as RDF from '@rdfjs/types';
import type { Algebra } from 'sparqlalgebrajs';

Expand Down Expand Up @@ -98,5 +98,3 @@ export type VariableExpression = IExpressionProps & {
// Export type Application = SimpleApplication | SpecialApplication;
export type SimpleApplication = (args: TermExpression[]) => TermExpression;
export type AsyncExtensionApplication = (args: TermExpression[]) => Promise<TermExpression>;

export type SpecialApplicationAsync = (context: IEvalContext) => Promise<TermExpression>;
50 changes: 4 additions & 46 deletions packages/expression-evaluator/lib/functions/Core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ export abstract class BaseFunction<Operator> implements IFunctionExpression {
}

public checkArity(args: E.Expression[]): boolean {
// If the function has overloaded arity, the actual arity needs to be present.
if (Array.isArray(this.arity)) {
return this.arity.includes(args.length);
}
if (this.arity === Number.POSITIVE_INFINITY) {
// Infinity is used to represent var-args, so it's always correct.
return true;
}

return args.length === this.arity;
}
Expand Down Expand Up @@ -115,48 +118,3 @@ export class NamedFunction extends BaseFunction<C.NamedOperator> {
throw new Err.InvalidArgumentTypes(args, this.operator);
}
}

// Special Functions ----------------------------------------------------------
/**
* Special Functions are those that don't really fit in sensible categories and
* have extremely heterogeneous signatures that make them impossible to abstract
* over. They are small in number, and their behaviour is often complex and open
* for multiple correct implementations with different trade-offs.
*
* Due to their varying nature, they need all available information present
* during evaluation. This reflects in the signature of the apply() method.
*
* They need access to an evaluator to be able to even implement their logic.
* Especially relevant for IF, and the logical connectives.
*
* They can have both sync and async implementations, and both would make sense
* in some contexts.
*/
export class SpecialFunction implements IFunctionExpression {
public arity: number;
public apply: E.SpecialApplicationAsync;
public checkArity: (args: E.Expression[]) => boolean;

public constructor(public operator: C.SpecialOperator, definition: ISpecialDefinition) {
this.arity = definition.arity;
this.apply = definition.applyAsync;
this.checkArity = definition.checkArity || defaultArityCheck(this.arity);
}
}

function defaultArityCheck(arity: number): (args: E.Expression[]) => boolean {
return (args: E.Expression[]): boolean => {
// Infinity is used to represent var-args, so it's always correct.
if (arity === Number.POSITIVE_INFINITY) {
return true;
}

return args.length === arity;
};
}

export interface ISpecialDefinition {
arity: number;
applyAsync: E.SpecialApplicationAsync;
checkArity?: (args: E.Expression[]) => boolean;
}
Loading

0 comments on commit fb036a4

Please sign in to comment.