Có phải tất cả các container có kích thước cố định functor mạnh, và / hoặc ngược lại?


9

Kiểu chữ Applicativeđại diện cho functor đơn hình lỏng lẻo bảo tồn cấu trúc đơn hình cartesian trên danh mục các hàm được gõ.

Nói cách khác, được đưa ra các đẳng cấu chính tắc chứng kiến (,)tạo thành một cấu trúc đơn hình:

-- Implementations left to the motivated reader
assoc_fwd :: ((a, b), c) -> (a, (b, c))
assoc_bwd :: (a, (b, c)) -> ((a, b), c)

lunit_fwd :: ((), a) -> a
lunit_bwd :: a -> ((), a)

runit_fwd :: (a, ()) -> a
runit_bwd :: a -> (a, ())

Kiểu chữ và luật của nó có thể được viết tương tự như thế này:

class Functor f => Applicative f
  where
  zip :: (f a, f b) -> f (a, b)
  husk :: () -> f ()

-- Laws:

-- assoc_fwd >>> bimap id zip >>> zip
-- =
-- bimap zip id >>> zip >>> fmap assoc_fwd

-- lunit_fwd
-- =
-- bimap husk id >>> zip >>> fmap lunit_fwd

-- runit_fwd
-- =
-- bimap id husk >>> zip >>> fmap runit_fwd

Người ta có thể tự hỏi một functor là oplax đơn hình đối với cùng cấu trúc có thể trông như thế nào:

class Functor f => OpApplicative f
  where
  unzip :: f (a, b) -> (f a, f b)
  unhusk :: f () -> ()

-- Laws:

-- assoc_bwd <<< bimap id unzip <<< unzip
-- =
-- bimap unzip id <<< unzip <<< fmap assoc_bwd

-- lunit_bwd
-- =
-- bimap unhusk id <<< unzip <<< fmap lunit_bwd

-- runit_bwd
-- =
-- bimap id unhusk <<< unzip <<< fmap runit_bwd

Nếu chúng ta nghĩ về các loại liên quan đến các định nghĩa và luật, sự thật đáng thất vọng được tiết lộ; OpApplicativekhông có ràng buộc cụ thể hơn Functor:

instance Functor f => OpApplicative f
  where
  unzip fab = (fst <$> fab, snd <$> fab)
  unhusk = const ()

Tuy nhiên, trong khi mọi Applicativefunctor (thực sự, bất kỳ Functor) là tầm thường OpApplicative, không nhất thiết phải có một mối quan hệ tốt đẹp giữa sự Applicativelỏng lẻo và sự OpApplicativemờ nhạt. Vì vậy, chúng ta có thể tìm kiếm các functor đơn hình mạnh mẽ trong cấu trúc đơn hình cartesian:

class (Applicative f, OpApplicative f) => StrongApplicative f

-- Laws:
-- unhusk . husk = id
-- husk . unhusk = id
-- zip . unzip = id
-- unzip . zip = id

Luật đầu tiên ở trên là tầm thường, vì cư dân duy nhất của loại () -> ()này là chức năng nhận dạng trên ().

Tuy nhiên, ba định luật còn lại, và do đó, chính lớp con, không tầm thường. Cụ thể, không phải mọi Applicativetrường hợp là một trường hợp hợp pháp của lớp này.

Dưới đây là một số Applicativechức năng mà chúng tôi có thể tuyên bố các trường hợp hợp pháp về StrongApplicative:

  • Identity
  • VoidF
  • (->) r
  • Monoid m => (,) m (xem câu trả lời)
  • Vec (n :: Nat)
  • Stream (vô hạn)

Và đây là một số Applicatives mà chúng ta không thể:

  • []
  • Either e
  • Maybe
  • NonEmptyList

Mẫu ở đây gợi ý rằng StrongApplicativelớp theo nghĩa là FixedSizelớp, trong đó "kích thước cố định" * có nghĩa là bội số ** của cư dân atrong một cư dân f alà cố định.

Điều này có thể được nêu là hai phỏng đoán:

  • Mỗi Applicativeđại diện cho một thùng chứa "kích thước cố định" của các phần tử của đối số loại của nó là một thể hiện củaStrongApplicative
  • Không có trường hợp StrongApplicativetồn tại trong đó số lần xuất hiện acó thể thay đổi

Bất cứ ai cũng có thể nghĩ về các phản mẫu mà bác bỏ những phỏng đoán này, hoặc một số lý luận thuyết phục chứng minh tại sao chúng là đúng hay sai?


* Tôi nhận ra rằng tôi đã không xác định đúng tính từ "kích thước cố định". Thật không may, nhiệm vụ là một chút thông tư. Tôi không biết bất kỳ mô tả chính thức nào về một container "kích thước cố định" và tôi đang cố gắng đưa ra một mô tả. StrongApplicativelà nỗ lực tốt nhất của tôi cho đến nay.

Tuy nhiên, để đánh giá xem đây có phải là một định nghĩa tốt hay không, tôi cần một cái gì đó để so sánh nó với. Đưa ra một số định nghĩa chính thức / không chính thức về ý nghĩa của một functor có kích thước hoặc bội số cho trước đối với các cư dân của đối số kiểu của nó, câu hỏi đặt ra là liệu sự tồn tại của một StrongApplicativethể hiện có phân biệt chính xác các hàm tử có kích thước cố định và khác nhau hay không.

Không nhận thức được một định nghĩa chính thức hiện có, tôi đang kêu gọi trực giác trong việc sử dụng thuật ngữ "kích thước cố định" của tôi. Tuy nhiên, nếu ai đó đã biết về một chủ nghĩa hình thức hiện có cho kích thước của một functor và có thể so sánh StrongApplicativevới nó, thì càng nhiều càng tốt.

** Bằng "bội số" Tôi đang đề cập một cách lỏng lẻo đến "có bao nhiêu" phần tử tùy ý của loại tham số của functor xảy ra trong một cư dân của loại tên miền của functor. Điều này không liên quan đến loại cụ thể mà functor được áp dụng, và do đó không liên quan đến bất kỳ cư dân cụ thể nào của loại tham số.

Không chính xác về điều này đã gây ra một số nhầm lẫn trong các bình luận, vì vậy đây là một số ví dụ về những gì tôi sẽ xem xét kích thước / bội số của các functor khác nhau là:

  • VoidF: đã sửa, 0
  • Identity: đã sửa, 1
  • Maybe: biến, tối thiểu 0, tối đa 1
  • []: biến, tối thiểu 0, vô hạn tối đa
  • NonEmptyList: biến, tối thiểu 1, vô hạn tối đa
  • Stream: cố định, vô hạn
  • Monoid m => (,) m: đã sửa, 1
  • data Pair a = Pair a a: cố định, 2
  • Either x: biến, tối thiểu 0, tối đa 1
  • data Strange a = L a | R a: đã sửa, 1

Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Samuel Liew

Một định nghĩa có thể có của "kích thước cố định" sẽ là "đại diện". Tất cả các functor có thể đại diện là những ứng dụng mạnh mẽ theo nghĩa được mô tả ở đây, bởi vì (->) rvà chúng là đẳng cấu theo đúng cách đó.
Daniel Wagner

@DanielWagner Tôi nghĩ rằng bạn cần nhiều hơn là một sự đồng hình hóa để kế thừa ứng dụng mạnh mẽ từ (->) r; bạn cần các thành phần của đẳng cấu để bảo toàn cấu trúc ứng dụng mạnh mẽ. Vì một lý do nào đó, kiểu chữ Representabletrong Haskell có một tabulate . return = returnđịnh luật bí ẩn (điều này thậm chí không thực sự có ý nghĩa đối với những người cai ngục không đơn điệu), nhưng nó mang lại cho chúng ta 1/4 điều kiện chúng ta cần nói tabulateziplà những hình thái của một loại đơn sắc phù hợp . 3 cái còn lại là luật bổ sung mà bạn phải yêu cầu.
Asad Saeeduddin

Xin lỗi, đó phải là " tabulateindexlà những hình thái của một thể loại phù hợp ..."
Asad Saeeduddin

@AsadSaeeduddin Cách mà luật được nêu trong các tài liệu là cụ thể kỳ lạ, có lẽ, nhưng hóa ra yêu cầu returnkhông phải là một vấn đề nghiêm trọng. cotraverse getConst . Constlà một triển khai mặc định cho return/ purevề mặt Distributive, và, vì các phân phối / đại diện có hình dạng cố định, nên việc triển khai đó là duy nhất.
song lập

Câu trả lời:


4
  • Mỗi Applicativeđại diện cho một thùng chứa "kích thước cố định" của các phần tử của đối số loại của nó là một thể hiện củaStrongApplicative
  • Không có trường hợp StrongApplicativetồn tại trong đó số lần xuất hiện acó thể thay đổi

Bất cứ ai cũng có thể nghĩ về các phản mẫu mà bác bỏ những phỏng đoán này, hoặc một số lý luận thuyết phục chứng minh tại sao chúng là đúng hay sai?

Tôi không chắc chắn về phỏng đoán đầu tiên đó và dựa trên các cuộc thảo luận với @AsadSaeeduddin có vẻ khó chứng minh, nhưng phỏng đoán thứ hai là đúng. Để xem tại sao, hãy xem xét StrongApplicativeluật pháp husk . unhusk == id; đó là, cho tất cả x :: f (), husk (unhusk x) == x. Nhưng trong Haskell, unhusk == const ()vì vậy luật đó tương đương với việc nói cho tất cả x :: f (), husk () == x. Nhưng điều này ngụ ý rằng chỉ có thể tồn tại một giá trị riêng biệt của loại f (): nếu có hai giá trị x, y :: f (), thì x == husk ()husk () == ynhư vậy x == y. Nhưng nếu chỉ có một f ()giá trị có thể , thì fphải có hình dạng cố định. (ví dụ: data Pair a = Pair a achỉ có một giá trị của loại Pair (), hiện tại Pair () (), nhưng có nhiều giá trị của loại Maybe ()hoặc [()].) Do đóhusk . unhusk == idngụ ý rằng fphải có hình dạng cố định.


Hừm. Có thực sự rõ ràng rằng "chỉ có thể tồn tại một giá trị riêng biệt của loại f ()" ngụ ý "số lần xuất hiện akhông thể thay đổi" với sự hiện diện của các GADT và công cụ ưa thích?
Daniel Wagner

@DanielWagner Hóa ra, số lần xuất hiện akhông thể thay đổi khác nhau không phải là điều kiện đủ cho một StrongApplicativeví dụ; ví dụ, data Writer w a = Writer (w,a)có bội số không thay đổi của a, nhưng không phải là a StrongApplicative. Bạn thực sự cần hình dạng của functor là bất biến, mà tôi tin rằng đó là hậu quả của f ()việc là một người độc thân.
bradrn

Tôi không chắc là tôi thấy nó liên quan như thế nào. Trong câu trả lời, trong khi xác nhận phỏng đoán thứ hai, bạn lập luận rằng "ứng dụng mạnh" ngụ ý "một f ()" khác biệt "ngụ ý" số lần xuất hiện akhông thể thay đổi ". Tôi phản đối rằng bước cuối cùng của lập luận đó không rõ ràng đúng; ví dụ như xem xét data Weird a where One :: a -> Weird a; None :: Weird Bool. Có một giá trị riêng biệt của loại Weird (), nhưng các hàm tạo khác nhau có số lượng as khác nhau bên trong. (Đây không phải là một ví dụ đầy đủ ở đây vì Functorkhó, nhưng làm thế nào để chúng ta biết rằng không thể sửa được?)
Daniel Wagner

@DanielWagner Điểm hay đó Weird ()là một singleton nhưng nó không có hình dạng cố định. Nhưng Weirdkhông phải là một Functor, vì vậy nó không thể là một StrongApplicativeanyway. Tôi cho rằng phỏng đoán có liên quan sẽ là: nếu flà một Functor, liệu có f ()phải là một đơn có nghĩa flà có hình dạng cố định không? Tôi hoàn toàn nghi ngờ điều này là đúng, nhưng như bạn đã lưu ý tôi thực sự chưa có bằng chứng nào.
bradrn

5

Chúng tôi có thể trả lời ít nhất một trong những câu hỏi này theo cách phủ định:

Mỗi ứng dụng đại diện cho một thùng chứa "kích thước cố định" của các phần tử của đối số loại của nó là một thể hiện của StrongApplicative

Trong thực tế, một trong những ví dụ về một luật hợp pháp StrongApplicativetrong câu hỏi ban đầu là sai. Các nhà văn ứng dụng Monoid => (,) mlà không StrongApplicative, bởi vì ví dụ husk $ unhusk $ ("foo", ()) == ("", ()) /= ("foo", ()).

Tương tự, ví dụ về một container có kích thước cố định:

data Strange a = L a | R a

của bội số cố định 1, không phải là một ứng dụng mạnh, bởi vì nếu chúng ta xác định husk = Leftthì husk $ unhusk $ Right () /= Right ()ngược lại. Một cách tương đương để xem điều này là đây chỉ là nhà văn áp dụng cho sự lựa chọn của bạn về monoid trên Bool.

Vì vậy, có tồn tại ứng dụng "kích thước cố định" mà không StrongApplicative. Cho dù tất cả StrongApplicatives có kích thước cố định vẫn được nhìn thấy.


5

Chúng ta hãy xem functor đại diện như định nghĩa của chúng tôi về "container kích thước cố định":

class Representable f where
    type Rep f
    tabulate :: (Rep f -> a) -> f a
    index :: f a -> Rep f -> a

Thực tế Representablecó một vài luật và siêu lớp, nhưng với mục đích của câu trả lời này, chúng ta thực sự chỉ cần hai thuộc tính:

tabulate . index = id
index . tabulate = id

(Được rồi, chúng tôi cũng cần tuân thủ luật pháp instance StrongApplicative ((->) r). Dễ dàng, bạn đã đồng ý nó tồn tại.)

Nếu chúng ta lấy định nghĩa đó, thì tôi có thể xác nhận phỏng đoán đó 1:

Mỗi Applicativeđại diện cho một thùng chứa "kích thước cố định" của các phần tử của đối số loại của nó là một trường hợp [tuân thủ luật]StrongApplicative

là đúng. Đây là cách thực hiện:

instance Representable f => Applicative f where
    zip (fa, fb) = tabulate (zip (index fa, index fb))
    husk = tabulate . husk

instance Representable f => OpApplicative f where
    unzip fab = let (fa, fb) = unzip (index fab) in (tabulate fa, tabulate fb)
    unhusk = unhusk . index

instance Representable f => StrongApplicative f

Có rất nhiều luật để chứng minh, nhưng tôi sẽ chỉ tập trung vào Big Four mà StrongApplicativethêm vào - bạn có thể đã tin những người dẫn đầu ApplicativeOpApplicative, nhưng nếu bạn không, bằng chứng của họ trông giống như những điều dưới đây ( mà lần lượt trông khá giống nhau). Để rõ ràng, tôi sẽ sử dụng zipf, huskfvv cho các trường hợp chức năng, và zipr, huskrvv cho các trường hợp biểu diễn, vì vậy bạn có thể theo dõi được mà. (Và để dễ dàng xác minh rằng chúng tôi không lấy điều chúng tôi đang cố chứng minh là giả định! Sử dụng unhuskf . huskf = idkhi chứng minh là ổn unhuskr . huskr = id, nhưng sẽ là sai khi giả sử unhuskr . huskr = idtrong cùng một bằng chứng.)

Bằng chứng của mỗi luật tiến hành về cơ bản theo cùng một cách: hủy bỏ các định nghĩa, loại bỏ sự đẳng cấu Representablemang lại cho bạn, sau đó sử dụng luật tương tự cho các hàm.

unhuskr . huskr
= { def. of unhuskr and huskr }
(unhuskf . index) . (tabulate . huskf)
= { index . tabulate = id }
unhuskf . huskf
= { unhuskf . huskf = id }
id

huskr . unhuskr
= { def. of huskr and unhuskr }
(tabulate . huskf) . (unhuskf . index)
= { huskf . unhuskf = id }
tabulate . index
= { tabulate . index = id }
id

zipr (unzipr fab)
= { def. of unzipr }
zipr (let (fa, fb) = unzipf (index fab) in (tabulate fa, tabulate fb))
= { def. of zipr }
let (fa, fb) = unzipf (index fab) in tabulate (zipf (index (tabulate fa), index (tabulate fb)))
= { index . tabulate = id }
let (fa, fb) = unzipf (index fab) in tabulate (zipf (fa, fb))
= { def. of (fa, fb) }
tabulate (zipf (unzipf (index fab)))
= { zipf . unzipf = id }
tabulate (index fab)
= { tabulate . index = id }
fab

unzipr (zipr (fa, fb))
= { def. of zipr }
unzipr (tabulate (zipf (index fa, index fb)))
= { def. of unzipr }
let (fa', fb') = unzipf (index (tabulate (zipf (index fa, index fb))))
in (tabulate fa', tabulate fb')
= { index . tabulate = id }
let (fa', fb') = unzipf (zipf (index fa, index fb))
in (tabulate fa', tabulate fb')
= { unzipf . zipf = id }
let (fa', fb') = (index fa, index fb)
in (tabulate fa', tabulate fb')
= { def. of fa' and fb' }
(tabulate (index fa), tabulate (index fb))
= { tabulate . index = id }
(fa, fb)

Hiện đang cân nhắc : instance StrongApplicative f => Representable f where type Rep f = forall x. f x -> x. indexdễ. Tôi chưa tìm ra mánh khóe tabulatenào, nhưng nó dường như rất gần gũi.
Daniel Wagner

Trong cuộc thảo luận với @AsadSaeeduddin, tôi cũng đã tìm được StrongApplicativetrường hợp tương tự , nhưng không thể chứng minh luật. Xin chúc mừng vì đã tìm ra nó! Tôi đã cố gắng làm Representableví dụ như được đưa ra StrongApplicative, nhưng không thể nghĩ ra một Reploại tốt - tôi tò mò muốn biết, làm thế nào để bạn forall x. f x -> xđạt được điều này?
bradrn

@bradrn Hãy nhớ lại giả thuyết rằng những thứ này có một bộ "lỗ" cố định vào các phần tử nào. Sau đó, các chức năng của loại forall x. f x -> xchính xác là các chức năng chọn một lỗ và trả về giá trị trong lỗ đó. (Và, trong khi suy nghĩ về cách thực hiện tabulate, tôi đã đưa ra một sự phản đối đối với loại cho unhusk; xem bình luận về chính câu hỏi để biết chi tiết.)
Daniel Wagner

Cảm ơn @DanielWagner! Đó là một cách tiếp cận thực sự thông minh - tôi sẽ không nghĩ về điều đó.
bradrn

Sau khi cố gắng thực hiện nó, tôi không nghĩ rằng tôi tin rằng nó forall x. f x -> xsẽ hoạt động như vậy Rep. Lý do của tôi là, bằng cách sử dụng này Rep, bạn có thể viết indexcho bất kỳ loại nào , không chỉ các StrongApplicativeloại - vì vậy tôi nghi ngờ rằng nó forall x. f x -> xcó thể quá chung chung.
bradrn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.