Skip to content
Snippets Groups Projects
Commit df59cd64 authored by joel.vonderwe's avatar joel.vonderwe
Browse files

Added result

parent ef0ac4f9
No related branches found
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(a) => OK(f(a))
case Err(e) => Err(e)
}
}
//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(a) => f(a)
case Err(e) => Err(e)
}
}
//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(a) => a
case Err(e) => f(e)
}
}
//Une seul implémentation possible !
def fold[B]( f: E => B, g: A => B ): B = {
this match {
case OK(a) => g(a)
case Err(e) => f(e)
}
}
def toOption: Option[A] = {
this match {
case OK(a) => Some(a)
case Err(_) => None
}
}
}
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 seqRec(res: List[Result[E,A]], acc: List[A]): Result[E,List[A]] = {
res match {
case Nil => OK(acc.reverse)
case OK(a) :: tl => seqRec(tl, a :: acc)
case Err(e) :: _ => Err(e)
}
}
seqRec(res, 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 safeInt(str: String): Result[String, Int] = {
try {
OK(str.toInt)
} catch {
case _:Throwable => Err("Invalid port")
}
}
private def mGet[K,V](mkv: Map[K,V], key: K): Result[String, V] = {
mkv.get(key) match {
case Some(value) => OK(value)
case None => Err(key.toString + " not found")
}
}
//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