Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

There should be a way of change the default class for sigiled attributes on custom classes #220

Closed
FCO opened this issue Aug 19, 2020 · 12 comments

Comments

@FCO
Copy link
Contributor

FCO commented Aug 19, 2020

It should be a way of using different/custom classes for Positional, Associative and Scalar by default.

@FCO
Copy link
Contributor Author

FCO commented Aug 19, 2020

Maybe we could, when creating a new type and generating an attribute, it could call a method on its metaclass (maybe something like multi method default-type-for(\type, Positional) { Array }. That way, when using a custom metaclass, it could use different default types.

@AlexDaniel
Copy link
Member

AlexDaniel commented Aug 20, 2020

There should be a way of change the default class for sigiled attributes on custom classes

Can you provide some examples? Because otherwise it can be hard to see why there should be a way to do that.

Edit: to clarify, I would like to see a code example where a feature like this can make someone's life easier.

@vrurg
Copy link
Contributor

vrurg commented Aug 20, 2020

@AlexDaniel I can probably give an example:

class LazyScalar { 
    has &.lazy;
    ...
}
nqp::setcontspec(LazyScalar, 'value_desc_cont', ...);
multi trait_mod:<is>(Variable \var, :&lazy!) {
    nqp::setvarcont(var, LazyScalar, :&lazy);
}
my $lazy-one is lazy(&get-a-value);

Very rough, but hope the idea is understandable.

@FCO
Copy link
Contributor Author

FCO commented Aug 20, 2020

I am thinking of using that on modules, I think if you are writing a custom metaclass you should be able to do any change you need on the way it creates its types.
I'm writing a component framework for Rakudo.js where the state of a component is a attribute of a custom class (using a custom meta class) and every component that uses a given state attribute should be rerendered when the value of that attribute changes, so on that case, all attributes with @ should not be a array, but a Positional that runs custom code when are added or removed values on itself and all items should be Proxies that run code when its value changes. As something like that should be true for all sigils. And the code should looks like this:

component Todo {
    has Str  $.title;
    has Bool $.done;

    method body {
        # it will probably be a html slang, but...
        "{ $!done ?? "DONE" !! "" } { $!title }"
    }
}

component App {
    has Todo @.todos;

    method body {
        # it's just an example, it should not say anything, just return some objects 
        say "{ @!todos.elems } todos:";
        # render uses body and returns its return
        .render.say for @!todos
    }
}

@niner
Copy link

niner commented Aug 20, 2020 via email

@FCO
Copy link
Contributor Author

FCO commented Aug 20, 2020

You can always specify the type you want with something like: has @.todos is Array as you would do for something different of Array now

@jnthn
Copy link
Contributor

jnthn commented Aug 20, 2020

@FCO Untested, but I believe this is already possible by reusing the same mechanism the is ContainerType does. You'd:

  1. Create a subclass of Attribute and override the method container_initializer. Check the sigil of the name and return a block that does the appropriate thing (or return Nil or some other type object if you want to have the default for that sigil).
  2. Poke it into your EXPORTHOW::DECLARE under the name foo-attr, where foo is the name of the package declarator you are also exporting a metaclass for (for example, to control the attribute metaclass for the model package declarator, it'd be model-attr).

@jnthn
Copy link
Contributor

jnthn commented Aug 20, 2020

I wonder, if you change the meaning of sigils this way, how would a user
specify that a certain attribute should not be magical, e.g. it's just some
internal thing that's not actually connected to the UI?

Following what I described, by writing the container_initializer as callsame // ... - that is, take any the user specified, but otherwise let us set defaults.

@jnthn
Copy link
Contributor

jnthn commented Aug 20, 2020

Poke it into your EXPORTHOW::DECLARE under the name foo-attr

In a weird bit of timing, I actually was looking at the code relating to this yesterday (working on the RakuAST support for packages), and pondering whether the -attr convention really is the best way to do this. I have a feeling I picked it before we supported - and ' in names. That said, there's nothing that says your package declarator keyword has to be an identifier at all; if you poke a Unicode burrito key into EXPORTHOW::DECLARE then it'll probably work out just fine. Given it's taken the better part of a decade for somebody to need this, I suspect we can live with it... :-)

@FCO
Copy link
Contributor Author

FCO commented Aug 20, 2020

@jnthn You mean something like this? https://glot.io/snippets/fqez0g83zh

@FCO
Copy link
Contributor Author

FCO commented Aug 20, 2020

@jnthn does that exists something like that for methods/submetods?

@jnthn
Copy link
Contributor

jnthn commented Aug 20, 2020

You mean something like this? https://glot.io/snippets/fqez0g83zh

Yes, although container_initializer would normally be making a new instance, not just returning the type. Basically, whatever it returns is bound into the attribute.

does that exists something like that for methods/submetods?

No, though you can always mix into each of them during add_method.

@FCO FCO closed this as completed Jun 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants