question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Better support / recipe for Null-Pattern of none collection based stores

See original GitHub issue

Key Question

How are you supposed to model nullable objects within fritz2’s store concept?

Problems

Think of selecting single items from a list, for example within our DataTable component. But there might be other situations, where you have some type T and want to create a store of Ts combined with the situation, that you want to know, whether the store holds a value or you are forced to handle nullable values (because of the surrounding infrastucture like a single selection).

Consider a simple model

data class Person(
    val id: Int,
    val name: String,
    val birthday: LocalDate,
)

Then you have a problem to use the sub-store mechanisms (to be more precise: the generated lenses):

// create / get a nullable store:
val store = storeOf<Person?>(null)

// some forms / tags you want to bind to a value of ``Person`` -> need a ``SubStore``
val name = store.sub(L.Person.name) // won't compile, as generated Lens is ``Lens<Person, String>`` 
                                    // and not ``Lens<Person?, String>``!

// craft by hand:
val nameLens = buildLens<FinalPerson?, String>(
        "name",
        { p -> p?.name ?: "" /* provide neutral representation */ },
        { p, t -> p?.copy(name= t) }
    ) // works, but cumbersome and bypasses the "sense" of a framework
val nameStore= store.sub(nameLens) 

This is cumbersome! It could be fixed, if there would be an overloaded version of buildLens, that would accept a neutral element for the getter and if the LensesAnnotationProcessor would support nullable Ts.

Another option is to choose a different model approach and to integrate the Null-Pattern manually into the model itself:

data class Person(
    val id: Int,
    val name: String,
    val birthday: LocalDate,
) {
    val isNull = id == 0 // define some "null"-object based upon special data constellations

    // could also add a factory function within the companion like ``newEmpty()`` or ``asNull`` or alike
}

A kind of variation would be to implement a generic container like an Optional<T>, as other languages prefer, to support the null-pattern.

This is possible, but it bypasses the chosen way of Kotlin how to deal with null-pattern!

Imho we need a better support for this problem - whether through our framework itself or by giving a clear advice to our users, how to deal with this situation!

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
metin-kale-cfcommented, Jun 8, 2022

I also had problems with nullable properties and found other ways of dealing with it. It might be an alternative approach to the above proposal.

In Kotlin we have following approaches to deal with null values: Safe calls: person?.name -> if person is null, name gets null Not-Null: person!!.name -> if person is null, it will throw an exception Elvis operator: person?.name? : “” -> if person is null, we use “” for name

To prevent NPEs i think Not-Null should be avoided, but we need something like Safe Calls and Elvis Operators for Lenses.

As alternative to safe calls i use a function like this:

inline fun <reified F, reified T> nullable(lens: Lens<F, T>) : Lens<F?, T?> = ...

so we can use

val store : Store<Person?> = storeOf(null)
val name : Store<String?> = store.sub(nullable(Person.name()))

For the Elvis Operator i have a function like this:

fun <F, T> Lens<F, T?>.ifNull(default: T): Lens<F, T> = ...

Which is basically a two way elvis operator. We can use to have a non-nullable name-substore:

val name : Store<String> = store.sub(nullable(Person.name()).ifNull(""))
0reactions
chausknechtcommented, Jul 25, 2022

Is orDefault the right name?

As I was closely involved in the current approach, I am a bit biased towards naming 😉

Should notNull and orDefault be public api or just the subs?

Question: Are there use cases of lenses without using sub? If not, there would be no value in provding them as public functions imho.

What about formats? At which point they have to be applied if there is the need for both: notNull / orDefault combined somehow with fomrating (lenses)? Without public access, the format has to be applied before the lifting. Is that the common / only useful approach? (If not this is even worse, as you cannot rely on the “automatic” special sub overloads / variants in that case…

Read more comments on GitHub >

github_iconTop Results From Across the Web

Scala best practice: How to use the Option/Some/None pattern
This is Recipe 20.6, “Scala best practice: How to use the Option/Some/None pattern.” Problem. For a variety of reasons, including removing null ......
Read more >
12 recipes for using the Optional class as it's meant to be used
In this article, I will cover the following three big categories with 12 recipes: Why am I getting null even when I use...
Read more >
Best practice to validate null and empty collection in Java
By the way, null collection is bad. If possible, make it empty collection instead. See Effective Java: Item 43 - Return empty arrays...
Read more >
Performing pattern negation to multiple nodes - Neo4j
Correct approach: collect nodes to exclude, and use WHERE NONE() on the collection to drive exclusion ; Ingredient) WHERE ; as excluded MATCH...
Read more >
Introduction to the Null Object Pattern | Baeldung
Most of the developers would return Collections.emptyList() from findByNameAndLastname() in case none of the customers matches the provided ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found