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:
IdentityVoidF(->) r(xem câu trả lời)Monoid m => (,) mVec (n :: Nat)Stream(vô hạn)
Và đây là một số Applicatives mà chúng ta không thể:
[]Either eMaybeNonEmptyList
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ệnacó 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, 0Identity: đã sửa, 1Maybe: biến, tối thiểu 0, tối đa 1[]: biến, tối thiểu 0, vô hạn tối đaNonEmptyList: biến, tối thiểu 1, vô hạn tối đaStream: cố định, vô hạnMonoid m => (,) m: đã sửa, 1data Pair a = Pair a a: cố định, 2Either x: biến, tối thiểu 0, tối đa 1data Strange a = L a | R a: đã sửa, 1
(->) rvà chúng là đẳng cấu theo đúng cách đó.
(->) 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 tabulatevà ziplà 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.
tabulatevà indexlà những hình thái của một thể loại phù hợp ..."
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.