aiscript icon indicating copy to clipboard operation
aiscript copied to clipboard

空ブロックと空オブジェクト式の干渉

Open marihachi opened this issue 3 years ago • 35 comments

#179 の無名関数() => {}を実現する上での問題点についての検討。 空ブロックと空オブジェクト式が干渉してしまう。

関係する仮仕様

  • 代入式でブロックが使える
  • 代入式でオブジェクト式が使える
  • 代入式で{}を渡した場合はオブジェクト式と解釈する
  • 無名関数でブロックが使える
  • 無名関数でオブジェクト式が使える。 その場合は({})と指定する。
  • 無名関数で{}を返した場合はブロックと解釈する

懸念

  • 対象によって式の優先度が変わるのはユーザーにとっても優しくなさそう。

marihachi avatar Aug 01 '22 04:08 marihachi

解決策1

ブロックをブロック文とブロック式の2つに分ける:

  • ブロック文: {}
  • ブロック式: ? (別で考える)

※ブロック式は仮称。全然別の名前をつけても良さそう。

{}はブロックの文として扱って、ブロックの式としては解釈しないようにする:

  • {}は常にオブジェクト。
  • {}は常にブロック。
  • よって、文({})はオブジェクト。
  • 式としてブロックを使いたい場合はブロック式を使う。

(2022/08/08更新) 名前を決定。 ブロック式→eval式eval {}

marihachi avatar Aug 01 '22 04:08 marihachi

ブロック式

let result = do {
  1 + 2
}

みたいなのどうじゃろ

syuilo avatar Aug 04 '22 09:08 syuilo

doも良さそう。 ただ、doはdo-whileから繰り返しのイメージがあるから混乱させる恐れがある。

marihachi avatar Aug 05 '22 01:08 marihachi

scope とかはどう?

let result = scope {
  let x = calc(2)
  let a = calc(1)
  2 * x + a
}

名詞じゃないほうが良いのかな... :thinking:

marihachi avatar Aug 05 '22 01:08 marihachi

evalとかexecとかでも良さそう

let result = ${
  let x = calc(2)
  let a = calc(1)
  2 * x + a
}

みたいに記号使うのも良さそう

syuilo avatar Aug 05 '22 05:08 syuilo

C#のLINQみたいにfrom とかどうだろう

let result = from {
  let x = calc(2)
  let a = calc(1)
  2 * x + a
}

marihachi avatar Aug 05 '22 05:08 marihachi

ほむん from はあまり馴染みなかった

syuilo avatar Aug 05 '22 05:08 syuilo

記号は何の記号だったか忘れるかも 初見だと意味を把握しづらいかも (直感的なものがあればあり)

marihachi avatar Aug 05 '22 05:08 marihachi

${}はテンプレート文字列でも使われる構文だからその点は分かりやすいと思った

syuilo avatar Aug 05 '22 05:08 syuilo

evalとかexecとかでも良さそう

この2つは普通にアリ

marihachi avatar Aug 05 '22 05:08 marihachi

fromは、続くブロック"から"その式の値を評価するって意味で良いかなと思った

marihachi avatar Aug 05 '22 05:08 marihachi

個人的には 1位 from 2位 eval って感じかも

あとは他の人の意見も参考にしたい感じ(アンケートなど)

marihachi avatar Aug 05 '22 05:08 marihachi

遅延評価とかも考えると、evalの方が良いのかな

marihachi avatar Aug 05 '22 06:08 marihachi

仮に遅延評価を、

lazy { }

とかにする場合に、即時評価を

eval { }

とするのは自然な気がする

marihachi avatar Aug 05 '22 06:08 marihachi

evalにするか

syuilo avatar Aug 05 '22 17:08 syuilo

eval式追加してみるか

marihachi avatar Aug 08 '22 03:08 marihachi

書きながら思ったけど、eval式を追加するとブロック文(文{})があまり要らなくなるかも。 ブロック文は値を返せないため、スコープが生成されるだけの構文になる。

marihachi avatar Aug 08 '22 06:08 marihachi

ブロックを触ってmatch式にも影響が出てるな

marihachi avatar Aug 08 '22 06:08 marihachi

解決策1ダメかも これまでのブロックはmatchなどの構文の一部としてごく一般的に使用されてる。 これを変えると、ちょっと整合しなくなるな...

marihachi avatar Aug 08 '22 07:08 marihachi

matchでstatementを取るようにすれば良いかも?

marihachi avatar Aug 08 '22 07:08 marihachi

  • ブロックが式である限りはオブジェクト式と干渉する
  • ブロックが式でなければifやmatchでブロックを用いて値を返すことができない

詰んできた

marihachi avatar Aug 08 '22 08:08 marihachi

ifやmatchの構文を変更してブロックを使わないようにすれば何とかなるかもしれないけど、あまり美しくはないな

marihachi avatar Aug 08 '22 08:08 marihachi

ブロックが式でなければifやmatchでブロックを用いて値を返すことができない

これどういうこと?

syuilo avatar Aug 08 '22 08:08 syuilo

例えば

<: match 2 {
	1 => "a"
	2 => "b"
	3 => "c"
}

のような一般的なコードは今回の変更に影響されない気がした

syuilo avatar Aug 08 '22 08:08 syuilo

ブロックを文に変更するとして、 =>の右側は式を受け取ってるけど、ここで文を受け取るようにする必要があると思う。 ただ、文に値を返す機能はない(と現状ぼくは思っている)ためダメだと思った。

marihachi avatar Aug 08 '22 08:08 marihachi

=>の右側は式を受け取ってるけど、ここで文を受け取るようにする必要があると思う。

これはなぜ?

syuilo avatar Aug 08 '22 08:08 syuilo

ブロックが文になるから

marihachi avatar Aug 08 '22 08:08 marihachi

mtach構文にブロック関係なくない?

syuilo avatar Aug 08 '22 08:08 syuilo

https://github.com/syuilo/aiscript/blob/master/src/parser/parser.peggy#L382 ここで式を受け取ってて、

<: match 2 {
	1 =>  {
		"a"
	}
	2 => {
		"b"
	}
	3 =>  {
		"c"
	}
}

をしたい場合にブロックの式が使われると思うけど...

marihachi avatar Aug 08 '22 09:08 marihachi

https://github.com/syuilo/aiscript/blob/master/src/parser/parser.peggy#L382 ここで式を受け取ってて、

<: match 2 {
	1 =>  {
		"a"
	}
	2 => {
		"b"
	}
	3 =>  {
		"c"
	}
}

をしたい場合にブロックの式が使われると思うけど...

この場合は

<: match 2 {
	1 =>  eval {
		"a"
	}
	2 => eval {
		"b"
	}
	3 =>  eval {
		"c"
	}
}

と書くようになると思ってた

syuilo avatar Aug 08 '22 09:08 syuilo

それなら可能だけど、見た目が微妙じゃない?

let a = if true eval {
  1
} else eval {
  2
}

とかになる

marihachi avatar Aug 08 '22 09:08 marihachi

あー matchに関して言えば、ブロック式が使われることはあまりないと思うから

<: match 2 {
	1 =>  eval {
		"a"
	}
	2 => eval {
		"b"
	}
	3 =>  eval {
		"c"
	}
}

で良いと思ってたけど、if はなあ

syuilo avatar Aug 08 '22 09:08 syuilo

「ifは式である」というのを一旦やめる(=ブロック必須にする)とか やめた後でやっぱり欲しくなったらif文とは別に三項演算子的な構文を設けるとか

syuilo avatar Aug 08 '22 09:08 syuilo

ifやmatchの構文を変更してブロックを使わないようにすれば何とかなるかもしれないけど、あまり美しくはないな

これをするとか そもそも構文として、その部分が「ブロック式」とは別物の構文であることにすれば解決するかも

marihachi avatar Aug 08 '22 09:08 marihachi

こんな感じで

ifContent = "{" Statements "}" / Statement

marihachi avatar Aug 08 '22 09:08 marihachi

オブジェクト式の構文を変更したら解決する感じがしてきた

marihachi avatar Aug 10 '22 11:08 marihachi

それできたら一番楽なんだけど、JSONをそのまま解釈したいという思いがある

syuilo avatar Aug 10 '22 11:08 syuilo

ブロックを文に変更して、 ブロック文は値を返せることにするとか

marihachi avatar Aug 10 '22 13:08 marihachi

良いかも

syuilo avatar Aug 10 '22 13:08 syuilo

文が値を返すのを良しとするかって話にはなるけど

marihachi avatar Aug 10 '22 13:08 marihachi

そもそも構文として、その部分が「ブロック式」とは別物の構文であることにすれば解決するかも

これで良さそう

syuilo avatar Aug 11 '22 12:08 syuilo

解決策2

  • オブジェクト式は現状維持
  • ブロック式を廃止し代わりにeval式を追加
  • 構文規則にブロック{}を直接追加 (ifやmatch、無名関数、他...)
  • 代入ではブロックではなくeval式を使用する

解決策2を採用した場合の要件カバー状況

  • [x] 代入で~~ブロックが使える~~ eval式が使える
  • [x] 代入でオブジェクト式が使える
  • [x] 代入で{}を渡した場合はオブジェクト式と解釈する
  • [x] 無名関数でブロック{}が使える
  • [x] 無名関数でオブジェクト式が使える。 その場合は({})と指定する。
  • [x] (無名関数でeval式を返すことも可。あんまり意味はないけど)

まとめた こんな感じかな

marihachi avatar Aug 12 '22 04:08 marihachi

👍🏻👍🏻

syuilo avatar Aug 13 '22 09:08 syuilo

実際の作業についてのissueを立てたのでcloseします

marihachi avatar Aug 14 '22 00:08 marihachi