diff --git a/build.sbt b/build.sbt index 4a0a148214a6957a66845564a1755f0750fef410..f1c25d9e866b097d55a7c69e8b133856aeef2342 100644 --- a/build.sbt +++ b/build.sbt @@ -1,30 +1,22 @@ name := "short-exercises" -organization := "de.uniwue.fp" +organization := "de.un.1iwue.fp" version := "0.1-SNAPSHOT" -scalaVersion := "2.13.1" +scalaVersion := "3.1.1" scalacOptions ++= Seq( "-deprecation", "-encoding", "UTF-8", "-feature", "-unchecked", - "-Xlint", - "-Wdead-code", - "-Wnumeric-widen", - "-Wvalue-discard", - "-Wunused:patvars,privates,locals,params,-imports", - "-Ypatmat-exhaust-depth", "40", "-language:higherKinds", "-language:implicitConversions", ) resolvers += Resolver.sonatypeRepo("releases") -addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.0" cross CrossVersion.full) - // Disallow some language construcs // your bonus exercises will have to compile with these options -addCompilerPlugin("org.wartremover" %% "wartremover" % "2.4.5" cross CrossVersion.full) +addCompilerPlugin("org.wartremover" %% "wartremover" % "3.0.2" cross CrossVersion.full) scalacOptions ++= Seq( "-P:wartremover:traverser:org.wartremover.warts.AsInstanceOf", "-P:wartremover:traverser:org.wartremover.warts.IsInstanceOf", @@ -36,5 +28,6 @@ scalacOptions ++= Seq( "-P:wartremover:traverser:org.wartremover.warts.While", ) -libraryDependencies += "org.scalactic" %% "scalactic" % "3.1.1" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.1.1" % "test" +libraryDependencies += "org.scalactic" %% "scalactic" % "3.2.11" +libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.11" % "test" +libraryDependencies += "org.typelevel" %% "cats-core" % "2.7.0" diff --git a/project/build.properties b/project/build.properties index 6624da70bf7d766a89dd9999acc2201572d7c225..c8fcab543a9cfc5c5c21bb0b6cc80414275625ef 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.3.5 +sbt.version=1.6.2 diff --git a/src/main/scala/algebra/Monoid.scala b/src/main/scala/algebra/Monoid.scala index 275b50cc93f6693d3418c0869bc965df84fca7ac..12656457e2a67602a5a4afe3e23a3599177e35c0 100644 --- a/src/main/scala/algebra/Monoid.scala +++ b/src/main/scala/algebra/Monoid.scala @@ -1,6 +1,5 @@ package algebra -trait Monoid[A] { +trait Monoid[A]: def op(a1: A, a2: A): A def zero: A -} diff --git a/src/main/scala/algebra/Monoids.scala b/src/main/scala/algebra/Monoids.scala index 8a75d1b1287268d7539fc48117d573963ee1d7ba..7639a43e10284b35e35ecce3fffce2197428c65b 100644 --- a/src/main/scala/algebra/Monoids.scala +++ b/src/main/scala/algebra/Monoids.scala @@ -1,6 +1,6 @@ package algebra -object Monoids { +object Monoids: def intAddition: Monoid[Int] = new Monoid[Int] { def zero = ??? def op(a: Int, b: Int): Int = ??? @@ -32,4 +32,3 @@ object Monoids { def bag[A](as: IndexedSeq[A]): Map[A, Int] = ??? -} diff --git a/src/main/scala/applicative/Applicative.scala b/src/main/scala/applicative/Applicative.scala index 31faa13d1144ba2c82c881a6efb88a4413622bec..52163f989825ef133e6929786686c2a0d55c0a2e 100644 --- a/src/main/scala/applicative/Applicative.scala +++ b/src/main/scala/applicative/Applicative.scala @@ -1,6 +1,6 @@ package applicative -trait Applicative[F[_]] { +trait Applicative[F[_]]: def pure[A](a: A): F[A] def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] = map2(ff, fa)((f, a) => f(a)) @@ -16,9 +16,9 @@ trait Applicative[F[_]] { // implement pure first // then write the signature for ap and map2 // then implement one of them - def compose[G[_]](implicit G: Applicative[G]): Applicative[Lambda[a => F[G[a]]]] = { + def compose[G[_]](implicit G: Applicative[G]): Applicative[[a] =>> F[G[a]]] = val F = this - new Applicative[Lambda[a => F[G[a]]]] { + new Applicative[[a] =>> F[G[a]]] { def pure[A](a: A): F[G[A]] = ??? //note: when you are done implementing this, the test for ap/map2 will result in a StackOverflow until you implement one of them @@ -28,5 +28,3 @@ trait Applicative[F[_]] { // or // override def map2... = ??? } - } -} diff --git a/src/main/scala/applicative/Monad.scala b/src/main/scala/applicative/Monad.scala index b14f6e8ee762d0d69d36765dc55f558c54ce7cb8..c1894a781185ccca7fc3f19b37500f56bde7dd8a 100644 --- a/src/main/scala/applicative/Monad.scala +++ b/src/main/scala/applicative/Monad.scala @@ -1,9 +1,8 @@ package applicative -trait Monad[F[_]] extends Applicative[F] { +trait Monad[F[_]] extends Applicative[F]: def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = flatten(map(fa)(f)) def flatten[A](fa: F[F[A]]): F[A] = flatMap(fa)(identity) override def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(f andThen pure) override def map2[A, B, C](fa: F[A], fb: F[B])(f: (A, B) => C): F[C] = flatMap(fa)(a => map(fb)(b => f(a,b))) -} diff --git a/src/main/scala/applicative/Validated.scala b/src/main/scala/applicative/Validated.scala index 3261f9a7a3a21fc3379913d8504f478bde079278..2f122b922945736da966a5b26627d03e199c0606 100644 --- a/src/main/scala/applicative/Validated.scala +++ b/src/main/scala/applicative/Validated.scala @@ -4,9 +4,7 @@ sealed trait Validated[+E, +A] case class Valid[+A](a:A) extends Validated[Nothing, A] case class Invalid[+E](head: E, tail: List[E] = List()) extends Validated[E, Nothing] -object Validated { - implicit def validatedApplicative[E]: Applicative[Validated[E, +?]] = new Applicative[Validated[E,+?]] { +object Validated: + given validatedApplicative[E]: Applicative[[a] =>> Validated[E, a]] with def pure[A](a: A) = Valid(a) // add map2 or ap here - } -} diff --git a/src/main/scala/datastructures/List.scala b/src/main/scala/datastructures/List.scala index 0076d6eb6c9514636788bf7a41a5c37f61a5a0ca..97e497d1f9c561aede0b831aaf9e13deb76e80d0 100644 --- a/src/main/scala/datastructures/List.scala +++ b/src/main/scala/datastructures/List.scala @@ -1,10 +1,9 @@ package datastructures -sealed trait List[+A] { - def head = this match { +sealed trait List[+A]: + def head = this match case Nil => sys.error("head of empty list") case Cons(a, _) => a - } def tail: List[A] = ??? @@ -13,12 +12,10 @@ sealed trait List[+A] { def setHead[AA >: A](head: AA): List[AA] = ??? def foldLeft[B](z: B)(f: (B, A) => B): B = ??? -} case object Nil extends List[Nothing] case class Cons[+A](elem: A, rest: List[A]) extends List[A] -object List { +object List: def apply[A](as: A*): List[A] = - if (as.isEmpty) Nil + if as.isEmpty then Nil else Cons(as.head, apply(as.tail: _*)) -} diff --git a/src/main/scala/datastructures/Parametricity.scala b/src/main/scala/datastructures/Parametricity.scala index 3ab04e536090ca5ef099fce1f91d22b55cdf2090..5e1a30e6f55ee7f0749ef2992cedbd93c5d6b08f 100644 --- a/src/main/scala/datastructures/Parametricity.scala +++ b/src/main/scala/datastructures/Parametricity.scala @@ -1,7 +1,6 @@ package datastructures -object Parametricity { +object Parametricity: // with our compiler settings from build.sbt, there are even less // possibilities to do something wrong here without the compiler complaining def para[A,B,C](a: A, b: B)(f: (A,B) => C): C = ??? -} diff --git a/src/main/scala/errors/Either.scala b/src/main/scala/errors/Either.scala index f5e53da0be7deaad32c754b54a043bb770948ea1..bec4e26e8ee04442845106e0cb9da0f6309349f6 100644 --- a/src/main/scala/errors/Either.scala +++ b/src/main/scala/errors/Either.scala @@ -1,18 +1,15 @@ package errors -sealed trait Either[+E, +A] { - def map[B](f: A => B): Either[E, B] = this match { +sealed trait Either[+E, +A]: + def map[B](f: A => B): Either[E, B] = this match case Left(e) => Left(e) case Right(a) => Right(f(a)) - } - def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = this match{ + def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = this match case Left(e) => Left(e) case Right(a) => f(a) - } def map2[EE >: E, B, C](other: Either[EE, B])(f: (A, B) => C): Either[EE, C] = ??? -} final case class Left[+E](value: E) extends Either[E, Nothing] final case class Right[+A](value: A) extends Either[Nothing, A] diff --git a/src/main/scala/errors/Option.scala b/src/main/scala/errors/Option.scala index 4ca2d00f8ca85781344a67fa4d53a3827c40a020..2c39eec502b372ae2be586f5a0d9596ef167267e 100644 --- a/src/main/scala/errors/Option.scala +++ b/src/main/scala/errors/Option.scala @@ -1,6 +1,6 @@ package errors -sealed trait Option[+A] { +sealed trait Option[+A]: def map[B](f: A => B): Option[B] = ??? def getOrElse[B >: A](default: => B): B = ??? @@ -8,11 +8,9 @@ sealed trait Option[+A] { def flatMap[B](f: A => Option[B]): Option[B] = ??? def filter[B](f: A => Boolean): Option[A] = ??? -} final case class Some[+A](get: A) extends Option[A] case object None extends Option[Nothing] -object Option { +object Option: def sequence[A](list: List[Option[A]]): Option[List[A]] = ??? -} diff --git a/src/main/scala/errors/Team.scala b/src/main/scala/errors/Team.scala index 5bc8f46dc8aa70db98d5c579263843941943ed18..44245fa89a6f96d8eb11636dce5752d3c5e1dcba 100644 --- a/src/main/scala/errors/Team.scala +++ b/src/main/scala/errors/Team.scala @@ -1,6 +1,6 @@ package errors -object Team { +object Team: val persons = List( Person("Dagobert", "Finanzabteilung"), Person("Donald", "Spassabteilung"), @@ -13,7 +13,6 @@ object Team { persons.find(_.name == name).fold(None: Option[Person])(a => Some(a)) def getTeam(name1: String, name2: String): Option[Team] = ??? -} final case class Person( name: String, diff --git a/src/main/scala/laziness/LazyList.scala b/src/main/scala/laziness/LazyList.scala index a9fcc7b8847c8ba82494b923a55843da7e6af837..7eb4d3aa923158e52c8bfd1b8dcb467d4852a700 100644 --- a/src/main/scala/laziness/LazyList.scala +++ b/src/main/scala/laziness/LazyList.scala @@ -1,6 +1,6 @@ package laziness -sealed trait LazyList[+A] { +sealed trait LazyList[+A]: // uncomment to be able to use cons(h, t) and empty directly // imports methods from the companion object //import LazyList._ @@ -16,31 +16,26 @@ sealed trait LazyList[+A] { // Methods shown during lecture - def headOption: Option[A] = this match { + def headOption: Option[A] = this match case Cons(h, _) => Some(h()) case Empty => None - } - def exists(p: A => Boolean): Boolean = this match { + def exists(p: A => Boolean): Boolean = this match case Cons(x, xs) => p(x()) || xs().exists(p) case Empty => false - } - def foldRight[B](z: => B)(f: (A, => B) => B): B = this match { + def foldRight[B](z: => B)(f: (A, => B) => B): B = this match case Cons(x, xs) => f(x(), xs().foldRight(z)(f)) case Empty => z - } -} case object Empty extends LazyList[Nothing] case class Cons[+A](h: () => A, t: () => LazyList[A]) extends LazyList[A] -object LazyList { // companion object +object LazyList: // companion object - def fibs: LazyList[Int] = { + def fibs: LazyList[Int] = // tip: write a recursive def here and call it with some start values ??? - } def unfold[A, S](z: S)(f: S => Option[(A, S)]): LazyList[A] = ??? @@ -49,18 +44,16 @@ object LazyList { // companion object // Methods shown during Lecture - def cons[A](h: => A, t: => LazyList[A]): LazyList[A] = { + def cons[A](h: => A, t: => LazyList[A]): LazyList[A] = lazy val head = h lazy val tail = t Cons(() => head, () => tail) - } def empty[A]: LazyList[A] = Empty def apply[A](as: A*): LazyList[A] = - if (as.isEmpty) empty + if as.isEmpty then empty else cons(as.head, apply(as.tail: _*)) val ones: LazyList[Int] = cons(1, ones) -} diff --git a/src/main/scala/monads/MonadFunctor.scala b/src/main/scala/monads/MonadFunctor.scala index 9c3f78cff737f42161656844419b373ddf01fe2e..789d5737e3613446f423af4e5e81ce33edcd901d 100644 --- a/src/main/scala/monads/MonadFunctor.scala +++ b/src/main/scala/monads/MonadFunctor.scala @@ -1,6 +1,6 @@ package monads -trait Monad[F[_]] { +trait Monad[F[_]]: def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] @@ -9,14 +9,11 @@ trait Monad[F[_]] { def sequence[A](fas: List[F[A]]): F[List[A]] = ??? def compose[A, B, C](f: A => F[B])(g: B => F[C]): A => F[C] = ??? -} -trait Functor[F[_]] { +trait Functor[F[_]]: def map[A, B](a: F[A])(f: A => B): F[B] -} -object Functor { +object Functor: def functorFromMonad[F[_]](M: Monad[F]): Functor[F] = new Functor[F] { def map[A, B](a: F[A])(f: A => B): F[B] = ??? } -} diff --git a/src/main/scala/monads/MonadId.scala b/src/main/scala/monads/MonadId.scala index f0c2e5e898e009a73544fcde29ea8595ea75ff1f..2c87fba791a938da05179b64b29c23247a824773 100644 --- a/src/main/scala/monads/MonadId.scala +++ b/src/main/scala/monads/MonadId.scala @@ -1,10 +1,8 @@ package monads final case class Id[A](value: A) -object Id { +object Id: // No tests. If it compiles, it's correct. - implicit val idMonad = new Monad[Id] { + given Monad[Id] with def pure[A](a: A): Id[A] = ??? def flatMap[A, B](fa: Id[A])(f: A => Id[B]): Id[B] = ??? - } -} diff --git a/src/main/scala/parsers/Combinators.scala b/src/main/scala/parsers/Combinators.scala index 5dbd8a6e1963dca30c002a6bb3df56a3673b05e4..3a25fd4f4e6ebafe1b383d142ef26e9858e29de8 100644 --- a/src/main/scala/parsers/Combinators.scala +++ b/src/main/scala/parsers/Combinators.scala @@ -5,10 +5,9 @@ import parsers.lib.Parsers._ import parsers.lib.ToParserOps._ import parsers.lib._ -object Combinators { +object Combinators: /* Implement many1, which matches at least one element, returning a NonEmptyList. * If no element is found, the parser should fail. You may use any other parsers and combinators we have defined. */ def many1[A](p: Parser[A]): Parser[NonEmptyList[A]] = ??? -} diff --git a/src/main/scala/parsers/ContactParser.scala b/src/main/scala/parsers/ContactParser.scala index 67d4e934c5f12e9617257bafd8d06ce6090be2d5..8b41f66fdd221409815d2d897f27cbe0027e7fe5 100644 --- a/src/main/scala/parsers/ContactParser.scala +++ b/src/main/scala/parsers/ContactParser.scala @@ -9,7 +9,7 @@ case class Contact(name:String, address: Address, phone: Option[Phone]) case class Address(street: String, number: Int, postCode: Int, city: String) case class Phone(prefix: String, suffix:String) -object ContactParser { +object ContactParser: /* This parser should parse strings of the form * <street> <number>, <postCode> <city> @@ -17,4 +17,3 @@ object ContactParser { * Each whitespace in the pattern above should be present (one or more whitespace characters). */ def address: Parser[Address] = ??? -} diff --git a/src/main/scala/parsers/lib/Parser.scala b/src/main/scala/parsers/lib/Parser.scala index aa84db04bd4f8fb0b6776b42a03d73b10b02fa73..b10e80a83fd3096c82710cc40148b4e6ada99b33 100644 --- a/src/main/scala/parsers/lib/Parser.scala +++ b/src/main/scala/parsers/lib/Parser.scala @@ -4,22 +4,19 @@ package parsers.lib * We only have a single function, representing the parsing of a string. So we can declare parsers by writing a * function literal for such a function */ -trait Parser[+A] { +trait Parser[+A]: def parse(input: String): ParseResult[A] -} -object Parser { +object Parser: implicit val parserMonad: Monad[Parser] = new Monad[Parser] { def pure[A](a: A): Parser[A] = success(a) def flatMap[A, B](fa: Parser[A])(f: A => Parser[B]): Parser[B] = - input => fa.parse(input) match { + input => fa.parse(input) match case Done(rest, a) => f(a).parse(rest) case Fail(rest, msg) => Fail(rest, msg) - } } def success[A](a: A): Parser[A] = input => Done(input, a) def fail[A](message: String): Parser[A] = input => Fail(input, message) -} diff --git a/src/main/scala/parsers/lib/ParserOps.scala b/src/main/scala/parsers/lib/ParserOps.scala index f019baea63b6f64feba37299bc7654135d73c0d2..cd0c88f3861d11edb06d819479c30ab5e95c3163 100644 --- a/src/main/scala/parsers/lib/ParserOps.scala +++ b/src/main/scala/parsers/lib/ParserOps.scala @@ -1,16 +1,14 @@ package parsers.lib -trait ParserOps[A] { +trait ParserOps[A]: val self: Parser[A] def | [B>:A](pb: Parser[B]): Parser[B] = Parsers.or(self, pb) def ~[B](next: => Parser[B]): Parser[(A, B)] = Parsers.andThen(self, next) def many: Parser[List[A]] = Parsers.many(self) -} -object ToParserOps { +object ToParserOps: implicit def toParserOps[A](p: Parser[A]): ParserOps[A] = new ParserOps[A] { val self: Parser[A] = p } -} diff --git a/src/main/scala/parsers/lib/Parsers.scala b/src/main/scala/parsers/lib/Parsers.scala index ff23f454bb388a92af1d9bdc5118016439226f25..1a75dd80662e62cc039f5f23d936321f2e837b66 100644 --- a/src/main/scala/parsers/lib/Parsers.scala +++ b/src/main/scala/parsers/lib/Parsers.scala @@ -4,29 +4,27 @@ import Monad._ import scala.util.matching.Regex import ToParserOps._ -trait Parsers { +trait Parsers: def char(c: Char): Parser[Char] def string(s: String): Parser[String] def regex(r: Regex): Parser[String] def digits: Parser[String] def int: Parser[Int] -} -object Parsers extends Parsers { +object Parsers extends Parsers: def string(s: String): Parser[String] = input => - if(input.startsWith(s)) Done(input.substring(s.length), s) + if input.startsWith(s) then Done(input.substring(s.length), s) else Fail(input, s"""expected "$s"""") def char(c: Char): Parser[Char] = string(c.toString).map(_.charAt(0)) def regex(r: Regex): Parser[String] = input => - r.findPrefixOf(input) match { + r.findPrefixOf(input) match case Some(m) => Done(input.substring(m.length), m) case None => Fail(input, s""""expected match for "$r"""") - } def digits: Parser[String] = regex("[0-9]+".r) @@ -34,21 +32,20 @@ object Parsers extends Parsers { def or[A](pa: Parser[A], pb: Parser[A]): Parser[A] = input => - pa.parse(input) match { + pa.parse(input) match case Done(rest, out) => Done(rest, out) case Fail(_, _) => pb.parse(input) - } - def andThen[A,B](p: Parser[A], next: => Parser[B]): Parser[(A, B)] = for { + def andThen[A,B](p: Parser[A], next: => Parser[B]): Parser[(A, B)] = for a <- p b <- next - } yield (a,b) + yield (a,b) def many[A](p: Parser[A]): Parser[List[A]] = ( - for { + for a <- p tail <- many(p) - } yield a :: tail + yield a :: tail ) | Monad[Parser].pure(Nil) def manyN[A](p: Parser[A], n: Int): Parser[List[A]] = @@ -60,6 +57,5 @@ object Parsers extends Parsers { /** Match any number of non-whitespace characters */ val word: Parser[String] = regex(raw"\S*".r) -} // address.parse("Hublandstraße 123, 97074 Würzburg") == // Done("", Address("Hublandstraße", 123, 97074, "Würzburg")) diff --git a/src/main/scala/parsers/lib/Typeclasses.scala b/src/main/scala/parsers/lib/Typeclasses.scala index 35e137f9d4289f764c0dbf9b98280f623e001537..e6e6927d9ee5a8cc7c149138ae0c6039bee9c8f0 100644 --- a/src/main/scala/parsers/lib/Typeclasses.scala +++ b/src/main/scala/parsers/lib/Typeclasses.scala @@ -1,51 +1,43 @@ package parsers.lib -object Id { +object Id: type Id[A] = A implicit val idMonad: Monad[Id] = new Monad[Id] { override def pure[A](a:A): Id[A] = a override def flatMap[A,B](a:Id[A])(f: A => Id[B]): Id[B] = f(a) } -} -trait Functor[F[_]] { +trait Functor[F[_]]: def map[A, B](fa: F[A])(f: A => B): F[B] -} -object Functor { +object Functor: def apply[A[_]](implicit F: Functor[A]): Functor[A] = F -} -trait Applicative[F[_]] extends Functor[F] { +trait Applicative[F[_]] extends Functor[F]: def pure[A](a: A): F[A] def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] = map2(ff, fa)((f, a) => f(a)) def map[A, B](fa: F[A])(f: A => B): F[B] = ap(pure(f))(fa) def map2[A, B, C](fa: F[A], fb: F[B])(f: (A, B) => C): F[C] = ap(map(fa)(a => (b:B) => f(a,b)))(fb) -} -object Applicative { +object Applicative: def apply[A[_]](implicit F: Applicative[A]): Applicative[A] = F -} -trait Monad[F[_]] extends Applicative[F] { +trait Monad[F[_]] extends Applicative[F]: def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def flatten[A](fa: F[F[A]]): F[A] = flatMap(fa)(identity) override def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(f andThen pure) override def map2[A, B, C](fa: F[A], fb: F[B])(f: (A, B) => C): F[C] = flatMap(fa)(a => map(fb)(b => f(a,b))) -} -object Monad { +object Monad: def apply[A[_]](implicit M: Monad[A]): Monad[A] = M - implicit class MonadOps[F[_] : Monad, A](fa: F[A]) { + implicit class MonadOps[F[_] : Monad, A](fa: F[A]): def flatMap[B](f: A => F[B]): F[B] = implicitly[Monad[F]].flatMap(fa)(f) def map[B](f: A => B): F[B] = implicitly[Monad[F]].map(fa)(f) - } -} -trait Traverse[F[_]] extends Functor[F] { +trait Traverse[F[_]] extends Functor[F]: def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]] = sequence(map(fa)(f)) @@ -54,14 +46,12 @@ trait Traverse[F[_]] extends Functor[F] { import Id._ def map[A,B](fa: F[A])(f: A => B): F[B] = traverse[Id,A,B](fa)(a => f(a)) -} -object Traverse { +object Traverse: def apply[A[_]](implicit M: Traverse[A]): Traverse[A] = M implicit val listTraverse: Traverse[List] = new Traverse[List] { override def traverse[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[List[B]] = fa.foldRight(G.pure(List[B]()))((a, b) => G.map2(f(a), b)(_ :: _)) } -} diff --git a/src/main/scala/readerwriter/Randoms.scala b/src/main/scala/readerwriter/Randoms.scala index 04c7a0ddb3229e76fb8febd6ed2a95b188dbd7f4..a5cc3ea19aa9e84ea2a290e6c2bef32fa192d578 100644 --- a/src/main/scala/readerwriter/Randoms.scala +++ b/src/main/scala/readerwriter/Randoms.scala @@ -3,32 +3,28 @@ package readerwriter import readerwriter.internal.State, State._ import util._ -object Randoms { +object Randoms: def threeInts: State[RNG, (Int, Int, Int)] = ??? /*@formatter:off*/ - def randomInt: State[RNG, Int] = for { + def randomInt: State[RNG, Int] = for rng <- get[RNG] (rng2, i) = rng.nextInt _ <- set(rng2) - } yield i + yield i /*@formatter:on*/ - def nonNegativeInt: State[RNG, Int] = for { + def nonNegativeInt: State[RNG, Int] = for i <- randomInt - } yield if (i < 0) -(i + 1) else i -} + yield if i < 0 then -(i + 1) else i -trait RNG { +trait RNG: def nextInt: (RNG, Int) -} -case class Simple(seed: Long) extends RNG { - def nextInt: (RNG, Int) = { +case class Simple(seed: Long) extends RNG: + def nextInt: (RNG, Int) = val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL val nextRNG = Simple(newSeed) val n = (newSeed >>> 16).toInt (nextRNG, n) - } -} diff --git a/src/main/scala/readerwriter/Readers.scala b/src/main/scala/readerwriter/Readers.scala index fb66563ec983eca3498bc311058ec078ec8c0117..18b7dc9cd8a99b8054b5305a92c8404d228f4ac6 100644 --- a/src/main/scala/readerwriter/Readers.scala +++ b/src/main/scala/readerwriter/Readers.scala @@ -13,7 +13,7 @@ final case class Request( now: LocalDate, ) -object Readers { +object Readers: /* everything required to use ask and the monad operations * is already imported */ @@ -22,4 +22,3 @@ object Readers { def formatTime: Reader[Request, String] = ??? def sayBye: Reader[Request, String] = ??? -} diff --git a/src/main/scala/readerwriter/Writers.scala b/src/main/scala/readerwriter/Writers.scala index cd90b40ae8778c2ae36962a9d1ed2b32678b61b3..3abbc1a184a37114d8c3ab91cfe1df74307cfa02 100644 --- a/src/main/scala/readerwriter/Writers.scala +++ b/src/main/scala/readerwriter/Writers.scala @@ -5,7 +5,7 @@ import readerwriter.internal.Writer, Writer._ import util._ -object Writers { +object Writers: /* everything required to use tell and the monad operations * is already imported */ @@ -13,27 +13,23 @@ object Writers { def collatzSearch(start: Int, limit: Int): Writer[List[String], Int] = ??? -} -object CollatzWithoutWriter { +object CollatzWithoutWriter: def collatzDepth(n: Int): (List[String], Int) = - if (n == 1) + if n == 1 then (List("got 1, doing nothing"), 0) - else if (n % 2 == 0) { + else if n % 2 == 0 then val (way, depth) = collatzDepth(n / 2) (s"got $n, halving" :: way, depth + 1) - } else { + else val (way, depth) = collatzDepth(n * 3 + 1) (s"got $n, tripling plus one" :: way, depth + 1) - } - def collatzSearch(start: Int, limit: Int): (List[String], Int) = { + def collatzSearch(start: Int, limit: Int): (List[String], Int) = val (way, depth) = collatzDepth(start) - if (depth < limit) { + if depth < limit then val (way2, number) = collatzSearch(start + 1, limit) (s"testing $start" :: way ++ (s"depth was $depth" :: way2), number) - } else + else (s"testing $start" :: way ++ List(s"depth was $depth", s"returning $start"), start) - } -} diff --git a/src/main/scala/readerwriter/internal/Reader.scala b/src/main/scala/readerwriter/internal/Reader.scala index c5550564a45fa83bd0c260a9994b41bdcda9b694..d345625819d059d4836dfab276e70e687c006c44 100644 --- a/src/main/scala/readerwriter/internal/Reader.scala +++ b/src/main/scala/readerwriter/internal/Reader.scala @@ -4,10 +4,10 @@ import applicative.Monad case class Reader[R, A](run: R => A) -object Reader { +object Reader: def ask[R]: Reader[R, R] = Reader(x => x) - implicit def readerM[R]: Monad[Reader[R, ?]] = new Monad[Reader[R, ?]] { + given readerM[R]: Monad[[a] =>> Reader[R, a]] with override def flatMap[A, B](fa: Reader[R, A])(f: A => Reader[R, B]): Reader[R, B] = Reader(in => { val a = fa.run(in) f(a).run(in) @@ -19,5 +19,3 @@ object Reader { f(a, b) }) override def map[A, B](fa: Reader[R, A])(f: A => B): Reader[R, B] = Reader(in => f(fa.run(in))) - } -} diff --git a/src/main/scala/readerwriter/internal/State.scala b/src/main/scala/readerwriter/internal/State.scala index 0d877514f90f7469f9d508efc9e70a843cc0829f..8a8ac62a85849b2e580f408b65e15d3e44c29c1a 100644 --- a/src/main/scala/readerwriter/internal/State.scala +++ b/src/main/scala/readerwriter/internal/State.scala @@ -7,15 +7,13 @@ import applicative.Monad case class State[S, A] private (run: S => (S, A)) -case object State { - implicit def stateMonad[S]: Monad[State[S, ?]] = new Monad[State[S, ?]] { +case object State: + given stateMonad[S]: Monad[[a] =>> State[S, a]] with override def pure[A](a: A): State[S, A] = State(s => (s, a)) override def flatMap[A, B](fa: State[S, A])(f: A => State[S, B]): State[S, B] = State(s => { val (s1, a) = fa.run(s) f(a).run(s1) }) - } def get[S]: State[S, S] = State(s => (s, s)) def set[S](s: S): State[S, Unit] = State(_ => (s, ())) -} diff --git a/src/main/scala/readerwriter/internal/Writer.scala b/src/main/scala/readerwriter/internal/Writer.scala index 0cd1ab02fbc6dbebb167a09ac3e19f916d210c49..0f824bde95e183bc6f799ac346c2923d892fa618 100644 --- a/src/main/scala/readerwriter/internal/Writer.scala +++ b/src/main/scala/readerwriter/internal/Writer.scala @@ -5,7 +5,7 @@ import applicative.Monad final case class Writer[L, A](v: (L, A)) -object Writer { +object Writer: implicit def listMonoid[A]: Monoid[List[A]] = new Monoid[List[A]] { def zero: List[Nothing] = List.empty def op(l1: List[A], l2: List[A]): List[A] = l1 ++ l2 @@ -13,15 +13,12 @@ object Writer { def tell[L](l: L): Writer[L, Unit] = Writer((l, ())) - implicit def writerM[L: Monoid]: Monad[Writer[L, ?]] = new Monad[Writer[L, ?]] { - override def flatMap[A, B](fa: Writer[L, A])(f: A => Writer[L, B]): Writer[L, B] = { + given writerMonad[L: Monoid]: Monad[[a] =>> Writer[L, a]] with + override def flatMap[A, B](fa: Writer[L, A])(f: A => Writer[L, B]): Writer[L, B] = val next = f(fa.v._2) Writer((implicitly[Monoid[L]].op(fa.v._1, next.v._1), next.v._2)) - } def pure[A](a: A): Writer[L, A] = Writer((implicitly[Monoid[L]].zero, a)) override def map2[A, B, C](fa: Writer[L, A], fb: Writer[L, B])(f: (A, B) => C): Writer[L, C] = Writer((implicitly[Monoid[L]].op(fa.v._1, fb.v._1), f(fa.v._2, fb.v._2))) override def map[A, B](fa: Writer[L, A])(f: A => B): Writer[L, B] = Writer((fa.v._1, f(fa.v._2))) - } -} diff --git a/src/main/scala/traverse/Fuse.scala b/src/main/scala/traverse/Fuse.scala index ca36a88061345e75ad7bd7197513579c24b9645a..962a965624bd8197a0115429b5fdabb108f86907 100644 --- a/src/main/scala/traverse/Fuse.scala +++ b/src/main/scala/traverse/Fuse.scala @@ -1,6 +1,6 @@ -object Fuse { +object Fuse: /* reduced to relevant parts */ - trait Traverse[F[_]] { + trait Traverse[F[_]]: def traverse[G[_]:Applicative,A,B](fa: F[A])(f: A => G[B]): G[F[B]] = sequence(map(fa)(f)) def sequence[G[_]:Applicative,A](fma: F[G[A]]): G[F[A]] = @@ -14,11 +14,9 @@ object Fuse { def fuse[G[_],H[_],A,B](fa: F[A])(f: A => G[B], g: A => H[B]) (implicit G: Applicative[G],H: Applicative[H]) : (G[F[B]], H[F[B]]) = ??? - } - /* reduced to relevant parts */ - trait Applicative[F[_]] { + trait Applicative[F[_]]: def map2[A,B,C](fa: F[A], fb: F[B])(f: (A, B) => C): F[C] = apply(map(fa)(f.curried))(fb) @@ -30,13 +28,10 @@ object Fuse { def map[A,B](fa: F[A])(f: A => B): F[B] = apply(pure(f))(fa) - def product[G[_]](G: Applicative[G]): Applicative[Lambda[x => (F[x], G[x])]] = { + def product[G[_]](G: Applicative[G]): Applicative[[x] =>> (F[x], G[x])] = val self = this - new Applicative[Lambda[x => (F[x], G[x])]]{ + new Applicative { def pure[A](a: => A) = (self.pure(a), G.pure(a)) override def apply[A,B](fs: (F[A => B], G[A => B]))(p: (F[A], G[A])) = (self.apply(fs._1)(p._1), G.apply(fs._2)(p._2)) } - } - } -} diff --git a/src/main/scala/traverse/SequenceMap.scala b/src/main/scala/traverse/SequenceMap.scala index 4469078544f72956b2ee8a664909772d06b9d4d6..7bde9193ff789a2d60e1c3998acb9a0c2469b146 100644 --- a/src/main/scala/traverse/SequenceMap.scala +++ b/src/main/scala/traverse/SequenceMap.scala @@ -1,5 +1,5 @@ -object SequenceMap { - trait Applicative[F[_]] { +object SequenceMap: + trait Applicative[F[_]]: // Exercise: implement sequence for maps instead of lists def sequenceMap[K,V](m: Map[K, F[V]]): F[Map[K,V]] = ??? @@ -24,5 +24,3 @@ object SequenceMap { def traverse[A,B](fas: List[A])(f: A => F[B]): F[List[B]] = fas.foldRight[F[List[B]]](pure(Nil))((a, b) => map2(f(a), b)(_::_)) - } -} diff --git a/src/main/scala/typeclasses/FunctorOption.scala b/src/main/scala/typeclasses/FunctorOption.scala index 131cf431878772b7d0f69d23bef7243e30391208..32c58ceba38408dd62fa090ae8a4e286be51c3b5 100644 --- a/src/main/scala/typeclasses/FunctorOption.scala +++ b/src/main/scala/typeclasses/FunctorOption.scala @@ -1,13 +1,11 @@ package typeclasses -object FunctorOption { - trait Functor[F[_]] { +object FunctorOption: + trait Functor[F[_]]: def map[A, B](fa: F[A])(f: A => B): F[B] - } //try to write the signature yourself instead of using the IDEs auto-implement //uncomment and implement: //implicit val optionFunctor: Functor[Option] = new Functor[Option] { //} -} diff --git a/src/main/scala/util/package.scala b/src/main/scala/util/package.scala index d002876f0539bc641ba0e5d9288dc39fbb2de812..b3402eacf8c76a175214803d7825f6a50bde07b2 100644 --- a/src/main/scala/util/package.scala +++ b/src/main/scala/util/package.scala @@ -1,8 +1,6 @@ import applicative.Monad -package object util { - implicit class MonadOps[F[_] : Monad, A](fa: F[A]) { +package object util: + implicit class MonadOps[F[_] : Monad, A](fa: F[A]): def flatMap[B](f: A => F[B]): F[B] = implicitly[Monad[F]].flatMap(fa)(f) def map[B](f: A => B): F[B] = implicitly[Monad[F]].map(fa)(f) - } -}