Type Driven Development @ Confitura 2014

Post on 29-Nov-2014

112 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Type Driven Development @ Confitura 2014

Transcript

Type driven development

Maciek Próchniak

Whoami?

Maciek Próchniak

Algebraic topologyhocolim

Group cohomology

Monads

GWT

TouKCamel

OSGi

CQRS

Scala

How to prevent bugs?

● Tests● Defensive programming● Tests● Contracts● Tests

Guerilla defensive programming

How to prevent bugs?

In the end, having static types is all about preventing bugs by allowing the über-smart compiler to humiliate you on a regular basis, making sure you’re doing the right thing before it’s too late.

(http://danielwestheide.com)

What bugs?

“Only 1% of issues in Python system are TypeErrors, AttributeErrors, NameErrors”

What about

● NullPointerException

● (uncaught) IOException

● ArrayOutOfBoundsException

● ItemNotFoundException

Security layersmain/servlet

parsing

validation

business “logic”

collections, utils

io

Scalaz

If you are thinking about using Scalaz, stop now while you still have your sanity!

And don’t get into arguments with the people who suggest that you use it – it is pointless.

Equal?

long userId = 14

User user = getUser(...)

userId.equals(user)

Equal?

val userId : Long = 14

val user : User = getUser(...)

import scalaz.syntax.equal._

userId === user

Names are important

BigDecimal balance;

BigDecimal price;

balance.compareTo(price) > 0

Names are important

double log(double a)

double log(positiveDouble)

double log(double positive)

double log(PositiveDouble a)

Tagged types

@Nullable

@Email

@Valid

@Notempty

private String mail

Oh, really?@PUT

@Path("/fulfilment/address")

@ApiOperation(value = "Set shipping method for cart", response = CartDTO.class)

@ApiResponses(value = {

@ApiResponse(code = 400, message = "Method cannot be used due to bussines rules"),

@ApiResponse(code = 404, message = "Unknown fulfilment option"),

@ApiResponse(response = CartDTO.class, code = 200, message = "Customer cart after selecting fulfilment")})

public CartDTO setFulfillmentAddress(

@ApiParam(value = "New shipping method", required = true) @Valid AddressDTO addressDTO

) throws PricingException, FulfillmentPriceException {

Tagged types

Double @@ Meter

sealed trait Meter

sealed trait Liter

Tagged types

val length = Tag[Double,Meter](10)

val capacity = Tag[Double,Liter](10)

length : Double @@ Meter

capacity : Double @@ Liter

length + 15 : Double

Value classes

Java??public class LengthInMeters(int value) {

private final int length;

public LengthInMeters(int value) {

this.length = value;

}

public int getValue() {

return length;

}

@Override

public int hashCode() {

return 31 * length;;

}

@Override public boolean equals(Object o) {

if (o == this)

return true;

if (!(o instanceof LengthInMeters))

return false;

LengthInMeters c = (LengthInMeters) o;

return Integer.compare(c.length, length) == 0;

}

}

Value classes

case class CustomerId(id:String)

extends AnyVal {

def load:Customer

}

def loadFriends

(cid:CustomerId, bid:BranchId)

val cid:CustomerId = parseId(cidString)

Value classes

implicit class CustomerId(

val id:String) extends AnyVal {

def load:Customer

}

“12345”.load : Customer

What about

● NullPointerException

● (uncaught) IOException

● ArrayOutOfBoundsException

● ItemNotFoundException

Option[A]

val maybeResult : Option[Result]

= Option(unsafeResult)

maybeResult.map(_.name):Option[String]

maybeResult

.getOrElse(defaultResult) : Result

maybeResult.get

Option[String]

val str = Option(“str”)

val none = Option(null)

val none2 = Option.empty[String]

val fake = Some(null)

Syntax sugar IS important

val maybe = Option.empty[String]

maybe.map(_.toUpperCase)

Syntax sugar IS importantOptional<String> maybeName =

Optional.absent();

maybeName.transform(

new Function<String, Object>() {

@Override

public Object apply(String s) {

return s.toUpperCase();

}

});

Syntax sugar IS important

String maybe = null;

if (maybe != null) {

maybe = maybe.toUpperCase();

}

What about

● NullPointerException

● (uncaught) IOException

● ArrayOutOfBoundsException

● ItemNotFoundException

Why not exceptions?

● They are invisible in the source code.● They create too many possible exit points

A better alternative is to have your functions return error values (...), no matter how verbose it might be

Joel Spolsky

IO[Result]val fileNameIO : IO[String] =

IO { System.getProperty("file.config") }

val config : IO[String] = for {

fileName <- fileNameIO

fileReader = new BufferedReader(

new FileReader(fileName))

line <-safeReadLn(fileReader)

} yield line

val data : String = config.unsafePerformIO

Security layersmain/servlet

parsing

validation

business “logic”

collections, utils

io

Mixing stuff

IO[State[Map, String]]

State[Map, IO[String,]]

Mixing stuff

https://www.flickr.com/photos/oddsock/100761143/

What about

● NullPointerException

● (uncaught) IOException

● ArrayOutOfBoundsException

● ItemNotFoundException

Empty list?

val list : List[String]

= Nil

list.head

import scalaz.NonEmptyList

val nel : NonEmptyList[String]

= NonEmptyList(“a”,List(“b”))

nel.head

Dependent types?

val list : List[String]

val listOfSize5 : List[String,2+3]

def sum[A,L1:Nat,L2:Nat]

(l1 : List[A,L1], l2:List[A,L2])

: List[A,L1+L2]

Is it a dream?

Path dependent types

class Family {

case class Child(name : String)

def quarell(child1 : Child,

child2 : Child) {

//blaaah

}

}

Path dependent types

val kowalscy = new Family()

val nowakowie = new Family()

val pawelK = kowalscy.Child("Pawel")

val pawelN = nowakowie.Child("Pawel")

kowalscy.quarell(pawelK, pawelN)

Path dependent types

def hide(family:Family)

(child:family.Child) {

}

Shapeless

"maybe it's bug in compiler, but we'll use it and assume it won't be fixed"

https://github.com/milessabin/shapeless

shapeless is an exploration of generic (aka polytypic) programming in Scala

Sized[Seq[A],N]

trait Sized[T, N] {

???

}

N = ???

Part I - numbers as types

trait Nat

class _0 extends Nat

case class Succ[P<:Nat]() extends Nat

val _2 : Succ[Succ[_0]] =

Succ(Succ(_0))

Sized[Seq[A],N]

type SSeq[A,M<:Nat] = Sized[Seq[A],M]

Sized("maciek","prochniak")

: SSeq[String,_2]

def sizedZip[A,B,M<:Nat]

(l1:SSeq[A,M],l2:SSeq[B,M])

: SSeq[(A,B),M] = ???

Part II - witness

def myFun[N<:Nat](a:SIter[String,N])

(implicit b:All[N]) {}

trait All[N<:Nat] {}

implicit object AllZero

extends All[_0]

implicit def allSucc[N<:Nat]

(implicit a:All[N])

= new All[Succ[N]] {}

Part II - witness

trait Even[K<:Nat] {}

implicit object Even0 extends Even[0_]

implicit def succ[K<:Nat]

(implicit n:Even[K])

:Even[Succ[Succ[K]] =

new Even[Succ[Succ[K]]{}

Part II - witness

def evenFun[N<:Nat]

(a:Sized[Iterable[String],N])

(implicit n:Even[N]) {}

evenFun(Sized[Seq](“a”,”b”))

//evenFun(Sized[Seq](“a”,”b”,”c))

Ultimate challenge...

isSum(_8 :: _4 :: HNil, _12)

//isSum(_8 :: _4 :: HNil, _10)

trait Summed[H <:HList, S<:Nat]

def isSum[L<:HList,S<:Nat](l:L,s:S)

(implicit sumProof: Summed[L,S]) {}

Ultimate code...

implicit object NilSummed

extends Summed[HNil, _0]

implicit def pSum

[H<:Nat,T<:HList,PS<:Nat,S<:Nat]

(implicit partial: Summed[T, PS],

sum: Sum.Aux[H, PS, S])

= new Summed[H :: T, S] {}

Security layersmain/servlet

parsing

validation

business “logic”

collections, utils

io

What about

● NullPointerException

● (uncaught) IOException

● ArrayOutOfBoundsException

● ItemNotFoundException

● (uncaught) ConstraintViolationException

Diagram chasing

https://www.flickr.com/photos/intvgene/370973576/in/photolist-yMkw9-7DzC2S-cFnmdQ-4zTfBU-4wuofP-5jGxP9-2auo-4eMTQm-9Napth-jrxsDZ-9cfBr2-ypFzW-7UyLLa-8tJckK-9N7DnM-5UhnaA-h82ub-4fUsNL-7vEVv7-aSj57v-5Ycmai-8sWpQY-8BPU1e-7vEVHQ-gJ3my-6bZkwr-87V3bY-dKfwc5-bpMqEM-8NLnCe-5B3jzN-9bdfAr-iSEFkV-4EEuP8-55TSMz-cee2wo-9SiXWP-8iVuse-7vB7fR-7ASTkd-6dpV1C-4j1h7w-4qB8yx-64pP5z-57gbdz-2QP4c-2Dt2iR-9N7Dc8-8nDt9B-ei86DD

Types as documentation

https://www.flickr.com/photos/videolux/2389320345/in/photolist-4D8TGn-41Hk4j-f5j58F-58cFd2-6jtGaU-8M9Rct-dKjTu-bFuM9p-288vk7-6MDA6U-9rpM2p-7uFTQX-by46VA-jtP7tW-d1yszw-4BxoMU-4Bt4Q8-4Bt8Eg-9qdvDe-9uQehi-9cbiDp-4Bxpz7-4BxmPL-4Bt8jZ-4Bt99T-4Bt7Nv-4Bt6Wz-4BxoCY-4Bxov7-8hqYi6-9X4XKN-4Dd9WY-9ZYLNT-27ie4m-7kJq5y-feBNUr-76WjbN-55V3TL-83eR5P-5fqy8i-4yewUt-4jgxK-7m1XhV-7hJ1yt-7ePuGr-e2x4rd-8Y9XXj-8iPy1e-795cWD-89z3Th

Are we there yet?

Do you do all this?

● IDE support

● library maturity

● verbosity

● is it worth it?

Generic vs concrete code

https://www.flickr.com/photos/oddsock/100761143/

Generic vs concrete code

Object

Collection

Controller

Domain

https://www.flickr.com/photos/oddsock/100761143/

Is your business logic logical?

k4j-f5j58F-58cFd2-6jtGaU-8M9Rct-dKjTu-bFuM9p-288vk7-6MDA6U-9rpM2p-7uFTQX-by46VA-jtP7tW-d1yszw-4BxoMU-4Bt4Q8-4Bt8Eg-9qdvDe-9uQehi-9cbiDp-4Bxpz7-4BxmPL-4Bt8jZ-4Bt99T-4Bt7Nv-4Bt6Wz-4BxoCY-4Bxov7-8hqYi6-9X4XKN-4Dd9WY-9ZYLNT-27ie4m-7kJq5y-feBNUr-76WjbN-55V3TL-83eR5P-5fqy8i-4yewUt-4jgxK-7m1XhV-7hJ1yt-7ePuGr-e2x4rd-8Y9XX

Value classes

Option

Dependent types

IOMonad

Validation

Scala Scalaz

Shapeless

Dziękihttp://mproch.blogspot.com

mpr@touk.plhttp://github.com/mproch

top related