2
\$\begingroup\$

I'm trying to make a scala interface to the spotify API. I'm using it to learn Scala. I come from mostly a python background.

import dispatch._, Defaults._
trait Request {
 val name: String
 val endpoint: String
}
case class Track(name: String) extends Request {
 override val endpoint = "track.json"
}
case class Album(name: String) extends Request {
 override val endpoint = "album.json"
}
case class Artist(name: String) extends Request {
 override val endpoint = "artist.json"
}
object Spotify {
 def getResponse(req: Request) = {
 val params = Map("q" -> req.name)
 val spotifyUrl = host("ws.spotify.com")
 spotifyUrl / "search"/ "1" / req.endpoint <<? params
 }
 val foo = Album("foo")
 val songs = Http(getResponse(foo) OK as.String)
}

I feel like getResponse should be in the Request trait, or something since it's generic enough to only need the overridden endpoint - but not sure. Any general recommendations on structuring this sort of thing would be helpful.

Simon Forsberg
59.7k9 gold badges157 silver badges311 bronze badges
asked Dec 14, 2013 at 21:21
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Decouple everything.

I am certain that Scala veterans will have more to say than I do and possibly comment over my comments.

Here are some changes and respective comments to your code.

import dispatch._, Defaults._
// decouple from any "master" class.
case class Track(name: String)
case class Album(name: String)
case class Artist(name: String)
// This is called a structural type. We are using a def
// because a val restricts extensibility.
type Named = {def name: String}
// Separate out the endpoints into their own category
object endpointResolver {
 // Use a partial function which you can re-use in order parts
 // of your program. You may use this in your testing as well.
 val endPoints: PartialFunction[Named, String] = {
 case _: Track => "track.json"
 case _: Album => "album.json"
 case _: Artist => "artist.json"
 }
 // use an implicit class. This allows you to add methods
 // to existing classes seamlessly.
 implicit class nrEndpoint(namedRequest: Named) {
 def endpoint: String =
 endPoints apply namedRequest
 }
}
object Spotify {
 // Specify the return type. I was not sure what it was initially.
 def getResponse(req: Named): Req = {
 val params = Map("q" -> req.name)
 val spotifyUrl = host("ws.spotify.com")
 // import and utilise this implicit class
 import endpointResolver.nrEndpoint
 spotifyUrl / "search"/ "1" / req.endpoint <<? params
 }
 val foo = Album("foo")
 // OK is another example of an implicit being used. Use an IDE
 // such as IntelliJ IDEA to navigate through the vast depths
 // of Scala source.
 val songs = Http(getResponse(foo) OK as.String)
}
answered Dec 14, 2013 at 21:58
\$\endgroup\$
1
  • \$\begingroup\$ Very interesting, thanks for the code (and more for the comments). Lots of stuff to play with here. I'm not going to accept for a bit uncase like you say a veteran comes in, but will if that doesn't happen. \$\endgroup\$ Commented Dec 14, 2013 at 22:35

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.