Zig: QOL Proposal: use(xxx) statement to reduce repeating element

Created on 11 Jun 2018  路  3Comments  路  Source: ziglang/zig

I propose zig uses the use statement as a way to reduce repeating code in a given block.
idea taken from D with statement but would want to support a reduced usage set. The look up rule works as such, it first tries to look up the symbol in the use block, if that fails it follows the normal scope look up rules.

Example:

const A = struct {  i: i32 };

fn hammertime(a: *A, b: i32) void {
    a.i = b;  // normal look up, OK
    use(a) { // use in a block example
            a.i = b;  // first looks up a. in a, Fails, looks up a in scope Fails, looks up in outer scope OK
            i = b; // looks up i in a OK
    }
   a = b; // normal look up, ERROR
}

Example of possible use on struct function, lets you avoid typing self.xxxx for everything.

const Foo = struct {
    a: f64,
    b: i32,

   pub fn foo1(use self: *Foo, x: i32) void {   b = x*2; } // in the arguments list
   pub fn foo2(self: *Foo, x: i32)  void use(self) {   b = x*2; } // on the function block
   pub fn foo3(self: *Foo, x: i32)  void  { use(self) {   b = x*2; }} // in its own block
};

Can be used on enum types:

enum SOME_LONG_ENUM_NAME_MAYBE_IN_A_NAME_SPACE {
    Ok,
    NotOk,
    Tufo,
};

// later on
var x = switch(on_enum_var) use(SOME_LONG_ENUM_NAME_MAYBE_IN_A_NAME_SPACE) {
    OK => 1,
    NoOk => -1,
    Tofu => 0
};

I would think we would not want it allowed on optional? or error! types.
It would be a nice to have on function signatures like foo1 or foo2, but not necessary.
It isn't difficult to support nesting use blocks, but I found in D that it makes the code harder to read.

PROS:

  • its DRY
  • function calls look more like a method (also could be a con :) )
  • makes switching on enums nicer

CONS:

  • More to remember
  • Can be miss/over used
proposal

Most helpful comment

I question that this actually solves anything you can't already do. What is the benefit of:

use(a.b.c.d.e.f) {
    g = 2;
    h = 4;
}

compared to:

{
    const f = &a.b.c.d.e.f;
    f.g = 2;
    f.h = 4;
}

All 3 comments

Kotlin also provides with but it implements it in userland.

I've been writing Kotlin code for years now, and I've _never_ used with. Adding _another_ control flow structure for something that isn't needed clutters up the language.

I'd once proposed something vaguely similar in #120. Here's another take.

The problem: people invent long names and use them over and over, making code reading hard.

some_very_very_long_name.x = ...
some_very_very_long_name.y = ...
some_very_very_long_name.z = ...

Possible solution, inspired by old typographic technique:

some_very_very_long_name.x = ...
                        .y = ... 
                        .z = ...

How does it work? Long name can omitted (replaced by spaces), if it happens on the same place at the next line. The parser is smart enough to detect this pattern and "correct" the code, checking for possible ambiguities.

It does not require new keyword, it is natural and used in practice, it improves readability.

Those with/use tricks are un-natural, prone to confusion.

Inference of fully qualified name in switch should be automatic, I am surprised it isn't.

I question that this actually solves anything you can't already do. What is the benefit of:

use(a.b.c.d.e.f) {
    g = 2;
    h = 4;
}

compared to:

{
    const f = &a.b.c.d.e.f;
    f.g = 2;
    f.h = 4;
}
Was this page helpful?
0 / 5 - 0 ratings