Quick scala question: return type is true type of "this"

I created a trait: “RestfulService” whose purpose is to build requests where knowledge about the request details are embedded in the object.

`
trait RestfulService {
protected def description : String // the description that Gatling will associate the request with for reporting
protected def method : String // the HTTP request method
protected def url : String // the URL to request
protected def body : String // the request body to send

// add request headers
protected var requestHeaders: Map[String,String] = Map()
protected def header( key: String, value: String ) = {
requestHeaders += ( key → value )
this
}

// add query parameters
protected var queryParams: Map[String,Any] = Map()
protected def queryParameter( key: String, value: Any ) = {
queryParams += ( key → value )
this
}

// add form parameters
protected var formParams: Map[String,Any] = Map()
protected def formParameter( key: String, value: Any ) = {
formParams += ( key → value )
this
}
// …
}

`

The intent is to create classes with this trait which creates public methods which are implemented by using these methods. Problem is, the return type of “this” is RestfulService, and not the true type of the object itself.

What is the scala syntax for saying that the return type of the method is the type of the object on which the method was called?

Why not having a self-bounded parameter type like you did until now?

Then, DON’T USE VAR, you’re going to shoot yourself in the foot!

I tried:

`
trait RestfulService[T <: RestfulService[T]]
trait RtdeService extends RestfulService[RtdeService]
trait PagingParameters extends RtdeService
trait StartAndEndDateParameters extends RtdeService

class SomeService extends RtdeService with PagingParameters with StartAndEndDateParameters {
def description = “the description”
def method = “GET”
def path = “/v1/method”
def body = “”

def someProperty( value: String ) = this.queryParameter( “property”, value )
def default =
this
.startDate( ISO_TIMESTAMP.daysAgo( 30 ) )
.endDate( ISO_TIMESTAMP.daysFromNow( 30 ) )
.someProperty( “foo” )
}

`

What happens is, the return type of .endDate does not contain someProperty.

I kept digging, and I found “this.type” in order to specify that the return type needs to be the type of the object. That seems to solve the problem.

I hear you about the use of “var” - because the code is only run during scenario construction, it should be okay. But at the same time, I wouldn’t mind converting it to be more “safe”. What would that look like?

I hear you about the use of "var" - because the code is only run during
scenario construction, it should be okay.

Not if you use elements as prototypes for building other elements (which
you can do with Gatling DSL elements as they're all immutable).

A few months ago, someone was doing the same kind of mutable DSL like you
and didn't understand why he had this kind of issue:

val request = myOwnCustomMutableRequestDSL

exec(request) // OMG, why does this send foo?! Gatling is buggy for sure!!!
.exec(request.setSomeAdditionalParameter("foo"))

You have to make all your DSL methods return new instance and not share any
mutable state, just like Gatling DSL. Case classes copy method is just
amazing for this.

I don’t think that can hit me, because I would never (be stupid enough) to re-use the same object. My DSL would look like:

exec( ServiceName.method.someParam(foo) )
.exec( ServiceName.method.someParam(bar) )

And ServiceName.method is a method that returns a new object. For that reason, I would have to do something like this:

var someRequestObject = ServiceName.method

exec( someRequestObject )
exec( someRequestObject.someParam(foo) )

THAT would bite me. But I would never do that. But I’m not the only one who will ever use my code. Sounds like reason enough to make it immutable! :slight_smile:

So, this is what I have:

// add query parameters protected var queryParams: Map[String,Any] = Map() protected def queryParameter( key: String, value: Any ) : this.type = { queryParams += ( key -> value ) this }

What would that look like in order to be immutable? I’d have to define all of my service methods as a case class:

Then in my base RestfulService trait, I need to copy and modify the object, this.copy( queryParams = queryParams += ( key → value ) )

Wait, that doesn’t work with a trait, because this.copy is not a member of type RestfulService.

And I hear inheriting from a case class is deprecated, so I can’t make RestfulService a case class.

So, back to the drawing board. Is there another way?