feat: Add andThen and compose to japi function (#2147)

* feat: Add andThen and compose to japi function

* Update actor/src/main/scala/org/apache/pekko/japi/function/Function.scala

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update actor/src/main/scala/org/apache/pekko/japi/function/Function.scala

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update actor/src/main/scala/org/apache/pekko/japi/function/Function.scala

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update actor/src/main/scala/org/apache/pekko/japi/function/Function.scala

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
He-Pin(kerr) 2025-09-06 01:36:10 +08:00 committed by GitHub
parent 61de190fca
commit 7cf3f6193d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 106 additions and 0 deletions

View file

@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pekko.japi;
import org.apache.pekko.japi.function.Function;
import org.apache.pekko.japi.function.Function2;
import org.apache.pekko.japi.function.Predicate;
import org.apache.pekko.japi.function.Predicate2;
import org.junit.Assert;
import org.junit.Test;
public class FunctionLawTest {
@Test
public void testFunctionCompose() throws Exception {
Function<Long, Long> f = v1 -> v1 + 1;
Function<Long, Long> g = v1 -> v1 * 2;
Function<Long, Long> h = f.compose(g);
Assert.assertEquals(5L, h.apply(2L).longValue());
Assert.assertEquals(6L, g.compose(f).apply(2L).longValue());
}
@Test
public void testFunctionAndThen() throws Exception {
Function<Long, Long> f = v1 -> v1 + 1;
Function<Long, Long> g = v1 -> v1 * 2;
Function<Long, Long> h = f.andThen(g);
Assert.assertEquals(6L, h.apply(2L).longValue());
Assert.assertEquals(5L, g.andThen(f).apply(2L).longValue());
}
@Test
public void testFunction2AndThen() throws Exception {
Function2<Long, Long, Long> f = Long::sum;
Function<Long, Long> g = v1 -> v1 * 2;
Function2<Long, Long, Long> h = f.andThen(g);
Assert.assertEquals(10L, h.apply(2L, 3L).longValue());
}
@Test
public void testPredicateNegate() {
final Predicate<Object> alwaysTrue = o -> true;
final Predicate<Object> alwaysFalse = alwaysTrue.negate();
Assert.assertTrue(alwaysTrue.test(new Object()));
Assert.assertFalse(alwaysFalse.test(new Object()));
Assert.assertTrue(alwaysTrue.negate().negate().test(new Object()));
}
@Test
public void testPredicate2Negate() {
final Predicate2<Object, Object> alwaysTrue = (o1, o2) -> true;
final Predicate2<Object, Object> alwaysFalse = alwaysTrue.negate();
Assert.assertTrue(alwaysTrue.test(new Object(), new Object()));
Assert.assertFalse(alwaysFalse.test(new Object(), new Object()));
Assert.assertTrue(alwaysTrue.negate().negate().test(new Object(), new Object()));
}
}

View file

@ -28,6 +28,18 @@ import scala.annotation.nowarn
trait Function[-T, +R] extends java.io.Serializable {
@throws(classOf[Exception])
def apply(param: T): R
/**
* That is, the resulting function is equivalent to `this(g(v))`.
* @since 2.0.0
*/
def compose[V](g: Function[V, T]): Function[V, R] = (v: V) => this.apply(g.apply(v))
/**
* Creates a composed function that first applies this function to its input, then applies `g` to the result.
* @since 2.0.0
*/
def andThen[V](g: Function[R, V]): Function[T, V] = (t: T) => g.apply(this.apply(t))
}
object Function {
@ -50,6 +62,16 @@ object Function {
trait Function2[-T1, -T2, +R] extends java.io.Serializable {
@throws(classOf[Exception])
def apply(arg1: T1, arg2: T2): R
/**
* Compose this function with another function `g`, such that the resulting function
* is equivalent to `g(this(x1, x2))`.
* This creates a composed function that first applies this function to its arguments,
* and then applies function `g` to the result.
* @since 2.0.0
*/
def andThen[V](g: Function[R, V]): Function2[T1, T2, V] =
(t1: T1, t2: T2) => g.apply(this.apply(t1, t2))
}
/**
@ -89,6 +111,12 @@ trait Effect extends java.io.Serializable {
@FunctionalInterface
trait Predicate[-T] extends java.io.Serializable {
def test(param: T): Boolean
/**
* Returns a predicate that represents the logical negation of this predicate.
* @since 2.0.0
*/
def negate: Predicate[T] = (t: T) => !this.test(t)
}
/**
@ -101,6 +129,12 @@ trait Predicate[-T] extends java.io.Serializable {
@FunctionalInterface
trait Predicate2[-T1, -T2] extends java.io.Serializable {
def test(param1: T1, param2: T2): Boolean
/**
* Returns a predicate that represents the logical negation of this predicate.
* @since 2.0.0
*/
def negate: Predicate2[T1, T2] = (t1: T1, t2: T2) => !this.test(t1, t2)
}
/**