Skip to content

Commit 732bec8

Browse files
significant performance improvements and cleanup of logic
1 parent 3a1ee55 commit 732bec8

File tree

7 files changed

+151
-138
lines changed

7 files changed

+151
-138
lines changed

src/main/scala/grammar.scala

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,11 @@ package object grammar {
5050
else if (m.isEmpty) {
5151
val lasterr = m.asInstanceOf[DatasetError[B]].value
5252
DatasetError[C](lasterr:_*).asInstanceOf[dataset[C]]
53-
} else
54-
f(m.asInstanceOf[B])
53+
} else {
54+
val res = f(m.asInstanceOf[B])
55+
//if(res.isContext) res.multifetch[C] else res
56+
res
57+
}
5558

5659
}
5760

@@ -155,18 +158,18 @@ implicit class toOption[A<:dataset[A]](src:dataset[A]){
155158
)
156159
})
157160

158-
// def runWith[U<:A ==> _,B<:U#dep](instU:U)(
159-
// implicit ttag: TypeTag[U],
160-
// tagA: TypeTag[A],
161-
// tagb:TypeTag[B]
162-
// ): dataset[A] =
163-
// if (src.isEmpty) DatasetError[A]()
164-
// else
165-
// instU.apply(src).fold(
166-
// e => DatasetError[A](new Error(s"Failure to run ${buildId[U]}")).append(e.value: _*)
167-
// )(
168-
// b => if(b.context.isEmpty) src.++(b) else src.withContext(src.context ++ b.context)
169-
// )
161+
def -->[B<:dataset[_]](instU:A ==> B)(
162+
implicit
163+
tagA: TypeTag[A],
164+
tagb:TypeTag[B]
165+
): dataset[B] =
166+
if (src.isEmpty) DatasetError[B](src.asInstanceOf[DatasetError[A]].value:_*)
167+
else
168+
instU.apply(src).fold(
169+
e => DatasetError[B](new Error(s"Failure to run ${instU.toString}")).append(e.value: _*)
170+
)(
171+
b => b
172+
)
170173

171174
}
172175

src/main/scala/test/AccountRates.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ object AccountRates{
1212
case BokerageAccount(id, balance) =>
1313
val growAccounts = scala.math.random() > baseProb
1414
val randomFudgeUp = scala.math.sqrt(scala.math.random())
15-
val randomFudgeDown = scala.math.random()
1615
if(growAccounts)
1716
baseRateUp * randomFudgeUp
1817
else

src/main/scala/test/Date.scala

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,9 @@ object Date{
2323
def nextPeriod:dataset[A] = src.+->[Date]
2424
}
2525

26-
sealed trait Date extends index[Date] with produces[LocalDate] with solveable [Date]{
26+
sealed trait Date extends index[Date] with produces[LocalDate]{
2727
val periodsInYear:Int
2828
val numberOfDays:Int
29-
30-
override def next(src: dataset[Date]): Seq[dataset[_]] = Seq(
31-
src.nextPeriod
32-
)
33-
34-
override def solved(src: dataset[Date]): Boolean = ???
3529
}
3630

3731
case class Day(value:LocalDate) extends Date{
@@ -86,8 +80,33 @@ object Date{
8680
)
8781
.headOption.fromOption
8882
}
83+
def findClosestPeriodRange(src:dataset[Date]):produces[Seq[Date]] = (for{
84+
date <- src.currentDate
85+
}yield {
86+
val withinPeriod = this
87+
.filter(d => date.isWithinPeriod(d))
88+
apply(withinPeriod)
89+
}).getOrElse(Seq.empty[Date])
8990
}
90-
case class dates(start:Date,end:LocalDate,value:Seq[Date]) extends DateRange {
91+
92+
implicit class DateRangeGrammar[A<:DateRange](src:dataset[A])(implicit taga:TypeTag[A]){
93+
def dateRange:dataset[DateRange] = if(src.isInstanceOf[DateRange]) src else src.<--[DateRange]
94+
def getRange:dataset[DateRange] =if(src.dateRange.getValue.exists && src.dateRange.getValue.value.size > 0)
95+
src
96+
else for {
97+
range <- src.dateRange
98+
sd <- range.start
99+
expandedDateRange <- (
100+
data[Date]() ++
101+
sd ++
102+
range
103+
)
104+
.<-+[DateRange]
105+
}yield expandedDateRange
106+
}
107+
108+
109+
case class dates(start:Date,end:LocalDate,value:Seq[Date] = Seq()) extends DateRange {
91110
override def apply(newvalue: Seq[Date]): DateRange = dates(start,end,newvalue)
92111
}
93112

src/main/scala/test/Income.scala

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,33 @@ import java.time.LocalDate
33

44
import Typical.core.dataset._
55
import Typical.core.grammar._
6-
import Date._
7-
import EventHandler._
8-
import Account._
6+
import test.Account._
7+
import test.Date._
8+
99
import scala.reflect.runtime.universe.TypeTag
1010
object Income {
1111
type IncomeEventGenDeps = Incomes with Date
12+
type IncomeEventDeps = Date
1213
//define incomes
13-
trait Income extends (IncomeEventGenDeps ==> Income) with produces[Seq[IncomeEvent]]{
14+
trait Income extends (IncomeEventDeps ==> Income) with produces[Seq[IncomeEvent]]{
1415
val id:Long
1516
val payableTo:Long
1617
val amount:Double
1718
val dateRange:DateRange
1819
}
19-
import scala.math.abs
2020
case class ficaTaxableincome(id:Long,amount:Double,payableTo:Long, dateRange:DateRange,eventLog:Seq[IncomeEvent] = Seq()) extends Income{
2121
override val value = eventLog
22-
override def apply(src: dataset[IncomeEventGenDeps]): dataset[Income] =
22+
override def apply(src: dataset[IncomeEventDeps]): dataset[Income] =
2323
(for{
2424
date <- src.currentDate
25-
expandedDateRange <- (data[Date]().++(dateRange.start).++(dateRange)).<-+[DateRange]
26-
possibleDate <- expandedDateRange.findClosestPeriod(src)
25+
expandedDateRange <- dateRange.getRange
2726
}yield {
2827
if( this.isActive(date)) {
29-
this.copy(eventLog = taxableIncomeEvent(this.id,this.payableTo,amount,possibleDate) +: eventLog)
28+
val pr = expandedDateRange.findClosestPeriodRange(src)
29+
val res = pr.map(d => taxableIncomeEvent(this.id,this.payableTo,amount,d))
30+
this.copy(eventLog = res ++ eventLog,dateRange = expandedDateRange)
3031
}
31-
else this
32+
else this.copy(dateRange = expandedDateRange)
3233
})
3334
def preceeds(date:Date):Boolean = date.isBefore(dateRange.start)
3435
def halted(date:Date):Boolean = date.isAfter(dateRange.end)
@@ -42,30 +43,19 @@ object Income {
4243
val amount:Double
4344
val date:LocalDate
4445
}
45-
case class taxableIncomeEvent(incomeId:Long,payableTo:Long,amount:Double,date:LocalDate) extends IncomeEvent{
46-
47-
}
46+
case class taxableIncomeEvent(incomeId:Long,payableTo:Long,amount:Double,date:LocalDate) extends IncomeEvent
4847

4948

50-
51-
case class IncomeEventGenerator(value:Seq[IncomeEvent]) extends
52-
(IncomeEventGenDeps ==> IncomeEventGenerator) with
53-
produces[Seq[IncomeEvent]]{
54-
override def apply(src: dataset[IncomeEventGenDeps]): dataset[IncomeEventGenerator] = for{
49+
case class Incomes(value:Seq[Income],eventLog:Seq[IncomeEvent] = Seq()) extends (IncomeEventGenDeps ==> Incomes) with produces[Seq[Income]]{
50+
override def apply(src: dataset[IncomeEventGenDeps]): dataset[Incomes] = for{
5551
incomes <- src.incomes
5652
}yield {
57-
val toBePaidOut = incomes.value.flatMap(i => {
58-
src.++(i).<-+[Income].getOrElse[Seq[IncomeEvent]](Seq())
59-
})
60-
IncomeEventGenerator(
61-
toBePaidOut
62-
)
53+
incomes.value.foldLeft(src)((accumSrc,i) => for{
54+
inc <- accumSrc.++(i).<-+[Income]
55+
incomes <- accumSrc.incomes
56+
updatedIncomes <- incomes.update(inc)
57+
}yield accumSrc ++ updatedIncomes).incomes
6358
}
64-
}
65-
66-
67-
case class Incomes(value:Seq[Income],eventLog:Seq[IncomeEvent] = Seq()) extends (Incomes ==> Incomes) with produces[Seq[Income]]{
68-
override def apply(src: dataset[Incomes]): dataset[Incomes] = src.<--[Incomes]
6959
private def apply(income:Income):dataset[Incomes] = {
7060
val incomeMapCurr = this.incomeMap
7161
val exists = incomeMap.get(income.id).isDefined
@@ -86,21 +76,21 @@ object Income {
8676
def incomes:dataset[Incomes] = if(src.isInstanceOf[Incomes]) src else src.<--[Incomes]
8777
def events: produces[Seq[IncomeEvent]] = src.incomes
8878
.biMap[produces[Seq[IncomeEvent]]](err => noVal(err.value:_*))(
89-
d => someval(d.asInstanceOf[Incomes].eventLog)
79+
d => someval(d.asInstanceOf[Incomes].eventLog ++ d.asInstanceOf[Incomes].value.flatMap(_.value))
9080
)
81+
def eventsAtDate(date: Date):produces[Seq[IncomeEvent]] = someval(
82+
src.events.filter(e => date.isWithinPeriod(Year(e.date)))
83+
)
9184
}
9285
implicit class IncomeDateDrivenBehavior[A<:Incomes with Date with Accounts](src:dataset[A])(implicit taga:TypeTag[A]){
93-
def generateIncomeEvents:dataset[IncomeEventGenerator] =
94-
(src +-IncomeEventGenerator(Seq())).<-+[IncomeEventGenerator]
9586
def receiveIncomes:dataset[A] = for{
96-
incomeEvents <- src.generateIncomeEvents
97-
incomes <- src.incomes
87+
date <- src.currentDate
9888
accounts <- src.accounts
99-
updatedIncomes <- incomes.addEvent(incomeEvents.value:_*)
100-
}yield incomeEvents.foldLeft(src)((accumsrc,income) => for{
89+
updatedIncomes <- src.<-+[Incomes]
90+
}yield (updatedIncomes.eventsAtDate(date).foldLeft(src ++ updatedIncomes)((accumsrc,income) => for{
10191
acctPayable <- accounts.getAccount(income.payableTo).fromOption
102-
}yield accumsrc.deposit(acctPayable, income.amount)
103-
) ++ updatedIncomes
92+
}yield accumsrc.deposit(acctPayable, income.amount)))
93+
10494
}
10595

10696

src/main/scala/test/Property.scala

Lines changed: 61 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -11,76 +11,81 @@ import scala.reflect.runtime.universe.TypeTag
1111

1212
object Property{
1313
//define property types
14-
trait Property extends ::[Property]{
14+
trait propertyEvent{
15+
val propertyId:Long
16+
val amount:Double
17+
val date:LocalDate
18+
19+
20+
}
21+
type PropertyEventDeps = Date
22+
trait Property extends (PropertyEventDeps ==> Property) with produces[Seq[propertyEvent]]{
1523
val id:Long
24+
val dateRange:DateRange
25+
def preceeds(date:Date):Boolean = date.isBefore(dateRange.start)
26+
def halted(date:Date):Boolean = date.isAfter(dateRange.end)
27+
def isActive(date:Date):Boolean = !preceeds(date) && !halted(date)
1628
}
17-
case class RentalProperty(id:Long, monthlyRent:Double) extends Property
18-
case class Home(id:Long,costBasis:Double,marketValue:Double)
19-
//define property events
20-
case class rentPaymentDue(propertyId:Long,amount:Double,date:Date) extends Event
21-
case class rentPaymentPaid(propertyId:Long,amount:Double,date:LocalDate) extends Event
22-
//generate rend due payments in model
23-
type RentGenDeps = Properties with Date
24-
case class GenerateRentDue(value:Seq[rentPaymentDue]) extends
25-
(RentGenDeps ==> GenerateRentDue) with
26-
produces[Seq[rentPaymentDue]]{
27-
override def apply(src: dataset[RentGenDeps]): dataset[GenerateRentDue] = for{
28-
properties <- src.properties
29+
case class RentalProperty(id:Long, rent:Double,dateRange:DateRange,eventLog:Seq[propertyEvent] = Seq()) extends Property{
30+
override val value = eventLog
31+
override def apply(src: dataset[PropertyEventDeps]): dataset[Property] = for{
32+
range <- dateRange.getRange
2933
date <- src.currentDate
30-
}yield GenerateRentDue(
31-
date match {
32-
case w:Week if w.value.getDayOfMonth < 7 => properties.collect({
33-
case r:RentalProperty => rentPaymentDue(r.id,r.monthlyRent,w)
34-
})
35-
case m:Month => properties.collect({
36-
case r:RentalProperty => rentPaymentDue(r.id,r.monthlyRent,m)
37-
})
38-
case y:Year => properties.collect({
39-
case r:RentalProperty => (0 to 11).foldLeft(Seq[rentPaymentDue]())(
40-
(accum,n) => rentPaymentDue(r.id,r.monthlyRent,Month(y.value.plusMonths(n))) +: accum
41-
)
42-
}).flatten
43-
case _ => Seq()
44-
})
34+
}yield {
35+
if(isActive(date)){
36+
val events = range.findClosestPeriodRange(src).map(d => rentPaymentDue(id,rent,d)) ++ eventLog
37+
this.copy(eventLog = events ,dateRange = range)
38+
}else this
39+
}
4540
}
46-
implicit class RentGeneratorGrammarPaymentHelper[A<:Properties with Date](src:dataset[A])(implicit taga:TypeTag[A]){
47-
def rentDue:dataset[GenerateRentDue] =
48-
src
49-
.+-(GenerateRentDue(Seq()))
50-
.<-+[GenerateRentDue]
41+
case class Home(id:Long,costBasis:Double,marketValue:Double,dateRange:DateRange,value:Seq[propertyEvent] = Seq()) extends Property{
42+
override def apply(v1: dataset[PropertyEventDeps]): dataset[Property] = this
5143
}
44+
//define property events
45+
case class rentPaymentDue(propertyId:Long,amount:Double,date:LocalDate) extends propertyEvent
46+
case class rentPaymentPaid(propertyId:Long,amount:Double,date:LocalDate) extends propertyEvent
47+
//generate rend due payments in model
5248

5349

54-
case class Properties(value:Seq[Property],eventLog:Seq[Event]) extends (Properties ==> Properties) with produces[Seq[Property]]{
50+
type PropertyEventGenDeps = Date
51+
case class Properties(value:Seq[Property],eventLog:Seq[propertyEvent]) extends (PropertyEventGenDeps with Properties ==> Properties) with produces[Seq[Property]]{
5552
lazy val propertyMap: Map[Long,Property] = value.map(a => a.id -> a).toMap
56-
override def apply(v1: dataset[Properties]): dataset[Properties] = v1.<--[Properties]
57-
private def apply(account:Property):dataset[Properties] = {
53+
override def apply(src: dataset[PropertyEventGenDeps with Properties]): dataset[Properties] = for{
54+
properties <- src.properties
55+
}yield {
56+
properties.value.foldLeft(src)((accumSrc, i) => for {
57+
inc <- accumSrc.++(i).<-+[Property]
58+
incomes <- accumSrc.properties
59+
updatedIncomes <- incomes.update(inc)
60+
} yield accumSrc ++ updatedIncomes).properties
61+
}
62+
63+
private def apply(property:Property):dataset[Properties] = {
5864
val pptyMap = this.propertyMap
59-
val exists = pptyMap.get(account.id).isDefined
60-
lazy val updatedMap = pptyMap.updated(account.id,account)
61-
val newAcctColl = if(exists) updatedMap.values.toSeq else value :+ account
65+
val exists = pptyMap.get(property.id).isDefined
66+
lazy val updatedMap = pptyMap.updated(property.id,property)
67+
val newAcctColl = if(exists) updatedMap.values.toSeq else value :+ property
6268
new Properties(newAcctColl,this.eventLog){
63-
override lazy val propertyMap = if (pptyMap != null) updatedMap else Map(account.id -> account)
69+
override lazy val propertyMap = if (pptyMap != null) updatedMap else Map(property.id -> property)
6470
}
6571
}
66-
private[Property] def addEvents(events:Event*) = Properties(value,events ++ eventLog)
72+
private[Property] def addEvents(events:propertyEvent*) = this.copy(eventLog = events ++ eventLog)
6773
def get(id:Long):dataset[Property] = propertyMap(id)
6874
def update(property:Property):dataset[Properties] = apply(property)
6975

7076
}
7177
implicit class PropertiesAPI[A<:Properties](src:dataset[A])(implicit taga:TypeTag[A]){
7278
def properties:dataset[Properties] = if(src.isInstanceOf[Properties]) src else src.<--[Properties]
73-
def events:produces[Seq[Event]] = src.properties.biMap[produces[Seq[Event]]](err => noVal(err.value:_*))(d => someval(d.asInstanceOf[Properties].eventLog))
79+
def events:produces[Seq[propertyEvent]] = src.properties.biMap[produces[Seq[propertyEvent]]](err => noVal(err.value:_*))(d => someval(d.asInstanceOf[Properties].eventLog ++ d.asInstanceOf[Properties].value.flatMap(_.value)))
80+
def eventsAtDate(date: Date):produces[Seq[propertyEvent]] = someval(
81+
src.events.filter(e => date.isWithinPeriod(Year(e.date)))
82+
)
7483
}
7584
implicit class GrowRents[A<:Properties with Date](src:dataset[A])(implicit taga:TypeTag[A]){
76-
def accrueRent:dataset[A] = for{
77-
properties <- src.properties
78-
rentdue <- src.rentDue
79-
}yield src.++(
80-
properties.addEvents(rentdue.value:_*)
81-
)
85+
def accrueRent:dataset[A] = src.+->[Properties]
8286
}
8387
implicit class PayRents[A<:Properties with Accounts with Date](src:dataset[A])(implicit taga:TypeTag[A]){
88+
8489
def payRents:dataset[A] = for{
8590
properties <- src.properties
8691
accounts <- src.accounts
@@ -89,24 +94,14 @@ object Property{
8994
.collectFirst({case c:CheckingAccount => c})
9095
.asInstanceOf[Option[Account]]
9196
.fromOption
92-
rentdue <- src.rentDue
93-
}yield date match {
94-
case _:Month | _:Week =>
95-
val rentpaymentsdue = properties.events.collect({
96-
case r:rentPaymentDue if scala.math.abs(date.getDayOfMonth() - r.date.getDayOfMonth()) < date.numberOfDays => r
97-
})
98-
rentdue.foldLeft(src)((accumSrc,paymentDue) =>
99-
accumSrc
100-
.withdraw(rentAcct,paymentDue.amount)
101-
.++(
102-
properties.addEvents(
103-
rentPaymentPaid(paymentDue.propertyId,paymentDue.amount,date)
104-
)
105-
)
106-
)
107-
case _ => DatasetError[A](new Error("rent payments other than Monthly not implemented"))
108-
109-
97+
}yield {
98+
val rentPaymentsPaid = properties.eventsAtDate(date).collect({
99+
case r:rentPaymentDue => rentPaymentPaid(r.propertyId,r.amount,r.date)
100+
})
101+
rentPaymentsPaid.foldLeft(src)(
102+
(accumSrc,event) =>
103+
accumSrc.withdraw(rentAcct,event.amount)
104+
) ++ properties.addEvents(rentPaymentsPaid:_*)
110105
}
111106
}
112107

0 commit comments

Comments
 (0)