Skip to content
Snippets Groups Projects
Commit ef697c04 authored by Quentin Leblanc's avatar Quentin Leblanc
Browse files

TP7

parent 304f079d
Branches main
No related tags found
No related merge requests found
package ch.hepia.tpscala.result
sealed trait Result[+E,+A] {
def map[B]( f: A=>B ): Result[E,B] = this match {
case OK(value) => OK(f(value))
case Err(value) => Err(value)
}
//F >: E, signifie "F est un supertype de E"
def flatMap[B, F >:E]( f: A=>Result[F,B] ): Result[F,B] = this match {
case OK(value) => f(value)
case Err(value) => Err(value)
}
//Retourne le résultat si OK
//transforme l'erreur en une valeur valide sinon
//B >: A signifie "B est un supertype de A
//Une seul implémentation possible !
def get[B >: A]( f: E => B ): B = this match {
case OK(value) => value
case Err(value) => f(value)
}
//Une seul implémentation possible !
def fold[B]( f: E => B, g: A => B ): B = this match {
case OK(value) => g(value)
case Err(value) => f(value)
}
def toOption: Option[A] =
fold[Option[A]] (_ => None, Some.apply)
}
case class OK[A]( value: A ) extends Result[Nothing,A]
case class Err[E]( value: E ) extends Result[E,Nothing]
object Result {
//Si il y a au moins une erreur dans la liste, le résultat est une erreur
//Si il n'y a que des succès, liste les succès
def sequence[E,A]( res: List[Result[E,A]] ): Result[E,List[A]] = {
def reqSequence[E, A](req: List[Result[E, A]], acc: Result[E, List[A]]): Result[E, List[A]] = req match {
case Nil => acc.map(_.reverse)
case h :: tail => reqSequence(tail, acc.flatMap( r => h.map( a => a :: r)))
}
reqSequence(res, OK(Nil))
}
}
case class Config( hostname: String, port: Int )
object Config {
private def line2keyValue( line: String ): Result[String,(String,String)] = {
val elements = line.split("=")
if( elements.size != 2 ) Err("Syntax error: " + line )
else OK( (elements(0), elements(1) ) )
}
private def lines2map( lines: List[String] ): Result[String,Map[String,String]] =
Result.sequence( lines.map(line2keyValue) ).map( _.toMap )
private def mget[K,V](mkv: Map[K,V], key: K): Result[String, V] = mkv.get(key) match {
case Some(v) => OK(v)
case None => Err("Missing Key: "+key)
}
private def safeInt(str: String): Result[String, Int] =
try {
OK(str.toInt)
}catch {
case _ => Err("Cannot convert String :" + str + " : to Int")
}
//Utilisez lines2map pour parvenir au résultat
//Attention au clés manquantes
def parse( lines: List[String] ): Result[String,Config] =
for {
keyVal <- lines2map(lines)
h <- mget(keyVal, "hostname")
pStr <- mget(keyVal, "port")
p <- safeInt(pStr)
} yield Config(h, p)
}
package ch.hepia.tpscala
import org.scalatest.funsuite.AnyFunSuite
import result._
/****
*
* Dans SBT vous pouvez n'exécuter que ces tests:
*
* > testOnly ch.hepia.tpscala.Result7Suite
*
*/
class Result7Suite extends AnyFunSuite {
test( "map" ) {
val hello: Result[String,String] = OK("hello")
val notFound: Result[Int,String] = Err(404)
assert( hello.map(_.size) == OK(5) )
assert( notFound.map(_.toString) == notFound )
assert( hello.map(identity) == hello )
assert( notFound.map(identity) == notFound )
assert( hello.map(_.size).map( _ > 2) ==
hello.map( _.size > 2 ) )
assert( notFound.map(_ * 2 ).map( _.toString) ==
notFound.map( i => (i*2).toString ) )
}
test( "flatMap" ) {
type Res[A] = Result[String,A]
val goodHost: Res[String] = OK("hello.com")
val badHost: Res[String] = Err("NA")
val goodPort: Res[Int] = OK(80)
val badPort: Res[Int] = Err("undefined")
assert(
goodHost.flatMap{ host =>
goodPort.map{ port =>
Config(host,port)
}
} == OK( Config("hello.com",80) )
)
assert(
goodHost.flatMap{ host =>
badPort.map{ port =>
Config(host,port)
}
} == badPort
)
assert(
badHost.flatMap{ host =>
goodPort.map{ port =>
Config(host,port)
}
} == badHost
)
assert(
badHost.flatMap{ host =>
badPort.map{ port =>
Config(host,port)
}
} == badHost
)
}
test("get") {
val found: Result[String, Long] = OK(665)
val notFound: Result[String,Long] = Err("not found")
assert( found.get( _ => -1 ) == 665 )
assert( notFound.get( _ => -1 ) == -1 )
def str2status( str: String ) = str match {
case "not found" => 404
case "conflict" => 409
case "unauthorized" => 403
case _ => 400
}
assert( found.map( _ => 200).get(str2status) == 200 )
assert( notFound.map( _ => 200).get(str2status) == 404 )
}
test( "parse config" ) {
val lines1 = List(
"port=8888",
"hostname=www.example.com",
)
val lines2 = List(
"port: 8888",
"hostname: www.example.com",
)
val lines3 = List(
"port=8888",
)
val lines4 = List(
"port=HTTP",
"hostname=www.example.com",
)
val default = Config("localhost", 8080 )
assert( Config.parse( lines1 ).get( _ => default )
== Config("www.example.com",8888) )
assert( Config.parse( lines2 ).get( _ => default )
== default )
assert( Config.parse( lines3 ).get( _ => default )
== default )
assert( Config.parse( lines4 ).get( _ => default )
== default )
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment