How to write this function without all the type declarations

Is there a way to write this function without all the type declarations?

code on scastie

I wanted to write the transform functions like this, but couldn’t make it work.

    def r90(x,y) = (/(y), x)
    def r180(x,y) = (/(x), /(y))
    ...

and then

    val transforms = List[(Byte,Byte) => POSITION](id, r90,  r180, r270,
                                                my, mx, mxy, mxr90)
    transforms.map { tr =>
      placement.map { p: POSITION => tr.tupled(p) }
    }

Do you care about the names of the functions?

If not, I would do this:

val transforms: List[POSITION => POSITION] = List(
  x => x // id = x, y
  { case (x, y) => (/(y), x) }, // r90 = -y,  x
  ....
)

If you care about the names, I would do this:

def genSymmetries(n: Byte, placement: Set[POSITION]): List[Set[POSITION]] = {
  def /(x: Byte): Byte = (n - x + 1).toByte

  type Transformation = POSITION => POSITION

  val id: Transformation = x => x
  val r90: Transformation = {
    case (x, y) => (/(y), x)
  } // -y,  x
  ...

  val transforms = List(id, r90, r180, r270, my, mx, mxy, mxr90)
1 Like

A side note:

type POSITION = (Byte, Byte)

That gets erased to (rather heavy):

type POSITION = Tuple2[Object, Object]

because Tuple2 isn’t specialized for Bytes https://github.com/scala/scala/blob/2.13.x/src/library/scala/Tuple2.scala

final case class Tuple2[@specialized(Int, Long, Double, Char, Boolean/*, AnyRef*/) +T1, @specialized(Int, Long, Double, Char, Boolean/*, AnyRef*/) +T2](_1: T1, _2: T2)
  extends Product2[T1, T2]

Solution with a set of named functions https://scastie.scala-lang.org/LZmsBK7YScWWg2K2xNIENw

type Position = (Byte, Byte) // erases to heavy Tuple2[Object, Object]

def genSymmetries(n: Byte, placement: Set[Position]): List[Set[Position]] = {
  type Transform = (Byte, Byte) => Position
  def /(x: Byte): Byte = (n - x + 1).toByte
  val id: Transform = (x, y) => (x, y)
  val r90: Transform = (x, y) => (/(y), x) // -y,  x
  val r180: Transform = (x, y) => (/(x), /(y)) // -x, -y
  val r270: Transform = (x, y) => (y, /(x)) //  y, -x
  val mx: Transform = (x, y) => (x, /(y)) //  x, -y   mirror about x axis
  val my: Transform = (x, y) => (/(x), y) // -x, y    mirror about y axis
  val mxy: Transform = (x, y) => (y, x) //  y, x
  val mxr90: Transform = (x, y) => (/(y), /(x)) // -y, -x
  
  val transforms: List[(Byte, Byte) => Position] =
    List(id, r90, r180, r270, my, mx, mxy, mxr90)
  
  transforms.map { transform =>
    val tupledTransform: Position => Position = transform.tupled
    placement.map { case p @ (x, y) =>
      transform(x, y)
      // or if tupling is needed for some reason then
      tupledTransform(p)
    }
  }
}


genSymmetries(5, Set(3.toByte -> 2.toByte)).foreach(println)
3 Likes

Variant:

type PosMapping = POSITION => POSITION
val id: PosMapping = identity //  x,  y
val r90: PosMapping = { case (x,y) => (/(y), x) } // -y,  x
// ...

That’s good to know. I was trying to use Byte to save memory because the program does an exhaustive search of permutations/combinations to attempt to satisfy a particular relationship. My how was that since my values on only ever between 1 and 10 inclusive, then I could use a Byte to store it. But two Objects is sure to be 2x64 bits. Potentially I could use Char, but I’m not sure whether I can do arithmetic on Char.

OK, so I have to use a val rather than a def, that was not obvious to me.

No, the names are not important to me. I just used them so I could work out all the useful permutations of x, -x, y, -y.
Before I wrote the functions, I didn’t see the pattern. Now it’s clear and could probably be written more concise. But most of my time was battling to make the numerical addition be Byte addition and not accidentally Integer addition, and battling against binary functions and unary functions taking a Tuple2.

So yeah, I would write all the functions directly in the list.

BTW, about what @tarsa said, the best would be to make your own position class.

final case class Position(x: Byte, y: Byte)
1 Like

Thanks for the help guys.

BTW, IntelliJ gets really confused on the indentation here. It seems to ignore the open paren.
That’s depressing. But otherwise, the function is pretty concise now.

  def genSymmetries(n: Byte, placement: Set[POSITION]): List[Set[POSITION]] = {
    def /(x:Byte):Byte = (n - x + 1).toByte
    val transforms = List[POSITION => POSITION](
    {case (x,y) => (x,y)},
    {case (x,y) => (/(x),y)},
    {case (x,y) => (/(x),/(y))},
    {case (x,y) => (x,/(y))},
    {case (y,x) => (x,y)},
    {case (y,x) => (/(x),y)},
    {case (y,x) => (/(x),/(y))},
    {case (y,x) => (x,/(y))})

    transforms.map { tr =>
      placement.map(tr)
    }
  }