自由変数によってパラメーター化されたラムダ項のこの表現を検討してください。(Bellegarde and Hook 1994、Bird and Paterson 1999、Altenkirch and Reus 1999の論文を参照してください。)
data Tm a = Var a
| Tm a :$ Tm a
| Lam (Tm (Maybe a))
確かにこれをa Functor
にして、名前変更Monad
の概念を取り込んだり、置換の概念を取り込んだりできます。
instance Functor Tm where
fmap rho (Var a) = Var (rho a)
fmap rho (f :$ s) = fmap rho f :$ fmap rho s
fmap rho (Lam t) = Lam (fmap (fmap rho) t)
instance Monad Tm where
return = Var
Var a >>= sig = sig a
(f :$ s) >>= sig = (f >>= sig) :$ (s >>= sig)
Lam t >>= sig = Lam (t >>= maybe (Var Nothing) (fmap Just . sig))
次に、閉じた条件を考えます。これらはの住民ですTm Void
。閉じた条件を任意の自由変数を持つ条件に埋め込むことができるはずです。どうやって?
fmap absurd :: Tm Void -> Tm a
もちろん、問題は、この関数が正確に何もしないという用語をトラバースすることです。しかし、それはより正直な感じですunsafeCoerce
。そしてそれvacuous
がに追加された理由ですData.Void
です...
または、エバリュエーターを作成します。以下は、の自由変数の値ですb
。
data Val b
= b :$$ [Val b] -- a stuck application
| forall a. LV (a -> Val b) (Tm (Maybe a)) -- we have an incomplete environment
ラムダをクロージャーとして表現したところです。エバリュエーターは、自由変数をa
を超える値にマッピングする環境によってパラメーター化されますb
。
eval :: (a -> Val b) -> Tm a -> Val b
eval g (Var a) = g a
eval g (f :$ s) = eval g f $$ eval g s where
(b :$$ vs) $$ v = b :$$ (vs ++ [v]) -- stuck application gets longer
LV g t $$ v = eval (maybe v g) t -- an applied lambda gets unstuck
eval g (Lam t) = LV g t
当たってるよ。任意のターゲットでクローズドタームを評価するには
eval absurd :: Tm Void -> Val b
より一般的にVoid
は、単独で使用されることはほとんどありませんが、ある種の不可能なことを示す方法で型パラメーターをインスタンス化する場合に便利です(たとえば、ここでは、閉じた条件で自由変数を使用します)。多くの場合、これらのパラメーター化された型には、パラメーターの操作を型全体の操作に持ち上げる高次関数が付属しています(たとえば、ここでfmap
は>>=
、、、eval
)。したがってabsurd
、の汎用操作として渡されますVoid
。
別の例として、使用して想像Either e v
できればあなたを与える計算キャプチャするv
が、種類の例外を発生させるかもしれませんがe
。このアプローチを使用して、不正な動作のリスクを均一に文書化できます。この設定で完全に適切に動作する計算を行うe
にはVoid
、次のようにしてから使用します
either absurd id :: Either Void v -> v
安全に実行するか
either absurd Right :: Either Void v -> Either e v
安全でないコンポーネントを安全でない世界に埋め込む。
ああ、そして最後にもう1つ、「起こり得ないこと」を扱います。これは、カーソルが配置できないすべての場所で、一般的なジッパー構造に表示されます。
class Differentiable f where
type D f :: * -> * -- an f with a hole
plug :: (D f x, x) -> f x -- plugging a child in the hole
newtype K a x = K a -- no children, just a label
newtype I x = I x -- one child
data (f :+: g) x = L (f x) -- choice
| R (g x)
data (f :*: g) x = f x :&: g x -- pairing
instance Differentiable (K a) where
type D (K a) = K Void -- no children, so no way to make a hole
plug (K v, x) = absurd v -- can't reinvent the label, so deny the hole!
完全に関連性はありませんが、残りを削除しないことにしました。
instance Differentiable I where
type D I = K ()
plug (K (), x) = I x
instance (Differentiable f, Differentiable g) => Differentiable (f :+: g) where
type D (f :+: g) = D f :+: D g
plug (L df, x) = L (plug (df, x))
plug (R dg, x) = R (plug (dg, x))
instance (Differentiable f, Differentiable g) => Differentiable (f :*: g) where
type D (f :*: g) = (D f :*: g) :+: (f :*: D g)
plug (L (df :&: g), x) = plug (df, x) :&: g
plug (R (f :&: dg), x) = f :&: plug (dg, x)
実際には、多分それは関連しています。冒険したい場合は、この未完成の記事Void
で、自由変数を使用して用語の表現を圧縮する方法を示します。
data Term f x = Var x | Con (f (Term f x)) -- the Free monad, yet again
Differentiable
およびTraversable
ファンクタから自由に生成された構文でf
。我々が使用しTerm f Void
ない自由変数と領域を表すために、そして[D f (Term f Void)]
表現するために管を単離し自由変数に、または二つ以上の自由変数へのパスにおける接合のいずれかにない自由変数と領域を介してトンネリング。いつかその記事を終えなければならない。
値のない型(または、少なくとも、丁寧な会社で話す価値がない型)の場合、Void
非常に役立ちます。そしてそれabsurd
はあなたがそれを使う方法です。
absurd
、Cont
モナドを扱うこの記事で関数が使用されたことを示しています:haskellforall.com/2012/12/the-continuation-monad.html