静的ポインタ
StaticPointers
7.10.1以降
静的ポインタの構文を使用できるようにする.
言語拡張StaticPointers は static e という形式の新たな構文を追加する.これは閉じた式⟨e⟩への参照を表す.この参照は安定性と移植性がある.どういう意味かというと,複数のプロセスにまたがって有効であり続け,それらのプロセスは異なるマシン上にあってもよいということである.したがって.プロセスは参照を作ってそれを別のプロセスへと送ることができ,受け取った側はそれを⟨e⟩へと参照を解決することができる.
この拡張がオンになっている状態では,staticは合法な識別子ではなくなる.
静的ポインタは,論文 Towards Haskell in the cloud, Jeff Epstein, Andrew P. Black and Simon Peyton-Jones, Proceedings of the 4th ACM Symposium on Haskell, pp. 118-129, ACM, 2011 で最初に提案された. 静的ポインタを使う
式 static e における e は閉じた式である必要がある.式が閉じているとは,式の自由な(型)変数が全て閉じていることを意味する.変数が閉じているとは,閉じた式にlet束縛されており型も閉じていることを意味する.型が閉じているでとは,自由変数を持たないことを意味する.
以下は全て許可される.
code:pointer1.hs
inc :: Int -> Int
inc x = x + 1
ref1 = static 1
ref2 = static inc
ref3 = static (inc 1)
ref4 = static ((\x -> x + 1) (1 :: Int))
ref5 y = static (let x = 1 in x)
ref6 y = let x = 1 in static x
一方で,以下の定義は拒絶される.
code:pointer2.hs
ref7 y = let x = y in static x -- xはclosedではない
ref8 y = static (let x = 1 in y) -- yはlet-boundではない
ref8 (y :: a) = let x = undefined :: a
in static x -- xはnon-closedな型を持つ
Note
:load コマンドを用いてGHCi内でロードされたモジュールはStaticPointers や static式を使えるのに対して,REPLに入力された文はそれらを使うことができない.これはGHCiの制約であり,詳しくは Trac #12356 を参照せよ. Note
静的ポインタテーブル内で静的ポインタの位置を突き止めるために使われるキーの集合は,異なるプログラムバイナリ間の安定性を持たない.言い換えると,同一のキーの集合が用いられることが保証されるのは同一のプログラムバイナリから起動されたプロセスのみである.
静的ポインタのstatic semantics
直感的に説明するなら,閉じた式
code:pointer3.hs
e :: forall a_1 ... a_n . t
があるとき、静的形式の型は
code:pointer4.hs
static e :: (IsStatic p, Typeable a_1, ... , Typeable a_n) => p t
である.
静的形式はStaticPtr t型を持つ値を決定するが,OverloadedLists や OverloadedStrings と同様,このリテラル表現はオーバーロードされており,そのためIsStatic classによって暗黙的に StaticPtrを他の型へと持ち上げられるようになっている.
code:point5.hs
class IsStatic p where
fromStaticPtr :: StaticPtr a -> p a
唯一の事前に定義されたインスタンスは,何もしない自明なインスタンスである.
code:point6.hs
instance IsStatic StaticPtr where
fromStaticPtr sptr = sptr
さらに,型tはTypeableのインスタンスを持つ必要があるという制約がある.ゆえに,以下のコードは違法である:
code:point7.hs
static show -- No Typeable instance for (Show a => a -> String)
static Control.Monad.ST.runST -- No Typeable instance for ((forall s. ST s a) -> a)
とはいえ,ラッパーデータ型の適切な使用をもってすれば,以上の制約は一切の一般性の喪失を引き起こさない:
code:point8.hs
{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE StaticPointers #-} import Control.Monad.ST
import Data.Typeable
import GHC.StaticPtr
data Dict c = c => Dict
g1 :: Typeable a => StaticPtr (Dict (Show a) -> a -> String)
g1 = static (\Dict -> show)
data Rank2Wrapper f = R2W (forall s. f s)
deriving Typeable
newtype Flip f a s = Flip { unFlip :: f s a }
deriving Typeable
g2 :: Typeable a => StaticPtr (Rank2Wrapper (Flip ST a) -> a)
g2 = static (\(R2W f) -> runST (unFlip f))