What does HList # foldLeft () return?

I am trying to play with HList from Shapeless.

This is my first attempt:

trait Column[T] { val name: String } case class CV[T](col: Column[T], value: T) object CV { object columnCombinator extends Poly2 { implicit def algo[A] = at[(String, String, String), CV[A]] { case ((suffix, separator, sql), cv) β‡’ (suffix, separator, if (sql == "") cv.col.name+suffix else sql+separator+cv.col.name+suffix) } } def combine[A <: HList](columns: A, suffix: String, separator: String = " and ") (implicit l: LeftFolder[A, (String, String, String), columnCombinator.type]): String = columns.foldLeft((suffix, separator, ""))(columnCombinator)._3 } 

The problem is that I do not know what foldLeft returns in this example.

I expect it to return (String, String, String) , but the compiler tells me that it returns l.Out . What is l.Out ?

The source code is a little hard to guess.

There is little information on the Internet about this.

Some information I consulted with:

+6
source share
2 answers

Your combine method returns what is called a β€œdependent type of method” , which means that its return type depends on one of its arguments β€” in this case, as a path-dependent type that includes l in its path.

In many cases, the compiler will statically know something about the dependent return type, but in your example this is not so. I will try to explain why per second, but first consider the following simpler example:

 scala> trait Foo { type A; def a: A } defined trait Foo scala> def fooA(foo: Foo): foo.A = foo.a fooA: (foo: Foo)foo.A scala> fooA(new Foo { type A = String; def a = "I'm a StringFoo" }) res0: String = I'm a StringFoo 

The res0 type in res0 is String , since the compiler statically knows that A argument foo String . However, we cannot write one of the following:

 scala> def fooA(foo: Foo): String = foo.a <console>:12: error: type mismatch; found : foo.A required: String def fooA(foo: Foo): String = foo.a ^ scala> def fooA(foo: Foo) = foo.a.substring <console>:12: error: value substring is not a member of foo.A def fooA(foo: Foo) = foo.a.substring ^ 

Because here the compiler does not statically know that foo.A is String .

Here's a more complex example:

 sealed trait Baz { type A type B def b: B } object Baz { def makeBaz[T](t: T): Baz { type A = T; type B = T } = new Baz { type A = T type B = T def b = t } } 

Now we know that it is impossible to create Baz with different types for A and B , but the compiler does not, so it will not accept the following:

 scala> def bazB(baz: Baz { type A = String }): String = baz.b <console>:13: error: type mismatch; found : baz.B required: String def bazB(baz: Baz { type A = String }): String = baz.b ^ 

This is exactly what you see. If we look at the code in shapeless.ops.hlist , we can make sure that the LeftFolder created here will have the same type for In and Out , but the compiler cannot (or, rather, win a 't-it design solution) follow us in this reasoning, which means that we will not consider l.Out as a tuple without additional evidence.

Fortunately, this proof is pretty easy to provide thanks to LeftFolder.Aux , which is just an alias for LeftFolder with an Out element as a parameter of the fourth type:

 def combine[A <: HList](columns: A, suffix: String, separator: String = " and ")( implicit l: LeftFolder.Aux[ A, (String, String, String), columnCombinator.type, (String, String, String) ] ): String = columns.foldLeft((suffix, separator, ""))(columnCombinator)._3 

(You can also use the syntax of a type element with the plain old LeftFolder type in l , but that would make this signature even more messy.)

The columns.foldLeft(...)(...) still returns l.Out , but now the compiler statically knows that a tuple of strings.

+11
source

After reading the full answer from Travis, here is a small variation of his solution:

 type CombineTuple = (String, String, String) def combine[A <: HList](columns: A, suffix: String, separator: String = " and ")( implicit l: LeftFolder[ A, CombineTuple, columnCombinator.type ] ): String = columns.foldLeft((suffix, separator, ""))(columnCombinator).asInstanceof[CombineTuple]._3 

Thus, an implicit signature is shorter as it is needed in many methods that cause this.

UPDATED: As Travis explained in the comments, it is better to use LeftFolder.Aux .

0
source

Source: https://habr.com/ru/post/977341/


All Articles