TransWikia.com

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

Stack Overflow Asked by six_minute_abs on December 18, 2021

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?

2 Answers

Consider the following simplified analogy

case class Score(v: Double) {
  def add(that: Score): Score = Score(this.v + that.v)
}

val l = List[Score](Score(1.1), Score(2.2), Score(0.1))

l.reduceLeft((a: Score, b: Score) => a.add(b))  // unsugared
l.reduceLeft((a: Score, b: Score) => a add b)   // infix notation
l.reduceLeft(_ add _)                           // anonymous function placeholder parameter

Note the usage of infix notation and anonymous function placeholder parameter

Answered by Mario Galic on December 18, 2021

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.)

Answered by Dmytro Mitin on December 18, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP