Hãy xem xét biểu diễn này cho các thuật ngữ lambda được tham số hóa bởi các biến tự do của chúng. (Xem các bài báo của Bellegarde và Hook 1994, Bird và Paterson 1999, Altenkirch và Reus 1999.)
data Tm a = Var a
| Tm a :$ Tm a
| Lam (Tm (Maybe a))
Bạn chắc chắn có thể biến điều này thành một Functor
, nắm bắt khái niệm đổi tên và Monad
nắm bắt khái niệm thay thế.
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))
Bây giờ hãy xem xét các điều khoản đã đóng : đây là những cư dân của Tm Void
. Bạn có thể nhúng các điều khoản đã đóng vào các điều khoản với các biến tự do tùy ý. Làm sao?
fmap absurd :: Tm Void -> Tm a
Tất nhiên, điểm bắt buộc là hàm này sẽ đi ngang qua thuật ngữ mà chính xác là không làm gì cả. Nhưng đó là một liên lạc trung thực hơn unsafeCoerce
. Và đó là lý do tại sao vacuous
được thêm vào Data.Void
...
Hoặc viết một người đánh giá. Đây là các giá trị với các biến miễn phí trong b
.
data Val b
= b :$$ [Val b] -- a stuck application
| forall a. LV (a -> Val b) (Tm (Maybe a)) -- we have an incomplete environment
Tôi vừa đại diện cho lambdas dưới dạng kết thúc. Bộ đánh giá được tham số hóa bởi một môi trường ánh xạ các biến tự do vào a
các giá trị hơn 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
Bạn đoán nó. Để đánh giá một kỳ hạn đã đóng ở bất kỳ mục tiêu nào
eval absurd :: Tm Void -> Val b
Nói chung, Void
hiếm khi được sử dụng riêng, nhưng rất tiện lợi khi bạn muốn khởi tạo một tham số kiểu theo cách chỉ ra một số loại bất khả thi (ví dụ: ở đây, sử dụng một biến tự do trong một thuật ngữ đóng). Thông thường các loại parametrized đi kèm với chức năng bậc cao nâng hoạt động trên các thông số để hoạt động trên toàn bộ loại (ví dụ, ở đây, fmap
, >>=
, eval
). Vì vậy, bạn chuyển absurd
như là hoạt động mục đích chung Void
.
Ví dụ khác, hãy tưởng tượng việc sử dụng Either e v
để nắm bắt các phép tính hy vọng cung cấp cho bạn v
nhưng có thể đưa ra một ngoại lệ về kiểu e
. Bạn có thể sử dụng phương pháp này để ghi nhận rủi ro hành vi xấu một cách thống nhất. Đối với một cách hoàn hảo cư xử rất tốt tính toán trong bối cảnh này, hãy e
trở thành Void
, sau đó sử dụng
either absurd id :: Either Void v -> v
để chạy một cách an toàn hoặc
either absurd Right :: Either Void v -> Either e v
để nhúng các thành phần an toàn vào một thế giới không an toàn.
Ồ, và một lần cuối cùng, xử lý "không thể xảy ra". Nó hiển thị trong cấu trúc dây kéo chung, ở mọi nơi mà con trỏ không thể có.
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!
Tôi quyết định không xóa phần còn lại, mặc dù nó không liên quan chính xác.
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)
Trên thực tế, có lẽ nó có liên quan. Nếu bạn đang cảm thấy mạo hiểm, bài viết chưa hoàn thành này chỉ ra cách sử dụng Void
để nén biểu diễn của các thuật ngữ với các biến tự do
data Term f x = Var x | Con (f (Term f x)) -- the Free monad, yet again
trong bất kỳ cú pháp nào được tạo tự do từ a Differentiable
và Traversable
functor f
. Chúng tôi sử dụng Term f Void
để đại diện cho các vùng không có biến tự do và [D f (Term f Void)]
để biểu diễn các ống chui qua các vùng không có biến tự do hoặc đến một biến tự do cô lập hoặc đến một điểm giao nhau trong đường dẫn đến hai hoặc nhiều biến tự do. Một lúc nào đó phải kết thúc bài báo đó.
Đối với một loại không có giá trị (hoặc ít nhất, không có giá trị nào đáng nói trong một công ty lịch sự), Void
rất hữu ích. Và absurd
là cách bạn sử dụng nó.
absurd
chức năng đã được sử dụng trong giao dịch bài viết này với cácCont
đơn nguyên: haskellforall.com/2012/12/the-continuation-monad.html