Search code examples
scalatuplesscala-3

A tuple and a tuple of predicates to boolean in Scala


Any idea on how to implement the following function in Scala 3?

I am okay with inline or not, and generally, a slight syntax change is ok (even macros are fine). But I don't know how to do this without asInstanceOf.

type Pred[A] = A => Boolean

/** Computes values of predicates and combines them with `&&`.
  * 
  * The implementation does not use `asInstanceOf` or `isInstanceOf` */
inline def tupled[Args <: Tuple](inline preds: Tuple.Map[Args,Pred], inline args: Args): Boolean

Example:

val pred: (Int => Boolean, Int => Boolean) = (_ < 0, _ > 0)

println(tupled[(Int, Int)](pred)( 1, 2)) // should print false
println(tupled[(Int, Int)](pred)(-1, 2)) // should print true

Solution

  • How about this one:

    trait Reducer[Args <: Tuple, Preds <: Tuple]:
        def reduce(args: Args, preds: Preds): Boolean
    
    object Reducer:
        given Reducer[EmptyTuple, EmptyTuple] = (args, preds) => true
    
        given [H, Args <: Tuple, Preds <: Tuple](using r: Reducer[Args, Preds]): Reducer[H *: Args, (H => Boolean) *: Preds] with
            def reduce(args: H *: Args, preds: (H => Boolean) *: Preds): Boolean = (args, preds) match
                case (ha *: ta, hp *: tp) => hp(ha) && r.reduce(ta, tp)
    
    type Pred[A] = A => Boolean
    
    /** Computes values of predicates and combines them with `&&`.
     *
     * The implementation does not use `asInstanceOf` or `isInstanceOf` */
    def tupled[Args <: Tuple](preds: Tuple.Map[Args, Pred], args: Args)
                             (using r: Reducer[Args, Tuple.Map[Args, Pred]]): Boolean =
        r.reduce(args, preds)
    
    
    scala> val pred: (Int => Boolean, Int => Boolean) = (_ < 0, _ > 0)
         |
         | println(tupled[(Int, Int)](pred, ( 1, 2))) // should print false
         | println(tupled[(Int, Int)](pred, (-1, 2)))
    false
    true
    

    And we can ignore the type parameter if swap the arguments:

    scala> def tupled2[Args <: Tuple](args: Args, preds: Tuple.Map[Args, Pred])
         |                          (using r: Reducer[Args, Tuple.Map[Args, Pred]]): Boolean =
         |     r.reduce(args, preds)
         |
    def tupled2[Args <: Tuple](args: Args, preds: Tuple.Map[Args, Pred])(using r: Reducer[Args, Tuple.Map[Args, Pred]]): Boolean
    
    scala> val pred: (Int => Boolean, Int => Boolean) = (_ < 0, _ > 0)
         |
         | println(tupled2(( 1, 2), pred)) // should print false
         | println(tupled2((-1, 2), pred))