Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Morphir Core and IR work #91

Merged
merged 4 commits into from
Feb 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 60 additions & 23 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ object Deps {
val zioLogging = "0.5.6"
val zioMagic = "0.1.8"
val zioNio = "1.0.0-RC10"
val zioPrelude = "1.0.0-RC1"
val zioPrelude = "1.0.0-RC2"
val zioProcess = "0.2.0"
val newtype = "0.4.4"
def decline(scalaVersion: String) = scalaVersion match {
Expand All @@ -52,6 +52,7 @@ object Deps {
val slf4zio = "1.0.0"
val scalactic = "3.1.2"
val scalaUri = "2.2.2"
val spark = "2.4.7"
val oslib = "0.6.2"
val quill = "3.6.0-RC3"
}
Expand Down Expand Up @@ -220,28 +221,28 @@ object morphir extends Module {
}
}
}

object scala extends Module {

object jvm extends Cross[JvmMorphirScalaModule](Versions.scala213)
class JvmMorphirScalaModule(val crossScalaVersion: String)
extends CrossScalaModule
with CommonJvmModule
with ScalaMacroModule
with MorphirPublishModule { self =>
def artifactName = "morphir-scala"
def moduleDeps = Seq(morphir.ir.jvm(crossScalaVersion))

def ivyDeps = Agg(
ivy"org.scalameta::scalameta:${Versions.scalameta}"
)

object test extends Tests {
def platformSegment: String = self.platformSegment
def crossScalaVersion = JvmMorphirScalaModule.this.crossScalaVersion
}
}
}
//
// object scala extends Module {
//
// object jvm extends Cross[JvmMorphirScalaModule](Versions.scala213)
// class JvmMorphirScalaModule(val crossScalaVersion: String)
// extends CrossScalaModule
// with CommonJvmModule
// with ScalaMacroModule
// with MorphirPublishModule { self =>
// def artifactName = "morphir-scala"
// def moduleDeps = Seq(morphir.ir.jvm(crossScalaVersion))
//
// def ivyDeps = Agg(
// ivy"org.scalameta::scalameta:${Versions.scalameta}"
// )
//
// object test extends Tests {
// def platformSegment: String = self.platformSegment
// def crossScalaVersion = JvmMorphirScalaModule.this.crossScalaVersion
// }
// }
// }
object sdk extends Module {

object core extends Module {
Expand All @@ -263,6 +264,42 @@ object morphir extends Module {
}
}
}

object spark extends Module {
object jvm
extends Cross[JvmMorphirSdkSpark](
Versions.scala212,
Versions.scala211
)
class JvmMorphirSdkSpark(val crossScalaVersion: String)
extends CrossScalaModule
with CommonJvmModule
with MorphirPublishModule { self =>

def artifactName = "morphir-sdk-spark"
def compileIvyDeps = Agg(
ivy"org.apache.spark::spark-sql:2.4.7",
ivy"com.github.ghik:::silencer-lib:${Versions.silencer}"
)
def ivyDeps = Agg(
ivy"dev.zio::zio-prelude:${Versions.zioPrelude}"
)
def scalacPluginIvyDeps = Agg(ivy"com.github.ghik:::silencer-plugin:${Versions.silencer}")
def moduleDeps = Seq(morphir.sdk.core.jvm(crossScalaVersion))

object test extends Tests {
def platformSegment: String = self.platformSegment
def crossScalaVersion = JvmMorphirSdkSpark.this.crossScalaVersion

override def ivyDeps = super.ivyDeps() ++
Agg(
ivy"dev.zio::zio-logging:${Versions.zioLogging}",
ivy"dev.zio::zio-logging-slf4j:${Versions.zioLogging}",
ivy"org.apache.spark::spark-sql:2.4.7"
)
}
}
}
}

object flowz extends Module {
Expand Down
99 changes: 89 additions & 10 deletions morphir/ir/src/morphir/ir/recursions.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package morphir.ir

import zio.ZIO

/**
* In look at how we can potentially model the Morphir IR, research has shown that recursIt woulkd
*/
Expand All @@ -24,27 +26,88 @@ object recursions {
type Name = String
type FQNAme = String

final case class Field[+Self](name: Name, value: Self)
final case class Field[+Self](name: Name, value: Self) {
def map[Self2](fn: Self => Self2): Field[Self2] = Field(name, fn(value))
def mapM[R, E, Self2](fn: Self => ZIO[R, E, Self2]): ZIO[R, E, Field[Self2]] = fn(value).map(Field(name, _))
}

sealed trait TypeCase[+Self, +Attrib] {
def map[Self2](fn: Self => Self2): TypeCase[Self2, Attrib] = ???
def map[Self2](fn: Self => Self2): TypeCase[Self2, Attrib]
def mapAttrib[Attrib2](f: Attrib => Attrib2): TypeCase[Self, Attrib2]
def mapM[R, E, Self2](fn: Self => ZIO[R, E, Self2]): ZIO[R, E, TypeCase[Self2, Attrib]]
}
object TypeCase {
final case class Variable[Attrib](a: Attrib) extends TypeCase[Nothing, Attrib]
final case class Variable[Attrib](a: Attrib) extends TypeCase[Nothing, Attrib] { self =>
def map[Self2](fn: Nothing => Self2): TypeCase[Self2, Attrib] = self

def mapAttrib[Attrib2](f: Attrib => Attrib2): TypeCase[Nothing, Attrib2] = copy(a = f(a))

def mapM[R, E, Self2](fn: Nothing => ZIO[R, E, Self2]): ZIO[R, E, TypeCase[Self2, Attrib]] = ZIO.succeed(self)
}
final case class Reference[Self, Attrib](attribute: Attrib, name: FQName, types: List[Self])
extends TypeCase[Self, Attrib]
final case class Tuple[Self, Attrib](attribute: Attrib, types: List[Self]) extends TypeCase[Self, Attrib]
final case class Record[Self, Attrib](attributes: Attrib, fields: List[Field[Self]]) extends TypeCase[Self, Attrib]
final case class ExtensibleRecord[Self, Attrib](attributes: Attrib, name: Name, fields: List[Field[Self]])
extends TypeCase[Self, Attrib]
final case class Function[Self, Attrib](attribute: Attrib, input: Self, output: Self) extends TypeCase[Self, Attrib]
final case class Unit[+Attrib](attribute: Attrib) extends TypeCase[Nothing, Attrib]
extends TypeCase[Self, Attrib] {
def map[Self2](fn: Self => Self2): TypeCase[Self2, Attrib] = Reference(attribute, name, types.map(fn))

def mapAttrib[Attrib2](f: Attrib => Attrib2): TypeCase[Self, Attrib2] = copy(attribute = f(attribute))

def mapM[R, E, Self2](fn: Self => ZIO[R, E, Self2]): ZIO[R, E, TypeCase[Self2, Attrib]] =
ZIO.foreach(types)(fn).map(Reference(attribute, name, _))
}
final case class Tuple[Self, Attrib](attribute: Attrib, types: List[Self]) extends TypeCase[Self, Attrib] {
def map[Self2](fn: Self => Self2): TypeCase[Self2, Attrib] = Tuple(attribute, types.map(fn))

def mapAttrib[Attrib2](f: Attrib => Attrib2): TypeCase[Self, Attrib2] = copy(attribute = f(attribute))

def mapM[R, E, Self2](fn: Self => ZIO[R, E, Self2]): ZIO[R, E, TypeCase[Self2, Attrib]] =
ZIO.foreach(types)(fn).map(Tuple(attribute, _))
}
final case class Record[Self, Attrib](attribute: Attrib, fields: List[Field[Self]]) extends TypeCase[Self, Attrib] {
def map[Self2](fn: Self => Self2): TypeCase[Self2, Attrib] = Record(attribute, fields.map(_.map(fn)))
def mapAttrib[Attrib2](f: Attrib => Attrib2): TypeCase[Self, Attrib2] = copy(attribute = f(attribute))

def mapM[R, E, Self2](fn: Self => ZIO[R, E, Self2]): ZIO[R, E, TypeCase[Self2, Attrib]] =
ZIO.foreach(fields)(_.mapM(fn)).map(Record(attribute, _))
}
final case class ExtensibleRecord[Self, Attrib](attribute: Attrib, name: Name, fields: List[Field[Self]])
extends TypeCase[Self, Attrib] {
def map[Self2](fn: Self => Self2): TypeCase[Self2, Attrib] =
ExtensibleRecord(attribute, name, fields.map(_.map(fn)))

def mapAttrib[Attrib2](f: Attrib => Attrib2): TypeCase[Self, Attrib2] = copy(attribute = f(attribute))

def mapM[R, E, Self2](fn: Self => ZIO[R, E, Self2]): ZIO[R, E, TypeCase[Self2, Attrib]] =
ZIO.foreach(fields)(_.mapM(fn)).map(ExtensibleRecord(attribute, name, _))
}
final case class Function[Self, Attrib](attribute: Attrib, input: Self, output: Self)
extends TypeCase[Self, Attrib] {
def map[Self2](fn: Self => Self2): TypeCase[Self2, Attrib] = Function(attribute, fn(input), fn(output))
def mapAttrib[Attrib2](f: Attrib => Attrib2): TypeCase[Self, Attrib2] = copy(attribute = f(attribute))

def mapM[R, E, Self2](fn: Self => ZIO[R, E, Self2]): ZIO[R, E, TypeCase[Self2, Attrib]] =
ZIO.mapN(fn(input), fn(output)) { case (input1, output1) =>
Function(attribute, input1, output1)
}
}
final case class Unit[+Attrib](attribute: Attrib) extends TypeCase[Nothing, Attrib] { self =>
def map[Self2](fn: Nothing => Self2): TypeCase[Self2, Attrib] = self
def mapAttrib[Attrib2](f: Attrib => Attrib2): TypeCase[Nothing, Attrib2] = copy(attribute = f(attribute))

def mapM[R, E, Self2](fn: Nothing => ZIO[R, E, Self2]): ZIO[R, E, TypeCase[Self2, Attrib]] = ZIO.succeed(self)
}
}

final case class Type[+Attrib](value: TypeCase[Type[Attrib], Attrib]) { self =>

//NOTE: Useful for bottom up type inference for example
def annotate[Attrib2](f: TypeCase[Type[Attrib2], Attrib] => Attrib2): Type[Attrib2] =
transformUpRecursive[Attrib2](value => value.mapAttrib(_ => f(value)))

def fold[Z](f: TypeCase[Z, Attrib] => Z): Z =
f(value.map(_.fold(f)))

def foldM[R, E, Z](f: TypeCase[Z, Attrib] => ZIO[R, E, Z]): ZIO[R, E, Z] =
value.mapM(_.foldM(f)).flatMap(f)

/**
* Transform the whole tree.
* Top down
Expand All @@ -58,13 +121,29 @@ object recursions {
def transformUp[Attrib2](f: TypeMapper[Attrib, Attrib2]): Type[Attrib2] =
Type(f(value.map(_.transformUp(f))))

def transformDownRecursive[Attrib1 >: Attrib, Attrib2](
f: TypeCase[Type[Attrib1], Attrib1] => TypeCase[Type[Attrib1], Attrib2]
): Type[Attrib2] =
Type(f(value).map(_.transformDownRecursive(f)))

def transformUpRecursive[Attrib2](
f: TypeCase[Type[Attrib2], Attrib] => TypeCase[Type[Attrib2], Attrib2]
): Type[Attrib2] =
Type(f(value.map(_.transformUpRecursive(f))))

}

object Type {
def unfold[Z, Attrib](initial: Z)(f: Z => TypeCase[Z, Attrib]): Type[Attrib] =
Type(f(initial).map(unfold(_)(f)))
}

final case class TypeMapperRec[A, B, C, D](fn: TypeCase[Type[A], B] => TypeCase[Type[C], D])
extends Function[TypeCase[Type[A], B], TypeCase[Type[C], D]] {
override def apply(v1: TypeCase[Type[A], B]): TypeCase[Type[C], D] = fn(v1)

}

trait TypeMapper[-AttribIn, +AttribOut] {
def apply[Self](value: TypeCase[Self, AttribIn]): TypeCase[Self, AttribOut]
}
Expand Down
14 changes: 14 additions & 0 deletions morphir/sdk/core/src-2.11/morphir/sdk/DecimalModuleCompat.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package morphir.sdk

trait DecimalModuleCompat {
import DecimalModuleCompat._
implicit def toBigDecimalOps(value: BigDecimal): BigDecimalOps =
new BigDecimalOps(value)
}

object DecimalModuleCompat {
class BigDecimalOps(private val self: BigDecimal) extends AnyVal {
def compareTo(that: BigDecimal): Int =
self.compare(that)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package morphir.sdk

trait DecimalModuleCompat {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package morphir.sdk

trait DecimalModuleCompat {}
41 changes: 23 additions & 18 deletions morphir/sdk/core/src/morphir/sdk/Decimal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,32 @@ package morphir.sdk
import morphir.sdk.Maybe.Maybe
import morphir.sdk.Basics.Order

import java.math.{ BigDecimal => BigDec, RoundingMode }
import java.math.{ BigDecimal => BigDec }
import scala.util.control.NonFatal

object Decimal {
object Decimal extends DecimalModuleCompat {

type Decimal = BigDec
type Decimal = BigDecimal

object Decimal {
def apply(value: BigDec): Decimal = value
def apply(value: scala.BigDecimal): Decimal = value.bigDecimal
def apply(value: BigDec): Decimal = BigDecimal(value)
def apply(value: scala.BigDecimal): Decimal = value
def apply(value: morphir.sdk.Float.Float): Decimal = BigDecimal.exact(value).bigDecimal
def apply(value: morphir.sdk.Int.Int): Decimal = BigDecimal(value).bigDecimal

}

@inline def apply(value: BigDec): Decimal = Decimal.apply(value)
@inline def apply(value: scala.BigDecimal): Decimal = Decimal.apply(value)
@inline def apply(value: morphir.sdk.Float.Float): Decimal = Decimal.apply(value)
@inline def apply(value: morphir.sdk.Int.Int): Decimal = Decimal.apply(value)

/**
* Absolute value (sets the sign as positive)
*/
def abs(value: Decimal): Decimal = value.abs()
def abs(value: Decimal): Decimal = value.abs

def add(a: Decimal)(b: Decimal): Decimal = a.add(b)
def add(a: Decimal)(b: Decimal): Decimal = a + b

def bps(n: morphir.sdk.Int.Int): Decimal = Decimal(n * 0.0001)

Expand All @@ -37,7 +42,7 @@ object Decimal {
if (b.compareTo(zero) == 0) Maybe.nothing
else
try {
Maybe.just(a.divide(b))
Maybe.just(a / b)
} catch {
case NonFatal(_) => Maybe.nothing
}
Expand Down Expand Up @@ -83,39 +88,39 @@ object Decimal {
def millionth(n: morphir.sdk.Int.Int): Decimal =
Decimal(n * 0.000001)

def mul(a: Decimal)(b: Decimal): Decimal = a.multiply(b)
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

def negate(value: Decimal): Decimal = value.negate()
def negate(value: Decimal): Decimal = -value

def round(decimal: Decimal): Decimal = {
val scale = decimal.scale()
decimal.setScale(scale, RoundingMode.HALF_EVEN)
val scale = decimal.scale
decimal.setScale(scale, BigDecimal.RoundingMode.HALF_EVEN)
}

def shiftDecimalLeft(n: morphir.sdk.Int.Int)(value: Decimal): Decimal =
value.scaleByPowerOfTen(-n.intValue()) //TODO: When we align Int to Int this should settle in correctly
value.bigDecimal.scaleByPowerOfTen(-n.intValue()) //TODO: When we align Int to Int this should settle in correctly

def shiftDecimalRight(n: morphir.sdk.Int.Int)(value: Decimal): Decimal =
value.scaleByPowerOfTen(n.intValue()) //TODO: When we align Int to Int this should settle in correctly
value.bigDecimal.scaleByPowerOfTen(n.intValue()) //TODO: When we align Int to Int this should settle in correctly

def sub(a: Decimal)(b: Decimal): Decimal = a.subtract(b)
def sub(a: Decimal)(b: Decimal): Decimal = a - b

def thousand(n: morphir.sdk.Int.Int): Decimal =
Decimal(n * 1000)

def toFloat(value: Decimal): morphir.sdk.Float.Float =
morphir.sdk.Float.Float(value.doubleValue())
morphir.sdk.Float.Float(value.toDouble)

//TODO: Make sure the Elm call and this call return the same value
def toString(value: Decimal): morphir.sdk.String.String = value.toString

def truncate(decimal: Decimal): Decimal = {
// Since morphir's Int is actually a Long this isn't really safe
val scale = decimal.scale()
decimal.setScale(scale, RoundingMode.DOWN)
val scale = decimal.scale
decimal.setScale(scale, BigDecimal.RoundingMode.DOWN)
}

/**
Expand Down
12 changes: 12 additions & 0 deletions morphir/sdk/core/test/src/morphir/sdk/DecimalSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package morphir.sdk

import zio.test.Assertion._
import zio.test.{ assert, DefaultRunnableSpec }

object DecimalSpec extends DefaultRunnableSpec {
def spec = suite("Decimal Spec")(
test("It should be possible to assign an int value to the Decimal") {
assert(Decimal(42))(equalTo(Decimal.fromInt(42)))
}
)
}
Loading