SlideShare ist ein Scribd-Unternehmen logo
1 von 29
API design: using type classes
    and dependent types

             Ben Lever
             @bmlever

         ScalaSyd Episode #6
             11 July 2012
What I want
traitComp[A] { ... }

              Specifies the
           “computation” of a
             value of type ‘A’


val ii: Comp[Int] = ...

vali: Int = exec(ii)

                         “Execute” the
                         computation
                        specified by ‘ii’.
What would be cool

val ii: Comp[Int] = ...
valss: Comp[String] = ...



val (i: Int, s: String) = exec(ii, ss)

                                          “Execute” multiple
                                           computations of
                                         different types, then
                                         return “embedded”
                                           types as a tuple.
More generally

valaa: Comp[A] = ...
val bb: Comp[B] = ...
valcc: Comp[C] = ...
valdd: Comp[D] = ...

val (a: A, b: B, c: C, d: D) = exec(aa, bb, cc, dd)


                                                Ability to “execute” many
                                                       computations
                                                simultaneously and return
                                                  all “embedded” values
                                                 together retaining types.
But wait, there’s more
traitIOComp[A] { ... }

             Specifies the “IO
            computation” of a
             value of type ‘A’


val ii: IOComp[Int] = ...
                                                            Err[A]
vali: Err[Int] = exec(ii)                                      ||
                                                       Either[String, A])
        Either return the        “Execute” the IO
        computed value             computation
        or an error string        specified by ‘ii’.
And of course

val ii: IOComp[Int] = ...
valss: IOComp[String] = ...



val (i: Err[Int],
s: Err[String]) = exec(ii, ss)

                                 “Execute” multiple IO
                                    computations of
                                  different types, then
                                  return “embedded”
                                    types as a tuple.
Finally

valaa: Comp[A] = ...
val bb: IOComp[B] = ...
valcc: Comp[C] = ...
valdd: IOComp[D] = ...

val (a: A, b: Err[B], c: C, d: Err[D]) =
exec(aa, bb, cc, dd)


                                           “Execute” a mixture
                                            of normal and IO
                                              computations.
Teaser - the final API



defexec[R](v: R)(implicit runner: Runner[R]): runner.Out
My inspiration
  “Couldn’t you use HLists/KLists for this?”

           Miles Sabin’s Shapeless
               provided much
          direction and inspirations

But … I’m not going to talk about Shapeless 
The tricks



defexec[R](v: R)(implicit runner: Runner[R]): runner.Out

      Tuple                  Type                     Dependent
    sweetness               classes                     types
Trick #1: tuple sweetness

defecho[A](x: A) { prinln(x) }



echo((“good”, 47, true))

echo(“good”, 47, true)     // equivalent
Trick #2: type classes
Get the “size” of an object

size(3) // 3
size(“hello”)// 5
size(false)// 1
...
Size type class
traitSize[T] {
defapply(in: T): Int                         Type class
}

object Size {
defsize[S](v: S)(implicits: Size[S]): Int = s(v)

implicit defIntSize = newSize[Int] {
defapply(in: Int): Int = in
  }
implicit defBoolSize = newSize[Boolean] {
defapply(in: Boolean): Int = 1
                                                          Type class
                                                          instances
  }
implicit defStringSize = newSize[String] {
defapply(in: String): Int = in.length
  }
}
Under the hood
Implicit objects inserted by compiler

size(3)(IntSize)// 3
size(“hello”)(StringSize)// 5
size(false)(BoolSize)// 1
...
Expanding the example
Now, get the “size” of multiple objects at once



size(3, “hello”,false)// (3,5,1)
Runner type class
traitRunner[In, Out] {
defapply(in: In): Out
}

object Runner {
defsize[I, O](v: I)(implicitr: Runner[I, O]): O = r(v)

implicit def Tup1[T1](implicit s1: Size[T1]) =
new Runner[T1, Int] {
defapply(in: T1): Int = s1(in)                                    Referencing
  }                                                                  ‘Size’

implicit def Tup2[T1,T2](implicit s1: Size[T1], s2: Size[T2]) =
new Runner[(T1,T2), (Int,Int)] {
defapply(in: (T1,T2)): (Int,Int) = (s1(in._1), s2(in._2))
  }

implicit def Tup3[T1,T2,T3](implicit s1: Size[T1], ...
Under the hood
Implicit object inserted by compiler



size(3, “hello”,false)
(Tup3
IntSize,
StringSize,
BoolSize))
Aside: type class sugar

defsize[S](v: S)(implicitsizer: Size[S]): Int


is equivalent to
defsize[S : Size](v: S) : Int

                     Type class
                     constraint
‘size’ is easy
Runner instances know output types are always Ints


                    Returns Int


size(3, “hello”,false)// (3,5,1)

      Returns Int                 Returns Int
‘exec’ is harder
Output types are always different

                  Returns               Returns
                   Err[B]                Err[D]


exec(aa, bb, cc, dd)

      Returns A             Returns C
Exec type class
traitExec[In, Out] {
defapply(in: In): Out
}

object Exec {

implicit defcompExec[A] = newExec[Comp[A], A] {
defapply(in: Comp[A]): A = execute(in)
 }

implicit defioCompExec[A] = newExec[IOComp[A], Err[A]] {
defapply(in: IOComp[A]): Err[A] = ioExecute(in)
  }
}
Updated Runner type class
Runner return type is dependent on Exec return type

traitRunner[In, Out] {
defapply(in: In): Out
}

object Runner {
defexec[I,O](v: I)(implicitr: Runner[I,O]): O = r(v)

implicit def Tup1[T1](implicit ex1: Exec[T1,?]) =
new Runner[T1, ?] {
defapply(in: T1): ? = ex1(in)
  }                                               Needs to be
  ...                                           dependent on ex1
It gets worse

implicit def Tup2[T1,T2](implicit ex1: Exec[T1,?],
                      ex2: Exec[T2,?]) =
new Runner[(T1,T2),(?,?)] {
   defapply(in: (T1,T2)): (?,?) = (ex1(in._1),
                        ex2(in._2))
 }

 ...
Trick #3: Dependent types


Dependent types are types where the realization
  of the type created is actually dependent on
           the values being provided.
                                      (SCALABOUND)
Type parameters vs members
 traitExec[In, Out] {           Type
                             parameter
 defapply(in: In): Out
 }

          is equivalent to

 traitExec[In] {
 typeOut                      Type
                             member
 defapply(in: In): Out
 }
Exec using type members
traitExec[In] {
typeOut
defapply(in: In): Out
}

object Exec {

implicit defcompExec[A] = newExec[Comp[A]] {
typeOut = A
defapply(in: Comp[A]): A = execute(in)
  }

implicit defioCompExec[A] = newExec[IOComp[A]] {
typeOut = Err[A]
defapply(in: IOComp[A]): Err[A] = ioExecute(in)
  }
}
Using dependent method types
traitRunner[In] {
typeOut
defapply(in: In): Out                                         Return type is
                                                              dependent on
}
                                                             ‘r’. Access type
                                                              as a member.
object Runner {
defexec[R](v: R)(implicitr: Runner[R]): r.Out = r(v)

implicit def Tup1[T1](implicit ex1: Exec[T1]) =
new Runner[T1] {
typeOut = ex1.Out
defapply(in: T1): ex1.Out = ex1(in)
   }
                                                   Return type is dependent on
   ...                                                  ex1’s return type
And again
implicit def Tup2[T1,T2](implicit ex1: Exec[T1],
                      ex2: Exec[T2]) =
new Runner[(T1,T2)] {
typeOut = (ex1.Out, ex2.Out)
defapply(in: (T1,T2)): Out = (ex1(in._1),
                       ex2(in._2))
  }

 ...
Remember -Y
Dependent method types are still experimental!
        (but “turned-on” in 2.10.x)



        -Ydependent-method-types

Weitere ähnliche Inhalte

Was ist angesagt?

Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)
stasimus
 
Groovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony CodeGroovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony Code
stasimus
 
The Death of Final Tagless
The Death of Final TaglessThe Death of Final Tagless
The Death of Final Tagless
John De Goes
 

Was ist angesagt? (20)

Testing for share
Testing for share Testing for share
Testing for share
 
Scalaz 8: A Whole New Game
Scalaz 8: A Whole New GameScalaz 8: A Whole New Game
Scalaz 8: A Whole New Game
 
Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)Introduction to Monads in Scala (1)
Introduction to Monads in Scala (1)
 
C# programming
C# programming C# programming
C# programming
 
Groovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony CodeGroovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony Code
 
OOP v3
OOP v3OOP v3
OOP v3
 
Core C#
Core C#Core C#
Core C#
 
Introduction to kotlin
Introduction to kotlinIntroduction to kotlin
Introduction to kotlin
 
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
 
The Death of Final Tagless
The Death of Final TaglessThe Death of Final Tagless
The Death of Final Tagless
 
Joy of scala
Joy of scalaJoy of scala
Joy of scala
 
From Functor Composition to Monad Transformers
From Functor Composition to Monad TransformersFrom Functor Composition to Monad Transformers
From Functor Composition to Monad Transformers
 
Introduction to ad-3.4, an automatic differentiation library in Haskell
Introduction to ad-3.4, an automatic differentiation library in HaskellIntroduction to ad-3.4, an automatic differentiation library in Haskell
Introduction to ad-3.4, an automatic differentiation library in Haskell
 
A taste of Functional Programming
A taste of Functional ProgrammingA taste of Functional Programming
A taste of Functional Programming
 
2 kotlin vs. java: what java has that kotlin does not
2  kotlin vs. java: what java has that kotlin does not2  kotlin vs. java: what java has that kotlin does not
2 kotlin vs. java: what java has that kotlin does not
 
Core c sharp and .net quick reference
Core c sharp and .net quick referenceCore c sharp and .net quick reference
Core c sharp and .net quick reference
 
Use Applicative where applicable!
Use Applicative where applicable!Use Applicative where applicable!
Use Applicative where applicable!
 
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
Quark: A Purely-Functional Scala DSL for Data Processing & AnalyticsQuark: A Purely-Functional Scala DSL for Data Processing & Analytics
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
 
Collection v3
Collection v3Collection v3
Collection v3
 
Hammurabi
HammurabiHammurabi
Hammurabi
 

Ähnlich wie API design: using type classes and dependent types

Computer science-2010-cbse-question-paper
Computer science-2010-cbse-question-paperComputer science-2010-cbse-question-paper
Computer science-2010-cbse-question-paper
Deepak Singh
 
Arrays and structures
Arrays and structuresArrays and structures
Arrays and structures
Mohd Arif
 

Ähnlich wie API design: using type classes and dependent types (20)

Scala Functional Patterns
Scala Functional PatternsScala Functional Patterns
Scala Functional Patterns
 
GoLightly: Building VM-Based Language Runtimes with Google Go
GoLightly: Building VM-Based Language Runtimes with Google GoGoLightly: Building VM-Based Language Runtimes with Google Go
GoLightly: Building VM-Based Language Runtimes with Google Go
 
Scalapeno18 - Thinking Less with Scala
Scalapeno18 - Thinking Less with ScalaScalapeno18 - Thinking Less with Scala
Scalapeno18 - Thinking Less with Scala
 
Leet Code May Coding Challenge - DataStructure and Algorithm Problems
Leet Code May Coding Challenge - DataStructure and Algorithm ProblemsLeet Code May Coding Challenge - DataStructure and Algorithm Problems
Leet Code May Coding Challenge - DataStructure and Algorithm Problems
 
Monadologie
MonadologieMonadologie
Monadologie
 
Computer science-2010-cbse-question-paper
Computer science-2010-cbse-question-paperComputer science-2010-cbse-question-paper
Computer science-2010-cbse-question-paper
 
Power of functions in a typed world
Power of functions in a typed worldPower of functions in a typed world
Power of functions in a typed world
 
What are monads?
What are monads?What are monads?
What are monads?
 
Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
 
C# Summer course - Lecture 2
C# Summer course - Lecture 2C# Summer course - Lecture 2
C# Summer course - Lecture 2
 
Type classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritanceType classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritance
 
Type Classes in Scala and Haskell
Type Classes in Scala and HaskellType Classes in Scala and Haskell
Type Classes in Scala and Haskell
 
Scala Back to Basics: Type Classes
Scala Back to Basics: Type ClassesScala Back to Basics: Type Classes
Scala Back to Basics: Type Classes
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator Pattern
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
 
Groovy grails types, operators, objects
Groovy grails types, operators, objectsGroovy grails types, operators, objects
Groovy grails types, operators, objects
 
Arrays and structures
Arrays and structuresArrays and structures
Arrays and structures
 
Coding in Style
Coding in StyleCoding in Style
Coding in Style
 
C++11
C++11C++11
C++11
 
Functions, Types, Programs and Effects
Functions, Types, Programs and EffectsFunctions, Types, Programs and Effects
Functions, Types, Programs and Effects
 

Kürzlich hochgeladen

Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider  Progress from Awareness to Implementation.pptxTales from a Passkey Provider  Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
FIDO Alliance
 
Breaking Down the Flutterwave Scandal What You Need to Know.pdf
Breaking Down the Flutterwave Scandal What You Need to Know.pdfBreaking Down the Flutterwave Scandal What You Need to Know.pdf
Breaking Down the Flutterwave Scandal What You Need to Know.pdf
UK Journal
 

Kürzlich hochgeladen (20)

Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider  Progress from Awareness to Implementation.pptxTales from a Passkey Provider  Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
 
ADP Passwordless Journey Case Study.pptx
ADP Passwordless Journey Case Study.pptxADP Passwordless Journey Case Study.pptx
ADP Passwordless Journey Case Study.pptx
 
Continuing Bonds Through AI: A Hermeneutic Reflection on Thanabots
Continuing Bonds Through AI: A Hermeneutic Reflection on ThanabotsContinuing Bonds Through AI: A Hermeneutic Reflection on Thanabots
Continuing Bonds Through AI: A Hermeneutic Reflection on Thanabots
 
WebRTC and SIP not just audio and video @ OpenSIPS 2024
WebRTC and SIP not just audio and video @ OpenSIPS 2024WebRTC and SIP not just audio and video @ OpenSIPS 2024
WebRTC and SIP not just audio and video @ OpenSIPS 2024
 
Working together SRE & Platform Engineering
Working together SRE & Platform EngineeringWorking together SRE & Platform Engineering
Working together SRE & Platform Engineering
 
Google I/O Extended 2024 Warsaw
Google I/O Extended 2024 WarsawGoogle I/O Extended 2024 Warsaw
Google I/O Extended 2024 Warsaw
 
Event-Driven Architecture Masterclass: Engineering a Robust, High-performance...
Event-Driven Architecture Masterclass: Engineering a Robust, High-performance...Event-Driven Architecture Masterclass: Engineering a Robust, High-performance...
Event-Driven Architecture Masterclass: Engineering a Robust, High-performance...
 
Where to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdfWhere to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdf
 
Breaking Down the Flutterwave Scandal What You Need to Know.pdf
Breaking Down the Flutterwave Scandal What You Need to Know.pdfBreaking Down the Flutterwave Scandal What You Need to Know.pdf
Breaking Down the Flutterwave Scandal What You Need to Know.pdf
 
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdfIntroduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
 
1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT
1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT
1111 ChatGPT Prompts PDF Free Download - Prompts for ChatGPT
 
Oauth 2.0 Introduction and Flows with MuleSoft
Oauth 2.0 Introduction and Flows with MuleSoftOauth 2.0 Introduction and Flows with MuleSoft
Oauth 2.0 Introduction and Flows with MuleSoft
 
The Metaverse: Are We There Yet?
The  Metaverse:    Are   We  There  Yet?The  Metaverse:    Are   We  There  Yet?
The Metaverse: Are We There Yet?
 
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdfSimplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
 
Using IESVE for Room Loads Analysis - UK & Ireland
Using IESVE for Room Loads Analysis - UK & IrelandUsing IESVE for Room Loads Analysis - UK & Ireland
Using IESVE for Room Loads Analysis - UK & Ireland
 
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
Secure Zero Touch enabled Edge compute with Dell NativeEdge via FDO _ Brad at...
 
TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...
TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...
TEST BANK For, Information Technology Project Management 9th Edition Kathy Sc...
 
Event-Driven Architecture Masterclass: Challenges in Stream Processing
Event-Driven Architecture Masterclass: Challenges in Stream ProcessingEvent-Driven Architecture Masterclass: Challenges in Stream Processing
Event-Driven Architecture Masterclass: Challenges in Stream Processing
 
Long journey of Ruby Standard library at RubyKaigi 2024
Long journey of Ruby Standard library at RubyKaigi 2024Long journey of Ruby Standard library at RubyKaigi 2024
Long journey of Ruby Standard library at RubyKaigi 2024
 
2024 May Patch Tuesday
2024 May Patch Tuesday2024 May Patch Tuesday
2024 May Patch Tuesday
 

API design: using type classes and dependent types

  • 1. API design: using type classes and dependent types Ben Lever @bmlever ScalaSyd Episode #6 11 July 2012
  • 2. What I want traitComp[A] { ... } Specifies the “computation” of a value of type ‘A’ val ii: Comp[Int] = ... vali: Int = exec(ii) “Execute” the computation specified by ‘ii’.
  • 3. What would be cool val ii: Comp[Int] = ... valss: Comp[String] = ... val (i: Int, s: String) = exec(ii, ss) “Execute” multiple computations of different types, then return “embedded” types as a tuple.
  • 4. More generally valaa: Comp[A] = ... val bb: Comp[B] = ... valcc: Comp[C] = ... valdd: Comp[D] = ... val (a: A, b: B, c: C, d: D) = exec(aa, bb, cc, dd) Ability to “execute” many computations simultaneously and return all “embedded” values together retaining types.
  • 5. But wait, there’s more traitIOComp[A] { ... } Specifies the “IO computation” of a value of type ‘A’ val ii: IOComp[Int] = ... Err[A] vali: Err[Int] = exec(ii) || Either[String, A]) Either return the “Execute” the IO computed value computation or an error string specified by ‘ii’.
  • 6. And of course val ii: IOComp[Int] = ... valss: IOComp[String] = ... val (i: Err[Int], s: Err[String]) = exec(ii, ss) “Execute” multiple IO computations of different types, then return “embedded” types as a tuple.
  • 7. Finally valaa: Comp[A] = ... val bb: IOComp[B] = ... valcc: Comp[C] = ... valdd: IOComp[D] = ... val (a: A, b: Err[B], c: C, d: Err[D]) = exec(aa, bb, cc, dd) “Execute” a mixture of normal and IO computations.
  • 8. Teaser - the final API defexec[R](v: R)(implicit runner: Runner[R]): runner.Out
  • 9. My inspiration “Couldn’t you use HLists/KLists for this?” Miles Sabin’s Shapeless provided much direction and inspirations But … I’m not going to talk about Shapeless 
  • 10. The tricks defexec[R](v: R)(implicit runner: Runner[R]): runner.Out Tuple Type Dependent sweetness classes types
  • 11. Trick #1: tuple sweetness defecho[A](x: A) { prinln(x) } echo((“good”, 47, true)) echo(“good”, 47, true) // equivalent
  • 12. Trick #2: type classes Get the “size” of an object size(3) // 3 size(“hello”)// 5 size(false)// 1 ...
  • 13. Size type class traitSize[T] { defapply(in: T): Int Type class } object Size { defsize[S](v: S)(implicits: Size[S]): Int = s(v) implicit defIntSize = newSize[Int] { defapply(in: Int): Int = in } implicit defBoolSize = newSize[Boolean] { defapply(in: Boolean): Int = 1 Type class instances } implicit defStringSize = newSize[String] { defapply(in: String): Int = in.length } }
  • 14. Under the hood Implicit objects inserted by compiler size(3)(IntSize)// 3 size(“hello”)(StringSize)// 5 size(false)(BoolSize)// 1 ...
  • 15. Expanding the example Now, get the “size” of multiple objects at once size(3, “hello”,false)// (3,5,1)
  • 16. Runner type class traitRunner[In, Out] { defapply(in: In): Out } object Runner { defsize[I, O](v: I)(implicitr: Runner[I, O]): O = r(v) implicit def Tup1[T1](implicit s1: Size[T1]) = new Runner[T1, Int] { defapply(in: T1): Int = s1(in) Referencing } ‘Size’ implicit def Tup2[T1,T2](implicit s1: Size[T1], s2: Size[T2]) = new Runner[(T1,T2), (Int,Int)] { defapply(in: (T1,T2)): (Int,Int) = (s1(in._1), s2(in._2)) } implicit def Tup3[T1,T2,T3](implicit s1: Size[T1], ...
  • 17. Under the hood Implicit object inserted by compiler size(3, “hello”,false) (Tup3 IntSize, StringSize, BoolSize))
  • 18. Aside: type class sugar defsize[S](v: S)(implicitsizer: Size[S]): Int is equivalent to defsize[S : Size](v: S) : Int Type class constraint
  • 19. ‘size’ is easy Runner instances know output types are always Ints Returns Int size(3, “hello”,false)// (3,5,1) Returns Int Returns Int
  • 20. ‘exec’ is harder Output types are always different Returns Returns Err[B] Err[D] exec(aa, bb, cc, dd) Returns A Returns C
  • 21. Exec type class traitExec[In, Out] { defapply(in: In): Out } object Exec { implicit defcompExec[A] = newExec[Comp[A], A] { defapply(in: Comp[A]): A = execute(in) } implicit defioCompExec[A] = newExec[IOComp[A], Err[A]] { defapply(in: IOComp[A]): Err[A] = ioExecute(in) } }
  • 22. Updated Runner type class Runner return type is dependent on Exec return type traitRunner[In, Out] { defapply(in: In): Out } object Runner { defexec[I,O](v: I)(implicitr: Runner[I,O]): O = r(v) implicit def Tup1[T1](implicit ex1: Exec[T1,?]) = new Runner[T1, ?] { defapply(in: T1): ? = ex1(in) } Needs to be ... dependent on ex1
  • 23. It gets worse implicit def Tup2[T1,T2](implicit ex1: Exec[T1,?], ex2: Exec[T2,?]) = new Runner[(T1,T2),(?,?)] { defapply(in: (T1,T2)): (?,?) = (ex1(in._1), ex2(in._2)) } ...
  • 24. Trick #3: Dependent types Dependent types are types where the realization of the type created is actually dependent on the values being provided. (SCALABOUND)
  • 25. Type parameters vs members traitExec[In, Out] { Type parameter defapply(in: In): Out } is equivalent to traitExec[In] { typeOut Type member defapply(in: In): Out }
  • 26. Exec using type members traitExec[In] { typeOut defapply(in: In): Out } object Exec { implicit defcompExec[A] = newExec[Comp[A]] { typeOut = A defapply(in: Comp[A]): A = execute(in) } implicit defioCompExec[A] = newExec[IOComp[A]] { typeOut = Err[A] defapply(in: IOComp[A]): Err[A] = ioExecute(in) } }
  • 27. Using dependent method types traitRunner[In] { typeOut defapply(in: In): Out Return type is dependent on } ‘r’. Access type as a member. object Runner { defexec[R](v: R)(implicitr: Runner[R]): r.Out = r(v) implicit def Tup1[T1](implicit ex1: Exec[T1]) = new Runner[T1] { typeOut = ex1.Out defapply(in: T1): ex1.Out = ex1(in) } Return type is dependent on ... ex1’s return type
  • 28. And again implicit def Tup2[T1,T2](implicit ex1: Exec[T1], ex2: Exec[T2]) = new Runner[(T1,T2)] { typeOut = (ex1.Out, ex2.Out) defapply(in: (T1,T2)): Out = (ex1(in._1), ex2(in._2)) } ...
  • 29. Remember -Y Dependent method types are still experimental! (but “turned-on” in 2.10.x) -Ydependent-method-types