-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Remove method_missing macro hook #4701
Comments
Thanks for putting this together in such a well written issue. I like abstract class BaseBox
private getter form
macro form(form_class)
@form = {{ form_class }}.new
end
macro inherited
form {{ @type.name.gsub(/Box/, "::BaseForm").id }}
end
macro method_missing(call)
form.{{ call.name }}.value = {{ call.args.first }}
self
end
def save
form.save
end
end This is so that you can easily create factories and makes it easy to chain methods. class UserBox
def admin
role_id find_admin_role_somehow.id
# Instead of...
form.role_id.value = find_admin_role_somehow.id
self
end
end
UserBox.new.admin.save I could see that some people think this is too magic, but I've found it to be super helpful in this case and I would be sad to not be able to do it. |
I don't really get what your example is about (seems like there are a few bits missing), but it's essentially a delegation. You could probably implement this with a custom macro similar to what {% for method in form_class %}
def {{method.id}}(val)
form.{{method.id}}.value = val
self
end
{% end %} |
Yeah there is a lot missing in the example, but you seemed to get it because I think that example will work. I'll give it a try. Thanks! |
Please don't break the language. Use-case exampleSay you have a bunch of models: Thus, for this the following example ensures that upon calling class JsonDecorator(T) # Generic JSON decorator
property inner : T # The inner object
forward_missing_to @inner # And forward all other methods on to @inner.
# This is the magic that let's us easily write this kind of decorator without fuzz,
# without cluttering this code with tons of delegator methods (Like Java requires),
# and without requiring the user to use an editor/IDE to generate these as to not break their hands writing all of that mess.
property json_exclude : Array(Symbol)
def initialize(@inner, @json_exclude)
end
# And now, we can easily implement our special #to_json, which actually just calls the
# inner to_json method but with our default argument for exclusion.
def to_json
@inner.to_json(exclude: @json_exclude)
end
end Note: The code above most likely violates the Using this technique, it becomes trivial to safely generate a JSON document of this, without being able to forget to pass anything special, and without cluttering each original model with extra logic. It also allows to dynamically change the behaviour of the inner model: For example, the owner of an With regards to #4701 (comment), this makes such code harder to write for no real gain. It's trivial right now to easily write more special rules if needed - And that's a feature, not a bug. A language is there to serve me, it however can expect me to know what's right. We don't need another Go lang. |
As I have written in the initial post, with You example is in a way special, that the methods provided by That doesn't mean that your use case would be completely impossible, it needs to be implemented in a different way. |
If I understand correctly the main problem with
Isn't that exactly how you'd expect a generic decorator to behave? |
Yes, it is. But I am not sure this should be in a static typed language at all. Besides, I can't think of many usecases where you would really need this and can't accomplish with other features of Crystal. And even if we'd agree to have generic decorators with variable interfaces, that would not necessarily require
Regarding your suggestion: I don't think |
I still don't see the problem. Is bad I also want a powerful language. A language which let's me try something new, and a language which trusts me to not do something totally stupid - as something totally stupid can be done in any language, regardless of the feature set. If I didn't want that, I would've chosen Go-lang. |
Yes, and for the case of the second feature I'm arguing with your assertion that a generic decorator should not vary its set of instance methods based on its type parameter in a statically typed language (regardless of how that variation is implemented). If it cannot do this, it severely limits the set of types it can usefully decorate.
Consistency could be ensured by ensuring Allowing objects to respond to arbitrary messages lies at the core of the object-oriented programming paradigm that Crystal inherited from Smalltalk via Ruby. It is a great strength of Crystal that it can combine this with compile-time enforced type safety. |
But what if Being able to send arbitrary messages is nice, I know. But what for? In Crystal we have great macro features, so you can just batch generate a bunch of methods instead of putting them together on demand. |
Not complete reading comments, but please keep this good part(come from Ruby) as much as possible. when you use method_missing macro, you should respect the risk and various potential impacts, and point it out in document is useful. |
Given that this proposal didn't got enough support, I think it's OK to close it, and let it come back as an RFC for when 2.0 is being discussed. BTW, I used |
Thank you, this gist i consider is useful for some cases, it give me a more clear result where the issue is, i add to my own never published |
BTW, i find our macro method_missing(call)
mm({{call}})
end class Reference
macro inherited
{% verbatim do %}
macro method_missing(call)
mm({{call}})
end
{% end %}
end
end |
This request is extracted from #4490 to keep the issue more focused.
method_missing
was copied over from Ruby but has quite some problems in a statically typed language, which particularly emerge when used in combination withresponds_to?
:responds_to?
will reportfalse
for a method defined bymethod_missing
unless it is invoked somewhere else in code, becauseresponds_to?
has no way of knowing which methods might be implemented throughmethod_missing
unless the method call is actually initialized. This also means that such methods will not be included in the documentation.There are a number of use cases for
method_missing
and it is actively used in many Crystal projects (c.f. uses in Crystal repositories on Github - though many are still using the signature from pre-0.18.0). These use cases however are actually quite different and in some cases macros might be an equal or even better approach.forward_missing_to
) is probably one of the most important use cases. This can be replaced by either a direct call todelegate
(with a predefined set of delegated methods) or a macro which dynamically delegates all methods of the receiving object's class.In combination with the
finished
macro, this provides nearly the same functionality asforward_missing_to
and works well withresponds_to?
. This would not include method defined in afinished
hook which is invoked after the on in the delegator class, but I'd consider this negligible.A simple delegator macro should replace
forward_missing_to
in the stdlib. It would also be nice ifdelegate
could optionally accept a block or proc to wrap delegated methods for example in atry
orrescue
block or modify the return value.This is merely a nice frontend for a Hash (or an other backing data structure) so instead of
config["conf_key"]
you can writeconfig.conf_key
. I don't think this adds a real benefit. As an alternative, a custom struct or class would also provide proper type restriction instead of always returning a union type if the store contains values with different types.This does not add much value IMHO and could be replaced by more explicit code.
find_by_first_name_and_email(first_name, email)
which is essentially equal tofind_by(first_name: first_name, email: email)
. I did not find any evidence, that this is actively used in Crystal, but it is possible. This could probably be achieved by creating all possible combinations of method names through macro loops.As a sidenote, the availability of
method_missing
might be a good selling point to Ruby developers.The text was updated successfully, but these errors were encountered: