scala - Implicit conversion not working with type-safe builder pattern -


i using scala type-safe builder pattern simple rest request. works great fluent api.

sealed abstract class method(name: string)  case object extends method("get") case object post extends method("post")  abstract class true abstract class false  case class builder[hasmethod, hasuri](   method: option[method],   uri: option[string]) {    def withmethod(method: method): builder[true, hasuri] = copy(method = some(method))   def withuri(uri: string): builder[hasmethod, true] = copy(uri = some(uri)) }  implicit val init: builder[false, false] = builder[false, false](none, none)  //fluent examples val b1: builder[true, false] = init.withmethod(get) val b2: builder[true, true] = init.withmethod(get).withuri("bar") 

i make more dsl-like allowing method instance converted builder instance, when add try implicitly include init builder combination of implicit conversion , type parameters confuse compiler.

implicit def tomethod[hasuri](m: method)   (implicit builder: builder[_, hasuri]): builder[true, hasuri] = builder.withmethod(m)  // ** error **: not find implicit value parameter builder:  //              builder[_, hasuri]   val b3: builder[true, true] = withuri "foo"  // implicit parameter discovered fine when function called directly val b4: builder[true, false] = tomethod(get) val b5: builder[true, true] = tomethod(get) withuri "foo" 

all lines compile except b3. when tomethod function called explicitly builder parameter can found implicitly. if remove generic arguments (and type-safety) code works expected.

is limitation in scala's implicit conversions? or missing correct syntax achieve this?

i want discover initial builder instance implicitly enable users provide own initial builder default values of builder's fields.

updated

i have left of code out keep example simple, since implicit conversion trying fix.

the type-safe builder pattern outlined here: http://blog.rafaelferreira.net/2008/07/type-safe-builder-pattern-in-scala.html

afterwards can call build method once builder has method , uri.

the reason want discover builder implicit parameter support following case in dsl.

url("http://api.service.org/person") apply { implicit b =>   assert(ok , validjson)   / "john.doe" assert(notfound)   post body johndoedata assert(ok)   / "john.doe" assert(ok , bodyis(johndoedata)) } 

in these cases

  1. a new builder created specified uri url
  2. this reused in side closure implicit b =>
  3. the assert method available because uri , method have been specified
  4. the / appends current uri, available because builder has uri specified.

another example method , uri specified

get url("http://api.service.org/secure/person") apply { implicit b =>   auth basic("harry", "password") assert(ok , validjson)   auth basic("sally", "password") assert(permissiondenied) } 

i have feeling implicit resolution problem not come limitation in scala type system, depends on existential type specify here:

implicit def tomethod[hasuri](m: method)   (implicit builder: builder[_, hasuri]): builder[true, hasuri] = builder.withmethod(m) 

if not wrong, existential type treated nothing in case. nothing subclass of every possible scala class, method becomes in fact:

implicit def tomethod[hasuri](m: method)   (implicit builder: builder[nothing, hasuri]): builder[true, hasuri] = builder.withmethod(m) 

scala in current scope find subclass of builder[nothing,hasuri] provide method , there no class can match required type except builder[nothing,hasuri], because builder class invariant, i.e. builder[a,b]<:<builder[c,d] iff a=:=c & b=:=d

you have therefore 2 options:

  • add parameter signature, tomethod[hasuri] becomes tomethod[a,hasuri]
  • exploit scala correct implementation of type variances

since want enforce builder[a,hasuri] subclass of builder[nothing,hasuri] and

nothing <:< 

you want enforce builder[a,hasuri] <:< builder[b,hasuri] iff b<:<a i.e. builder controvariant in first type parameter. enforce controvariance putting - simbol in front of type:

builder[-hasmethod, hasuri] controvariant in hasmethod , invariant in hasuri


conclusion

type systems powerful, it's not mandatory use complex patterns simple tasks:

  • hasuri not inferred m, type parameter method tomethod
  • hasmethod not inferred, because erase using _

what point of having implicit parameter 2 generics arguments if arguments not involved in resolution ? write:

case class defaultbuilder(m:method) extends builder[true,hasuri] 

when end these kind of situations, has said, it's because design wrong problem. can explain why builder has implicit in tomethod?

implicit def tomethod(m:method) = defaultbuilder(m) 

Comments

Popular posts from this blog

c# - DetailsView in ASP.Net - How to add another column on the side/add a control in each row? -

javascript - firefox memory leak -

Trying to import CSV file to a SQL Server database using asp.net and c# - can't find what I'm missing -