enso-org / enso

Hybrid visual and textual functional programming.
https://enso.org
Apache License 2.0
7.32k stars 318 forks source link

Pattern matching with `_` forces computation of lazy fields too eagerly #10465

Open radeusgd opened 6 days ago

radeusgd commented 6 days ago

Let me show this on example:

from Standard.Base import all

type My_Ref
    Lazy ~lazy
    Eager eager

    get_if_eager self = case self of
        My_Ref.Eager x -> x
        My_Ref.Lazy _ -> Nothing

    get self should_compute:Boolean = case self of
        My_Ref.Eager x -> x
        My_Ref.Lazy x -> if should_compute then x else Nothing

    tricky_get_if_eager self =
        atom = Meta.meta self
        if atom.constructor.name == "Eager" then atom.value else Nothing

main =
    v1 = My_Ref.Lazy <|
        IO.println "Computing v1"
        42

    IO.println (v1.get_if_eager)

    v2 = My_Ref.Lazy <|
        IO.println "Computing v2"
        44

    IO.println (v2.get False)

    v3 = My_Ref.Lazy <|
        IO.println "Computing v3"
        46

    IO.println (v3.tricky_get_if_eager)

The current behaviour is as follows:

Computing v1
Nothing
Computing v2
Nothing
Nothing

As we can see both v1 and v2 are computed. I think this is wrong.

Ideally, neither of them should be computed as we don't use either value.

It seems acceptable to compute v2 as we are binding the value to x so it seems OK to force it to compute. However ideally, the variable x could 'inherit' the laziness and only be computed if it is actually demanded (as if it were an ~suspended argument). It could possibly be marked by using My_Ref.Lazy ~x -> ... in the pattern match. Currently I have checked and adding the ~ to x and _ does not change the behaviour.

However, for _ it should really be noted to not be computed as we explicitly ignore the value. This makes decomposing a value without triggering execution quite hard - as shown in tricky_get_if_eager - the only variant that currently works as desired.

radeusgd commented 6 days ago

I've encountered this issue when debugging why Snowflake batching does not improve performance.

Apparently, our SQL_Type_Reference.to_type_override function that was supposed to avoid triggering the lazy computation, was actually triggering it due to this problem.

radeusgd commented 4 days ago

Marking as lowest priority since an easy workaround exists - as demonstrated by tricky_get_if_eager.

The workaround is slightly annoying, but usable.