sounisi5011/vec-draw

[WIP] この独自DSLにおける関数/メソッドの定義と構文

Opened this issue · 9 comments

概要

DSLで使用する関数に関するアイデアの募集

詳細

高度な処理を行うには、組込み関数や、ユーザ定義の関数は必須である。
そこでこのissueでは、関数の挙動に関するアイデアやルール・構文について書き残したい。

このDSLはSVGに変換することを主目的にしている。
すなわち、関数の構文は、SVGやCSS内で使用されているfuncname(param1, param2, param3)のようなものである必要があるだろう。

パイプライン演算子(左辺の値を、右辺の関数の第一引数に渡す演算子)を使用することを想定した場合、全ての関数はオブジェクトのメソッドとしてではなく、グローバル/ローカルな変数として宣言するのが良いと思う。
処理の分岐は多重定義を用いる。すなわち、引数の型などを見て言語側で自動判定する。

処理の分岐は多重定義を用いる。すなわち、引数の型などを見て言語側で自動判定する。

組込み型などの値なら良いが、独自のオブジェクト/クラス/構造体の場合、多重定義をどのようにすべきだろう?
他のライブラリの構造体を引数に求める関数を定義したい場合もあるだろうし。

パイプライン演算子|>なんて使わずに、いっそ「空白区切りの値と関数」をパイプラインとみなすのはどうだろう。

[1, 2, 3].map(add).map(multi(3))

[1, 2, 3] |> add |> multi(3)

ではなく

[1, 2, 3] add multi(3)
[1, 2, 3] add() multi(3)

と表記する感じ。

SVGのtransform属性の記法とも合わせられる。

transform=(m => m translate(30) rotate(45 50 50))

みたいに表記できそう。
…できない?

処理の分岐は多重定義を用いる。すなわち、引数の型などを見て言語側で自動判定する。

組込み型などの値なら良いが、独自のオブジェクト/クラス/構造体の場合、多重定義をどのようにすべきだろう?
他のライブラリの構造体を引数に求める関数を定義したい場合もあるだろうし。

Rustのような型に関数を割り当てるtrait方式にするか、引数で分岐する多重定義にするか…
型にメソッドのようなものを生やすやり方のほうが分かりやすいとは思うけれど、パイプライン処理を考えるなら、関数そのものは型に制約されないもののほうが良いと思う…

例えば、文字列化するstr()関数を定義するとして、新しい型を定義するたびにtoString()関数を定義する必要がある。それをどうやって判定する?interfaceが実装されているか判定する機能が必要では?

例えば、文字列化するstr()関数を定義するとして、新しい型を定義するたびにtoString()関数を定義する必要がある。それをどうやって判定する?interfaceが実装されているか判定する機能が必要では?

toString()関数は要らないでしょう。str()関数を多重定義すればいい。
とはいえ、interface的な型判定が必要なのは確かに言える。
「文字列化可能な値」を全て受け付ける引数をもつ関数を書くには、interfaceなり、型のエイリアスなりが必要になる…

とはいえ、interface的な型判定が必要なのは確かに言える。
「文字列化可能な値」を全て受け付ける引数をもつ関数を書くには、interfaceなり、型のエイリアスなりが必要になる…

型の判定を動的に行うのは?例えば、str()関数が適用可能な値かどうかを動的に判定する、みたいな。

とはいえ、interface的な型判定が必要なのは確かに言える。
「文字列化可能な値」を全て受け付ける引数をもつ関数を書くには、interfaceなり、型のエイリアスなりが必要になる…

型の判定を動的に行うのは?例えば、str()関数が適用可能な値かどうかを動的に判定する、みたいな。

その判定コードそのものがinterfaceに該当するのでは?