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 (30)
Showing
with 287 additions and 87 deletions
......@@ -8,15 +8,19 @@ To mark your progress during the lecture, please vote on
## Overview:
Templates are within `src/main/scala`. Parts that you should complete are
usually marked with `???`.
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: Typeclasses | [`typeclasses`](src/main/scala/typeclasses/) | `testOnly typeclasses.*` (needs some uncommenting)
| 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,
......
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",
"-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",
......@@ -42,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 op(a1: A, a2: A): A
trait Monoid[A]:
def combine(a1: A, a2: A): A
def zero: A
}
extension (a1: A)
def |+| (a2: A) = combine(a1, a2)
package algebra
object Monoids {
def intAddition: Monoid[Int] = new Monoid[Int] {
def zero = ???
def op(a: Int, b: Int): Int = ???
}
def intAddition: Monoid[Int] = new Monoid:
def zero = ???
def combine(a: Int, b: Int): Int = ???
def intMultiplication: Monoid[Int] = ???
def intMultiplication: Monoid[Int] = new Monoid:
def zero = ???
def combine(a: Int, b: Int): Int = ???
def booleanOr: Monoid[Boolean] = ???
def booleanOr: Monoid[Boolean] = new Monoid:
def zero = ???
def combine(a: Boolean, b: Boolean): Boolean = ???
def booleanAnd: Monoid[Boolean] = ???
def booleanAnd: Monoid[Boolean] = new Monoid:
def zero = ???
def combine(a: Boolean, b: Boolean): Boolean = ???
def optionMonoid[A]: Monoid[Option[A]] = ???
def optionMonoid[A]: Monoid[Option[A]] = ???
def endoMonoid[A]: Monoid[A => A] = ???
def endoMonoid[A]: Monoid[A => A] = ???
def foldMap[A, B](as: List[A], m: Monoid[B])(f: A => B): B = ???
def bag[A](as: List[A]): Map[A, Int] = ???
def foldMapBalanced[A, B](as: IndexedSeq[A], m: Monoid[B])(f: A => B): B = ???
/* 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))
)
)
def mapMergeMonoid[K,V](V: Monoid[V]): Monoid[Map[K, V]] =
new Monoid[Map[K, V]] {
def zero = Map[K,V]()
def op(a: Map[K, V], b: Map[K, V]) =
(a.keySet ++ b.keySet).foldLeft(zero) { (acc,k) =>
acc + (k ->
V.op(a.getOrElse(k, V.zero), b.getOrElse(k, V.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)
def bag[A](as: IndexedSeq[A]): Map[A, Int] = ???
}
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
sealed trait Either[+E, +A] {
def map[B](f: A => B): Either[E, B] = this match {
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{
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]
package errors
sealed trait Option[+A] {
enum Option[+A]:
case Some(get: A)
case None
def map[B](f: A => B): Option[B] = ???
def getOrElse[B >: A](default: => B): B = ???
......@@ -8,11 +11,7 @@ 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]] = ???
}
package errors
object Team {
val persons = List(
Person("Dagobert", "Finanzabteilung"),
Person("Donald", "Spassabteilung"),
Person("Daniel", "R&D"),
)
// This allows us to write Some and None without prefix, as we would inside the Option enum
import Option.{None, Some}
type Team = (Person, Person)
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: Option[Person])(a => Some(a))
def lookup(name: String): Option[Person] =
persons.find(_.name == name).fold(None)(a => Some(a))
def getTeam(name1: String, name2: String): Option[Team] = ???
}
def getTeam(name1: String, name2: String): Option[Team] = ???
final case class Person(
name: 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 typeclasses
package foldfunc
// Create the Show typeclass here
package typeclasses
package foldfunc
object ShowMain {
/* Remove this line after the Show exercises. The below methods should compile and the main should output two lines.
def main(args: Array[String]): Unit = {
printList(List(Person.odersky, Person.curry))
@main def main(): Unit =
printList(List(Person.janedoe, Person.odersky, Person.curry))
/*
expected output:
Martin Odersky is 60 years old
Haskell Curry is 119 years old
Jane Doe is 42 years old
Martin Odersky is 62 years old
Haskell Curry is 121 years old
*/
}
def printList[A : Show](as: List[A]): Unit ={
as.foreach(a => println(implicitly[Show[A]].show(a)))
}
def printList[A](as: List[A])(using Show[A]): Unit =
as.foreach(a => println(a.show)
// */
}
package laziness
sealed trait Stream[+A] {
enum LazyList[+A]:
case Empty
case Cons(h: () => A, t: () => LazyList[A])
// uncomment to be able to use cons(h, t) and empty directly
// imports methods from the companion object
//import Stream._
//import LazyList._
def toList: List[A] = ???
def take(n: Int): Stream[A] = ???
def take(n: Int): LazyList[A] = ???
//This can be tailrecursive. Uncomment below to let the compiler check
//This can be tailrecursive. Uncomment below to let the compiler check for it.
//@annotation.tailrec
def drop(n: Int): Stream[A] = ???
def drop(n: Int): 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 Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]
object Stream { // companion object
object LazyList: // companion object
def fibs: Stream[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)]): Stream[A] = ???
def unfold[A, S](z: S)(f: S => Option[(A, S)]): LazyList[A] = ???
def fibsViaUnfold: Stream[Int] = ???
def fibsViaUnfold: LazyList[Int] = ???
// Methods shown during Lecture
def cons[A](h: => A, t: => Stream[A]): Stream[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]: Stream[A] = Empty
def empty[A]: LazyList[A] = Empty
def apply[A](as: A*): Stream[A] =
if (as.isEmpty) empty
def apply[A](as: A*): LazyList[A] =
if as.isEmpty then empty
else cons(as.head, apply(as.tail: _*))
val ones: Stream[Int] = cons(1, ones)
}
val ones: LazyList[Int] = cons(1, ones)