Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.
Contract first,
session types later
Marco Borst
::
vlsi -> java -> scala
Slava Schmidt
Play-Swagger plugin
@
Play-Swagger plugin
API-First single source of truth
code generation framework
=>
Specification
URLs
Verbs
Parameters
Security
Definitions Validations
Model
Test Data
Validations
Play Routes
Marshallers
T...
Richardson Maturity Model
Level 0 - Wild Wild Web
Level 1 - Resources
Level 2 - HTTP Verbs
Level 3 - Hypermedia Controls
Level 2
of Richardson Maturity Model
Level 3
HATEOAS (Hypertext As The Engine Of Application State)
Specification
URLs
Verbs
Parameters
Definitions Mime-Types
Hypermedia
Controls
Server
Mime-Types
Hypermedia
Controls Client
Code
Client
Mime-Types
Hypermedia
Controls
Rules
Specification
Swagger RAML Apiary Blueprint
Spec formats
Swagger RAML Apiary Blueprint
Spec formats
Level 2 capable
Stateless REST communication
but
Stateful Resources
Stateful Clients
Stateful Sessions
Interacting state machines
Time
The arrows form a contract
between the server and its clients
Misbehaving state machines
Time
How to ensure right behaviour?
UBF - “who checks what goes over the wire”
UBF contract checker
Contract checker
CC
RS
AS
RS’
AS
RS’
AS’
S{HC} HC
HATEOAS
only allows us to check the
client behaviour
For the server we need
additional information
State
transitions
x-api-first-transitions:
packaged:
sending:
condition: Post is open
self:
sending:
moving:
condition: Ac...
State
transitions
UBF Session State
HATEOAS - State Locations
Contract checker
CC CC
RS
AS
RS’
AS
RS’
AS’
RS RS’ S{HC} HC
Contract checker is stateful
and
acts at runtime
Encode State => Type System
Runtime =>
Compile Time
Session types
session types do the same
for communication
as types do for data
http://groups.inf.ed.ac.uk/abcd/
Session types
codify structure
(communication, data)
and make it available to
programming tools
Scala Example
States
		sealed	trait	State	
		case	class	Packaged				(byWhom:							String)	extends	State	
		case	class	Sending						(who...
State Transitions
		sealed	abstract	class	~>[-A,+B]	
		implicit	case	object	GoToThePost			 	 	 	extends	~>[Packaged,Sendin...
Resource
	case	class	Parcel[+S<:State](content:	Content,	receiver:	Receiver,	address:	Address)
Server part
			
def	server	=	
				In	{	p:	Parcel[Packaged]	=>	
						val	content	=	"Changed	by	server	"	+	p.content	
					...
Client part
			
		def	client	=	{	
				val	parcel	=	Parcel[Packaged]("Content",	"Receiver",	"Address")	
				val	finish	=	St...
Communication steps
	 	
		case	class	Stop(msg:	String)	
	 case	class	In	[R[S<:State],A<:State,B<:State,+C](recv:	R[A]	=>	(...
Running Session
			
runSession(server,	client)
Session Runner
			
		trait	Session[S]	{	
				type	Self	=	S	
				type	Dual	
				type	DualOf[D]	=	Session[Self]	{	type	Dual	...
Stop Dual
	 	
	implicit	object	StopDual	extends	Session[Stop]	{	
	 	 type	Dual	=	Stop	
	 	 def	run	(self:	Self,	dual:	Dual...
In Dual
	 	
		implicit	def	InDual[R[S<:State],RA<:State,RB<:State,C](implicit	cont:	Session[C])		
				=	new	Session[In[R,R...
Type of In Dual
	 	
		implicit	def	InDual[R[S<:State],RA<:State,RB<:State,C](implicit	cont:	Session[C])		
				=	new	Sessio...
Out Dual
			
	implicit	def	OutDual[R[S<:State],RA<:State,RB<:State,C](implicit	cont:	Session[C])		
			=	new	Session[Out[R,...
Source Code
http://bit.ly/1VnWX2R
Run
scala>	:load	SessionTypes.scala	
Loading	SessionTypes.scala...	
defined	module	SessionTypes	
scala>	SessionTypes.doRun...
Close the post
		sealed	abstract	class	~>[-A,+B]	
		//implicit	case	object	GoToThePost			 	 	 	extends	~>[Packaged,Sending...
Run
scala>	:load	SessionTypes.scala	
Loading	SessionTypes.scala...	
<console>:75:	error:	state	transition	from	SessionType...
Avoid the post
			
def	server	=	
				In	{	p:	Parcel[Packaged]	=>	
						val	content	=	"Changed	by	server	"	+	p.content	
		...
Run
scala>	:load	SessionTypes.scala	
Loading	SessionTypes.scala...	
<console>:75:	error:	state	transition	from	SessionType...
Wrong initial state
			
def	server	=	
				In	{	p:	Parcel[Sending]	=>	
						val	content	=	"Changed	by	server	"	+	p.content...
Run
scala>	:load	SessionTypes.scala	
Loading	SessionTypes.scala...	
<console>:110:	error:	could	not	find	implicit	value	fo...
Misaligned step
			
def	server	=	
				In	{	p:	Parcel[Sending]	=>	
						val	content	=	"Changed	by	server	"	+	p.content	
		...
Run
scala>	:load	SessionTypes.scala	
Loading	SessionTypes.scala...	
<console>:110:	error:	could	not	find	implicit	value	fo...
?
Session Types Primitives
Sending
Receiving
Sequence
Choice
Recursion
Oops….
Session Types Primitives
Sending
Receiving
Sequence
Choice
Recursion
Session
Types
REST
Contract first, session types never?
• No API Specification standard is about REST
• REST “allows” for client-side contrac...
Questions ?
Thank you!
Contract first, session types later?
Nächste SlideShare
Wird geladen in …5
×

Contract first, session types later?

535 Aufrufe

Veröffentlicht am

The talk given by Marco Borst and Slava Schmidt at Scalar Conf 2016

Veröffentlicht in: Software
  • Als Erste(r) kommentieren

Contract first, session types later?

  1. 1. Contract first, session types later
  2. 2. Marco Borst :: vlsi -> java -> scala
  3. 3. Slava Schmidt
  4. 4. Play-Swagger plugin @
  5. 5. Play-Swagger plugin API-First single source of truth code generation framework
  6. 6. =>
  7. 7. Specification URLs Verbs Parameters Security Definitions Validations Model Test Data Validations Play Routes Marshallers Tests Controllers Security
  8. 8. Richardson Maturity Model Level 0 - Wild Wild Web Level 1 - Resources Level 2 - HTTP Verbs Level 3 - Hypermedia Controls
  9. 9. Level 2 of Richardson Maturity Model
  10. 10. Level 3 HATEOAS (Hypertext As The Engine Of Application State)
  11. 11. Specification URLs Verbs Parameters Definitions Mime-Types Hypermedia Controls Server
  12. 12. Mime-Types Hypermedia Controls Client Code Client
  13. 13. Mime-Types Hypermedia Controls Rules Specification
  14. 14. Swagger RAML Apiary Blueprint Spec formats
  15. 15. Swagger RAML Apiary Blueprint Spec formats Level 2 capable
  16. 16. Stateless REST communication but Stateful Resources Stateful Clients Stateful Sessions
  17. 17. Interacting state machines Time
  18. 18. The arrows form a contract between the server and its clients
  19. 19. Misbehaving state machines Time
  20. 20. How to ensure right behaviour?
  21. 21. UBF - “who checks what goes over the wire”
  22. 22. UBF contract checker
  23. 23. Contract checker CC RS AS RS’ AS RS’ AS’ S{HC} HC
  24. 24. HATEOAS only allows us to check the client behaviour
  25. 25. For the server we need additional information
  26. 26. State transitions x-api-first-transitions: packaged: sending: condition: Post is open self: sending: moving: condition: Accepted at the post self: moving: delivering: condition: Receiver is at home lost: self: destroyed: delivering: delivered: condition: Package undamaged self: lost: moving:
  27. 27. State transitions
  28. 28. UBF Session State
  29. 29. HATEOAS - State Locations
  30. 30. Contract checker CC CC RS AS RS’ AS RS’ AS’ RS RS’ S{HC} HC
  31. 31. Contract checker is stateful and acts at runtime
  32. 32. Encode State => Type System Runtime => Compile Time
  33. 33. Session types session types do the same for communication as types do for data http://groups.inf.ed.ac.uk/abcd/
  34. 34. Session types codify structure (communication, data) and make it available to programming tools
  35. 35. Scala Example
  36. 36. States sealed trait State case class Packaged (byWhom: String) extends State case class Sending (whoAccepts: String) extends State case class Received (whoDelivers: String) extends State case class Delivered (toWhom: String) extends State case class Lost (witness: String) extends State case class Moving (tracking: Long ) extends State case class Destroyed (when: Long ) extends State
  37. 37. State Transitions sealed abstract class ~>[-A,+B] implicit case object GoToThePost extends ~>[Packaged,Sending] implicit case object AcceptFromSender extends ~>[Sending,Moving] implicit case object BringToTheAddress extends ~>[Moving,Received] implicit case object GiveToReceiver extends ~>[Received,Delivered] implicit case object GetAngry extends ~>[Moving,Destroyed] implicit case object DrinkAlcohol extends ~>[Moving,Lost] implicit case object StopDrinking extends ~>[Lost,Moving]
  38. 38. Resource case class Parcel[+S<:State](content: Content, receiver: Receiver, address: Address)
  39. 39. Server part def server = In { p: Parcel[Packaged] => val content = "Changed by server " + p.content val result = Parcel[Sending](content, p.receiver, p.address) val stop = Stop("With server result: " + result) (stop, result) }
  40. 40. Client part def client = { val parcel = Parcel[Packaged]("Content", "Receiver", "Address") val finish = Stop("With client result: " + parcel) Out(parcel, finish) }
  41. 41. Communication steps case class Stop(msg: String) case class In [R[S<:State],A<:State,B<:State,+C](recv: R[A] => (C,R[B])) (implicit stateTransition: ~>[A,B]) case class Out[+R[S<:State],A<:State,+C](data: R[A], cont: C)
  42. 42. Running Session runSession(server, client)
  43. 43. Session Runner trait Session[S] { type Self = S type Dual type DualOf[D] = Session[Self] { type Dual = D } def run (self: Self, dual: Dual): Unit } def runSession[AS,D:Session[AS]#DualOf](session: AS, dual: D) = implicitly[Session[AS]#DualOf[D]].run(session, dual)
  44. 44. Stop Dual implicit object StopDual extends Session[Stop] { type Dual = Stop def run (self: Self, dual: Dual): Unit = {} }
  45. 45. In Dual implicit def InDual[R[S<:State],RA<:State,RB<:State,C](implicit cont: Session[C]) = new Session[In[R,RA,RB,C]] { type Dual = Out[R,RA,cont.Dual] def run(self: Self, dual: Dual) = cont.run(self.recv(dual.data)._1, dual.cont) }
  46. 46. Type of In Dual implicit def InDual[R[S<:State],RA<:State,RB<:State,C](implicit cont: Session[C]) = new Session[In[R,RA,RB,C]] { type Dual = Out[R,RA,cont.Dual] def run(self: Self, dual: Dual) = cont.run(self.recv(dual.data)._1, dual.cont) }
  47. 47. Out Dual implicit def OutDual[R[S<:State],RA<:State,RB<:State,C](implicit cont: Session[C]) = new Session[Out[R,RA,C]] { type Dual = In[R,RA,RB,cont.Dual] def run(self: Self, dual: Dual) = cont.run(self.cont, dual.recv(self.data)._1) }
  48. 48. Source Code http://bit.ly/1VnWX2R
  49. 49. Run scala> :load SessionTypes.scala Loading SessionTypes.scala... defined module SessionTypes scala> SessionTypes.doRun Client results: Parcel(First,Receiver,Address) and Parcel(Second,Receiver,Address) Server result: Parcel(Changed by server First,Receiver,Address) scala>
  50. 50. Close the post sealed abstract class ~>[-A,+B] //implicit case object GoToThePost extends ~>[Packaged,Sending] implicit case object AcceptFromSender extends ~>[Sending,Moving] implicit case object BringToTheAddress extends ~>[Moving,Received] implicit case object GiveToReceiver extends ~>[Received,Delivered] implicit case object GetAngry extends ~>[Moving,Destroyed] implicit case object DrinkAlcohol extends ~>[Moving,Lost] implicit case object StopDrinking extends ~>[Lost,Moving]
  51. 51. Run scala> :load SessionTypes.scala Loading SessionTypes.scala... <console>:75: error: state transition from SessionTypes.Packaged to SessionTypes.Sending is not allowed In { p: Parcel[Packaged] => ^ scala>
  52. 52. Avoid the post def server = In { p: Parcel[Packaged] => val content = "Changed by server " + p.content val result = Parcel[Moving](content, p.receiver, p.address) val stop = Stop("With server result: " + result) (stop, result) }
  53. 53. Run scala> :load SessionTypes.scala Loading SessionTypes.scala... <console>:75: error: state transition from SessionTypes.Packaged to SessionTypes.Moving is not allowed In { p: Parcel[Packaged] => ^ scala>
  54. 54. Wrong initial state def server = In { p: Parcel[Sending] => val content = "Changed by server " + p.content val result = Parcel[Moving](content, p.receiver, p.address) val stop = Stop("With server result: " + result) (stop, result) }
  55. 55. Run scala> :load SessionTypes.scala Loading SessionTypes.scala... <console>:110: error: could not find implicit value for evidence parameter of type SessionTypes.Session[SessionTypes.In[SessionTypes.Parcel,SessionTypes .Sended,SessionTypes.Moving,SessionTypes.Stop]]{type Dual = SessionTypes.Out[SessionTypes.Parcel,SessionTypes.Packaged,SessionTyp es.Stop]} def run = runSession(server, client) ^ scala>
  56. 56. Misaligned step def server = In { p: Parcel[Sending] => val content = "Changed by server " + p.content val result = Parcel[Moving](content, p.receiver, p.address) val stop = Stop("With server result: " + result) (Out(result, stop), result) }
  57. 57. Run scala> :load SessionTypes.scala Loading SessionTypes.scala... <console>:110: error: could not find implicit value for evidence parameter of type SessionTypes.Session[SessionTypes.In[SessionTypes.Parcel,SessionTypes .Sended,SessionTypes.Moving,SessionTypes.Out[SessionTypes.Parcel,Sess ionTypes.Moving,SessionTypes.Stop]]]{type Dual = SessionTypes.Out[SessionTypes.Parcel,SessionTypes.Packaged,SessionTyp es.Stop]} def run = runSession(server, client) ^ scala>
  58. 58. ?
  59. 59. Session Types Primitives Sending Receiving Sequence Choice Recursion
  60. 60. Oops….
  61. 61. Session Types Primitives Sending Receiving Sequence Choice Recursion
  62. 62. Session Types REST
  63. 63. Contract first, session types never? • No API Specification standard is about REST • REST “allows” for client-side contract checking • Session Types don’t fit RESTful communications
  64. 64. Questions ?
  65. 65. Thank you!

×