I will try to post my own answer, improving it later. We start with a motivational scenario, but you can go to TL; DR below, and then return here as needed.
In one scenario, the parameters of evidence can be considered as a means of enriching the class with some behavior (method / s) outside its initial definition.
Soft redistribution of a large position in Cake Solutions :
In case you have not yet intuitively written such code before, here is a code that demonstrates the used parameters of the proof.
object EvidenceExample { // class with no methods case class Bar(value: String) // a trait the class Bar had not implemented trait WithFoo[A] { def foo(x: A): String } // object that attaches an implementation of the trait for Bar - for methods // willing to play along with this kind of trait attachment - see immediately below implicit object MakeItFoo extends WithFoo[Bar] { def foo(x: Bar) = x.value } // method willing to recognize anything as having trait WithFoo, // as long as it has evidence that it does - the evidence being the previous object def callFoo[A](thing: A)(implicit evidence: WithFoo[A]) = evidence.foo(thing) callFoo(Bar("hi")) // and it works }
You can read this code from the bottom up to understand that the Bar class has been enriched beyond its original definition. However, only the functions that play along with the testimony ceremony can see it enriched.
There is very little magic in this template - although it is a unique language function - the wrapper object associates the attribute with Bar , and callFoo relies on this association.
We could even write the same template without implications, but then the last line that calls the method will require an additional parameter - the cost-effectiveness of using implicit or not - is completely up to you.
You can use sugar or sugar as you wish, for example, with a slight improvement in syntax:
(only the last def is changed here and the comments are deleted now)
object EquivalentEvidenceExample { case class Bar(value: String) // a trait the class Bar had not implemented trait WithFoo[A] { def foo(x: A): String } implicit object MakeItFoo extends WithFoo[Bar] { def foo(x: Bar) = x.value } def callFoo[A:WithFoo](thing: A) = implicitly[WithFoo[A]].foo(thing) // lightly sugared syntax, frankly only more confusing callFoo(Bar("hi")) }
And nothing requires you to name anything with a string of evidence . The compiler simply knows that this is a proof parameter, since it is used in all these equivalent cases.
More generally or etymologically, borrowing from another answer , a proof parameter is one that “proves” a particular property of the type, and this requires a compiler where the method signature expresses such a requirement (in another answer there is no data presented for the Any type, which is <:< Foo , as required by the signature of the method, therefore, this is a case of lack of evidence).
Failure to have an evidence object accessible as implicit will result in the well-known could not find implicit value for evidence parameter of type ... because the compiler knows that this is part of the evidence template and not just the missing implicit (how important this is for you),
TL; DR:
In short, the proof parameter for some class S is a parameter of type T[S] (so, a parameter that is a class) that defines one or more things about S - thus “proving” something about S - what S does acceptable for extended use by the caller, beyond the original definition of S An exact form, such as T[S] , should be given in my borrowed examples above, an implicit object MakeItFoo .