2018/01/05 復活。 TypedEmacsValue いらねーかな? purescript でいう Foreign と同じ感じで。 FromEmacsValue と ToEmacsValue は分けるか。 関数のやつはFn2 a b c とか EffFn3 a b c d とかoを踏襲。 どこまでを保証するラインをどこが simplize するべし。
関数は生の形で。EmacsValue を使おう
2016/11/21 hook,buffer local variable に関して
Warning: do not use make-local-variable for a hook variable. The hook variables are automatically made buffer-local as needed if you use the local argument to add-hook or remove-hook.
2016/11/07 StablePtrのところまとめないとね。
2016/11/07 じゃないと、まずいよね。逆に Emacs 側に設定した関数(から辿れるオブジェ クトもしかり)は GC の対象外になってくれるのかな?
-- TODO: ??? これ StablePtr の効果も兼ねている?
foreign import ccall "wrapper"
wrapEFunctionStub :: EFunctionStub -> IO (FunPtr EFunctionStub)
ああ、ちゃんと gc かの対象に外れるは。
You have to release the generated code explicitly with `freeHaskellFunPtr` to avoid memory leaks: GHC has no way to know if the function pointer is still referenced in some foreign code, hence it doesn’t collect it.
2016/11/06 GitHub - knupfer/haskell-emacs: Write Emacs extensions in Haskell が stackサポートしていた… メリットないな。こっちだと多分別プロセスが起 動するだけなので、スレッドが使える。 knupfer/haskell-emacs#60 にあるように、転送 オーバーヘッドだけが、haskell-emacs のデメリットなので現在のところダイ ナミックモジュールへの転向は考えていないそうだ。 あとデメリットとして haskell -> emacs 側への関数呼び出しに制約があるよ うだ。 あと動的な関数の登録は haskell-emacs ではできないのかな?
2016/11/06 TODO
- [ ] ベンチマークを取る
- haskell-emacs を参考にすればいいかな?
- [X] シンボルのキャッシュ化
- [ ] TH 依存なくすか?コンパイルが糞遅い
- [ ] ライブラリ化
- [ ] githubに登録
- [ ] ドキュメント書く
- [ ] hackage に登録
- [ ] 例外処理
- [ ] 外部プロセスとのやりとり
- emacs 自身がバッファ通してでしか入力を取れないのえ
2016/11/06 helm の candidate 生成に使えたよ! ただ遅い?haskellからemacsへの呼び出しが重い。 immutable な EmacsValue はキャッシュすのがいいかな。 ただ weak pointer とかの処理をする必要がある。 cons とか list の関数
2016/11/05 Tagged は必要なのか?という根本的な疑問。 まず Emacs と Haskell 間のやりとり、を考える。 それを安全に扱うのにメリットがなければ導入する必要はない。
EmacsValue と EmacsValue -> Maybe Int Int -> EmacsValue があればいいのでは…
- funcall: haskell から emacs の関数を呼び出す
- 問題となるのは引数と返値
- 引数は
- Int や Text, (Symbol Text) を渡す
- EmacsValue を渡す
- ただ引数は他の
- 返値は
- EmacsValue として
- 自分で型が必要であれば EmacsValue -> Maybe a みたいな変換をか ます
- 引数は
- 問題となるのは引数と返値
- emacs の変数に設定
結局 Haskell 側では Tagged a EmacsValue を扱うメリットはなく、Haskell の型に変換すればいいだけなので、その時に安全性は確保されている(という かそれ以上の安全を提供していない)。
複雑になるだけのなので、できれば除外するか。今の状態は残しておきたいの で、このタイミングで dots は git 移行しよう。
Haskell で投げられた例外の対応
Emacs -> Haskell から呼ばれるところに設置する必要がある。例外が補足で きないと恐らく emacs がクラッシュする。非同期例外については考える必要 はない。
二つの場合を対処する必要がある(多段)
- Haskell 側で例外が発生した
- Haskell から呼び出した emacs 関数の中で signal(or throw)された
- の場合、emacsから返ってきた時に non local exit かどうか確認し、
もしそうであれば haskellの例外を投げる。そして haskell -> emacsに戻る 場所で haskellの例外は補足する。その場合、non-local-exit は既に設定さ れているので、
_non_local_exit_signal で haskellエラーであることを設定する。ただし これが簡単にはいかず、
- IO モナドの中で実現する必要がある
- emacs関数を呼び出す際に例外が発生しうるものを呼び出せない
signal, throw中でも利用できる関数だけしか利用してはいけない。
どこで補足するかだな。。。 runEmacsM の中かな? ただ runEmacsM の外側で mkFunciton の stub と、emacsModuleInit かな?
2016/11/11 あれー。 以下の関数で、exit した後のメッセージが飛ばない。やっぱ jmp しているの か?
defun' "error-test" $ do
s <- mkString "fooo"
message "before signaling"
nonLocalExitSignal "test" s
message "not exited here"
いや、やっぱ jump していなかった。emacs-module.c のソースコードのコメ ント読めば例外ハンドリングをどうするか書かれている。
- emacs では通常 setjmp/longjmp を使っている
- ただ外部モジュールではジャンプしたら不都合があるため、
emacs-module.c で(haskell -> emacsで)呼び出す関数は
- throw,catch されてもちゃんとキャッチしてくれる
- pending_non_local_exitメンバに throw か signal が設定される
- haskell 側で明示的に設定した場合は non_local_exitsignal,throw
- pending_non_local_exit に non-return が設定されている、 emacs-module.c で提供されている関数は使えなくなる(ものがある)
- pending_non_local_exit に non-return を設定したまま emacs に戻ると signal か throw される
defun' "error-test" $ do
s <- mkString "fooo"
message "before signaling" -- ok
nonLocalExitSignal "test" s
message "not exited here" -- ng
nonLocalExitClear
message "how about here" -- ok
longjmpしないと行けないかと思ったがそうでもないかな。
- non_local_exitsignal,throw が env->prviate_members の特定のメン
バを設定し、haskell -> c に帰ったときに、メンバが設定されていれば例
外を投げる感じかな?
- non_local_exit_get はそのメンバの値を取得
- non_local_exit_check はそのメンバが設定されているかのチェック
- non_local_exit_clear はクリア
- emacs に帰っても例外は投げられなくなるかな?
そもそも emacsでのエラーハンドリングってどうなっているんだ? GNU Emacs Lisp Reference Manual: Nonlocal Exits
- Nonlocal Exists の仕組みが何故か二つある
- Catch/Throw
- どっちというと制御構造的なもの?
- モジュールの中で完結して使う必要がある?
- モジュールの外に出す場合は error が適切かな?
- Error
- signalがエラーを投げるための関数
(signal 'no-such-error '("My unknown error condition"))
- error 関数は ‘error シンボルで signal しているようだ
- signalがエラーを投げるための関数
- Catch/Throw
/* Non-local exit handling. */
enum emacs_funcall_exit (*non_local_exit_check) (emacs_env *env);
void (*non_local_exit_clear) (emacs_env *env);
enum emacs_funcall_exit (*non_local_exit_get)
(emacs_env *env,
emacs_value *non_local_exit_symbol_out,
emacs_value *non_local_exit_data_out);
void (*non_local_exit_signal) (emacs_env *env,
emacs_value non_local_exit_symbol,
emacs_value non_local_exit_data);
void (*non_local_exit_throw) (emacs_env *env,
emacs_value tag,
emacs_value value);
/* Possible Emacs function call outcomes. */
enum emacs_funcall_exit
{
/* Function has returned normally. */
emacs_funcall_exit_return = 0,
/* Function has signaled an error using `signal'. */
emacs_funcall_exit_signal = 1,
/* Function has exit using `throw'. */
emacs_funcall_exit_throw = 2,
};
emacs-module.c から抜粋。種別が違うだけで、投げられるのは同じようだ。
static void
module_non_local_exit_signal_1 (emacs_env *env, Lisp_Object sym,
Lisp_Object data)
{
struct emacs_env_private *p = env->private_members;
if (p->pending_non_local_exit == emacs_funcall_exit_return)
{
p->pending_non_local_exit = emacs_funcall_exit_signal;
p->non_local_exit_symbol = sym;
p->non_local_exit_data = data;
}
}
static void
module_non_local_exit_throw_1 (emacs_env *env, Lisp_Object tag,
Lisp_Object value)
{
struct emacs_env_private *p = env->private_members;
if (p->pending_non_local_exit == emacs_funcall_exit_return)
{
p->pending_non_local_exit = emacs_funcall_exit_throw;
p->non_local_exit_symbol = tag;
p->non_local_exit_data = value;
}
}
例えば良く利用するシンボルをキャッシュするなどしたい。これを実現するに は、
- make_function 時に最後の引数にデータを渡す。これはその関数が呼び出
された際に最後の引数として渡される
- この値は haskell 側で GC の対象から明示に外す必要がある(haskell側 では参照を持たないので)
weakpointer かな?- Foreign.StablePtr だ必要なのは
- haskell 側で emacs_value を保持する場合は、emacs に制御が返った際に、
それらが GC の対象から外れるように指定する必要がある
- make_global_ref/free_global_ref 関数を使えば良い
- モジュール側が保持しておく emacs_value のメモリが勝手に解放されるのを防ぐ(リファレンスを保持してくれる)。
emacs_value (*make_function) (emacs_env *env,
ptrdiff_t min_arity,
ptrdiff_t max_arity,
emacs_value (*function) (emacs_env *env,
ptrdiff_t nargs,
emacs_value args[],
void *)
EMACS_NOEXCEPT,
const char *documentation,
void *data);
/* Memory management. */
emacs_value (*make_global_ref) (emacs_env *env,
emacs_value any_reference);
void (*free_global_ref) (emacs_env *env,
emacs_value global_reference);
- Internal.hs
- emacs が提供する emacs_module.h の各関数のラッパー + α
- 主に扱うのは EmacsValue、EmacsM
- EmacsValue の型などのチェック
- EmacsValue <-> Haskell の型(Int, Text) など
- 関数呼び出し関数 funcall’ も提供
- 実際に実行するための関数 getEnv/runEmacsM も提供する
- EmacsType が登場するのは typeOf, isTypeOf だけ
- 基本的にこれを直接触るのはなしにするべきかな
- [ ] extractEInteger があるのは why?
- [ ] 名前は ’ 付けているけど、無しでいいきがする
- [ ] 名前は mkEString ではなく、mkString でいいかな
- Core.hs
- Internal.hs を使い ラップ? or 拡張?
- 型は Tagged tag EmacsValue のみ扱う?べき
- [ ] EmacsValue を受け取っている箇所は Tagged a EmacsValue を取る べきかな
普通に build すると require 時に以下のエラーが出てしまう。
undefined symbol: stg_forkOnzh
どうやら haskell runtime にリンクする必要があるらしい、が、 cabal では
指定できない? なので ビルド時のオプションとして渡す。
指定できないことはないはず。。。
$ stack build --ghc-options -lHSrts-ghc$(stack exec -- ghc-pkg field ghc version --simple-output)
https://mail.haskell.org/pipermail/haskell-cafe/2012-September/103227.html How to link to Haskell static runtime with cabal and stack without hard coding ghc version? - Stack Overflow を参考にするのがいいかな?
- Tagged or NewType
- Tagged EInteger EmacsValue (+ 利便性のために型シノニムも?)
newtype EInterger = EInteger EmacsValue- これだとどちらにしろ -> EmacsValue への型クラスが必要になる
- Haskell -> Tagged a EmacsValue
- Haskell側から生成した値を Emacs 側に渡す場合
- 単一の値を setq
- Haskell関数をの返値を Emacs 側から呼び出すときの返値 これが 必須なので結局必要だよね
- Emacs関数を Haskell 側で呼び出す際の引数とか?
- EmacsM Monad の中で計算する必要がある
- Haskell のほうが型が豊富なため、単射ではないことに注意。例えば Word8, Int, Int8 などは全部 emacs の integer になる
そもそも写像か?例えば Haskell の Int を Emacs への複数の型へと変換したいことはないだろうか- いや、これは止めたほうがいいかな
- Text を Emacs の 文字列もしくは シンボルに変換するという分岐が あるな。。
- なるほお
- Integer とかは値が収まっている間は
- つまり例外処理が必要かな?
- 選択肢二つ
- 個別のspecific な関数
- ToEmacsValue 型クラス + 型family
- いや両方組合せがいいかな?後者は利便性 + haskell関数をemacs側に持っ ていく時に必要かな? それとも明示させるか?
- 綺麗に一対一でも単車でも前者でもないので、明示的な関数を極力使う べき(2 のみかな
- Haskell側から生成した値を Emacs 側に渡す場合
- EmacsValue の tag付け
- そそも EmaccValue の Emacs側での型を求める関数が必要になる
- Emacs毎の型毎に
tryTagInteger :: EmacsValue -> Maybe (Tagged EInteger EmacsValue)
- これは Proxy とかで使えば個別の関数は作る必要なし?
- Haskell <- (Tagged?) EmacsValue
- これも EmacsM の中で行なう必要がある
- 用途
- Emacs側から Haskell 側の関数を呼び出したいとき
- Emacs関数を Haskell側で呼び出したときに返値
- FromEmacsValue
- Maybe の必要がる
- Tagged 版の場合は
- Tagged EmacsValue のまま操作する関数も色々
- 例えばバッファとかは Haksell 側ではネイティブな
{-# LANGUAGE OverloadedStrings,FlexibleInstances,UndecidableInstances #-}
module Main where
data EmacsValue = EOne
deriving Show
class EV a where
toEv :: a -> EmacsValue
fromEv :: EmacsValue -> a
class Callable a where
call :: a -> [EmacsValue] -> EmacsValue
instance {-# OVERLAPPING #-} EV a => Callable a where
call a _ = toEv a
instance {-# OVERLAPPING #-} (EV a, Callable b) => Callable (a -> b) where
call f (e:es) = call (f (fromEv e)) es
call _ [] = undefined
instance EV Int where
toEv _ = EOne
fromEv _ = 1
plusOne :: Int -> Int
plusOne = (+1)
mul :: Int -> Int -> Int
mul = (*)
main :: IO ()
main = do
print $ call plusOne [EOne]
Text | EString |
Int | EInteger |
Symbol(*1) | ESymbol |
Nil(*2) | ENil(※3) |
Cons * | |
List * |
※3 emacs 側には nil という型は存在しない。特殊なシンボルとして表現さ れている。
多分バッファ関連が必要になる。
GNU Emacs Lisp Reference Manual: Defining Commands 参照。 GNU Emacs Lisp Reference Manual: Symbol Properties
- interactive は special form なので emacs-module からは呼べない(evil 経由ならいけるが。。)
- interactive はそもそも関数に対してフラグを設定するようなもの。その フラグを call-interactivly が読んで必要な引数を渡す仕組みになってい る
- 代わりに interactive-form 属性および interactive-only 属性が使えそ
うかな?
- というか関数じゃなくてシンボル側に設定があるのか。。
interactive-form に設定するべき値が分からん。 ソースを見た。
data.c callint.c
FInteractive_form(..) は nil でなければ ‘interactove-form の値を form として返している。
call-interactively は以下のように利用している。
form = Finteractive_form (function);
if (CONSP (form))
specs = filter_specs = Fcar (XCDR (form));
else
wrong_type_argument (Qcommandp, function);
cdr の car を返している。ということは以下のような形式を設定すればよさ げ。
(interactiev nil) (interactive "b\hoge")
そもそも ToESymbol はないほうがいいかな? いや、型の対応をきっちりするべきかな? Text -> ESymbol は多分まずい。 Text -> EString と混同する恐れがあるから。
funcall1 :: ToEmacsValue a => Text -> a -> EmacsM EmacsValue