Is it possible to accept a sequence of values and ...
# getting-started
a
Is it possible to accept a sequence of values and have a method that accepts them? Example:
(255, 255, 255, 255)toRGBA()
r
What's the value of having that over a top level function?
Copy code
fun toRGBA(r: Int, g: Int, b: Int, a: Int): RGBA
v
(255, 255, 255, 255)
is not a thing. If you make it a thing like with
listOf(255, 255, 255, 255)
, you should then be able to create an extension function
List<Int>.toRGBA()
if you really want to
r
You could make an extension function on a collection:
Copy code
fun List<Int>.toRGBA(): RGBA {
   return // something
}

val codes = listOf(255, 255, 255, 255)
val rgba = codes.toRGBA()
a
The goal is to replace
toRGBA(255, 255, 255, 255)
v
But why?
1
🤔 1
a
Not sure, I asked one of my developers and they asked for it.
apart of the component DSL builder
m
(255, 255, 255, 255)toRGBA()
to replace
toRGBA(255, 255, 255, 255)
?
you can do something like this: but you can't redefine
,
with the infix operator, so it has to be some other character `
Copy code
fun main() {
    (1 ç 2 ç 3).toRgba()
}

infix fun Int.ç(other: Int) = listOf(this, other)
infix fun List<Int>.ç(other: Int) = this + other
fun List<Int>.toRgba() = { TODO() }
1
t
I'm gonna go with what others seem to think here and just suggest not to try this at all. I wouldn't want a function that I could just call on any arbitrary Int-List. I also think that readability is much better when the keyword (RGBA) is in front, so you immediately know what's happening and don't need to wonder what these numbers are about. But maybe you might want to change the function name. The 4 numbers are probably already the RGBA values, so I guess you're not actually converting anything to RGBA. Maybe you're converting to a hex string or something. The name should reflect what's actually happening. But just regarding the general question - independent from the RGBA context - no, this is not directly possible in Kotlin and I can't think of a case where I would even prefer this syntax over a normal function. The list-based workaround is possible, but just brings some unsafety and uglifies your code.
👍 2
e
in theory you could do something like
Copy code
@JvmInline value class RG(val value: Int)
@JvmInline value class RGB(val value: Int)
@JvmInline value class RGBA(val value: Int)

infix fun Int.ç(g: Int) = RG(this.and(0xFF).shl(8) or g.and(0xFF))
infix fun RG.ç(b: Int) = RGB(value.and(0xFFFF).shl(8) or b.and(0xFF))
infix fun RGB.ç(a: Int) = RGBA(value.and(0xFFFFFF).shl(8) or a.and(0xFF))

(1 ç 2 ç 3 ç 4).value == 0x01020304
and make it type-safe, but I see absolutely no reason for something as ludicrous as this
👍 2
s
I assume
RGBA
is a data class (you could even have a default argument for A!
Copy code
data class RGBA (
  val red: Int,
  val green: Int,
  val blue: Int,
  val alpha: Int = 255,
) {
  init {
    fun requireInRange(x: Int, name: String) {
      require(x in 0..255) {"param $name is supposed to be in range 0..255 but was $x"}
    }
    requireInRange(red, "red")
    requireInRange(green, "green")
    requireInRange(blue, "blue")
    requireInRange(alpha, "alpha")
  }
}
)!? So every literal form could simply be
Copy code
RGBA(255, 255, 255, 255)
or
Copy code
RGBA(255, 255, 255) // alpha = 255
and whenever you don't don't work with hardcoded values but with a list of Ints that you read in from somewhere, you could have the an extension function
Copy code
// maybe also/or on Array<Int>
fun List<Int>.toRGBA(): RGBA = when(this.size) {
    4 -> {
      val (red, green, blue, alpha) = this
      RGBA(red, green, blue, alpha)
    }
    3 -> {
      val (red, green, blue) = this
      RGBA(red, green, blue)
    }
    else -> {
      throw IllegalArgumentException("3 or 4 elements expected in list, but ${this.size} found.")
    }
}
But like everyone noticed, this List<Int>.toRGBA-function should only be visible in the code that reads in the colours values from the Int data stream.
m
@Stephan Schroeder although the require field is cool and all, if anything, I'd use a UByte for the values to make it compiler typesafe
but then again it wouldn't be the same syntax as the prime example
a
@Stephan Schroeder It is stored in an Int, not a class.
Where the 4 bytes are split into Red, Green, Blue, and Alpha.
s
@Anthony Flores in that case I'd suggest (assuming you compile your Kotlin to JVM bytecode)
Copy code
@JvmInline
value class RGBA private constructor(private val compressedColorInfo: Int) { // there might be some scenarios where it's useful to not make `compressedColorInfo` private, but it's a good default.
    
    // insert reverse bit-shifting here
    val red: Int get() = 5 
    val green: Int get() = 6
    val blue: Int get() = 7
    val alpha: Int get() = 255 
    
    companion object {
        fun from(red: Int, green: Int, blue: Int, alpha: Int = 255): RGBA {
            
            fun requireInRange(x: Int, name: String) {
              require(x in 0..255) {"param $name is supposed to be in range 0..255 but was $x"}
            }
            requireInRange(red, "red")
            requireInRange(green, "green")
            requireInRange(blue, "blue")
            requireInRange(alpha, "alpha")
            
            // insert bit-shifting here
            return RGBA(13)
        }
    }
    
    override fun toString() = "RGBA(red:$red, green:$green, blue:$blue, alpha:$alpha)"
}
In case you're not familiar with value/inline classes, the class overhead is (in most circumstances except e.g. when you have a list of RGBA values) compiled away, so it's (mostly) an Int at runtime. More here https://kotlinlang.org/docs/inline-classes.html