scala - Infinite recursion with Shapeless select[U] -


i had neat idea (well, that's debatable, let's had idea) making implicit dependency injection easier in scala. problem have if call methods require implicit dependency, must decorate calling method same dependency, way through until concrete dependency in scope. goal able encode trait requiring group of implicits @ time it's mixed in concrete class, go calling methods require implicits, defer definition implementor.

the obvious way kind of selftype la psuedo-scala:

object thingdoer {   def getsomething(implicit foo: foo): int = ??? }  trait mytrait { self: requires[foo , bar , bubba] =>   //this fails compile unless dothing takes implicit foo   def dothing = thingdoer.getsomething } 

after few valiant attempts implement trait and[a,b] in order nice syntax, thought smarter start shapeless , see if anywhere that. landed on this:

import shapeless._, ops.hlist._  trait requires[l <: hlist] {   def required: l   implicit def provide[t]:t = required.select[t] }  object thingdoer {   def needsint(implicit i: int) = + 1 }  trait mytrait { self: requires[int :: string :: hnil] =>   val foo = thingdoer.needsint }  class myimpl extends mytrait requires[int :: string :: hnil] {   def required = 10 :: "hello" :: hnil   def showme = println(foo) } 

i have say, pretty excited when compiled. but, turns out when instantiate myimpl, infinite mutual recursion between myimpl.provide , required.provide.

the reason think it's due mistake i've made shapeless when step through, it's getting select[t] , steps hlistops (makes sense, since hlistops has select[t] method) , seems bounce call requires.provide.

my first thought it's attempting implicit selector[l,t] provide, since provide doesn't explicitly guard against that. but,

  1. the compiler should have realized wasn't going selector out of provide, , either chosen candidate or failed compile.
  2. if guard provide requiring receive implicit selector[l,t] (in case apply selector t) doesn't compile anymore due diverging implicit expansion type shapeless.ops.hlist.selector[int :: string :: hnil], don't know how go addressing.

aside fact idea misguided begin with, i'm curious know how people typically go debugging these kinds of mysterious, nitty-gritty things. pointers?

when confused related implicits / type-level behaviour, tend find reify technique useful:

scala> import scala.reflect.runtime.universe._ import scala.reflect.runtime.universe._ scala> val required: hlist = hnil required: shapeless.hlist = hnil scala> reify { implicit def provide[t]:t = required.select[t] } res3: reflect.runtime.universe.expr[unit] = expr[unit]({   implicit def provide[t]: t = hlist.hlistops($read.required).select[t](provide);   () }) 

at point it's easy see what's gone wrong - compiler thinks provide can provide arbitrary t (because that's you've told it), calls provide required selector[l, t]. @ compile time resolves once, there no diverging implicit, no confusion @ compile time - @ run-time.

the diverging implicit expansion happens because compiler looks selector[int :: string :: hnil], thinks provide give 1 if given selector[int :: string :: hnil, selector[int :: string :: hnil]], thinks provide give 1 if given selector[int :: string :: hnil, selector[int :: string :: hnil, selector[int :: string :: hnil]] , @ point realises infinite loop. where/how expecting selector needs? think provide misguided because it's general. try making call thingdoer.needsint explicit int work first before trying make implicit.

this general approach work - i've written applications use di mechanism -though beware of quadratic compile times.


Comments

Popular posts from this blog

OpenCV OpenCL: Convert Mat to Bitmap in JNI Layer for Android -

android - org.xmlpull.v1.XmlPullParserException: expected: START_TAG {http://schemas.xmlsoap.org/soap/envelope/}Envelope -

python - How to remove the Xframe Options header in django? -