Skip to content

Commit

Permalink
Merge pull request #221 from DamianReeves/scala-docs
Browse files Browse the repository at this point in the history
Scala docs
  • Loading branch information
DamianReeves authored Dec 20, 2023
2 parents 90c3e2b + 5fe9f39 commit 31f486a
Show file tree
Hide file tree
Showing 11 changed files with 1,669 additions and 242 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
./.idea
.fake
.ionide
.ionide

.vscode/
91 changes: 91 additions & 0 deletions docs/scala/datamodel/enums.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
id: enums
title: Enum (Discriminated Union) Encoding
---
Given the following Scala types and values:
```scala
// Types:
case class OneStreamSink(topic: String)
sealed trait Sink
object Sink {
case class OneStream(sinkData: OneStreamSink) extends Sink
case object ConsoleLog extends Sink
}

// Values:
val os = Sink.OneStream(OneStreamSink(topic = "123"))
val oc = Sink.ConsoleLog
```
and the equivalent in Morphir/ELM:
```elm
-- Types
type alias OneStreamSink = { topic: String }
type Sink =
OneStream {- sinkData: -} OneStreamSink
| ConsoleLog

-- Values:
os: Sink
os = OneStream { topic = "123" }

oc: Sink
oc = ConsoleLog
```

The value `os` would be represented in the Morphir data-model as the following:
```scala
val os = Data.Case(
values = List(
EnumLabel.Named("sinkData") ->
Data.Record(L("topic") -> Data.String("123"))
)
enumLabel = "OneStream",
shape = enumConcept /* will be described in just a minute */
)
```

Note how the OneStream enum fields `sinkData` is represented as `EnumLabel.Named("sinkData")`. Not all languages
support the naming for enum fields. As you can see in the Morphir/ELM example abovem it is commented out. Therefore
instead of `EnumLabel.Named("sinkData")` in the Moprhir-data model, it would be represented as `EnumLabel.Empty`.
```scala
val os = Data.Case(
values = List(
EnumLabel.Empty ->
Data.Record(L("topic") -> Data.String("123"))
)
enumLabel = "OneStream",
shape = enumConcept /* will be described in just a minute */
)
```

The value `oc` would be represented as the following:
```scala
// val oc: Sink = Sink.ConsoleLog // (Scala)
// oc = ConsoleLog // (Morphir/ELM)

val oc = Data.Case(
values = List()
enumLabel = "ConsoleLog",
shape = enumConcept /* will be described in just a minute */
)
```

On a schema-level the `Concept` for this enum would be the following:
```scala
Concept.Enum(
name = "Sink",
cases = List(
Concept.Enum.Case(
L("OneStream"),
fields = List(
EnumLabel.Named("sinkData") ->
Concept.Record(L("topic") -> Concept.String)
)
),
Concept.Enum.Case(
L("ConsoleLog"),
fields = List()
)
)
)
```
11 changes: 11 additions & 0 deletions docs/scala/datamodel/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
id: datamodel
title: Data Model
---

The Morphir data model (MDM) was created to simplify integration between data formats and the Morphir IR. Most data
formats have no concept of logic, and as such, a significant portion of the Morphir IR is not relevant if your
intention is to provide a front-end or back-end for a data format.

The Morphir data model implements a front-end and back-end to the Morphir IR and allows those integrating data formats
to only have to integrate with MDM, which closer resembles other data format integration activities.
56 changes: 56 additions & 0 deletions docs/scala/datamodel/lists.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
id: lists
title: Lists
---
Given the following Scala:
```scala
val items = List("one", "two", "three")
```
and the equivalent Morphir/ELM lists:
```elm
items: List String
items = ["one", "two", "three"]
```

This data should be represented in the Morphir data-model as the following:
```scala
Data.List(
values = List(Data.String("one"), Data.String("two"), Data.String("three")),
shape = Concept.List(elementType = Concept.String)
)
```

List should be able to contain Records, Enums, or any other subtype of Data.
For example, the following data in Scala and Morphir/Elm:
```scala
// Scala
case class Person(name: String, age: Int)
val people = List(Person("Joe", 123), Person("Jim", 456))
```
```elm
-- Morphir/ELM
type alias Person = { name: String, age: Int }
people: List Person
people = [ {name = "Joe", age = 123}, {name = "Jim", age = 456} ]
```
should be represented in the Morphir data-model as the following:
```scala
Data.List(
values = List(
Data.Record(
L("name") -> Data.String("Joe"), L("age") -> Data.Int32(123),
),
Data.Record(
L("name") -> Data.String("Jim"), L("age") -> Data.Int32(456),
)
),
shape = Concept.List(
elementType =
Concept.Record(
L("name") -> Concept.String,
L("age") -> Concept.Int32
)
)
)

```
28 changes: 28 additions & 0 deletions docs/scala/datamodel/maps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
id: maps
title: Maps
---
Given the following Scala Map:
```scala
val myMap = Map("foo" -> 123, "bar" -> 456)
```
and the equivalent Morphir/ELM dictionary:
```elm
myMap: Dict String Int
myMap = Dict.fromList
[
("foo", 123),
("bar", 456)
]
```

This data should be represented in the Morphir data-model as the following:
```scala
Data.Map(
values = Map(
Data.String("foo") -> Data.Int(123),
Data.String("bar") -> Data.Int(456),
)
shape = Concept.Map(keyType = Concept,String, valueType = Concept.Int)
)
```
16 changes: 16 additions & 0 deletions docs/scala/datamodel/primitives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
id: primitives
title: Primitive Encoding
---
The following encodings are supported from Scala values into Morphir DDL primitives:

| Morphir DDL | Morphir DDL Type | Scala Class | Scala Example |
|--------------------------|-------------------|---------------------|-----------------------|
| Data.Boolean(true) | Concept.Boolean | scala.Boolean | true |
| Data.Byte(0xf) | Concept.Byte | scala.Byte | 0xf |
| Data.Decimal(BigDecimal) | Concept.Decimal | scala.BigDecimal | BigDecimal("123") |
| Data.Integer(BigInt) | Concept.Integer | scala.BigInt | BigInt("123") |
| Data.Int16(123) | Concept.Int16 | scala.Short | 123.toShort |
| Data.Int32(123) | Concept.Int32 | scala.Int | 123 |
| Data.String("value") | Concept.String | java.lang.String | value |
| Data.LocalDate | Concept.LocalDate | java.time.LocalDate | LocalDate(2023, 1, 1) |
68 changes: 68 additions & 0 deletions docs/scala/datamodel/records.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
id: records
title: Record (Case Class) Encoding
---
Scala Case Classes and Morphir/ELM records are represented as the Data.Record type.
Given the following Scala values:
```scala
case class Person(name: String, age: Int)
val joe = Person("Joe", 123)
```

and the equivalent Morphir/ELM value:
```elm
person: { name: String, age: Int }
person = { name = "Joe", age = 123 }
```

The Data and Concept that represents the above is as follows:
```scala
Data.Record(
values = List(
L("name") -> Data.String("Joe"),
L("age") -> Data.Int(123)
)
shape = Concept.Record(
values = List(
L("name") -> Concept.String
L("age") -> Concept.Int
)
)
)
```

The fields of records may themselves be records (as well as collections, enums, or any other kind of Data object).
Given the following Scala data:
```scala
case class Name(first: String, last: String)
case class Person(name: Name, age: Int)

val joe = Person(Name("Joe", "Bloggs"), 123)
```

and the equivalent Morphir/ELM data:
```elm
type alias Name = { first: String, last: String }

joe: { name: Name, age: Int }
joe = {
name = { first = "Joe", last = "Bloggs" },
age = 123
}
```
The data is represented as the following:
```scala
Data.Record(
values = List(
L("name") -> Data.Record(L("first") -> "Joe", L("last") -> "Bloggs")
L("age") -> Data.Int32(123)
),
concept = Concept.Record(
L("name") ->
Data.Record(L("first") -> Concept.String, L("last") -> Concept.String)
L("age") ->
Data.Int32
)
)

```
85 changes: 85 additions & 0 deletions docs/scala/datamodel/schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
id: schema
title: Data and Schema
---

Every data-instance in the Morphir data-model is a subtype of the Morphir Data interface. The instances contain their
corresponding data as well as a Data.shape property that represents the schema of the data. For example, for the
following simple record type:
```scala
case class Person(name: String, age: Int)
```

An instance of this record would have the following Morphir data-model encoding.
```scala
// Instance
val joe = Person("Joe", 123)

// Encoding
Data.Record(
values = List(
Label("name") -> Data.String("Joe"),
Label("age") -> Data.Int32(123)
)
)
```

Note that the equivalent representation of this data in ELM is the following:
```elm
person: { name: String, age: Int }
person = { name = "Joe", age = 123 }
```

Every single instance of the data-model has a Data.shape property that represents the schema. The schema of this
record will look like the following:

```scala
Concept.Record(
values = List(
Label("name") -> Concept.String
Label("age") -> Concept.Int
)
)
```

With this in mind, complete data available on the Data.Record instance above can be thought of as the following.
(NOTE: the shorthand L("name") will be used for Label("name") now on).
```scala
// val joe = Person("Joe", 123)
Data.Record(
values = List(
L("name") -> Data.String("Joe"),
L("age") -> Data.Int32(123)
)
shape = Concept.Record(
values = List(
L("name") -> Concept.String
L("age") -> Concept.Int32
)
)
)
```

Notice how the general shape of the Data.Record.shape.values information corresponds to the shape of the Data.Record.
values information. This is a general idea that the Morphir data-model attempts to maintain. The Concept data within
the Data.shape of various Data instances should approximately mirror what is available on the value level.

Also please note that from now on, the variadic constructor shorthand for Data.Record and Concept.Record will be used:
```scala
Data.Record(
L("name") -> Data.String("Joe"), L("age") -> Data.Int(123))
shape =
Concept.Record(L("name") -> Concept.String, L("age") -> Concept.Int)
)
```

This relationship between Data and Concept is maintained down to the primitive level. For example, Data.string is
represented as the following:
```scala
Data.String(
value = "Joe",
shape = Concept.String
)
```

In the case of primitives, the Data.shape field represents a leaf-level Concept element.
9 changes: 9 additions & 0 deletions docs/scala/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
id: morphir-scala
title: Morphir Scala Overview
---

For those Morphir users who are targeting Scala, the `morphir-scala`
[repository](https://github.com/finos/morphir-scala) offers a set of libraries
and tools aimed at supporting a variety of use-cases that are possible when
working with Morphir with Scala.
Loading

0 comments on commit 31f486a

Please sign in to comment.