目的
Haskellのパッケージに含まれるあるシンボルをパッケージに外に見せたくない場合に、
逆に見せたいシンボルを列挙して対応していませんか?
今回紹介する方法はGHC Source Pluginを使ってシンボルをパッケージ外に出さない方法を紹介します。
動機の確認
シンボルをパッケージ外に出さない方法を紹介する前になぜそのようなことをやりたいのか確認しておきます。
C++の関数をHaskellで呼び出せるようにinline-c-cppでラッパを作りたい場合に基本的には外に見えるようにしたいが
inline_c_ffi_適当な番号
というinline-c-cppの内部関数が生成されました。
inline-c-cppのtemplate haskellに渡すデータは次のようなファイルなのですが、
module モジュール名 where
関数1
関数2
関数3
template haskellによって次のような関数が生成されていました。
module モジュール名 where
関数1
inline_c_ffi_23232
関数2
inline_c_ffi_23233
関数3
inline_c_ffi_23234
生成する関数は数千に及んだため複数のファイルでinline_c_ffi_適当な番号
の番号が衝突する問題が発生し、
すべて外に見えるようにするわけにはいかなくなりました。
下記のように列挙してもいいですが、コードも読みにくくなるので、GHC Source Pluginで隠すことにしました。
module モジュール名(
関数1
関数2
関数3
) where
関数1
関数2
関数3
やり方
今回修正するのはdesugar
というCore言語にする前のtypeCheckResultAction
というモジュールからエクスポートする関数を削除する方法で考えました。
typeCheckResultAction
はTcGblEnv
というトップレベルのモジュールを返す関数になっています。
そこからエクスポートしたくない関数を除外すれば完了です。
次のようなコードになります。
{-# LANGUAGE CPP #-}
module GHC.NotExport.Plugin (plugin) where
#if MIN_VERSION_ghc(9,0,0)
import GHC.Driver.Plugins
import GHC.Types.Avail
import GHC.Types.Name
import GHC.Tc.Types
import GHC.Utils.Outputable
#else
import GhcPlugins
import Plugins
import TcRnTypes
import Avail
#endif
import Data.List (isPrefixOf)
plugin :: Plugin
plugin =
defaultPlugin
{ typeCheckResultAction = notExportPlugins,
pluginRecompile = purePlugin
}
notExportPlugins cmdOptions modSummary env = do
let updated_tcg_exports = filter (\v -> not (isPrefixOf "inline_c_ffi" ((showSDocUnsafe . ppr . nameOccName . availName) v))) $ tcg_exports env
return env {tcg_exports = updated_tcg_exports}
このあとghc-optionsで上記のモジュールを渡せばinline_c_ffi
で始まる関数がエクスポートされなくなります。
ghc-options: -fplugin GHC.NotExport.Plugin
まとめ
GHC Source Pluginを使って特定のパターンの関数をエクスポートしないようにするプラグインを作りました。hasktorchというlibtorchをラップしているライブラリで実際に使用中です。