casey / just

🤖 Just a command runner
https://just.systems
Creative Commons Zero v1.0 Universal
21.12k stars 467 forks source link

var_or_default() like env_var_or_default() ? #2121

Closed gl-yziquel closed 4 months ago

gl-yziquel commented 4 months ago

Hi.

I'd like to be able to write var_or_default(variable, default) and have that evaluated to default if the variable does not exist.

This would be compile time, so I have no clue as to the correct syntax that would be welcome.

casey commented 4 months ago

Why wouldn't a variable exist?

gl-yziquel commented 4 months ago

Because, if it existed in an imported justfile, until override, it would have to get a default value of, say, "".

So, option 1:

Option 2:

This example is contrived. But it does have an impact in terms of readability of the Justfile, and that impact may scale in the medium to long run.

Basically, I'm missing the Option type and the ? operator, broadly speaking.

Context: I'm trying to make a library of justfile fragments that allow me to scaffold projects. Justfile code reusability runs me into such cases.

casey commented 4 months ago

Can you give a specific use-case? What would the variable be, and how would it be used?

In general, just is more or less statically typed. Although all types are strings, using an undefined variable is an error, and I am hesitant to relax that.

gl-yziquel commented 4 months ago

I do plan to publish these just snippets. They are not quite ready, yet.

My use case is the following, w/r to that not yet available code.

set allow-duplicate-variables

PROJECT := "myproject"
DESCRIPTION = "Project description"

import "just_fragments/project.just" # Provides various recipes, like "help", "readme", "license", "format" and so on.

[private]
default: help

The problem is the way I use it. Whether or not I want compile errors or not depending on whether or not the user should use a DESCRIPTION under the threat of compile errors or whether or not a DESCRIPTION is provided by default in the just fragments.

If I want to have both behaviours, I have to find a way to be able to toggle and on off the existence of variables. With a profile, an environment variable, whatever.

It may be too soon to make such a design decision.

But the use case seems to happen as soon as you try to use justfile fragments like you would use a library. At least, this is my experience.

Maybe wait till I publish these fragments.

laniakea64 commented 4 months ago

Whether or not I want compile errors or not depending on whether or not the user should use a DESCRIPTION under the threat of compile errors or whether or not a DESCRIPTION is provided by default in the just fragments.

If I want to have both behaviours,

Why not have recipes that require DESCRIPTION error if it's empty?

In the imported justfile:

DESCRIPTION := ""

description-required:
    {{if DESCRIPTION == "" { error("Please provide a description") } else { "" } }}
    echo Foo

description-not-required:
    echo 'Foo:' {{quote(DESCRIPTION)}}
gl-yziquel commented 4 months ago

I'm not saying there are no workarounds. I'm just observing they do not scale that well, nor are they magnificently clean.

Anyhow, I'll try to deal with it for the time being with things like what you propose. When it becomes a pain and I have code to show, I'll raise the issue up again.

starthal commented 4 months ago

I don't like the idea of possibly-undefined values, but special values like "" feel hacky too, not least because they can be expanded without error. One "static" way around this is to use the equivalent of Option<String> as a first-class datatype. But going from 1 possible type to 2 possible types is a big complexity leap. Maybe a special syntax could be used to make it more explicit for the end-user.

Throwing out a fuzzy brainstorm here; no need to respond:

# "Always" variable
foo := 'apple'
# "Maybe" variable -- `?` is always used as part of the name
bar? := nothing()
baz? := 'banana'

# This runs
pass: 
   # Prints 'apple'
    echo {{ foo }}
    # Prints 'banana'
    echo {{ baz? }}

# This fails
fail: 
    echo {{ bar? }}

# This has syntax errors
error:
    # `bar` is not a variable name
    echo {{ bar }}
    # `foo?` is not a variable name
    echo {{ foo? }} 

# "If-let" syntax; `bar` only exists within the true branch
fancy:
   echo {{ if bar? }}  { 'Value: ' + bar } else {  'No value'  } }}
gl-yziquel commented 4 months ago

@starthal @casey This is the kind of nonsense I'd like to avoid:

https://github.com/gl-yziquel/mud-parse2D/blob/master/just.d/_title.just#L6

I can live with it. I just do not like it.

casey commented 4 months ago

Going from one type to two types is indeed a big complexity leap. I don't want just to become dynamically typed, so multiple types means a static type system. just also depends on being terse to write, so it would also mean a type system with full type inference.

gl-yziquel commented 4 months ago

@casey I fully understand. And agree.