Здравствуйте: я недавно изучал Scala (мой опыт работы в основном связан с шаблонами C ++), и я столкнулся с тем, чего в настоящее время не понимаю в Scala, и это сводит меня с ума.: (
(Кроме того, это мой первый пост в StackOverflow, где я заметил, что большинство действительно потрясающих людей из Scala тусуются, поэтому мне очень жаль, если я сделаю что-то ужасно глупое смеханизм.)
Моя конкретная путаница связана с неявным связыванием аргументов: я выдвинул конкретный случай, когда неявный аргумент отказывается связываться, но функция с, казалось бы, идентичной семантикой делает.Конечно, это может быть ошибка компилятора, но, учитывая, что я только начал работать со Scala, вероятность того, что я уже столкнулся с какой-то серьезной ошибкой, достаточно мала, и я ожидаю, что кто-то объяснит, что я сделал неправильно;P
Я просмотрел код и немного его урезал, чтобы найти единственный пример, который не работает. К сожалению, этот пример все еще достаточно сложен, поскольку проблема, кажется, возникает тольков обобщении.: (
1) упрощенный код, который не работает в ваy Я ожидал
import HList.::
trait HApplyOps {
implicit def runNil
(input :HNil)
(context :Object)
:HNil
= {
HNil()
}
implicit def runAll[Input <:HList, Output <:HList]
(input :Int::Input)
(context :Object)
(implicit run :Input=>Object=>Output)
:Int::Output
= {
HCons(0, run(input.tail)(context))
}
def runAny[Input <:HList, Output <:HList]
(input :Input)
(context :Object)
(implicit run :Input=>Object=>Output)
:Output
= {
run(input)(context)
}
}
sealed trait HList
final case class HCons[Head, Tail <:HList]
(head :Head, tail :Tail)
extends HList
{
def ::[Value](value :Value) = HCons(value, this)
}
final case class HNil()
extends HList
{
def ::[Value](value :Value) = HCons(value, this)
}
object HList extends HApplyOps {
type ::[Head, Tail <:HList] = HCons[Head, Tail]
}
class Test {
def main(args :Array[String]) {
HList.runAny( HNil())(null) // yay! ;P
HList.runAny(0::HNil())(null) // fail :(
}
}
Этот код, скомпилированный с Scala 2.9.0.1, возвращает следующую ошибку:
broken1.scala:53: error: No implicit view available from HCons[Int,HNil] => (java.lang.Object) => Output.
HList.runAny(0::HNil())(null)
В этом случае я ожидаю, что runAll
будетбыть привязанным к неявному аргументу run
к runAny
.
Теперь, если я изменю runAll
, чтобы вместо непосредственного получения двух аргументов он возвращал функцию, которая в свою очередь принимает эти двааргументы (трюк, который я подумал попробовать, как я видел в чужом коде), он работает:
2) модифицированный код, который имеет то же поведение во время выполнения и фактически работает
implicit def runAll[Input <:HList, Output <:HList]
(implicit run :Input=>Object=>Output)
:Int::Input=>Object=>Int::Output
= {
input =>
context =>
HCons(0, run(input.tail)(context))
}
По сути, мой вопрос: почему это работает?; (Я ожидаю, что эти две функции имеют одинаковую общую сигнатуру типа:
1: [Input <:HList, Output <:HList] (Int::Input)(Object):Int::Output
2: [Input <:Hlist, Output <:HList] :Int::Input=>Object=>Int::Output
Если это помогает понять проблему, некоторые другие изменения также «работают» (хотя они изменяют семантику функции, иследовательно, не пригодные для использования решения):
3) жесткое кодирование runAll
только для второго уровня путем замены Output на HNil
implicit def runAll[Input <:HList, Output <:HList]
(input :Int::Input)
(context :Object)
(implicit run :Input=>Object=>HNil)
:Int::HNil
= {
HCons(0, run(input.tail)(context))
}
4Удаление аргумента контекста из неявных функций: (
(В настоящее время мое лучшее предположение состоит в том, что порядок неявного аргумента по отношению к другим аргументам является ключевым фактором, который я пропускаю, но тот, который меня смущает: runAny
также имеет неявный аргумент в конце, поэтому очевидное «implicit def
не очень хорошо работает с завершающим implicit
» не имеет смысла для меня.)