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)
-  }
-}