diff --git a/.gitignore b/.gitignore index 437528b..c0e6c38 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ .bsp/ .idea/ .metals/ -target/ \ No newline at end of file +target/ +project/ +.bloop/ +.vscode/ \ No newline at end of file diff --git a/project/metals.sbt b/project/metals.sbt index aa04ddb..7dadd5e 100644 --- a/project/metals.sbt +++ b/project/metals.sbt @@ -1,4 +1,4 @@ // DO NOT EDIT! This file is auto-generated. // This file enables sbt-bloop to create bloop config files. -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.4-13-408f4d80") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.5-6-4768184c") diff --git a/src/main/scala/net/asachdeva/01_adts.scala b/src/main/scala/net/asachdeva/01_adts.scala index a09fc59..8bd9f00 100644 --- a/src/main/scala/net/asachdeva/01_adts.scala +++ b/src/main/scala/net/asachdeva/01_adts.scala @@ -1,7 +1,10 @@ package net.asachdeva import java.time.Instant +import java.util.Date +import net.asachdeva.bank.Customer.BankCustomer +import net.asachdeva.portfolio.Currency /* * INTRODUCTION * @@ -29,6 +32,7 @@ object credit_card { * * Security code */ type CreditCard + final case class Credit_Card[CreditCard](number: Long, name: String, expiratioDate: Date, securityCode:String) /** EXERCISE 2 * @@ -37,7 +41,22 @@ object credit_card { * or a digital product, such as a book or movie, or access to an event, such * as a music concert or film showing. */ - type Product + sealed trait Product + object Product{ + sealed trait PhysicalProduct extends Product + sealed trait DigitalProduct extends Product + sealed trait Event extends Product + } + +// case object Milk extends PhysicalProduct +// case object Book extends DigitalProduct +// case object Movie extends DigitalProduct +// case object Concert extends Event + + /// this actually looks like I'm doing straight up polymorphism + + + /** EXERCISE 3 * @@ -45,7 +64,14 @@ object credit_card { * of a product price, which could be one-time purchase fee, or a recurring * fee on some regular interval. */ - type PricingScheme +// type PricingScheme + + sealed trait PricingScheme + + case class OneTimePayment() extends PricingScheme + + case class RecurringFee(interval: Option[String]) extends PricingScheme + } /** EVENT PROCESSING - EXERCISE SET 3 @@ -60,48 +86,36 @@ object events { * Refactor the object-oriented data model in this section to a more * functional one, which uses only sealed traits and case classes. */ - abstract class Event(val id: Int) { - +// abstract class Event(val id: Int) { +// +// def time: Instant +// } + sealed trait Event { + val id: Int def time: Instant } // Events are either UserEvent (produced by a user) or DeviceEvent (produced by a device), // please don't extend both it will break code!!! - trait UserEvent extends Event { - def userName: String - } - - // Events are either UserEvent (produced by a user) or DeviceEvent (produced by a device), - // please don't extend both it will break code!!! - trait DeviceEvent extends Event { - def deviceId: Int + object Event { + sealed trait UserEvent extends Event { + def userName: String + } + object UserEvent { + final case class UserPurchase(id: Int, item: String, price: Double, time: Instant, userName: String) extends UserEvent + final case class UserAccountCreated(id: Int, userName: String, time: Instant) extends UserEvent + } + // Events are either UserEvent (produced by a user) or DeviceEvent (produced by a device), + // please don't extend both it will break code!!! + trait DeviceEvent extends Event { + def deviceId: Int + } + + object DeviceEvent { + final case class SensorUpdated(id: Int, deviceId: Int, time: Instant, reading: Option[Double]) extends DeviceEvent + final case class DeviceActivated(id: Int, deviceId: Int, time: Instant) extends DeviceEvent + } } - - class SensorUpdated( - id: Int, - val deviceId: Int, - val time: Instant, - val reading: Option[Double] - ) extends Event(id) - with DeviceEvent - - class DeviceActivated(id: Int, val deviceId: Int, val time: Instant) - extends Event(id) - with DeviceEvent - - class UserPurchase( - id: Int, - val item: String, - val price: Double, - val time: Instant, - val userName: String - ) extends Event(id) - with UserEvent - - class UserAccountCreated(id: Int, val userName: String, val time: Instant) - extends Event(id) - with UserEvent - } /** DOCUMENT EDITING - EXERCISE SET 4 @@ -119,7 +133,11 @@ object documents { * Using only sealed traits and case classes, create a simplified but somewhat * realistic model of a Document. */ - type Document +// type Document + sealed trait Document + object Document{ + final case class DocumentList(userId: UserId, docId: DocId, content: DocContent) extends Document + } /** EXERCISE 2 * @@ -127,7 +145,20 @@ object documents { * type that a given user might have with respect to a document. For example, * some users might have read-only permission on a document. */ - type AccessType +// type AccessType +// type ReadAcess +// sealed trait Access_Type[AccessType] { +// def docId: DocId +// def userId: UserId +// } +// final case class ReadAccess[ReadAccess](docId: DocId, userId: UserId) extends Access_Type[AccessType] +// final case class WriteAccess(docId:DocId, userId: UserId, content: DocContent) extends Access_Type[AccessType] + sealed trait AccessType + object AccessType{ + final case object NoAccess extends AccessType + final case object ReadAccess extends AccessType + final case object WriteAccess extends AccessType + } /** EXERCISE 3 * @@ -135,7 +166,11 @@ object documents { * permissions that a user has on a set of documents they have access to. * Do not store the document contents themselves in this model. */ - type DocPermissions +// type DocPermissions +// sealed trait Permissions[DocPermissions]{ +// def givePermission +// } + final case class Permission(userId: UserId, permissionsMap: Map[DocId, AccessType]) } /** BANKING - EXERCISE SET 5 @@ -148,7 +183,10 @@ object bank { * * Using only sealed traits and case classes, develop a model of a customer at a bank. */ - type Customer + sealed trait Customer + object Customer{ + final case class BankCustomer(id: Long, name: String, accNo: Long, accType: String) extends Customer + } /** EXERCISE 2 * @@ -156,8 +194,15 @@ object bank { * type. For example, one account type allows the user to write checks * against a given currency. Another account type allows the user to earn * interest at a given rate for the holdings in a given currency. - */ - type AccountType + * */ +// type AccountType + sealed trait AccountType + object AccountType{ + final case class InterestChecking(interestRate: Double) extends AccountType + final case class Holdings(currency: String) extends AccountType + final case object PersonalChecing extends AccountType + } + /** EXERCISE 3 * @@ -165,7 +210,7 @@ object bank { * account, including details on the type of bank account, holdings, customer * who owns the bank account, and customers who have access to the bank account. */ - type Account + final case class BankAccount(accType: AccountType, ownerCust: BankCustomer, accessCust: List[BankCustomer]) } /** STOCK PORTFOLIO - GRADUATION PROJECT @@ -179,35 +224,55 @@ object portfolio { * Using only sealed traits and case classes, develop a model of a stock * exchange. Ensure there exist values for NASDAQ and NYSE. */ - type Exchange + sealed trait StockExchange + object StockExchange{ + final case object NYSE extends StockExchange + final case object NASDAQ extends StockExchange + } /** EXERCISE 2 * * Using only sealed traits and case classes, develop a model of a currency * type. */ - type CurrencyType + + sealed trait Currency + object Currency{ + final case object USD extends Currency + } /** EXERCISE 3 * * Using only sealed traits and case classes, develop a model of a stock * symbol. Ensure there exists a value for Apple's stock (APPL). */ - type StockSymbol +// type StockSymbol + sealed trait StockSymbol + object StockSymbol{ + final case object AAPL extends StockSymbol + } /** EXERCISE 4 * * Using only sealed traits and case classes, develop a model of a portfolio * held by a user of the web application. */ - type Portfolio +// type Portfolio + sealed trait Portfolio + object Portfolio{ + final case class UserPortfolio(user: User, stocks: Seq[StockExchange]) extends Portfolio + } + /** EXERCISE 5 * * Using only sealed traits and case classes, develop a model of a user of * the web application. */ - type User +// type User + + final case class User(userId: Long, userName: String) + /** EXERCISE 6 * diff --git a/src/main/scala/net/asachdeva/02_ops.scala b/src/main/scala/net/asachdeva/02_ops.scala index b93cadf..0259fea 100644 --- a/src/main/scala/net/asachdeva/02_ops.scala +++ b/src/main/scala/net/asachdeva/02_ops.scala @@ -1,5 +1,9 @@ package net.asachdeva + +import java.io.BufferedInputStream +import scala.concurrent.ExecutionContext + /* * INTRODUCTION * @@ -49,7 +53,12 @@ object input_stream { * try to create the first input stream, but if that fails by throwing * an exception, it will then try to create the second input stream. */ - def orElse(that: => IStream): IStream = ??? + def orElse(that: => IStream): IStream = + try { + IStream(self.createInputStream) + } catch { + case e: Exception => IStream.empty + } /** EXERCISE 3 * @@ -57,7 +66,7 @@ object input_stream { * create the input stream, but wrap it in Java's `BufferedInputStream` * before returning it. */ - def buffered: IStream = ??? + def buffered: IStream = IStream(() => new BufferedInputStream(self.createInputStream())) } object IStream { @@ -105,21 +114,23 @@ object email_filter { * Add an "and" operator that will match an email if both the first and * the second email filter match the email. */ - def &&(that: EmailFilter): EmailFilter = ??? + def &&(that: EmailFilter): EmailFilter = + //EmailFilter((self.matches(_: Email) && that.matches(_: Email))) + EmailFilter(email => self.matches(email) && that.matches(email)) /** EXERCISE 2 * * Add an "or" operator that will match an email if either the first or * the second email filter match the email. */ - def ||(that: EmailFilter): EmailFilter = ??? + def ||(that: EmailFilter): EmailFilter = EmailFilter(email => self.matches(email) || that.matches(email)) /** EXERCISE 3 * * Add a "negate" operator that will match an email if this email filter * does NOT match an email. */ - def unary_! : EmailFilter = ??? + def unary_! : EmailFilter = EmailFilter(email => !self.matches(email)) } object EmailFilter { def senderIs(address: Address): EmailFilter = EmailFilter( @@ -146,9 +157,9 @@ object email_filter { * addressed to "john@doe.com". Build this filter up compositionally * by using the defined constructors and operators. */ - lazy val emailFilter1 = ??? -} + lazy val emailFilter1: EmailFilter = EmailFilter.subjectContains("discount") && EmailFilter.bodyContains("N95") && EmailFilter.recipientIs(Address("jogn@doe.com")).unary_! +} /** DATA TRANSFORM - EXERCISE SET 3 * * Consider an email marketing platform, which allows users to upload contacts. @@ -304,7 +315,7 @@ object contact_processing { final case class SchemaMapping( map: ContactsCSV => MappingResult[ContactsCSV] ) { self => - + import MappingResult.{Success, Failure} /** EXERCISE 1 * * Add a `+` operator that combines two schema mappings into one, applying @@ -313,7 +324,13 @@ object contact_processing { * then the result must also fail. Only if both schema mappings succeed * can the resulting schema mapping succeed. */ - def +(that: SchemaMapping): SchemaMapping = ??? + def +(that: SchemaMapping): SchemaMapping = SchemaMapping(contactCSV => self.map(contactCSV) match { + case Success(warnings1, first) => that.map(contactCSV) match { + case Success(warnings2, second) => Success(warnings1 + warnings2, first + second) + case Failure(errors) => Failure(errors) + } + case Failure(errors) => Failure(errors) + }) /** EXERCISE 2 * @@ -321,14 +338,21 @@ object contact_processing { * applying the effects of the first one, unless it fails, and in that * case, applying the effects of the second one. */ - def orElse(that: SchemaMapping): SchemaMapping = ??? + def orElse(that: SchemaMapping): SchemaMapping = SchemaMapping( + contacts => self.map(contacts) match { + case Success(warnings, value) => Success(warnings, value) + case Failure(errors) => that.map(contacts) match { + case Success(warnings, value) => Success(warnings, value) + case Failure(errors) => Failure(errors) + } + }) /** BONUS: EXERCISE 3 * * Add an `protect` operator that returns a new schema mapping that * preserve the specified column names in the final result. */ - def protect(columnNames: Set[String]): SchemaMapping = ??? + def protect(columnNames: Set[String]): SchemaMapping = SchemaMapping(csv => copy(csv.schema.columnNames = columnNames)) } object SchemaMapping { @@ -336,7 +360,10 @@ object contact_processing { * * Add a constructor for `SchemaMapping` that renames a column. */ - def rename(oldName: String, newName: String): SchemaMapping = ??? + def rename(oldName: String, newName: String): SchemaMapping = SchemaMapping { csv => + val csv2 = csv.rename(oldColumn = oldName, newColumn = newName) + MappingResult.Success(if (csv == csv2) List("Eror message") else Nil, csv2) + } /** EXERCISE 5 * @@ -344,21 +371,31 @@ object contact_processing { */ def combine(leftColumn: String, rightColumn: String)(newName: String)( f: (String, String) => String - ): SchemaMapping = ??? + ): SchemaMapping = SchemaMapping { csv => + // combine returns + val csv2: Option[ContactsCSV] = csv.combine(leftColumn, rightColumn)(f(leftColumn, rightColumn)) + MappingResult.Success(if (csv == csv2.get) List("Couldn't combine columns") else Nil, csv2.get) + } /** EXERCISE 6 * * Add a constructor for `SchemaMapping` that moves the column of the * specified name to the jth position. */ - def relocate(column: String, j: Int): SchemaMapping = ??? + def relocate(column: String, j: Int): SchemaMapping = SchemaMapping { csv => + val csv2: Option[ContactsCSV] = csv.relocate(column, j) + MappingResult.Success(if (csv == csv2.get) List("Couldn't relocate columns") else Nil, csv2.get) + } /** EXERCISE 7 * * Add a constructor for `SchemaMapping` that deletes the column of the * specified name. */ - def delete(name: String): SchemaMapping = ??? + def delete(name: String): SchemaMapping = SchemaMapping { csv => + val csv2 = csv.delete(name) + MappingResult.Success(if (csv == csv2) List("Couldn't relocate columns") else Nil, csv2) + } } /** EXERCISE 8 @@ -367,7 +404,7 @@ object contact_processing { * company's official schema for contacts, by composing schema mappings * constructed from constructors and operators. */ - lazy val schemaMapping: SchemaMapping = ??? + lazy val schemaMapping: SchemaMapping = val UserUploadSchema: SchemaCSV = SchemaCSV(List("email", "fname", "lname", "country", "street", "postal")) @@ -423,7 +460,7 @@ object ui_events { * Add a method `+` that composes two listeners into a single listener, * by sending each game event to both listeners. */ - def +(that: Listener): Listener = ??? + def +(that: Listener): Listener = Listener(event => self.onEvent(event) + that.onEvent(event)) /** EXERCISE 2 * @@ -431,21 +468,29 @@ object ui_events { * by sending each game event to either the left listener, if it does not * throw an exception, or the right listener, if the left throws an exception. */ - def orElse(that: Listener): Listener = ??? + def orElse(that: Listener): Listener = try { + Listener(self.onEvent + that.onEvent) + } catch{ + case e: Exception => Listener(self.onEvent + that.onEvent) + } /** EXERCISE 3 * * Add a `runOn` operator that returns a Listener that will call this one's * `onEvent` callback on the specified `ExecutionContext`. */ - def runOn(ec: scala.concurrent.ExecutionContext): Listener = ??? + def runOn(ec: scala.concurrent.ExecutionContext): Listener = Listener(event => + ec.execute(() => self.onEvent(event))) /** EXERCISE 4 * * Add a `debug` unary operator that will call the `onEvent` callback, but * before it does, it will print out the game event to the console. */ - def debug: Listener = ??? + def debug: Listener = Listener(event => { + println(event) + onEvent(event) + }) } /** EXERCISE 5 @@ -454,7 +499,8 @@ object ui_events { * listeners in response to each game event, making the gfxUpdateListener * run on the `uiExecutionContext`, and debugging the input events. */ - lazy val solution = ??? + lazy val solution = gfxUpdateListener.runOn(uiExecutionContext).debug + + twinkleAnimationListener.debug + motionDetectionListener.debug lazy val twinkleAnimationListener: Listener = ??? lazy val motionDetectionListener: Listener = ??? @@ -628,4 +674,4 @@ object education { Checker.isTrue(10) ) ) -} +} \ No newline at end of file