scalamethodsscala-catscircearity

Why does the circe `or` function (an apparently unary function) work with reduceLeft which requires a binary op?


A and I are doing some work with circe to encode/decode some ADTs and we ran into some functionality we fundamentally don't understand. The examples given in the circe documentation work as expected, but upon drilling down - it's not clear why the decoding example works and therefore we are having a hard time reasoning about how to modify if needed.

The functionality (from Circe Examples about ADTs):

import cats.syntax.functor._
import io.circe.{ Decoder, Encoder }, io.circe.generic.auto._
import io.circe.syntax._

object GenericDerivation {
  // Encoder Redacted

  implicit val decodeEvent: Decoder[Event] =
    List[Decoder[Event]](
      Decoder[Foo].widen,
      Decoder[Bar].widen,
      Decoder[Baz].widen,
      Decoder[Qux].widen
    ).reduceLeft(_ or _)
}

I get it - basically pick the first decoder that works from this list- makes sense BUT(!)

or appears to be a unary function taking a by name argument. Type signature is:

final def or[AA >: A](d: => Decoder[AA]): Decoder[AA]

And reduceLeft (from IterableOnce) requires a binary function. Type Signature is: def reduceLeft[B >: A](op: (B, A) => B): B

And then my brain exploded. I am clearly missing something and can't figure it out.

The example most decidedly works to convert ADTs. Why/how does this work given that the or function doesn't seem to be meeting the required type by reduceLeft?


Solution

  • or is a method of one parameter but don't forget about this.

    decoder1.or(decoder2) (aka decoder1 or decoder2) is a binary function with respect to decoder1, decoder2.

    + is also a method of one parameter

    final abstract class Int private extends AnyVal {
      ...
    
      /** Returns the sum of this value and `x`. */
      def +(x: Int): Int
    
      ...
    }
    

    but you can still add two Ints: 1 + 1 aka 1.+(1).

    All methods have one "parameter" more than listed in their signatures, namely this.

    (All ordinary parameters are resolved statically and this is resolved dynamically.)