読者です 読者をやめる 読者になる 読者になる

Code Aquarium

minazoko's blog -*- 水底のブログ -*-

(Racket) 無名関数をcutで評価

Racket Scheme

lamda式等で無名関数を作り即時評価するコードは次のようになります。

((lambda (x y) (* x y)) 5 7) ;=> 35

このように無名関数のコードが短ければいいのですが長くなると適用される引数をずっと後ろの方に書く事になり可読性が下がります。

((lambda (x y) 
   ...
   ...
   ... ) 5 7)


そこで srfi26 の cut を使い次のように書くと良いんじゃないかと思いました。

(require srfi/26)

((cut <> 5 7)
 (lambda (x y)
   ...
   ...
   ...))

cut のプレースホルダ <> に無名関数を渡してやるわけです。

racketではミックスイン*1は「クラスへ適用するとミックスインされた新たなクラスを返す関数」として表現されます。そしてそのような関数を作るショートカットに mixin というマクロを用意しています。つまり mixin は関数を作るマクロです。
例えば、racket/gui で幅や高さを固定したUIパーツを作るためのミックスインは次のように書くことができます。

(define (fixed-size-mixin % #:width (width -1) #:height (height -1))
  ((cut <> %)
   (mixin (area<%>) ()
          (super-new)
          (when (>= width 0)
            (send* this
              (stretchable-width #f)
              (min-width width)))
          (when (>= height 0)
            (send* this
              (stretchable-height #f)
              (min-height height))))))

引数 % はミックスインを適用するターゲットクラス。 #:XXXX はキーワード引数です。
mixinマクロが作るミックスイン関数をターゲットクラス % へ適用しています。
cutを使わないと、% をmixinフォームの後ろに書くにことになりますが、それはちょっと読みにくいですよね。

記事の主題からは外れますが、上のミックスインの使い方も一応書いておきます。ミックスインでクラスを定義する例です。

;; 垂直パネルを幅だけ100に固定
(define my-panel%
  (fixed-size-mixin vertical-panel% #:width 100))

;; サイズ 100x200 のキャンバス派生クラスを定義
(define my-canvas%
  (fixed-size-mixin
   #:width 100 #:height 200
   (class canvas% (super-new)
     (define/override (on-paint)
       ...))))

area<%>インターフェースを実装しているクラスであれば、簡単にサイズ固定できます。*2

*1:「ミキシン」と呼ぶ方が正しいらしい。

*2:racket/guiではpanel%やcanvas%等のクラスはデフォルトで親コンテナにオートフィットする設定になっています。