kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

JSONのようでそうでないデータフォーマットNSON 0.0.1リリース

きっかけは、昨夜の思いつきでした。

JSONでも以前から、末尾のカンマを許すようにしてほしいとかそういう要望があったと記憶していましたが、そもそもカンマを要らなくすれば解決では?というのが動機です。

NSONは次のような特徴を持っています。

  1. オブジェクトの属性を区切るカンマが必要ない
  2. 配列の要素を区切るカンマが必要ない
  3. 属性のキーとして、文字列リテラルの他に通常の識別子が使える
  4. JSONの完全上位互換である

まず最初の点についてですが、たとえば、

{ "x": 1 "y": 2}

のような記述ができるということです。また、改行で要素を区切って

{ "x" : 1
  "y" : 2 }

のように書くこともできます。

その次の点ですが、これは最初の点と同じようなものです。

{ "like": ["Scala" "Nemerle" "Scheme"] }

このように、配列の要素をカンマで区切る必要がありません。

3番目の点ですが、これは、JavaScriptでは許されていたものの、JSONでは許されなくなった点の一つであり、たとえば、

{ x: 1 y: 2}

のような記述が許されます。

最後の点ですが、これはそのままです。JSONで正しいデータはNSONでも正しいデータです。たとえば、

{ "x": 1, "y": 1}

は正しいJSONのデータであり、かつ、正しいNSONのデータです。

詳しくは、以下のURLを参照してください。

GitHub - kmizu/nson: NSON: an object notation that is not a JSON but alike JSON.

NSONは構文上の実験であり、今後メンテナンスしていくかは正直微妙です。これを拡張していくことで何か書きやすいデータフォーマットが生まれる可能性もあるので、思いついたら何か手を加えていくかもしれません。

ちなみに、思いついてからNSONのパーザを書いて、ある程度動作するようになるまで3時間くらいでした(自作のKlassic言語のパーザがある程度流用できたので、ちょっとズルいかもしれません)。こと構文解析においてはさすがだな自分と自画自賛しておきます。

Onion開発日誌/2016/11/05 単一式を本体として持つメソッドや関数定義を可能に

最近、Klassicと同時にOnionもちょくちょく更新するようになったので、変更点など書いていこうと思う。

今日は、メソッドや関数定義に、文ではなく式を取れるようにする改良を行った。今どきの言語だと当たり前だが、Onionを最初に開発した2004-2005年の時点ではそれほど当たり前ではなかったのだ…と思う。

というわけで、

class Adder {
public:
  def add(x: Int, y: Int): Int = x + y;
}

のような定義が可能になった。セミコロンが必須なところがださいので、この辺もじきに不要になるようにしたい(多少面倒だが)。

github.com

Scala初学者はvarを使っても良い

TwitterScala関係のつぶやきをみていると、どうも、特に初学者の方に、

varを使ってはいけないので、Scalaは難しい…

という意見が散見されるようです。

しかし、Scalaを学び始めのときにいきなりvarを断つ必要はなく安心してvarを使ってもいいと思います (そもそもvarが全く不要なら言語仕様にあるわけないですし)。慣れれば、varを明らかに使わなくて 良い箇所はわかってくるので、そこからval/varを使うのを意識しても遅くないと思います。

もちろん、いつもでも全部var使ってるとどうかという議論はあるのですが、それはScalaに慣れてから 考えればよい話です。Scalaを学ぶのにvarを使わないという縛りがきついのなら、無理しなくていいよ というのが言いたいことです。

Elixirを学んでみる(1) - parser combinator

最近、Elixirがなんとなく盛り上がってきている気がするので、この機会に入門してみることにした。お題は相変わらずparser combinator。解説はめんどうくさいのでコードだけ貼り付けておく。

わかったこと。

  • Elixirの文法は結構くせがある。文法はRubyに影響を受けたということだけど、Rubyの文法とはまた別の癖がある
  • Elixirは動的型付き言語だが、関数が定義されているか、引数の数があっているかなどがある程度静的にチェックされるので、凡ミスは意外にコンパイラが見つけてくれる。
  • f.(x) という文法はちょっと気持ち悪い。
  • defで定義したものをオブジェクト化するときに、 &f/1のように引数の数を明示しなければいけないのは、Erlang譲りっぽいけど、ちょっと面倒くさい
  • パターンマッチは普通に使いやすい
  • String.sliceの第二引数で与える範囲がJavaとかのsubstringと違ってちょっと迷った
  • mixで簡単にプロジェクトの雛形とテストの雛形を生成できて便利
  • 関数(第一級でないものの方)を呼び出すときに、fのように書くだけで呼び出せるのはRubyっぽい
  • Ruby以上になんでもかんでもendで閉じる傾向がある

文法は癖があるが、おおむねまっとうな言語だなあというのは初見の印象。

gist.github.com

Kotlinの謎(解明編)

今日書いた

kmizu.hatenablog.com

についてですが、原因がわかりました。まずは以下のコードを見てください

Welcome to Kotlin version 1.0.0 (JRE 1.8.0_91-b14)
Type :help for help, :quit for quit
>>> val a: () -> Int = { 2 }
>>> val b: () -> Unit = { 2 }
>>> println(a())
2
>>> println(b())
kotlin.Unit

変数abの右辺は共に{ 2 }になっており、これは0引数の無名関数を表しています。一方、aの型は引数なしでIntを返す関数、bの型は引数なしでUnitを返す関数となっており、型に互換性がないにも関わらず、代入することができています。

このことから、無名関数の式の型は、期待される型によって(も)決定されるのだろうということがわかります。具体的には、2行目の{ 2 }の型は期待される型が() -> Unitなので、{ 2 }自体が() -> Unit型になり、そこから、

{ -> 2; Unit }

のようにUnitが補われているのだろうと思います。

F.kt:3:25: warning: the expression is unused
  val b: () -> Unit = { 2 }
                        ^

という警告が出る辺りもそれを裏付けていると言えそうです。

Kotlinの謎

Kotlinという言語の型システムは凄くおおざっぱに言ってしまうと、Scalaと非常によく似ています。

  • TopがAny
  • BottomがNothing
  • Byte, Short, Int, Float, ....といった、プリミティブ型相当の型が継承階層に組み込まれていて、型名まで同じ
  • Generics
  • Declaration-site variance
  • Use-site variance

違う部分、たとえばnullabilityを型システムレベルでサポートしてるとかはもちろんあるのですが、AnyとNothingという名前まで真似してる辺り、非常にScalaの影響が大きいのは間違いありません。

一方で、Kotlinがあえて採用しなかったものの一つに、暗黙の型変換があります。たとえば、Scalaでは、任意の型はUnitに暗黙に型変換できます。そのため、たとえば

val x: Unit = 1

というコードはコンパイル可能です。一方で、Kotlinは

val x: Unit = 1

というコードはコンパイルを通りません。これはまあ設計判断としてはどっちもありだと思います。

さて、それはいいのですが、一つ妙な点があります。次のプログラムを見てください:

gist.github.com

runは引数として渡された0引数無名関数を呼び出し、その最後の評価値を返すメソッドです。ここで、run<Unit> { 1 + 1 }とすることで、1 + 1の結果を強制的にUnitに変換しようとしています。普通に考えると、IntからUnit への暗黙の型変換が駄目なのだからコンパイルも通らなそうなのですが、何故かコンパイルを通ってしまいます。

これはKotlinが暗黙の型変換をサポートしていないという事実と反しているようにも思える。あるいは、実装のバグなのかもしれません。というわけで、Kotlinに詳しい方、何故これがコンパイルを通るか(どういうコードが生成されるかはわかっているので、仕様的に何故通るべきなのか)教えていただけないでしょうか(教えてクン)

Macro PEG 0.0.10 リリース

かなり久しぶりのリリースです。主な変更点は、

Conflict between an expression parenthesis syntax and call syntax · Issue #16 · kmizu/macro_peg · GitHub

を修正した点でしょうか。これで、

STRING = STRING_MACRO("\"") / STRING_MACRO("'");
STRING_MACRO(Q) = Q (!(Q / "\\") . / "\\" .)* Q;

のようなPEGにおいて、Q (!(Q / "\\") が正しく Q(!(Q / "\\") の連接として解釈されます(今までは Qの呼び出しと解釈されていた)。修正方法としては、マクロ名につづいてスペースが続かずに引数の括弧が続く場合はマクロ呼出し、スペースが挟まれている場合は連接として解釈するようにしました。当初、グルーピングは[]という別の記号を使おうと思いましたが、記述として気持ち悪いので、現状のシンタックスをあまり変えないようにしました。