Chúng có phải là đẳng cấu không?
Vâng, chúng là đẳng cấu trong Haskell. Xem sự khác biệt giữa gói Fix, Mu và Nu trong gói chương trình đệ quy của Ed Kmett để biết thêm một số nhận xét.
Nếu vậy, làm thế nào để bạn chứng minh điều đó?
Hãy bắt đầu bằng cách xác định các chức năng để thực hiện chuyển đổi:
muToFix :: Mu f -> Fix f
muToFix (Mu s) = s Fix
fixToMu :: Functor f => Fix f -> Mu f
fixToMu t = Mu (\alg -> cata alg t)
Để hiển thị các hàm đó chứng kiến một đẳng cấu, chúng ta phải chỉ ra rằng:
muToFix . fixToMu = id
fixToMu . muToFix = id
Từ Fix
và trở lại
Một trong những hướng của sự đẳng cấu đi ra có phần đơn giản hơn so với hướng khác:
muToFix (fixToMu t) = t
muToFix (fixToMu t) -- LHS
muToFix (Mu (\f -> cata f t))
(\f -> cata f t) Fix
cata Fix t -- See below.
t -- LHS = RHS
Đoạn cuối cùng ở trên, cata Fix t = t
có thể được xác minh thông qua định nghĩa cata
:
cata :: Functor f => (f a -> a) -> Fix f -> a
cata alg = alg . fmap (cata alg) . unfix
cata Fix t
, sau đó, là Fix (fmap (cata Fix) (unfix t))
. Chúng ta có thể sử dụng cảm ứng để chỉ ra nó t
, ít nhất là đối với một hữu hạn t
(nó trở nên tinh tế hơn với các cấu trúc vô hạn - xem phần phụ lục ở cuối câu trả lời này). Có hai khả năng để xem xét:
unfix t :: f (Fix f)
trống rỗng, không có vị trí đệ quy để đào sâu vào. Trong trường hợp đó, nó phải bằng fmap absurd z
với một số z :: f Void
, và do đó:
cata Fix t
Fix (fmap (cata Fix) (unfix t))
Fix (fmap (cata Fix) (fmap absurd z))
Fix (fmap (cata Fix . absurd) z)
-- fmap doesn't do anything on an empty structure.
Fix (fmap absurd z)
Fix (unfix t)
t
unfix t
không có sản phẩm nào. Trong trường hợp đó, ít nhất chúng tôi biết rằng fmap (cata Fix)
không thể làm gì ngoài việc áp dụng cata Fix
cho các vị trí đệ quy. Giả thuyết cảm ứng ở đây là làm như vậy sẽ khiến những vị trí đó không thay đổi. Chúng tôi sau đó có:
cata Fix t
Fix (fmap (cata Fix) (unfix t))
Fix (unfix t) -- Induction hypothesis.
t
(Cuối cùng, cata Fix = id
là một hệ quả tất yếu của Fix :: f (Fix f) -> Fix x
việc ban đầu F-đại số. Resorting trực tiếp đến thực tế rằng trong bối cảnh chứng minh điều này có lẽ sẽ là quá nhiều của một phím tắt.)
Từ Mu
và trở lại
Đưa ra muToFix . fixToMu = id
, để chứng minh rằng fixToMu . muToFix = id
nó đủ để chứng minh:
đó muToFix
là tiêm, hoặc
đó fixToMu
là tính từ.
Hãy chọn tùy chọn thứ hai và xem xét các định nghĩa liên quan:
newtype Mu f = Mu (forall a. (f a -> a) -> a)
fixToMu :: Functor f => Fix f -> Mu f
fixToMu t = Mu (\alg -> cata alg t)
fixToMu
sau đó là tính từ, có nghĩa là, với bất kỳ cụ thể nào Functor
f
, tất cả các chức năng của loại forall a. (f a -> a) -> a
có thể được định nghĩa là \alg -> cata alg t
, đối với một số cụ thể t :: Fix f
. Sau đó, tác vụ sẽ trở thành mục lục các forall a. (f a -> a) -> a
chức năng và xem liệu tất cả chúng có thể được thể hiện dưới dạng đó hay không.
Làm thế nào chúng ta có thể định nghĩa một forall a. (f a -> a) -> a
chức năng mà không cần dựa vào fixToMu
? Không có vấn đề gì, nó phải liên quan đến việc sử dụng f a -> a
đại số được cung cấp làm đối số để có a
kết quả. Các tuyến đường trực tiếp sẽ được áp dụng nó cho một số f a
giá trị. Một cảnh báo chính là, vì a
là đa hình, chúng ta phải có khả năng gợi ra f a
giá trị nói cho bất kỳ sự lựa chọn nào a
. Đó là một chiến lược khả thi miễn là giá f
trị tồn tại. Trong trường hợp đó, chúng ta có thể làm:
fromEmpty :: Functor f => f Void -> forall a. (f a -> a) -> a
fromEmpty z = \alg -> alg (fmap absurd z)
Để làm cho ký hiệu rõ ràng hơn, hãy xác định một loại cho những thứ chúng ta có thể sử dụng để xác định các forall a. (f a -> a) -> a
hàm:
data Moo f = Empty (f Void)
fromMoo :: Functor f => Moo f -> forall a. (f a -> a) -> a
fromMoo (Empty z) = \alg -> alg (fmap absurd z)
Bên cạnh tuyến đường trực tiếp, chỉ có một khả năng khác. Cho rằng đó f
là một Functor
, nếu bằng cách nào đó chúng ta có một f (Moo f)
giá trị, chúng ta có thể áp dụng đại số hai lần, ứng dụng đầu tiên nằm dưới f
lớp ngoài , thông qua fmap
và fromMoo
:
fromLayered :: Functor f => f (Moo f) -> forall a. (f a -> a) -> a
fromLayered u = \alg -> alg (fmap (\moo -> fromMoo moo alg) u)
Xem xét rằng chúng ta cũng có thể tạo forall a. (f a -> a) -> a
ra các f (Moo f)
giá trị, thật hợp lý khi thêm chúng vào như một trường hợp Moo
:
data Moo f = Empty (f Void) | Layered (f (Moo f))
Theo đó, fromLayered
có thể được kết hợp để fromMoo
:
fromMoo :: Functor f => Moo f -> forall a. (f a -> a) -> a
fromMoo = \case
Empty z -> \alg -> alg (fmap absurd z)
Layered u -> \alg -> alg (fmap (\moo -> fromMoo moo alg) u)
Lưu ý rằng, bằng cách làm như vậy, chúng tôi đã lén lút chuyển từ áp dụng alg
dưới một f
lớp sang áp dụng đệ quy alg
dưới một số f
lớp tùy ý .
Tiếp theo, chúng ta có thể lưu ý một f Void
giá trị có thể được đưa vào hàm Layered
tạo:
emptyLayered :: Functor f => f Void -> Moo f
emptyLayered z = Layered (fmap absurd z)
Điều đó có nghĩa là chúng ta không thực sự cần nhà Empty
xây dựng:
newtype Moo f = Moo (f (Moo f))
unMoo :: Moo f -> f (Moo f)
unMoo (Moo u) = u
Còn Empty
trường hợp trong fromMoo
thì sao? Sự khác biệt duy nhất giữa hai trường hợp là, trong Empty
trường hợp, chúng ta có absurd
thay vì \moo -> fromMoo moo alg
. Vì tất cả các Void -> a
chức năng là absurd
, chúng tôi không cần một Empty
trường hợp riêng ở đó:
fromMoo :: Functor f => Moo f -> forall a. (f a -> a) -> a
fromMoo (Moo u) = \alg -> alg (fmap (\moo -> fromMoo moo alg) u)
Một tinh chỉnh mỹ phẩm có thể là lật các fromMoo
đối số, do đó chúng ta không cần phải viết đối số thành fmap
lambda:
foldMoo :: Functor f => (f a -> a) -> Moo f -> a
foldMoo alg (Moo u) = alg (fmap (foldMoo alg) u)
Hoặc, nhiều điểm hơn:
foldMoo :: Functor f => (f a -> a) -> Moo f -> a
foldMoo alg = alg . fmap (foldMoo alg) . unMoo
Tại thời điểm này, cái nhìn thứ hai về các định nghĩa của chúng tôi cho thấy một số đổi tên theo thứ tự:
newtype Fix f = Fix (f (Fix f))
unfix :: Fix f -> f (Fix f)
unfix (Fix u) = u
cata :: Functor f => (f a -> a) -> Fix f -> a
cata alg = alg . fmap (cata alg) . unfix
fromFix :: Functor f => Fix f -> forall a. (f a -> a) -> a
fromFix t = \alg -> cata alg t
Và đó là: tất cả các forall a. (f a -> a) -> a
chức năng có hình thức \alg -> cata alg t
cho một số t :: Fix f
. Do đó, fixToMu
là tính từ, và chúng ta có sự đẳng cấu mong muốn.
Phụ lục
Trong các ý kiến, một câu hỏi phàm tục được đưa ra về khả năng áp dụng của đối số cảm ứng trong cata Fix t = t
đạo hàm. Tối thiểu, luật functor và tham số đảm bảo rằng fmap (cata Fix)
sẽ không tạo thêm công việc (ví dụ: nó sẽ không phóng to cấu trúc hoặc giới thiệu các vị trí đệ quy bổ sung để đào sâu vào), điều này chứng minh tại sao bước vào các vị trí đệ quy là tất cả các vấn đề trong bước quy nạp của đạo hàm. Điều đó là như vậy, nếu t
là một cấu trúc hữu hạn, trường hợp cơ bản của một sản phẩm trống f (Fix t)
cuối cùng sẽ đạt được, và tất cả đều rõ ràng. t
Tuy nhiên, nếu chúng ta cho phép là vô hạn, chúng ta có thể tiếp tục giảm dần vô tận, fmap
sau fmap
đó fmap
, mà không bao giờ đạt đến trường hợp cơ sở.
Tuy nhiên, tình huống với các cấu trúc vô hạn không phải là khủng khiếp như lúc đầu. Sự lười biếng, đó là điều làm cho các cấu trúc vô hạn trở nên khả thi ngay từ đầu, cho phép chúng ta tiêu thụ các cấu trúc vô hạn một cách lười biếng:
GHCi> :info ListF
data ListF a b = Nil | Cons a b
-- etc.
GHCi> ones = Fix (Cons 1 ones)
GHCi> (\(Fix (Cons a _)) -> a) (cata Fix ones)
1
GHCi> (\(Fix (Cons _ (Fix (Cons a _)))) -> a) (cata Fix ones)
1
Mặc dù sự thành công của các vị trí đệ quy kéo dài vô tận, chúng ta có thể dừng lại ở bất kỳ điểm nào và nhận được kết quả hữu ích từ ListF
bối cảnh functorial xung quanh . Các bối cảnh như vậy, nó lặp đi lặp lại, không bị ảnh hưởng fmap
, và vì vậy bất kỳ phân đoạn hữu hạn nào của cấu trúc chúng ta có thể tiêu thụ sẽ không bị ảnh hưởng cata Fix
.
Sự lười biếng này phản ánh làm thế nào, như đã đề cập ở nơi khác trong cuộc thảo luận này, sự lười biếng làm sụp đổ sự phân biệt giữa các điểm cố định Mu
, Fix
và Nu
. Không có sự lười biếng, Fix
không đủ để mã hóa corecursion sản xuất, và vì vậy chúng tôi phải chuyển sang Nu
, điểm cố định lớn nhất. Đây là một minh chứng nhỏ cho sự khác biệt:
GHCi> :set -XBangPatterns
GHCi> -- Like ListF, but strict in the recursive position.
GHCi> data SListF a b = SNil | SCons a !b deriving Functor
GHCi> ones = Nu (\() -> SCons 1 ()) ()
GHCi> (\(Nu c a) -> (\(SCons a _) -> a) (c a)) ones
1
GHCi> ones' = Fix (SCons 1 ones')
GHCi> (\(Fix (SCons a _)) -> a) ones'
^CInterrupted.