type driven development @ confitura 2014
DESCRIPTION
Type Driven Development @ Confitura 2014TRANSCRIPT
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
Typesafe stronghold
... strings and nulls shall not overcome
http://en.wikipedia.org/w
iki/File:Cit%
C3%
A9_de_C
arcassonne,_w
oman_on_w
all.jpg
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
@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
IO...
https://www.flickr.com/photos/oddsock/100761143/
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?
https://www.flickr.com/photos/drgaz/277221424/in/photolist-quQgJ-5KKwCZ
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
[email protected]://github.com/mproch