From 13dc0f6a1344d71de0de8018e7407fcb705dadee Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Tue, 7 Jun 2022 14:14:38 +0200 Subject: [PATCH 1/3] Fix code formatting. --- morphir/sdk/json/src/morphir/sdk/basics/Codec.scala | 4 ++-- morphir/sdk/json/src/morphir/sdk/list/Codec.scala | 2 +- morphir/sdk/json/src/morphir/sdk/localdate/Codec.scala | 2 +- morphir/sdk/json/src/morphir/sdk/set/Codec.scala | 2 +- morphir/sdk/json/src/morphir/sdk/string/Codec.scala | 2 +- morphir/sdk/json/src/morphir/sdk/tuple/Codec.scala | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/morphir/sdk/json/src/morphir/sdk/basics/Codec.scala b/morphir/sdk/json/src/morphir/sdk/basics/Codec.scala index 42401116..99b2d4d2 100644 --- a/morphir/sdk/json/src/morphir/sdk/basics/Codec.scala +++ b/morphir/sdk/json/src/morphir/sdk/basics/Codec.scala @@ -1,7 +1,7 @@ package morphir.sdk.basics -import io.circe.{Decoder, Encoder} -import morphir.sdk.Basics.{Bool, Decimal, Float, Int} +import io.circe.{ Decoder, Encoder } +import morphir.sdk.Basics.{ Bool, Decimal, Float, Int } object Codec { diff --git a/morphir/sdk/json/src/morphir/sdk/list/Codec.scala b/morphir/sdk/json/src/morphir/sdk/list/Codec.scala index 8a1cf4f4..a824e09d 100644 --- a/morphir/sdk/json/src/morphir/sdk/list/Codec.scala +++ b/morphir/sdk/json/src/morphir/sdk/list/Codec.scala @@ -1,6 +1,6 @@ package morphir.sdk.list -import io.circe.{Decoder, Encoder} +import io.circe.{ Decoder, Encoder } import morphir.sdk.List.List /** Encoder and Decoder for items in a list */ diff --git a/morphir/sdk/json/src/morphir/sdk/localdate/Codec.scala b/morphir/sdk/json/src/morphir/sdk/localdate/Codec.scala index 5725f279..5ff6cf0a 100644 --- a/morphir/sdk/json/src/morphir/sdk/localdate/Codec.scala +++ b/morphir/sdk/json/src/morphir/sdk/localdate/Codec.scala @@ -1,6 +1,6 @@ package morphir.sdk.localdate -import io.circe.{Decoder, Encoder} +import io.circe.{ Decoder, Encoder } import morphir.sdk.LocalDate.LocalDate object Codec { diff --git a/morphir/sdk/json/src/morphir/sdk/set/Codec.scala b/morphir/sdk/json/src/morphir/sdk/set/Codec.scala index 495027b9..ba019e24 100644 --- a/morphir/sdk/json/src/morphir/sdk/set/Codec.scala +++ b/morphir/sdk/json/src/morphir/sdk/set/Codec.scala @@ -1,6 +1,6 @@ package morphir.sdk.set -import io.circe.{Decoder, Encoder} +import io.circe.{ Decoder, Encoder } import morphir.sdk.Set.Set object Codec { diff --git a/morphir/sdk/json/src/morphir/sdk/string/Codec.scala b/morphir/sdk/json/src/morphir/sdk/string/Codec.scala index fb3305b9..266779f0 100644 --- a/morphir/sdk/json/src/morphir/sdk/string/Codec.scala +++ b/morphir/sdk/json/src/morphir/sdk/string/Codec.scala @@ -1,6 +1,6 @@ package morphir.sdk.string -import io.circe.{Decoder, Encoder} +import io.circe.{ Decoder, Encoder } import morphir.sdk.String.String object Codec { diff --git a/morphir/sdk/json/src/morphir/sdk/tuple/Codec.scala b/morphir/sdk/json/src/morphir/sdk/tuple/Codec.scala index a56e2e75..2bc8520b 100644 --- a/morphir/sdk/json/src/morphir/sdk/tuple/Codec.scala +++ b/morphir/sdk/json/src/morphir/sdk/tuple/Codec.scala @@ -1,6 +1,6 @@ package morphir.sdk.tuple -import io.circe.{Decoder, Encoder, HCursor, Json} +import io.circe.{ Decoder, Encoder, HCursor, Json } import morphir.sdk.Tuple.Tuple object Codec { From 2425ade4aab0f50fabdbe4aaff04696e239e3345 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Wed, 10 Aug 2022 14:30:50 +0200 Subject: [PATCH 2/3] Add join functions. Removed Bool module in favor of Basics.Bool. --- morphir/sdk/core/src/morphir/sdk/Basics.scala | 33 ++++++------ morphir/sdk/core/src/morphir/sdk/Bool.scala | 50 ------------------- .../sdk/core/src/morphir/sdk/Decimal.scala | 14 +++--- morphir/sdk/core/src/morphir/sdk/List.scala | 20 ++++++++ morphir/sdk/core/src/morphir/sdk/Rule.scala | 11 ++-- morphir/sdk/core/src/morphir/sdk/Set.scala | 10 ++-- .../core/test/src/morphir/sdk/ListSpec.scala | 14 ++++++ .../core/test/src/morphir/sdk/RuleSpec.scala | 14 +++--- 8 files changed, 78 insertions(+), 88 deletions(-) delete mode 100644 morphir/sdk/core/src/morphir/sdk/Bool.scala diff --git a/morphir/sdk/core/src/morphir/sdk/Basics.scala b/morphir/sdk/core/src/morphir/sdk/Basics.scala index eec73070..7b43798d 100644 --- a/morphir/sdk/core/src/morphir/sdk/Basics.scala +++ b/morphir/sdk/core/src/morphir/sdk/Basics.scala @@ -15,7 +15,6 @@ limitations under the License. */ package morphir.sdk -import morphir.sdk.{ Bool => BoolModule } object Basics { @@ -31,24 +30,28 @@ object Basics { val GT: Order = Order.GT // Bool - type Bool = BoolModule.Bool - val Bool: BoolModule.Bool.type = BoolModule.Bool - @inline def not(a: Bool): Bool = BoolModule.not(a) - @inline def and(a: Bool)(b: Bool): Bool = BoolModule.and(a)(b) - @inline def or(a: Bool)(b: Bool): Bool = BoolModule.or(a)(b) - @inline def xor(a: Bool)(b: Bool): Bool = BoolModule.xor(a)(b) + type Bool = Boolean + object Bool { + @inline def apply(value: Boolean): Bool = value + } + val True: Bool = true + val False: Bool = false + @inline def not(a: Bool): Bool = !a + @inline def and(a: Bool)(b: Bool): Bool = a && b + @inline def or(a: Bool)(b: Bool): Bool = a || b + @inline def xor(a: Bool)(b: Bool): Bool = a ^ b // Equality - @inline def equal[A](a: A)(b: A): Bool = BoolModule.equal(a)(b) - @inline def notEqual[A](a: A)(b: A): Bool = BoolModule.notEqual(a)(b) + @inline def equal[A](a: A)(b: A): Bool = a == b + @inline def notEqual[A](a: A)(b: A): Bool = a != b // Comparable - @inline def lessThan[A: Ordering](a: A)(b: A): Bool = BoolModule.lessThan(a)(b) - @inline def lessThanOrEqual[A: Ordering](a: A)(b: A): Bool = BoolModule.lessThanOrEqual(a)(b) - @inline def greaterThan[A: Ordering](a: A)(b: A): Bool = BoolModule.greaterThan(a)(b) - @inline def greaterThanOrEqual[A: Ordering](a: A)(b: A): Bool = BoolModule.greaterThanOrEqual(a)(b) - @inline def min[A: Ordering](a: A)(b: A): A = BoolModule.min(a)(b) - @inline def max[A: Ordering](a: A)(b: A): A = BoolModule.max(a)(b) + @inline def lessThan[A: Ordering](a: A)(b: A): Bool = implicitly[Ordering[A]].lt(a, b) + @inline def lessThanOrEqual[A: Ordering](a: A)(b: A): Bool = implicitly[Ordering[A]].lteq(a, b) + @inline def greaterThan[A: Ordering](a: A)(b: A): Bool = implicitly[Ordering[A]].gt(a, b) + @inline def greaterThanOrEqual[A: Ordering](a: A)(b: A): Bool = implicitly[Ordering[A]].gteq(a, b) + @inline def min[A: Ordering](a: A)(b: A): A = if (lessThan(a)(b)) a else b + @inline def max[A: Ordering](a: A)(b: A): A = if (greaterThan(a)(b)) a else b // Int construction type Int = morphir.sdk.Int.Int diff --git a/morphir/sdk/core/src/morphir/sdk/Bool.scala b/morphir/sdk/core/src/morphir/sdk/Bool.scala deleted file mode 100644 index ab8c1241..00000000 --- a/morphir/sdk/core/src/morphir/sdk/Bool.scala +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright 2020 Morgan Stanley - -Licensed 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 morphir.sdk - -import morphir.sdk.String.String - -object Bool { - type Bool = Boolean - object Bool { - @inline def apply(value: Boolean): Bool = value - } - - val True: Bool = true - val False: Bool = false - - @inline def and(a: Bool)(b: Bool): Bool = a && b - @inline def not(value: Bool): Bool = !value - @inline def or(a: Bool)(b: Bool): Bool = a || b - @inline def xor(a: Bool)(b: Bool): Bool = a ^ b - @inline def toString(value: Bool): String = value.toString - - // Equality - @inline def equal[A](a: A)(b: A): Bool = a == b - @inline def notEqual[A](a: A)(b: A): Bool = a != b - - // Comparable - def lessThan[A: Ordering](a: A)(b: A): Bool = implicitly[Ordering[A]].lt(a, b) - def lessThanOrEqual[A: Ordering](a: A)(b: A): Bool = - implicitly[Ordering[A]].lteq(a, b) - def greaterThan[A: Ordering](a: A)(b: A): Bool = - implicitly[Ordering[A]].gt(a, b) - def greaterThanOrEqual[A: Ordering](a: A)(b: A): Bool = - implicitly[Ordering[A]].gteq(a, b) - def min[A: Ordering](a: A)(b: A): A = if (lessThan(a)(b)) a else b - def max[A: Ordering](a: A)(b: A): A = if (greaterThan(a)(b)) a else b -} diff --git a/morphir/sdk/core/src/morphir/sdk/Decimal.scala b/morphir/sdk/core/src/morphir/sdk/Decimal.scala index 9bf22966..7cceaae6 100644 --- a/morphir/sdk/core/src/morphir/sdk/Decimal.scala +++ b/morphir/sdk/core/src/morphir/sdk/Decimal.scala @@ -1,7 +1,7 @@ package morphir.sdk import java.math.{ BigDecimal => BigDec } -import morphir.sdk.Basics.Order +import morphir.sdk.Basics.{ Bool, Order } import morphir.sdk.Maybe.Maybe import scala.util.control.NonFatal @@ -51,7 +51,7 @@ object Decimal extends DecimalModuleCompat { def divWithDefault(default: Decimal)(a: Decimal)(b: Decimal): Decimal = Maybe.withDefault(default)(div(a)(b)) - def eq(a: Decimal)(b: Decimal): morphir.sdk.Bool.Bool = a.compareTo(b) == 0 + def eq(a: Decimal)(b: Decimal): Bool = a.compareTo(b) == 0 def fromInt(value: morphir.sdk.Basics.Int): Decimal = Decimal(value) @@ -78,10 +78,10 @@ object Decimal extends DecimalModuleCompat { def hundredth(n: morphir.sdk.Int.Int): Decimal = Decimal(n * 0.01) - def gt(a: Decimal)(b: Decimal): morphir.sdk.Bool.Bool = a.compareTo(b) > 0 - def gte(a: Decimal)(b: Decimal): morphir.sdk.Bool.Bool = a.compareTo(b) >= 0 + def gt(a: Decimal)(b: Decimal): Bool = a.compareTo(b) > 0 + def gte(a: Decimal)(b: Decimal): Bool = a.compareTo(b) >= 0 - def lt(a: Decimal)(b: Decimal): morphir.sdk.Bool.Bool = a.compareTo(b) < 0 + def lt(a: Decimal)(b: Decimal): Bool = a.compareTo(b) < 0 def million(n: morphir.sdk.Int.Int): Decimal = Decimal(n * 1000000) @@ -91,8 +91,8 @@ object Decimal extends DecimalModuleCompat { def mul(a: Decimal)(b: Decimal): Decimal = a * b - @inline def ne(a: Decimal)(b: Decimal): morphir.sdk.Bool.Bool = neq(a)(b) - def neq(a: Decimal)(b: Decimal): morphir.sdk.Bool.Bool = a.compareTo(b) != 0 + @inline def ne(a: Decimal)(b: Decimal): Bool = neq(a)(b) + def neq(a: Decimal)(b: Decimal): Bool = a.compareTo(b) != 0 def negate(value: Decimal): Decimal = -value diff --git a/morphir/sdk/core/src/morphir/sdk/List.scala b/morphir/sdk/core/src/morphir/sdk/List.scala index 5840377e..902afcea 100644 --- a/morphir/sdk/core/src/morphir/sdk/List.scala +++ b/morphir/sdk/core/src/morphir/sdk/List.scala @@ -16,6 +16,7 @@ limitations under the License. package morphir.sdk +import morphir.sdk.Basics.Bool import morphir.sdk.Maybe.{ Just, Maybe } object List { @@ -151,4 +152,23 @@ object List { if (list.isEmpty) Maybe.Nothing else Maybe.Just(list.max) @inline def sum[A: Numeric](list: List[A]): A = list.sum @inline def product[A: Numeric](list: List[A]): A = list.product + + def innerJoin[A, B](listB: List[B])(f: A => B => Bool)(listA: List[A]): List[(A, B)] = + for { + itemA <- listA + itemB <- listB + if f(itemA)(itemB) + } yield (itemA, itemB) + + def leftJoin[A, B](listB: List[B])(f: A => B => Bool)(listA: List[A]): List[(A, Maybe[B])] = + listA.flatMap { itemA => + val filteredListB = listB.filter(f(itemA)) + if (filteredListB.isEmpty) { + scala.List((itemA, Maybe.nothing)) + } else { + for { + itemB <- filteredListB + } yield (itemA, Maybe.just(itemB)) + } + } } diff --git a/morphir/sdk/core/src/morphir/sdk/Rule.scala b/morphir/sdk/core/src/morphir/sdk/Rule.scala index 9cd08912..e77ccda3 100644 --- a/morphir/sdk/core/src/morphir/sdk/Rule.scala +++ b/morphir/sdk/core/src/morphir/sdk/Rule.scala @@ -16,6 +16,7 @@ limitations under the License. package morphir.sdk +import morphir.sdk.Basics.{ Bool, True } import morphir.sdk.List.List import morphir.sdk.Maybe.Maybe @@ -29,16 +30,16 @@ object Rule { .find(rule => rule(input).isDefined) .flatMap(rule => rule(input)) - def any[A]: A => Bool.Bool = - _ => Bool.True + def any[A]: A => Bool = + _ => True - def is[A](ref: A)(input: A): Bool.Bool = + def is[A](ref: A)(input: A): Bool = ref == input - def anyOf[A](ref: List[A])(input: A): Bool.Bool = + def anyOf[A](ref: List[A])(input: A): Bool = ref.contains(input) - def noneOf[A](ref: List[A])(input: A): Bool.Bool = + def noneOf[A](ref: List[A])(input: A): Bool = !anyOf(ref)(input) } diff --git a/morphir/sdk/core/src/morphir/sdk/Set.scala b/morphir/sdk/core/src/morphir/sdk/Set.scala index 3f381f69..6810da11 100644 --- a/morphir/sdk/core/src/morphir/sdk/Set.scala +++ b/morphir/sdk/core/src/morphir/sdk/Set.scala @@ -15,6 +15,8 @@ limitations under the License. */ package morphir.sdk +import morphir.sdk.Basics.Bool + object Set { type Set[A] = scala.collection.immutable.Set[A] private val Set = scala.collection.immutable.Set @@ -43,12 +45,12 @@ object Set { /** * Determine if a set is empty. */ - @inline def isEmpty[A](set: Set[A]): Bool.Bool = set.isEmpty + @inline def isEmpty[A](set: Set[A]): Bool = set.isEmpty /** * Determine if a value is in a set. */ - @inline def member[A](value: A)(set: Set[A]): Bool.Bool = set.contains(value) + @inline def member[A](value: A)(set: Set[A]): Bool = set.contains(value) /** * Determine the number of elements in a set. @@ -104,13 +106,13 @@ object Set { /** * Only keep elements that pass the given test. */ - def filter[A](predicate: A => Bool.Bool)(set: Set[A]): Set[A] = + def filter[A](predicate: A => Bool)(set: Set[A]): Set[A] = set.filter(predicate) /** * Create tow new sets. The first contains all the elements that passed the given test, * and the second contains all the elements that did not. */ - def partition[A](predicate: A => Bool.Bool)(set: Set[A]): (Set[A], Set[A]) = + def partition[A](predicate: A => Bool)(set: Set[A]): (Set[A], Set[A]) = set.partition(predicate) } diff --git a/morphir/sdk/core/test/src/morphir/sdk/ListSpec.scala b/morphir/sdk/core/test/src/morphir/sdk/ListSpec.scala index 6661b8cf..f3a58974 100644 --- a/morphir/sdk/core/test/src/morphir/sdk/ListSpec.scala +++ b/morphir/sdk/core/test/src/morphir/sdk/ListSpec.scala @@ -223,6 +223,20 @@ object ListSpec extends DefaultRunnableSpec { test("reverse should reverse a list") { assert(List.reverse(List(1, 2, 3, 4)))(equalTo(List(4, 3, 2, 1))) } + ), + suite("List.innerJoin specs")( + test("innerJoin should join two lists discarding non-matching rows") { + assert(List.innerJoin(List(1, 2, 3, 4))((a: Int) => (b: Int) => a == b)(List(4, 3, 2)))( + equalTo(List((4, 4), (3, 3), (2, 2))) + ) + } + ), + suite("List.leftJoin specs")( + test("leftJoin should join two lists including non-matching rows with a value of Maybe.Nothing") { + assert(List.leftJoin(List(2, 3, 4))((a: Int) => (b: Int) => a == b)(List(4, 3, 2, 1)))( + equalTo(List((4, Maybe.just(4)), (3, Maybe.just(3)), (2, Maybe.just(2)), (1, Maybe.nothing))) + ) + } ) ) } diff --git a/morphir/sdk/core/test/src/morphir/sdk/RuleSpec.scala b/morphir/sdk/core/test/src/morphir/sdk/RuleSpec.scala index ac40e21d..3c7f6a65 100644 --- a/morphir/sdk/core/test/src/morphir/sdk/RuleSpec.scala +++ b/morphir/sdk/core/test/src/morphir/sdk/RuleSpec.scala @@ -39,12 +39,12 @@ object RuleSpec extends DefaultRunnableSpec { ), suite("Rule.any specs")( testM("Calling any on anything should return True") { - check(Gen.alphaNumericChar)(input => assert(Rule.any(input))(equalTo(Bool.True))) + check(Gen.alphaNumericChar)(input => assert(Rule.any(input))(equalTo(Basics.True))) } ), suite("Rule.is specs")( testM("Calling is by passing in the same value twice should return True") { - check(Gen.alphaNumericChar)(input => assert(Rule.is(input)(input))(equalTo(Bool.True))) + check(Gen.alphaNumericChar)(input => assert(Rule.is(input)(input))(equalTo(Basics.True))) }, testM("Calling is by passing in two different values should return False") { val gen = @@ -54,7 +54,7 @@ object RuleSpec extends DefaultRunnableSpec { if ref != input } yield (ref, input) check(gen) { case (ref, input) => - assert(Rule.is(ref)(input))(equalTo(Bool.False)) + assert(Rule.is(ref)(input))(equalTo(Basics.False)) } } ), @@ -66,7 +66,7 @@ object RuleSpec extends DefaultRunnableSpec { if ref.nonEmpty } yield (ref, ref.head) check(gen) { case (ref, input) => - assert(Rule.anyOf(ref)(input))(equalTo(Bool.True)) + assert(Rule.anyOf(ref)(input))(equalTo(Basics.True)) } }, testM("Calling anyOf by passing in a list and a non-member should return False") { @@ -77,7 +77,7 @@ object RuleSpec extends DefaultRunnableSpec { if !ref.contains(input) } yield (ref, input) check(gen) { case (ref, input) => - assert(Rule.anyOf(ref)(input))(equalTo(Bool.False)) + assert(Rule.anyOf(ref)(input))(equalTo(Basics.False)) } } ), @@ -89,7 +89,7 @@ object RuleSpec extends DefaultRunnableSpec { if ref.nonEmpty } yield (ref, ref.head) check(gen) { case (ref, input) => - assert(Rule.noneOf(ref)(input))(equalTo(Bool.False)) + assert(Rule.noneOf(ref)(input))(equalTo(Basics.False)) } }, testM("Calling noneOf by passing in a list and a non-member should return True") { @@ -100,7 +100,7 @@ object RuleSpec extends DefaultRunnableSpec { if !ref.contains(input) } yield (ref, input) check(gen) { case (ref, input) => - assert(Rule.noneOf(ref)(input))(equalTo(Bool.True)) + assert(Rule.noneOf(ref)(input))(equalTo(Basics.True)) } } ) From dad6f03f68ad231cf1dcf7fbab256c936c8b63fd Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Wed, 11 Oct 2023 21:51:47 +0200 Subject: [PATCH 3/3] Added missing functions. #151 --- morphir/sdk/core/src/morphir/sdk/Basics.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/morphir/sdk/core/src/morphir/sdk/Basics.scala b/morphir/sdk/core/src/morphir/sdk/Basics.scala index 7b43798d..3fe25b06 100644 --- a/morphir/sdk/core/src/morphir/sdk/Basics.scala +++ b/morphir/sdk/core/src/morphir/sdk/Basics.scala @@ -108,6 +108,10 @@ object Basics { if (a < min) min else if (a > max) max else a + @inline def logBase(base: Float, number: Float): Float = + divide(scala.math.log(number))(scala.math.log(base)) + @inline def e: Float = + Math.E @inline def isNaN(a: Float): Bool = a.isNaN @inline def isInfinite(a: Float): Bool = a.isInfinite