Can I hide a method in a trait?

14 views
Skip to first unread message

Siyuan Chen

unread,
Aug 13, 2020, 3:12:27 PM8/13/20
to racket users
Dear all,

Just imagine, suppose we have two traits `spots-trait` and `stripes-trait`, they are developed independently.

These two traits depend on a "private" method `helper` respectively.

See following code:

```
(define spots-trait
  (trait
   (define/public (helper)
     (println "spots-trait helper"))
   (define/public (eat/spots)
     (println "spots-trait eat/spots")
     (helper))
   ))

(define stripes-trait
  (trait
   (define/public (helper)
     (println "stripes-trait helper"))
   (define/public (eat/stripes)
     (println "stripes-trait eat/spots")
     (helper))
   ))
```

Now I want to define the 3rd trait (called `spots+stripes-trait`), something like:

```
(define spots+stripes-trait
  (trait-sum spots-trait stripes-trait))
```

But this is impossible because there is a name colliding (the`helper` method).

One workaround to this problem is "renaming":

```
(define spots+stripes-trait
  (trait-sum
   (trait-rename spots-trait helper helper/spots)
   (trait-rename stripes-trait helper helper/stripes)))
```

This works.

But is this a good way? (It looks not very "hygienic")

Note that the `helper` is just a "private" method, it should not be visible to its user.

PS: It seems that Racket does not support private members for traits. Although I don't think "private members" is a good idea because private members are not friendly to unit-testing.

An alternative way is moving the private `helper` method to a different trait, splitting the original responsibilities across multiple traits leading to simpler, better designs.

See following code:

```
(define spots-helper-trait
  (trait
   (define/public (helper)
     (println "spots-helper-trait helper"))
   ))

(define spots-core-trait
  (trait
   (inherit helper)
   (define/public (eat/spots)
     (println "spots-core-trait eat/spots")
     (helper))
   ))

(define stripes-helper-trait
  (trait
   (define/public (helper)
     (println "stripes-trait helper"))
   ))

(define stripes-core-trait
  (trait
   (inherit helper)
   (define/public (eat/stripes)
     (println "stripes-core-trait eat/spots")
     (helper))
   ))
```

Now I can redefine `spots-trait` and `stripes-trait`.

My attempt is `trait-sum` the `spots-core-trait` and `spots-helper-trait` first, and then "hide" the `helper`, something like:

```
(define spots-trait
  (trait-hide ;; <-- attempt to hide `helper` method (Not real Racket)
   (trait-sum spots-core-trait spots-helper-trait)
   helper))

(define stripes-trait
  (trait-hide ;; <-- attempt to hide `helper` method (Not real Racket)
   (trait-sum stripes-core-trait stripes-helper-trait)
   helper))
   
(define spots+stripes-trait
  (trait-sum spots-trait stripes-trait))
```

Is this possible in Racket?

The advantage of "hiding" is that

1. The `spots-trait` now only exposes its interface `eat/spots `, we can easily reuse it in other traits without worrying about name colliding (the name `helper` is very commonly used, imo) .

2. Compared with the "renaming" solution, "hiding" looks more "hygienic", because it can avoid further name colliding.

PS: I admit this example may be a bit weird, but you can think of "spots" and "stripes" as some sort of service traits.

Thanks.

Best regards,
Siyuan Chen


Reply all
Reply to author
Forward
0 new messages