Skip to content

Commit 113c5b2

Browse files
gselzerctrueden
authored andcommitted
SciJava Ops Engine Readme
1 parent bab8db0 commit 113c5b2

1 file changed

Lines changed: 96 additions & 8 deletions

File tree

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,100 @@
1-
The SciJava Ops projects offers a framework for typed operations, or Ops.
1+
# SciJava Ops Engine: A default implementation of SciJava Ops API
22

3-
Each op implements a particular functional interface, possessing typed inputs
4-
or outputs. The system provides the ability to request ops matching particular
5-
constraints, including implementing interface, input types and output types.
3+
This module ([currently](https://github.com/scijava/scijava/issues/55)) uses SciJava Common's `Plugin` framework to create the `DefaultOpEnvironment`. By discovering Op implementations at compile time, there is no need to provide an explicit list of Op classes.
64

7-
It is like Java's method overloading, but more powerful, and more extensible.
5+
The `DefaultOpEnvironment` utilizes many different routines to match as many Ops as possible. These routines are outlined below, along with a given example Op:
86

9-
More documentation coming later.
7+
```java
8+
@OpField(names = "math.add")
9+
public final BiFunction<Number, Number, Double> fooOp = (in1, in2) -> in1.doubleValue() + in2.doubleValue();
10+
```
1011

11-
See also [ImageJ Ops](https://github.com/imagej/imagej-ops), a collection of
12-
ops focused on scientific image processing and analysis.
12+
## Direct matching
13+
14+
The simplest type of Op matching involves a direct comparison on the functional types of the requested Op and the Ops known to the environment. Thus our example `math.add` Op might be returned as a direct match for the following `OpBuilder` call:
15+
16+
```java
17+
BiFunction<Number, Number, Double> op = OpEnvironment.op("math.add") //
18+
.inType(Number.class, Number.class) //
19+
.outType(Double.class) //
20+
.function();
21+
```
22+
23+
## Runtime-safe matching
24+
25+
`DefaultOpEnvironment` can also return Ops whose assignment would throw compile-time errors, but are safe to call at runtime. These situations often arise with function generics, when the requested input types are more specific than those defined by the Op, or the requested output type more generic than that declared by the Op. Runtime-safe matching provides much flexibility, allowing users to deviate from the Op's signature when more convenient. Thus our example `math.add` Op might be returned as a runtime-safe match for the following `OpBuilder` call:
26+
27+
```java
28+
BiFunction<Double, Double, Number> op = OpEnvironment.op("math.add") //
29+
.inType(Double.class, Double.class) //
30+
.outType(Number.class) //
31+
.function();
32+
```
33+
34+
Since `Double` is a subtype of `Number`, it is assignable to `Number` and that the inputs we wish to provide to the Op will satisfy the Op we have. A similar argument can be made about the output type.
35+
36+
## Adaptation
37+
38+
`DefaultOpEnvironment` is able to retype Ops whose functional type is not the same as the requested type when it is aware of the Ops needed to do the retyping. For example, a `BiFunction<I1, I2, O>` can be converted into a `Computer.Arity2<I1, I2, O>` iff there exists a `Computers.Arity1<O, O>` `copy` Op to copy the output of the `BiFunction` into the output provided by the `Computer`. Thus, supposing we have some Op:
39+
40+
```java
41+
@OpField(names = list.populator)
42+
public final BiFunction<Double, Double, List<Double>> barOp = (in1, in2) -> Arrays.asList(in1, in2);
43+
```
44+
45+
it might be returned as an adaptation for the following `OpBuilder` call:
46+
47+
48+
```java
49+
Computers.Arity2<Double, Double, List<Double>> op = OpEnvironment.op("math.add") //
50+
.inType(Double.class, Double.class) //
51+
.outType(new Nil<List<Double>>() {}) // note the need for a Nil, because we need to specify a generic type.
52+
.computer();
53+
```
54+
55+
## Simplification
56+
57+
`DefaultOpEnvironment` is able to retype Ops whose input and output parameter types are not the same as the requested arguments types when it is aware of the Ops needed to do the retyping. For example, a `BiFunction<A1, A2, O>` can be converted into a `BiFunction<B1, B2, P>` iff there exists a chain of Ops to convert from `A1` to `B1`, from `A2` to `B2`, and from `O` to `P`. Thus, supposing we have some Op:
58+
59+
```java
60+
@OpField(names = list.populator)
61+
public final BiFunction<Double, Double, List<Double>> barOp = (in1, in2) -> Arrays.asList(in1, in2);
62+
```
63+
64+
it might be returned as a simplification for the following `OpBuilder` call:
65+
66+
67+
```java
68+
BiFunction<Integer, Integer, List<Integer>> op = OpEnvironment.op("math.add") //
69+
.inType(Integer.class, Integer.class) //
70+
.outType(new Nil<List<Integer>>() {}) // note the need for a Nil, because we need to specify a generic type.
71+
.function();
72+
```
73+
74+
## Reduction
75+
76+
`DefaultOpEnvironment` supports optional parameters by enabling the retrieval Ops **with or without** their optional parameters (denoted with the `@Optional` annotation). Parameters can only be optional when **all input parameters to its right in the signature are also optional**. When a parameter is declared as optional, `null` is passed to the parameter and it is the **Op's** responsibility to replace that `null` value with a reasonable default. To prevent confusion, an optional parameter can **only** be omitted when all optional parameters to its right are **also omitted**. Thus, supposing we have some Op:
77+
78+
```java
79+
@Plugin(type = Op.class, name = math.add)
80+
public class bazOp implements BiFunction<Double, Double, Double> {
81+
82+
@Override
83+
public Double apply(Double in1, @Optional Double in2) {
84+
if (in2 == null) in2 = 0.0;
85+
return in1 + in2;
86+
87+
}
88+
89+
}
90+
```
91+
92+
it might be returned as a reduction for the following `OpBuilder` call:
93+
94+
95+
```java
96+
BiFunction<Double, Double, Double> op = OpEnvironment.op("math.add") //
97+
.inType(Double.class) //
98+
.outType(Double.class) //
99+
.function();
100+
```

0 commit comments

Comments
 (0)