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)
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.
```
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 ?
Thanks.
Best regards,
Siyuan Chen
On Tue, Aug 11, 2020 at 10:09 PM Ryan Culpepper <[email protected]>
wrote:
> I don't know of a way to solve that problem using the Racket class system
> alone, but here is a solution that uses traits instead, which allow you to
> rename the hook methods after the fact so they don't collide.
>
> ;; ....
> (require racket/trait)
>
> (define secure-trait
> (trait
> (inherit needed-item)
> (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))))))
>
> (define locked-needed-trait
> (trait
> (define/public (needed-item)
> (println "locked-needed-mixin neededItem")
> the-key)))
>
> (define magic-needed-trait
> (trait
> (define/public (needed-item)
> (println "magic-needed-mixin neededItem")
> the-spell-book)))
>
> (define locked-mixin
> (trait->mixin
> (trait-rename (trait-sum secure-trait locked-needed-trait)
> needed-item needed-item/locked)))
>
> (define magic-mixin
> (trait->mixin
> (trait-rename (trait-sum secure-trait magic-needed-trait)
> needed-item needed-item/magic)))
> ;; ....
>
> Ryan
>
>
> On Sun, Aug 9, 2020 at 11:12 PM Siyuan Chen <[email protected]> wrote:
>
>> Hi all,
>>
>> Recently I read the paper "Classes and Mixins" by Matthew Flatt, Shriram
>> Krishnamurthi and Matthias Felleisen.
>>
>> In this paper, the authors presented MIXEDJAVA.
>>
>> In MIXEDJAVA,
>>
>>> A programmer implements mixins in exactly the same
>>> way as a derived class, except that the programmer cannot
>>> rely on the implementation of the mixin's superclass, only
>>> on its interface. We consider this an advantage of mixins
>>> because it enforces the maxim "program to an interface, not
>>> an implementation".
>>>
>> It is very close to the mixin form in Racket, because we can specific
>> interface in the mixin form:
>>
>> ```
>> (mixin (interface-expr ...) (interface-expr ...)
>> class-clause ...)
>> ```
>>
>> In Chapter 3, they also introduced an example (a maze adventure game, I
>> called it LockedMagicDoor) which uses the system.
>>
>> My question is:
>>
>> Is it possible to implement LockedMagicDoor in Racket?
>>
>>
>> I did some experiments but failed.
>>
>> See following code (or
>> https://gist.github.com/chansey97/aecffabb2885c83fa040ba677bde5de4):
>>
>> ```
>> #lang racket
>>
>> (define the-key 'the-key)
>> (define the-spell-book 'the-spell-book)
>>
>> (define person%
>> (class object%
>> (init-field items h)
>> (super-new)
>>
>> (define/public (has-item item)
>> (member item items))
>>
>> (define/public (height)
>> h)
>> ))
>>
>> (define door<%> (interface () can-open can-pass))
>>
>> (define door-mixin
>> (mixin () (door<%>)
>> (super-new)
>>
>> (define/public (can-open p)
>> (println "door% can-open")
>> #t)
>>
>> (define/public (can-pass p)
>> (println "door% can-pass")
>> #t)
>> ))
>>
>> (define secure-door<%> (interface (door<%>) needed-item))
>>
>> (define secure-mixin
>> (mixin (door<%>) (secure-door<%>)
>> (super-new)
>>
>> (define/public (needed-item) ;; error??
>> (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))))
>> ))
>>
>> (define locked-needed-mixin
>> (mixin (secure-door<%>) (secure-door<%>)
>> (super-new)
>> (define/override (needed-item)
>> (println "locked-needed-mixin neededItem")
>> the-key)
>> ))
>>
>> (define magic-needed-mixin
>> (mixin (secure-door<%>) (secure-door<%>)
>> (super-new)
>> (define/override (needed-item)
>> (println "magic-needed-mixin neededItem")
>> the-spell-book)
>> ))
>>
>> (define door%
>> (door-mixin object%))
>>
>> (define locked-mixin (compose locked-needed-mixin secure-mixin))
>> (define magic-mixin (compose magic-needed-mixin secure-mixin))
>>
>> (define locked-magic-mixin (compose locked-mixin magic-mixin))
>> (define locked-magic-door% (locked-magic-mixin door%))
>>
>> (define door (new locked-magic-door%))
>> (send door can-open (new person% [items (list the-key the-spell-book)] [h
>> 0.5]))
>>
>> ; class*: superclass already contains method
>> ; superclass: #<class:...agic-door-failed.rkt:62:2>
>> ; method name: needed-item
>> ; class name: ...agic-door-failed.rkt:36:2
>>
>> ```
>>
>> The problem is how to implement `secure-mixin`?
>>
>> Notice that since the `secure-mixin` is mixed into `locked-magic-door%`
>> twice, there will be two `needed-item` methods in the inheritance chain, so
>> they are naming conflict.
>>
>> However in MIXEDJAVA, they do not conflict:
>>
>>> Specifically, a composition m1 compose m2 contains two methods named
>>> x if both m1 and m2 declare x and m1's inheritance interface does not
>>> contain x. Both x methods are accessible in an instance of the composite
>>> mixin since the object can be viewed specifically as an instance of m1
>>> or m2.
>>>
>> Can someone help me? (e.g. make some changes to this code above and let
>> it work in Racket)
>>
>> Very thanks.
>>
>>
>> I also attempt to use Inner to simulate the behavior of MIXEDJAVA:
>>
>> ```
>> (define secure-mixin
>> (mixin (door<%>) (secure-door<%>)
>> (super-new)
>>
>> (define/pubment (needed-item)
>> (println "secure-mixin needed-item")
>> #f)
>>
>> (define/override (can-open p)
>> (println "secure-mixin can-open")
>> (define item (inner #f 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))))
>> ))
>>
>> (define locked-needed-mixin
>> (mixin (secure-door<%>) (secure-door<%>)
>> (super-new)
>> (define/augride (needed-item)
>> (println "locked-needed-mixin neededItem")
>> the-key)
>> ))
>>
>> (define magic-needed-mixin
>> (mixin (secure-door<%>) (secure-door<%>)
>> (super-new)
>> (define/augride (needed-item)
>> (println "magic-needed-mixin neededItem")
>> the-spell-book)
>> ))
>> ```
>>
>> But still failed:
>>
>> ; class*: superclass already contains method
>> ; superclass: #<class:...y-pubment-failed.rkt:62:2>
>> ; method name: needed-item
>>
>> See https://gist.github.com/chansey97/264d3435a8f506153709cc9804227fdf
>>
>> PS: I also tried `overment` and `augment` for the `needed-item` in
>> `secure-mixin`, but failed as well (although the error messages are
>> different).
>>
>>
>> IMO, it seems that `Inner` can be used to simulate the behavior of
>> MIXEDJAVA, but I don't know how to do it.
>>
>> Notice that in the paper, "Super and Inner — Together at Last!" by David
>> S. Goldberg, Robert Bruce Findler and Matthew Flatt, the authors originally
>> used `beta` and `java` to mark the methods, no need `pubment` a method
>> first. In that model, it may not be conflicting (just a very immature
>> opinion).
>>
>> So can we use the `Inner` to simulate the behavior of MIXEDJAVA?
>>
>>
>> Another question:
>>
>> In MIXEDJAVA, there is a `view` concept, which is very powerful.
>>
>> Can we get the corresponding equivalent in Racket?
>>
>> Very thanks.
>>
>>
>> Best regards,
>>
>> Siyuan Chen
>>
>>
>> --
>> 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/CAHWTsYnZCbRt2U%2Bt7JQPNt7rBNQAwSy8_4ycUqNkQ23ZOdncvQ%40mail.gmail.com
>> <https://groups.google.com/d/msgid/racket-users/CAHWTsYnZCbRt2U%2Bt7JQPNt7rBNQAwSy8_4ycUqNkQ23ZOdncvQ%40mail.gmail.com?utm_medium=email&utm_source=footer>
>> .
>>
>
--
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/CAHWTsYk2envNNr1Rkrgyp-i5tHxrrkA5RS2UHDS3RDj7pWapjQ%40mail.gmail.com.