遺伝子をモチーフにした言語「Genomy」を作りました
最近、3年くらい前に書いた「そろそろ例のプロジェクトについて言及するか」についてTwitterで言及があったので思い出しました。「条件を満たしたものをすべて呼び出す」という設計思想でプログラムが書けてしまうという点について意外とみんなピンと来ないみたいだからコンセプトプルーフを実装してみようと思っていたんでした。
という訳で作りました。https://github.com/nishio/genomy
解説
「遺伝子はタンパク質の設計図」というところまでは教科書などでもよく言及されます。でも、その設計図には「どういう状況になったら作るべきか」「どういう状況では作るべきではないか」という情報も書かれています。
この「作るべきではない」(発現の抑制)がどう実現されているか、ザックリ説明しましょう。体の中にあるタンパク質があると、これがある遺伝子の周辺にへばりつき、その遺伝子からタンパク質を作る過程を邪魔します。これによってその遺伝子からはタンパク質を作ることができなくなります。
つまり、この邪魔をするタンパク質をS、邪魔されるタンパク質をPとすれば、SがないときだけPが作られ、SがあるときにはPが作られないということになります。つまりP := not(S)です。この仕組みを使って論理否定が実装できます。
逆に、あるタンパク質Eは遺伝子の周辺にへばりついて、その遺伝子の発現を高めることができます。あるタンパク質E1があるときにはタンパク質Pがたくさん作られる、そしてタンパク質E2があるときにもタンパク質Pがたくさん作られる、という仕組みにすれば、これはつまりP := E1 or E2です。この仕組みを使って論理和が実装できます。
論理和と論理否定があれば半加算器を実装できますよね!半加算器のサンプル>https://github.com/nishio/genomy/blob/master/sample/half_adder.gnm
生命はこのような仕組みを使って、外部からの刺激に対する応答だとか、複雑な体の構造の構築などをプログラミングしているわけです。
では実際にプログラムを動かしてみましょう。sample/hello.gnmの遺伝子を持った生物を、タンパク質STARTを含んだ環境においてみます。
genomy$ python2.7 main.py -f sample/hello.gnm START 0 START 1 OUT_H 2 OUT_E 3 OUT_L 4 OUT_L SUP_L 5 OUT_O SUP_L SUP_O 6 7 8 9 10
はい、STARTの刺激に応答してHELLOと出力されました。では遺伝子を見てみましょう。
+START :OUT_H +OUT_H :OUT_E +OUT_E :OUT_L -SUP_L +OUT_L :OUT_L +OUT_L :SUP_L -SUP_O +SUP_L :OUT_O :SUP_O
STARTの刺激があると、この生物の体内ではOUT_Hが作られ始めます。OUT_Hの刺激でOUT_Eが作られ、OUT_Eの刺激でOUT_Lが作られます。OUT_Lの刺激でOUT_Lを作ると、OUT_Lがずっと作られっぱなしになってしまうので、それを抑制するタンパクSUP_Lを作って抑制しています。このOUT_LとかSUP_Lとかは人間がわかりやすいようにつけただけの識別子で、実際の制御はその頭に付いている+と-と:で決まっています。:で始まる識別子が「作られるタンパク質」で、+と-は、まあご想像通りでしょう。
このコードを読む上で重要なポイントは、行の順序が全く関係ないという点です。行をシャッフルしても挙動が変化しません。それどころか、他の遺伝子を持った生物と混ぜあわせても、文法的には問題ありません。運悪く使っている識別子がかぶってしまうと、予期していない実行フローが発生します。でも、もしかしたらそのフローによってより環境に適応できる個体が生まれるかも知れません。うまくいかなかった個体は子孫を残さずに死ぬことによってデリートされ、うまくいく個体だけが残っていきます。
今回実装した処理系は、さらに進めて行より細かい単位で混ぜたり、突然変異と称して一文字消したり足したり書き換えたりをしても、文法エラーにならないことを意図して実装しています。そこらへんのことをためしてみるのも面白いかも知れませんね。
あとはよりプログラミング言語っぽいチャレンジの方向性としては、数値をどうやってタンパク質で表現するかですかね。すべての値がタンパク質なのでタンパク質指向プログラミングですね(ぇ)