Using Go generics to implement a chain of processors - go

I am trying to implement a kind of simple processing pipeline in Go, where each processor has a determined input and output type, and a list of successor processors that take current processor output type as input, and may have successor processors of their own.
I am running into issues on how to add successor processors to the current one, regardless of their output type. I tried using any as a wildcard type like I would do with ? in Java, but Go is not having it.
What I have in Go is this:
type Processor[InputType any, OutputType any] struct {
nextProcessors []*Processor[OutputType, any]
ProcessingFunction func(InputType) OutputType
}
func (b *Processor[InputType, OutputType]) Process(input InputType) {
result := b.ProcessingFunction(input)
for _, nextProcessor := range b.nextProcessors {
nextProcessor.Process(result)
}
}
func (b *Processor[InputType, OutputType]) AddOutputProcessor(p *Processor[OutputType, any]) {
b.nextProcessors = append(b.nextProcessors, p)
}
func main() {
outputer := Processor[int, string]{ProcessingFunction: func(input int) string {
print(input)
return string(input)
}}
doubler := Processor[int, int]{ProcessingFunction: func(input int) int { return input * 2 }}
rng := Processor[int, int]{ProcessingFunction: func(input int) int { return rand.Intn(input) }}
rng.AddOutputProcessor(&doubler)
doubler.AddOutputProcessor(&outputer)
rng.Process(20)
}
Which gives me a compilation error:
cannot use &doubler (value of type *Processor[int, int]) as type *Processor[int, any]
Is there a way to ignore the output type of the successor processor? Or should I maybe go a different way about it? I would just like to make sure that successor processors can accept the right type of input.
For reference, here is the interface definition in Java that works the way I intend it to.
public interface Processor<InputType, OutputType> {
void addOutputProcessor(Processor<OutputType, ?> outputProcessor);
void process(InputType input);
}
public class Pipeline {
private abstract class BaseProcessor<InputType, OutputType> implements Processor<InputType, OutputType> {
List<Processor<OutputType, ?>> nextProcessors = new ArrayList<>();
abstract OutputType processHelper(InputType input);
#Override
public void addOutputProcessor(Processor<OutputType, ?> outputProcessor) {
nextProcessors.add(outputProcessor);
}
#Override
public void process(InputType input) {
OutputType result = processHelper(input);
for (Processor<OutputType, ?> nextProcessor : nextProcessors) {
nextProcessor.process(result);
}
}
}
private class RandomNumberGenerator extends BaseProcessor<Integer, Integer> {
#Override
Integer processHelper(Integer input) {
int generatedNumber = new Random().nextInt(input);
return generatedNumber;
}
}
private class IncrementProcessor extends BaseProcessor<Integer, Integer> {
#Override
Integer processHelper(Integer input) {
return input + 1;
}
}
private class DoubleProcessor extends BaseProcessor<Integer, Integer> {
#Override
Integer processHelper(Integer input) {
return input * 2;
}
}
private class Outputer extends BaseProcessor<Integer, String> {
String name;
public Outputer(String name) {
this.name = name;
}
#Override
String processHelper(Integer input) {
String output = String.format("Pipeline %s result: %d", name, input);
System.out.println(output);
return output;
}
}
public void buildAndRunPipeline() {
Processor<Integer, String> doublingOutputter = new Outputer("Doubling");
Processor<Integer, String> incrementingOutputter = new Outputer("Incrementing");
Processor<Integer, String> rngOutputter = new Outputer("Generating number");
Processor<Integer, Integer> doubler = new DoubleProcessor();
doubler.addOutputProcessor(doublingOutputter);
Processor<Integer, Integer> incrementer = new IncrementProcessor();
incrementer.addOutputProcessor(incrementingOutputter);
Processor<Integer, Integer> starter = new RandomNumberGenerator();
starter.addOutputProcessor(rngOutputter);
starter.addOutputProcessor(doubler);
starter.addOutputProcessor(incrementer);
starter.process(20);
}
public static void main(String[] args) {
Pipeline p = new Pipeline();
p.buildAndRunPipeline();
}
}

Is there a way to ignore the output type of the successor processor?
No.
In Go any is just a static type (alias of interface{}. It can never be a replacement for Java's unbounded wildcard ?. So *Processor[int, any] is just not the same type as *Processor[int, int] and you can't assign one to the other, as reported by your error message.
In order to construct an arbitrarily long chain you would need to parametrize the Process method itself, but this is not possible in Go 1.18. You must declare all type parameters on the type itself. Though, even if you do this, you will keep incurring in the same issue of not knowing the output type of the next processor.
Generally speaking, using a for loop can't work because the static types of the in/out values keep changing.
I believe the closest you can get without reflection is to implement some sort of composition operator — like . in haskell, via a top-level function. But you would have to manually nest calls.
A simplified example (the type Processor is redundant, but keeping it closer to your code):
package main
import (
"fmt"
"strconv"
)
type Processor[In, Out any] func(In) Out
func Process[In, Out any](input In, processor Processor[In, Out]) Out {
return processor(input)
}
func main() {
parser := Processor[string, int](func(input string) int { s, _ := strconv.Atoi(input); return s })
doubler := Processor[int, int](func(input int) int { return input * 2 })
outputer := Processor[int, string](func(input int) string { return fmt.Sprintf("%d", input) })
out := Process(Process(Process("20", parser), doubler), outputer)
fmt.Println(out)
}
Playground: https://go.dev/play/p/Iv-virKATyb

You can't use any keyword to instantiate the value of generic type.
nextProcessors []*Processor[OutputType, any] // keyword any is not a valid type here
You can actually, but the second parameter always should be interface{}. But it's not a part of answer to your question.
To solve your issue you can use generic interface instead
type IProcess[InputType any] interface {
Process(input InputType)
}
type Processor[InputType any, OutputType any] struct {
nextProcessors []IProcess[OutputType]
ProcessingFunction func(InputType) OutputType
}
func (b *Processor[InputType, OutputType]) AddOutputProcessor(p IProcess[OutputType]) {
b.nextProcessors = append(b.nextProcessors, p)
}
https://go.dev/play/p/B1wlOvSbb0I

Related

Java8 Method chaining for Single object without Stream/Optional?

I felt it easiest to capture my question with the below example. I would like to apply multiple transformations on an object (in this case, they all return same class, Number, but not necessarily). With an Optional (Method 3) or Stream (Method 4), I can use the .map elegantly and legibly. However, when used with a single object, I have to either just make an Optional just to use the .map chaining (with a .get() in the end), or use Stream.of() with a findFirst in the end, which seems like unnecessary work.
[My Preference]: I prefer methods 3 & 4, as they seem better for readability than the pre-java8 options - methods 1 & 2.
[Question]: Is there a better/neater/more preferable/more elegant way of achieving the same than all the methods used here? If not, what method would you use?
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Tester {
static class Number {
private final int value;
private Number(final int value) {
this.value = value;
}
public int getValue() {
return value;
}
#Override
public String toString() {
return String.valueOf(value);
}
}
private static Number add(final Number number, final int val) {
return new Number(number.getValue() + val);
}
private static Number multiply(final Number number, final int val) {
return new Number(number.getValue() * val);
}
private static Number subtract(final Number number, final int val) {
return new Number(number.getValue() - val);
}
public static void main(final String[] args) {
final Number input = new Number(1);
System.out.println("output1 = " + method1(input)); // 100
System.out.println("output2 = " + method2(input)); // 100
System.out.println("output3 = " + method3(input)); // 100
System.out.println("output4 = " + method4(input)); // 100
processAList();
}
// Processing an object - Method 1
private static Number method1(final Number input) {
return subtract(multiply(add(input, 10), 10), 10);
}
// Processing an object - Method 2
private static Number method2(final Number input) {
final Number added = add(input, 10);
final Number multiplied = multiply(added, 10);
return subtract(multiplied, 10);
}
// Processing an object - Method 3 (Contrived use of Optional)
private static Number method3(final Number input) {
return Optional.of(input)
.map(number -> add(number, 10))
.map(number -> multiply(number, 10))
.map(number -> subtract(number, 10)).get();
}
// Processing an object - Method 4 (Contrived use of Stream)
private static Number method4(final Number input) {
return Stream.of(input)
.map(number -> add(number, 10))
.map(number -> multiply(number, 10))
.map(number -> subtract(number, 10))
.findAny().get();
}
// Processing a list (naturally uses the Stream advantage)
private static void processAList() {
final List<Number> inputs = new ArrayList<>();
inputs.add(new Number(1));
inputs.add(new Number(2));
final List<Number> outputs = inputs.stream()
.map(number -> add(number, 10))
.map(number -> multiply(number, 10))
.map(number -> subtract(number, 10))
.collect(Collectors.toList());
System.out.println("outputs = " + outputs); // [100, 110]
}
}
The solution is to build your methods into your Number class. For example:
static class Number {
// instance variable, constructor and getter unchanged
public Number add(final int val) {
return new Number(getValue() + val);
}
// mulitply() and subtract() in the same way
// toString() unchanged
}
Now your code becomes very simple and readable:
private static Number method5(final Number input) {
return input
.add(10)
.multiply(10)
.subtract(10);
}
You may even write the return statement on one line if you prefer:
return input.add(10).multiply(10).subtract(10);
Edit: If you can't change the Number class, my personal taste would be for method2. Using Optional or Stream would be misuse or at least misplaced and could easily confuse your reader. If you insist, write your own Mandatory class, like Optional except it always holds a value, which makes it simpler. For my part I wouldn't bother.

Dealing with one field that is sometimes boolean and sometimes int

I'm trying to work with the reddit JSON API. There are post data objects that contain a field called edited which may contain a boolean false if the post hasn't been edited, or a timestamp int if the post was edited.
Sometimes a boolean:
{
"edited": false,
"title": "Title 1"
}
Sometimes an int:
{
"edited": 1234567890,
"title": "Title 2"
}
When trying to parse the JSON where the POJO has the field set to int, I get an error: JsonDataException: Expected an int but was BOOLEAN...
How can I deal with this using Moshi?
I also ran into a similar problem where I had fields that were sometimes booleans, and sometimes ints. I wanted them to always be ints. Here's how I solved it with Moshi and kotlin:
Make a new annotation that you will use on fields to should convert from boolean to int
#JsonQualifier
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FUNCTION)
annotation class ForceToInt
internal class ForceToIntJsonAdapter {
#ToJson
fun toJson(#ForceToInt i: Int): Int {
return i
}
#FromJson
#ForceToInt
fun fromJson(reader: JsonReader): Int {
return when (reader.peek()) {
JsonReader.Token.NUMBER -> reader.nextInt()
JsonReader.Token.BOOLEAN -> if (reader.nextBoolean()) 1 else 0
else -> {
reader.skipValue() // or throw
0
}
}
}
}
Use this annotation on the fields that you want to force to int:
#JsonClass(generateAdapter = true)
data class Discovery(
#Json(name = "id") val id: String = -1,
#ForceToInt #Json(name = "thanked") val thanked: Int = 0
)
The easy way might be to make your Java edited field an Object type.
The better way for performance, error catching, and appliaction usage is to use a custom JsonAdapter.
Example (edit as needed):
public final class Foo {
public final boolean edited;
public final int editedNumber;
public final String title;
public static final Object JSON_ADAPTER = new Object() {
final JsonReader.Options options = JsonReader.Options.of("edited", "title");
#FromJson Foo fromJson(JsonReader reader) throws IOException {
reader.beginObject();
boolean edited = true;
int editedNumber = -1;
String title = "";
while (reader.hasNext()) {
switch (reader.selectName(options)) {
case 0:
if (reader.peek() == JsonReader.Token.BOOLEAN) {
edited = reader.nextBoolean();
} else {
editedNumber = reader.nextInt();
}
break;
case 1:
title = reader.nextString();
break;
case -1:
reader.nextName();
reader.skipValue();
default:
throw new AssertionError();
}
}
reader.endObject();
return new Foo(edited, editedNumber, title);
}
#ToJson void toJson(JsonWriter writer, Foo value) throws IOException {
writer.beginObject();
writer.name("edited");
if (value.edited) {
writer.value(value.editedNumber);
} else {
writer.value(false);
}
writer.name("title");
writer.value(value.title);
writer.endObject();
}
};
Foo(boolean edited, int editedNumber, String title) {
this.edited = edited;
this.editedNumber = editedNumber;
this.title = title;
}
}
Don't forget to register the adapter on your Moshi instance.
Moshi moshi = new Moshi.Builder().add(Foo.JSON_ADAPTER).build();
JsonAdapter<Foo> fooAdapter = moshi.adapter(Foo.class);

Why java Map.merge does not pass a supplier?

I want in java a method which allows me to modify a value if exist, or insert one if it doesn't. Similar to merge, but:
I want to pass a value supplier and not a value, to avoid creating it when not needed
In case the value exists, I don't want to reinsert it nor remove it, just access its methods with a container.
I had to write this. The problem with writing it myself is that the version for Concurrent maps is not trivial
public static <K, V> V putOrConsume(Map<K, V> map, K key, Supplier<V> ifAbsent, Consumer<V> ifPresent) {
V val = map.get(key);
if (val != null) {
ifPresent.accept(val);
} else {
map.put(key, ifAbsent.get());
}
return val;
}
The best "standard" way of achieving it is to use compute():
Map<String, String> map = new HashMap<>();
BiFunction<String, String, String> convert = (k, v) -> v == null ? "new_" + k : "old_" + v;
map.compute("x", convert);
map.compute("x", convert);
System.out.println(map.get("x")); //prints old_new_x
Now, say, you have your Supplier and Consumer and would like to follow DRY principle. Then you could use a simple function combinator:
Map<String, String> map = new HashMap<>();
Supplier<String> ifAbsent = () -> "new";
Consumer<String> ifPresent = System.out::println;
BiFunction<String, String, String> putOrConsume = (k, v) -> {
if (v == null) return ifAbsent.get();
ifPresent.accept(v);
return v;
};
map.compute("x", putOrConsume); //nothing
map.compute("x", putOrConsume); //prints "new"
Obviously, you could write a combinator function that takes supplier and consumer and returns BiFunction to make the code above even more generic.
The drawback of this proposed approach is in the extra call to map.put() even if you simply consume the value, i.e. it will be slightly slower, by the time of key lookup. The good news are, map implementations will simply replace the value without creating the new node. I.e. no new objects will be created or garbage collected. Most of the time such trade-offs are justified.
map.compute(...) and map.putIfAbsent(...) are much more powerful than fairly specialized proposed putOrConsume(...). It is so asymmetrical I would actually review the reasons why you need it in the code.
You can achieve what you want with Map.compute and a trivial helper method, as well as with the help of a local class to know if your ifAbsent supplier has been used:
public static <K, V> V putOrConsume(
Map<K, V> map,
K key,
Supplier<V> ifAbsent,
Consumer<V> ifPresent) {
class AbsentSupplier implements Supplier<V> {
boolean used = false;
public V get() {
used = true;
return ifAbsent.get();
}
}
AbsentSupplier absentSupplier = new AbsentSupplier();
V computed = map.compute(
key,
(k, v) -> v == null ?
absentSupplier.get() :
consumeAndReturn(v, ifPresent));
return absentSupplier.used ? null : computed;
}
private static <V> V consumeAndReturn(V v, Consumer<V> consumer) {
consumer.accept(v);
return v;
}
The tricky part is finding whether you have used your ifAbsent supplier to return either null or the existent, consumed value.
The helper method simply adapts the ifPresent consumer so that it behaves like a unary operator that consumes the given value and returns it.
different from others answers, you also using Map.compute method and combine Functions with interface default methods / static methods to make your code more readable. for example:
Usage
//only consuming if value is present
Consumer<V> action = ...;
map.compute(key,ValueMapping.ifPresent(action));
//create value if value is absent
Supplier<V> supplier = ...;
map.compute(key,ValueMapping.ifPresent(action).orElse(supplier));
//map value from key if value is absent
Function<K,V> mapping = ...;
map.compute(key,ValueMapping.ifPresent(action).orElse(mapping));
//orElse supports short-circuit feature
map.compute(key,ValueMapping.ifPresent(action)
.orElse(supplier)
.orElse(() -> fail("it should not be called "+
"if the value computed by the previous orElse")));
<T> T fail(String message) {
throw new AssertionError(message);
}
ValueMapping
interface ValueMapping<T, R> extends BiFunction<T, R, R> {
default ValueMapping<T, R> orElse(Supplier<R> other) {
return orElse(k -> other.get());
}
default ValueMapping<T, R> orElse(Function<T, R> other) {
return (k, v) -> {
R result = this.apply(k, v);
return result!=null ? result : other.apply(k);
};
}
static <T, R> ValueMapping<T, R> ifPresent(Consumer<R> action) {
return (k, v) -> {
if (v!=null) {
action.accept(v);
}
return v;
};
}
}
Note
I used Objects.isNull in ValueMapping in previous version. and #Holger point out that is an overusing case, and should replacing it with simpler condition it != null.

Return enum type based on string value in swift?

I'm trying to do the following in a playground to assign an enum type based on a string, but getting an error in the changeType function. How can I get this to work properly?
enum TransactionType {
case purchase,charge
case deposit,payment
func description() -> String {
switch self {
case .purchase:
return "purchase"
case .charge:
return "charge"
case .deposit:
return "deposit"
case .payment:
return "payment"
}
}
func typeFromString(value:String) -> TransactionType {
switch value {
case "charge":
return .charge
case "deposit":
return .deposit
case "payment":
return .payment
default:
return .purchase
}
}
}
class Tester {
var transactionType = TransactionType.purchase
func changeType() {
transactionType = TransactionType.typeFromString("charge")
}
}
var tester = Tester()
print(tester.transactionType.description())
tester.changeType()
print(tester.transactionType.description())
The solution is simpler than you think:
enum TransactionType : String {
case purchase = "purchase", charge = "charge"
case deposit = "deposit", payment = "payment"
}
class Tester {
var transactionType = TransactionType.purchase
func changeType() {
transactionType = TransactionType.fromRaw("charge")!
}
}
var tester = Tester()
print(tester.transactionType.toRaw())
tester.changeType()
print(tester.transactionType.toRaw())
The trick is to set a raw value of String type, which defines the type associated to each enum case.
More info Raw Values in Enumerations
You can define the typeFromString method as static in order to avoid complications with optional values. After all, it just contains constants anyway. Simply add the word static before the func definition.
static func typeFromString(value:String) -> TransactionType {

IEqualityComparer for anonymous type

I have this
var n = ItemList.Select(s => new { s.Vchr, s.Id, s.Ctr, s.Vendor, s.Description, s.Invoice }).ToList();
n.AddRange(OtherList.Select(s => new { s.Vchr, s.Id, s.Ctr, s.Vendor, s.Description, s.Invoice }).ToList(););
I would like to do this if it where allowed
n = n.Distinct((x, y) => x.Vchr == y.Vchr)).ToList();
I tried using the generic LambdaComparer but since im using anonymous types there is no type associate it with.
"Help me Obi Wan Kenobi, you're my only hope"
The trick is to create a comparer that only works on inferred types. For instance:
public class Comparer<T> : IComparer<T> {
private Func<T,T,int> _func;
public Comparer(Func<T,T,int> func) {
_func = func;
}
public int Compare(T x, T y ) {
return _func(x,y);
}
}
public static class Comparer {
public static Comparer<T> Create<T>(Func<T,T,int> func){
return new Comparer<T>(func);
}
public static Comparer<T> CreateComparerForElements<T>(this IEnumerable<T> enumerable, Func<T,T,int> func) {
return new Comparer<T>(func);
}
}
Now I can do the following ... hacky solution:
var comp = n.CreateComparerForElements((x, y) => x.Vchr == y.Vchr);
Most of the time when you compare (for equality or sorting) you're interested in choosing the keys to compare by, not the equality or comparison method itself (this is the idea behind Python's list sort API).
There's an example key equality comparer here.
I note that JaredPar's answer does not quite answer the question since the set methods like Distinct and Except require an IEqualityComparer<T> not an IComparer<T>. The following assumes that an IEquatable will have a suitable GetHashCode, and it certainly has a suitable Equals method.
public class GeneralComparer<T, TEquatable> : IEqualityComparer<T>
{
private readonly Func<T, IEquatable<TEquatable>> equatableSelector;
public GeneralComparer(Func<T, IEquatable<TEquatable>> equatableSelector)
{
this.equatableSelector = equatableSelector;
}
public bool Equals(T x, T y)
{
return equatableSelector.Invoke(x).Equals(equatableSelector.Invoke(y));
}
public int GetHashCode(T x)
{
return equatableSelector(x).GetHashCode();
}
}
public static class GeneralComparer
{
public static GeneralComparer<T, TEquatable> Create<T, TEquatable>(Func<T, TEquatable> equatableSelector)
{
return new GeneralComparer<T, TEquatable>(equatableSelector);
}
}
Where the same inference from a static class trick is used as in JaredPar's answer.
To be more general you could provide two Funcs: a Func<T, T, bool> to check equality and Func<T, T, int> to select a hash code.

Resources