Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • intro-to-fp/short-exercises
  • s472501/short-exercises
  • s410344/short-exercises
3 results
Show changes
Commits on Source (50)
Showing
with 307 additions and 34 deletions
.idea/
target/
# short-exercises
Source templates for short exercises during the lecture
\ No newline at end of file
Source templates for short exercises during the lecture, sometimes with tests.
To mark your progress during the lecture, please vote on
[sabix.eu:31337](http://sabix.eu:31337/)
## Overview:
Templates are within `src/main/scala`. Parts that you should complete are
usually marked with `???`. Lectures not listed here are not migrated to Scala
3 yet, please run `git pull` before the lecture.
| lecture | package | tests
|---------------------------------|-------------------------------------------------------------------|----------------------------
| 2: Functional Data Structures | [`datastructures`](src/main/scala/datastructures) | `testOnly datastructures.*`
| 3: Error Handling | [`errors`](src/main/scala/errors) | `testOnly errors.*`
| 4: Laziness | [`laziness`](src/main/scala/laziness/) | `testOnly laziness.*`
| 5: Algebras, Laws and monoids | [`algebra`](src/main/scala/algebra/) | `testOnly algebra.*`
| 6: Foldables and Functors | [`foldfunc`](src/main/scala/foldfunc/) | `testOnly foldfunc.*`
| 7: Monads | [`monads`](src/main/scala/monads/) | `testOnly monads.*`
| 8: Applicative Functors | [`applicative`](src/main/scala/applicative/) | `testOnly applicative.*`
| 9: An algebraic View on Monads | [`readerwriter`](src/main/scala/readerwriter/) | `testOnly readerwriter.*`
## Usage tips:
To keep your local solutions to the exercises when pulling from the repository,
use
```shell
git pull --rebase --autostash
```
This will keep both commited and uncommited changes.
If you are using Intellij IDEA, "rebase" is available as option in the update dialog and stashing is default.
### Tests:
If you are using `sbt` directly, you can use `testOnly` to run a specific test suite (see table).
To automatically run tests, whenever a file changes, use `~testOnly` (`~` also works with other sbt commands).
name := "short-exercises"
organization := "de.uniwue.fp"
version := "0.1-SNAPSHOT"
scalaVersion := "2.12.8"
scalaVersion := "3.2.2"
scalacOptions ++= Seq(
"-deprecation",
"-encoding", "UTF-8",
"-feature",
"-unchecked",
"-Xfatal-warnings",
"-Xlint",
"-Yno-adapted-args",
"-Ywarn-dead-code",
"-Ywarn-numeric-widen",
"-Ywarn-value-discard",
"-Ypartial-unification",
"-Xfuture",
"-Ywarn-unused-import",
"-Ywarn-unused:implicits",
"-Ywarn-unused:locals",
"-Ywarn-unused:params",
"-Ywarn-unused:patvars",
"-Ywarn-unused:privates",
"-Ypatmat-exhaust-depth", "40"
"-language:higherKinds",
"-Ykind-projector:underscores",
)
resolvers += Resolver.sonatypeRepo("releases")
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.3")
// Disallow some language construcs
// your bonus exercises will have to compile with these options
addCompilerPlugin("org.wartremover" %% "wartremover" % "2.4.1")
addCompilerPlugin("org.wartremover" %% "wartremover" % "3.1.1" cross CrossVersion.full)
scalacOptions ++= Seq(
"-P:wartremover:traverser:org.wartremover.warts.AsInstanceOf",
"-P:wartremover:traverser:org.wartremover.warts.IsInstanceOf",
......@@ -43,5 +28,6 @@ scalacOptions ++= Seq(
"-P:wartremover:traverser:org.wartremover.warts.While",
)
libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.5"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % "test"
libraryDependencies += "org.scalactic" %% "scalactic" % "3.2.11"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.11" % "test"
libraryDependencies += "org.typelevel" %% "cats-core" % "2.7.0"
sbt.version=1.6.2
package algebra
trait Monoid[A]:
def combine(a1: A, a2: A): A
def zero: A
extension (a1: A)
def |+| (a2: A) = combine(a1, a2)
package algebra
def intAddition: Monoid[Int] = new Monoid:
def zero = ???
def combine(a: Int, b: Int): Int = ???
def intMultiplication: Monoid[Int] = new Monoid:
def zero = ???
def combine(a: Int, b: Int): Int = ???
def booleanOr: Monoid[Boolean] = new Monoid:
def zero = ???
def combine(a: Boolean, b: Boolean): Boolean = ???
def booleanAnd: Monoid[Boolean] = new Monoid:
def zero = ???
def combine(a: Boolean, b: Boolean): Boolean = ???
def optionMonoid[A]: Monoid[Option[A]] = ???
def endoMonoid[A]: Monoid[A => A] = ???
def bag[A](as: List[A]): Map[A, Int] = ???
/* merges maps, if their value type has a monoid. See lecture */
given mapMergeMonoid[K,V](using MV: Monoid[V]): Monoid[Map[K, V]] =
new Monoid[Map[K, V]]:
def zero = Map[K,V]()
def combine(a: Map[K, V], b: Map[K, V]) =
(a.keySet ++ b.keySet).foldLeft(zero) ( (acc,k) =>
acc + (
k -> (a.getOrElse(k, MV.zero) |+| b.getOrElse(k, MV.zero))
)
)
/* Uses a monoid to combine all elements of a list into one. See lecture */
def combineAll[A](as: List[A])(using m: Monoid[A]): A =
as.foldLeft(m.zero)(m.combine)
package applicative
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] = ff.map2(fa)((f, a) => f(a))
extension [A](fa: F[A])
// implement map using pure and ap, but not flatMap/flatten
def map[B](f: A => B): F[B] = ???
// implement map2 using pure and ap
// hint: f.curried converts `(A,B) => C` to `A => (B => C)`
def map2[B,C](fb: F[B])(f: (A, B) => C): F[C] = ???
object Applicative:
// implement pure first
// then implement ap and map2
def compose[F[_], G[_]](
using F: Applicative[F], G: Applicative[G]
): Applicative[[a] =>> F[G[a]]] =
new Applicative[[a] =>> F[G[a]]]:
def pure[A](a: A) = ???
// implement one of these:
//override def ap[A,B](fgf: F[G[A => B]])(fga: F[G[A]]): F[G[B]] = ???
//extension [A](fga: F[G[A]])
// override def map2[B,C](fgb: F[G[B]])(f: (A, B) => C): F[G[C]] = ???
package applicative
trait Functor[F[_]]:
extension [A](fa: F[A])
def map[B](f: A => B): F[B]
package applicative
trait Monad[F[_]] extends Applicative[F]:
extension [A](fa: F[A])
def flatMap[B](f: A => F[B]): F[B]
override def map[B](f: A => B): F[B] = fa.flatMap((a:A) => pure(f(a)))
override def map2[B, C](fb: F[B])(f: (A, B) => C): F[C] =
fa.flatMap(a => fb.flatMap(b => pure(f(a, b))))
def flatten[A](ffa: F[F[A]]): F[A] = flatMap(ffa)(fa => fa)
def map3[A, B, C, D](fa: F[A], fb: F[B], fc: F[C])(f: (A, B, C) => D): F[D] =
fa.flatMap(a => fb.flatMap(b => fc.flatMap(c => pure(f(a, b, c)))))
def sequence[A](fas: List[F[A]]): F[List[A]] =
fas.foldRight[F[List[A]]](pure(Nil))((a, b) => a.map2(b)(_::_))
def compose[A, B, C](f: A => F[B])(g: B => F[C]): A => F[C] =
a => flatMap(f(a))(g)
package applicative
enum Validated[+E, +A]:
case Valid(a:A)
case Invalid(head: E, tail: List[E])
import Validated.*
object Validated:
given validatedApplicative[E]: Applicative[[a] =>> Validated[E, a]] with
def pure[A](a: A) = Valid(a)
// add map2 or ap here
//extension [A](fa: Validated[E,A])
// override def map2[B,C](fb: Validated[E,B])(f: (A,B) => C) = ???
//override def ap[A,B](ff: Validated[E,A => B])(fa: Validated[E,A]) =
package datastructures
sealed trait List[+A] {
def head = this match {
enum List[+A]:
case Nil
case Cons(_head: A, _tail: List[A])
def head: A = this match
case Nil => sys.error("head of empty list")
case Cons(a, _) => a
}
/** removes the first element of a list and returns the rest */
def tail: List[A] = ???
/** returns all but the last element of a list */
def init: List[A] = ???
/** replaces the first element of a list */
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]
/** recurses through the list, combining elements with the given function
* Uncomment the annotation to enable checking for tail-recursiveness */
//@annotation.tailrec
final def foldLeft[B](z: B)(f: (B, A) => B): B = ???
object List {
object List:
/** construct a list by passing elements
*
* Remember: `apply` makes the object behave like a function,
* you can call it as `List(elem1, elem2,...)`
**/
def apply[A](as: A*): List[A] =
if (as.isEmpty) Nil
if as.isEmpty then Nil
else Cons(as.head, apply(as.tail: _*))
}
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 = ???
}
package errors
enum Either[+E, +A]:
case Left(value: E)
case Right(value: 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
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] = ???
package errors
enum Option[+A]:
case Some(get: A)
case None
def map[B](f: A => B): Option[B] = ???
def getOrElse[B >: A](default: => B): B = ???
def flatMap[B](f: A => Option[B]): Option[B] = ???
def filter[B](f: A => Boolean): Option[A] = ???
object Option:
def sequence[A](list: List[Option[A]]): Option[List[A]] = ???
package errors
// This allows us to write Some and None without prefix, as we would inside the Option enum
import Option.{None, Some}
val persons = List(
Person("Dagobert", "Finanzabteilung"),
Person("Donald", "Spassabteilung"),
Person("Daniel", "R&D"),
)
type Team = (Person, Person)
def lookup(name: String): Option[Person] =
persons.find(_.name == name).fold(None)(a => Some(a))
def getTeam(name1: String, name2: String): Option[Team] = ???
final case class Person(
name: String,
department: String,
)
package foldfunc
import algebra.Monoid
object Foldable:
/* Implement this using a single foldLeft (no map and no combineAll) */
def foldMap[A,B](as: List[A])(f: A => B)(using m: Monoid[B]): B = ???
// uncomment and implement the Foldable instance for List
//given Foldable[List] with
/* Implement a generic toList method for any foldable */
//def toList
trait Foldable[F[_]]:
extension [A](as: F[A])
def foldRight[B](z: B)(f: (A,B) => B): B
def foldLeft[B](z: B)(f: (B,A) => B): B
def foldMap[B](f: A => B)(using mb: Monoid[B]): B =
foldLeft(mb.zero)((b, a) => mb.combine(b, f(a)))
def combineAll(m: Monoid[A]): A =
foldLeft(m.zero)(m.combine)
package foldfunc
trait Functor[F[_]]:
extension [A](fa: F[A])
def map[B](f: A => B): F[B]
//try to write the signature yourself instead of using the IDEs auto-implement
//uncomment and implement:
//given Functor[Option] with
//...
package foldfunc
case class Person(lastName: String, firstName: String, age: Int)
object Person:
val janedoe = Person("Doe", "Jane", 42)
val odersky = Person("Odersky", "Martin", 62)
val curry = Person("Curry", "Haskell", 121)
package foldfunc
// Create the Show typeclass here
package foldfunc
/* Remove this line after the Show exercises. The below methods should compile and the main should output two lines.
@main def main(): Unit =
printList(List(Person.janedoe, Person.odersky, Person.curry))
/*
expected output:
Jane Doe is 42 years old
Martin Odersky is 62 years old
Haskell Curry is 121 years old
*/
def printList[A](as: List[A])(using Show[A]): Unit =
as.foreach(a => println(a.show)
// */