こんにちは、びしょ〜じょです。 気づけば5年次4年生になりました。 ようやく卒研フェーズになったので、ね。

あとバイトの時給や労働可能時間1が上がったりして金が手に入る分余計に労働を重ねてしまうようになったが金は手に入った。 また毎週焼肉月間などをやりたいぜ。

もう少し序文を書こうと思ったけど特に無いので終わり。


Typed Racket

最近はRacketの、特にTyped Racketという物体を書き始めたので、ちょっとやっていきたいと思います。 動機としては、なんとなくマクロに興味を持って、マクロといえばLispやろなぁということですね。 身近にはschemeでポエムを書くwasao君やEmacsに生かされているリザウド君などがいるのでそういった毒電波の影響を無意識のうちに受けていたという可能性もあります。

で、なんやかんやでRacketを書こうとなったところで色々漁ってみたらTyped Racketという言語に出会ったわけですね。

racket/typed-racket

これもマクロによるcore Racketの拡張なんですねぇ。 数日前にPerlのソースフィルターという機能を知って(Perl5を書いたことは1回もない)、それで型を突っ込んでいくみたいなことを話したあとだったので、それにだいぶ近いというかほとんど同じだなと思った。

Occurrence Typing

さて、Typed RacketはOccurrence typingという型付け規則を採用している。 簡単にいうと、ある変数xに関して型情報がわかりそうなconditionalがあると、その後のbodyにその型情報が伝播するようだ。

(: flexible-length (-> (U String (Listof Any)) Integer))
(define (flexible-length str-or-lst)
  (if (string? str-or-lst)
      (string-length str-or-lst)
      (length str-or-lst)))

(5.1 Basic Occurrence Typingより引用)

型注釈のシンタックスもLispのそれのように後置記法となっているため、ちょっと混乱する。 余談だが、arityが0の関数(fun () -> a的な)の型は(-> R)など、独特な空気がある。

さて、example codeに戻る。 flexible-lengthはStringと(Listof Any)のUnion型をとり、Integerを返す。 if構文の中で、String型のときとそうでないときに分かれている。 このif構文の中でstr-or-listを型が異なる関数に適用させても全体として型が一致するのは、このOccurrence Typingによる型情報の伝播のおかげ、とのこと。

pros

型のないRacketないしはLispを知らないので、なかった頃との比較はできない。 ただわりとイケる感じがある(雑)。

cons

やはりデータ型の定義が難点ですね。 たとえばよくあるOptional型だと

(define-type (Optional A) (U (Some A) (None)))
(struct: (A) Some ([v : A]))
(struct: None ())

のように書くことになる。 OCamlやHaskellと比べるとあまりにも…という感じですが、Typed Racketのデータ型の実態としてはまさに書いたとおりstructureのUnionですからねぇ…けどねぇ…。 やはりデータ型を定義するときはdefine-datatypeみたいなマクロを作ってstructureの定義は隠蔽したいものです。

追記 2017/04/16

ずばりなものを実装している人がおりました。 ADTs in Typed Racket with macros

これにより良い感じになる。

(define-datatype (Optional A)
                 (Some A)
                 None)

(define-datatype (Either L R)
                 (Left L)
                 (Right R))

ただ、各structureに#:transparent(Haskellなどにおけるderiving Show働きとしては近い)など、オプションを渡すことができないため、もう少し工夫が必要になるだろう。

Syntasticと罠

みなさんVimはお使いかと思いますが、Syntasticも使っているはずですね。 Syntasticがあると型の不一致もちゃんと怒ってくれている…のですが…。 SyntasticのRacket syntastic checkerはデフォルトではracketコマンドをただ叩いているだけ…つまりファイルを実行してしまっているんですね。最悪です。

これはさすがにマズイということでraco expandでマクロを展開するだけにさせました。 Typed Racketはマクロ展開時に型チェックをおこなうというタイプのものなのでこれでOK。

~/.vim/bundle/syntastic/syntax_checker/racket.vimをちょいと拝借してちょいちょい変えて、特に以下を追加する。

" ここでコマンドにサブコマンドを渡す
let makeprg = self.makeprgBuild({
    \ 'args_before': 'expand' })

これで適当にガッとやって終わりや。 そのうちSyntasticにprなどをサッと打ちたい。

追記 2017/04/16

やっていきをした vim-syntastic/syntastic PR #2001


"Typed" Racketとしてみるとなんともいえない感じを残しましたが、Racketはなかなかおもしろい言語だと思いました。 モジュールシステムや協力でhygenicなマクロ…。Luaと比べては何もかもが高機能…。 ファイル先頭に書く#lang hogehogeというものでファイル内をhogehogeというモジュールにあるmacro expanderで展開していきcore Racketに落とすという手法はなかなか渋い。 そのためPrologのような論理型言語が書けてしまったりする。

このOccurrence TypingにRefinement Typeを合わせるといった試みもあり、今後も見守っていきたい。


この語に及んでな〜〜に言語を取得しようとしてるんだ、大丈夫か…。 漫画や小説、技術書まで積んでしまっている、お前の人生も詰んでいる、つまりはそういうこと…。


  1. フレックスタイム制なので適当に行って適当に帰る、また1ヶ月n時間まで働いてOKという上限がある。