Dear Ryan,
No, traits can contain fields, which are mutable, so a trait can manage
> state.
>
Thanks, I tested it.
You are right, traits can manage states.
Right, one trait cannot override another's definitions. One thing you
> could do instead is use `trait-exclude` to remove the definition of
> `needed-item` from `secure-trait` before linking a trait (like
> `locked-needed-trait`, but using `define/public` instead of
> `define/override`) that defines the `needed-item` method. But if you do
> that, I think there's no way to chain to the original definition. (I
> haven't tried any of this, but that's what I get from the docs.)
>
Yes, "remove the definition of `needed-item` from `secure-trait`" can
solve the name colliding problem, but there's no way to chain to the
original definition, because `needed-item` has been excluded.
Anyway, that feature sounds maybe more dangerous than worthwhile.
>
Do you mean the feature of MIXEDJAVA?
Why do you think it is dangerous?
As the paper says:
> We consider this an advantage of mixins
> because it enforces the maxim "program to an interface, not
> an implementation".
See the following code:
```
(define door<%> (interface () can-open can-pass))
(define secure-mixin
(mixin (door<%>) (secure-door<%>) ; Inherit from a interface instead of
implementation
(super-new)
(define/public (needed-item)
(println "secure-mixin needed-item")
#f)
(define/override (can-open p)
(println "secure-mixin can-open")
(define item (needed-item))
(cond
((not (send p has-item item)) (printf "You don't have the Key ~v\n"
item)
#f)
(else (printf "Using Key... ~v\n" item)
(super can-open p))))
))
```
Ideally, the developer of `secure-mixin` only needs to care about the
interface of the super-class, not the implementation.
Notice that the interface `door<%>` only has `can-open` and `can-pass`, no
`needed-item`, so even if the implementation class of the `door<%>` has a
method with the same name (`needed-item`), it should not cause conflicts.
It seems to be a good idea, IMO.
But if you know that you might want to apply the secure mixin multiple
> times with different needed items, then there are other ways to write
> that code. One is to parameterize the mixin by the needed item directly:
>
That's a good idea.
It is something like "prefer composition over inheritance" or "dependency
injection".
Another is to parameterize `secure-mixin` over the *name* of the auxiliary
> hook method, using `define-member-name` etc. That avoids the problem of
> needing the same *name* to refer to different "slots" at different points
> in time; instead, just use two different names
>
Nice.
Racket supports parameterizing identifiers in a `class`, that's an amazing
feature!
Best regards,
Siyuan Chen
On Thu, Aug 13, 2020 at 6:28 AM Ryan Culpepper <[email protected]>
wrote:
> On Wed, Aug 12, 2020 at 7:19 PM Siyuan Chen <[email protected]> wrote:
>
>> Dear Ryan,
>>
>> Thanks for your solution, it works nicely!
>>
>> But I still have some questions:
>>
>> For this particular problem of LockedMagicDoor, we can use `trait` to
>> solve the `mixin` problem, but can we use the same method for more general
>> problems?
>>
>> Since traits and mixins are essentially different, they may not mimic
>> each other...
>>
>> IMO, they are at least 2 differences:
>>
>> 1. Traits can not have states, but mixin can.
>>
>> For example, if the `secure-trait`, `locked-needed-trait`, and
>> `magic-needed-trait` have mutable states, then you can not use `trait`. (A
>> workaround may be to convert `trait` to `mixin` first and then compose a
>> "state" mixin. But this will cause the same problem of name colliding,
>> because `mixin` does not support renaming)
>>
>
> No, traits can contain fields, which are mutable, so a trait can manage
> state.
>
> 2. `trait-sum` is symmetric, it does not support overriding.
>>
>> For example, in your solution:
>>
>> ```
>> (define locked-mixin
>> (trait->mixin
>> (trait-rename (trait-sum secure-trait locked-needed-trait)
>> needed-item needed-item/locked)))
>> ```
>>
>> To construct `locked-mixin`, you `trait-sum` the `secure-trait` and
>> `locked-needed-trait`, but what if there is a default implementation of
>> `needed-item` in the `secure-trait`? We even hope the `locked-needed-trait`
>> can delegate to `super`, something like:
>>
>> ```
>> (define locked-needed-trait
>> (trait
>> (define/override (needed-item)
>> (println "locked-needed-mixin neededItem")
>> (super needed-item)))) ;; We want to call super.
>> ```
>>
>
> Right, one trait cannot override another's definitions. One thing you
> could do instead is use `trait-exclude` to remove the definition of
> `needed-item` from `secure-trait` before linking a trait (like
> `locked-needed-trait`, but using `define/public` instead of
> `define/override`) that defines the `needed-item` method. But if you do
> that, I think there's no way to chain to the original definition. (I
> haven't tried any of this, but that's what I get from the docs.)
>
> The key question is:
>>
>> Does the current racket class system have the equivalent capabilities of
>> MIXEDJAVA?
>>
>> More concretely, we know that Racket supports mixin, but Racket's `mixin`
>> is slightly different from MIXEDJAVA. This is because we seemingly can not
>> implement LockMagicDoor by using only `mixin-form` (Is this by design? I
>> heard that there are some historical origins of MIXEDJAVA and MzScheme).
>>
>> But Racket supports two additional features, i.e. `inner` and `trait`,
>> which are not in MIXEDJAVA.
>>
>> Consequently, I'm curious about whether there is any design pattern in
>> Racket that can simulate the semantics of MIXEDJAVA ?
>>
>
> I'm not sure, but I think the answer is no. If we distinguish "method
> names" from "method slots" (or "vtable slots", to put it in terms of one
> implementation strategy), then I think this example requires the ability to
> change a method name to refer to a new method slot (but only for
> subclasses; existing references point to the original slot), and I don't
> think Racket's class system offers that ability. In particular, I think
> that feature is not expressible using `inner`, although `inner` does
> require a more complicated notation of "method slot" than a Java-style
> class system. Anyway, that feature sounds maybe more dangerous than
> worthwhile.
>
> But if you know that you might want to apply the secure mixin multiple
> times with different needed items, then there are other ways to write that
> code. One is to parameterize the mixin by the needed item directly:
>
> ;; make-secure-mixin : Item -> (impl/c door<%>) -> (impl/c door<%>)
> (define (make-secure-mixin the-needed-item)
> (mixin (door<%>) (door<%>)
> (super-new)
> (define/override (can-open p)
> (println "secure-mixin can-open")
> (cond [(send p has-item the-needed-item) ....]
> [else ....]))))
>
> (define locked-needed-mixin (make-secure-mixin the-key))
> (define magic-needed-mixin (make-secure-mixin the-spell-book))
>
> Another is to parameterize `secure-mixin` over the *name* of the auxiliary
> hook method, using `define-member-name` etc. That avoids the problem of
> needing the same *name* to refer to different "slots" at different points
> in time; instead, just use two different names. But IIRC, if you read the
> APLAS paper on traits in Racket (Flatt et al), you discover that `trait` is
> just syntactic sugar for this kind of member-name manipulation. So traits
> are this design pattern turned into a linguistic construct.
>
> Ryan
>
>
--
You received this message because you are subscribed to the Google Groups
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/racket-users/CAHWTsYkAd7dXdV1vn5GY_EKAKFy9pL8Po9uUCdpMf9Ym1K7HYg%40mail.gmail.com.