https://kotlinlang.org logo
#getting-started
Title
# getting-started
r

Rob Elliot

02/22/2023, 10:42 AM
I think the answer to this is "no" but just in case... I want to write this extension function on the jOOQ SQL DSL:
Copy code
fun <R : Record> SelectConditionStep<R>.andIf(
    check: () -> Boolean,
    clause: () -> Condition,
): SelectConditionStep<R> = if (check()) this.and(clause()) else this
The idea is you can do something like this:
Copy code
val name: String = TODO()
val maybeId: Id? = TODO()
dsl
  .select(MY_TABLE.fields())
  .from(MY_TABLE)
  .where(MY_TABLE.NAME.eq(name))
  .andIf({ maybeId != null }) { MY_TABLE.COLLECTION_ID.eq(maybeId.value) }
  .fetch()
but of course the compiler cannot infer that
maybeId
is not null in the second lambda... could a custom contract make it possible for the compiler to work this out?
It's basically meant to be a more readable form of this:
Copy code
val name: String = TODO()
val maybeId: Id? = TODO()
dsl
  .select(MY_TABLE.fields())
  .from(MY_TABLE)
  .where(MY_TABLE.NAME.eq(name))
  .run { if (catalogueId != null) and(MY_TABLE.COLLECTION_ID.eq(maybeId.value)) else this }
  .fetch()
where the compiler can do the smart cast - but the
run
and the
if
and the
else
rather obscure the legibility of the DSL.
There's a less flexible version which does work:
Copy code
fun <R : Record, T : Any> SelectConditionStep<R>.andIf(
    nullable: T?,
    clause: (T) -> Condition,
): SelectConditionStep<R> = nullable?.let { notNull -> this.and(clause(notNull)) } ?: this
but apart from being less flexible it's also a bit more implicit - I like the explicitness of the first one where it's completely explicit inline that the check is for the nullness.
d

David Kubecka

02/22/2023, 10:47 AM
I would use this ^ but with
takeIf
, e.g.
Copy code
something.takeIf(check())?.let { clause(something) }
r

Rob Elliot

02/22/2023, 11:01 AM
But at that point I'm up to three arguments to
andIf
, and I'm still constrained to there just being the one argument that can be cast from nullable to not null.
I was sort of hoping there might be a way to state in a contract that anything the compiler can infer from
check
returning
true
can be assumed to be true in the lambda passed as
clause
d

David Kubecka

02/22/2023, 11:08 AM
It depends on how flexible you want your
andIf
to be. If you want to pass 1) any value, 2) perform an arbitrary check on it, and 3) if that check succeeds do something with the value, I think you need those 3 parameters. But you are talking specifically about nullability checks, so not sure how generic you want that to be...
Anyway, I do not know whether/how your original
check
and
clause
parameters can share a contract 😞
r

Rob Elliot

02/22/2023, 11:19 AM
This isn't too bad:
Copy code
fun <R : Record> SelectOnConditionStep<R>.and(clause: () -> Condition?): SelectOnConditionStep<R> {
    val maybeCondition = clause()
    return if (maybeCondition != null) and(maybeCondition) else this
}

dsl
  .select(MY_TABLE.fields())
  .from(MY_TABLE)
  .where(MY_TABLE.NAME.eq(name))
  .and { if (maybeId != null) MY_TABLE.COLLECTION_ID.eq(maybeId.value) else null }
  .fetch()
Shame about that ugly little
else null
at the end... guess we'd need partial application to avoid that though.
d

David Kubecka

02/22/2023, 11:32 AM
I see now that you were concerned with the implicitness of your previous solution. So why not solve it with proper naming, e.g.
andIfNotNull(maybeId) { MY_TABLE.COLLECTION_ID.eq(maybeId.value) }
? Or are there also functional concerns?
r

Rob Elliot

02/22/2023, 11:34 AM
It would have to be:
andIfNotNull(maybeId) { id -> MY_TABLE.COLLECTION_ID.eq(id.value) }
It's more that it's inflexible, it accepts one parameter, checks for one condition and does one smart cast. With my latest version I can do arbitrary checks on N parameters benefitting from smart casting:
Copy code
.and { if (x is String && y != null) MY_TABLE.COLLECTION_ID.eq("$x_$y") else null }
👍 1
Oh, of course I could do
.and { maybeId?.run { MY_TABLE.COLLECTION_ID.eq(maybeId.value) } }
to lose the ugly
else null
. Though it feels a bit less legible.
r

Rob Elliot

02/22/2023, 12:12 PM
Thanks, voted!
d

dmitriy.novozhilov

02/22/2023, 12:12 PM
I really hope to implement it in Kotlin 2.1
👍 2
3 Views