Typeclass in a directive

315 views
Skip to first unread message

Mark Grey

unread,
Aug 2, 2016, 9:28:08 PM8/2/16
to spray.io User List
I'd like to define a directive that does some custom filtering on an object fetched further up the request chain.  Ideally I'd like the logic to be determined by an injectable typeclass.  Here's a really simple example to demonstrate the technique

trait Queryable[T] {
 
def matches(elem:T, q:String):Boolean
}

trait
DirectivesMixin{
 
implicit object QueryInt extends Queryable[Int] {
   
def matches(elem:Int, q:String) = elem.toString.contains(q)
 
}

 
def directive[T : Queryable](l:Seq[T]) = parameter('query.?).flatMap {
    case Some(q) => provide(l.filter(implicitly[Queryable[T]].matches))
    case None => l
  }
}

Naturally the context bounds here would expect a `Queryable[Int]` be implicitly available, and since it's implementation is part of the same mix in it should be usable.

However, I can't seem to get usage of this to compile, EG:

get {
  onSuccess
(fetchIntFutureFunction){ints =>
    directive
(ints){ out =>
      complete
(out)
   
}
}

This fails to compile with: 

Missing parameter type
directive(ints){ out =>
                 ^


Fiddling around with it, I figured out that no matter what I do, it expects me to pass the `Queryable[Int]` as a parameter.  But then I am returning a directive and not a route, and the compilation fails with:

Found spray.routing.Directive1[Seq[Int]]
Required: RequestContext => Unit

It seems whatever is wrong with my signature is making it oblivious to the inner route being passed.

Is there some other directive I should use than provide here?

Thanks!

Mark


Age Mooij

unread,
Aug 3, 2016, 6:13:46 AM8/3/16
to spray...@googlegroups.com
Mixing directives (i.e. higher order functions) with implicit parameter lists is very tricky because the compiler just sees a function that takes two parameter lists and then you call it with what the compiler can only interpret as two parameter lists. This is one of the reasons for the heavy use of the magnet pattern in spray routing.

In this case, it might be easier to move the filtering operation to non-directive code. Perhaps something like this:

import scala.concurrent._
import spray.routing._

trait Queryable[T] {
  def matches(elem: T, q: String): Boolean
}

object Queryable {
  implicit object QueryInt extends Queryable[Int] {
    def matches(elem: Int, q: String) = elem.toString.contains(q)
  }
}

trait NonDirectivesMixin {
  def fetchIntFutureFunction: Future[Seq[Int]] = ???

  implicit class SeqWithOptionalFiltering[T: Queryable](ts: Seq[T]) {
    def optionalFilter(query: Option[String]): Seq[T] = query match {
      case Some(q) ⇒ ts.filter(t ⇒ implicitly[Queryable[T]].matches(t, q))
      case None    ⇒ ts
    }
  }
}

trait UserlistExperimentRoutes extends Directives with NonDirectivesMixin {
  implicit def ec: ExecutionContext

  def route = get {
    parameter('query.?) { q ⇒
      onSuccess(fetchIntFutureFunction) { ints ⇒
        complete(ints.optionalFilter(q).mkString)
        // I used mkString for this example since I don't
        // have a marshaller[Seq[Int]] in scope
      }
    }
  }
}

Hope this helps
Age


--
You received this message because you are subscribed to the Google Groups "spray.io User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to spray-user+...@googlegroups.com.
Visit this group at https://groups.google.com/group/spray-user.
To view this discussion on the web visit https://groups.google.com/d/msgid/spray-user/fb823e95-9c6e-4efe-aea3-d452885525d1%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Mark Grey

unread,
Aug 3, 2016, 11:59:56 AM8/3/16
to spray.io User List
Thanks a bunch for the help!  Definitely confirms what I thought was the main obstacle. 

This is more or less what I ended up settling on, though my design probably needs some pretty significant refactors :/  Your approach with the future is much nicer than what I landed on :)

~MGII
Reply all
Reply to author
Forward
0 new messages